您的位置:首页 > 编程语言 > Java开发

JDK动态代理原理

2016-12-05 15:15 337 查看

静态代理

生活中代理的例子不胜枚举:

张三由于工作很忙,迟迟没有结婚,家里人很着急。他爸爸拿着他的照片帮他张罗着相亲,他爸爸就是他的一个代理。

- 定义接口

public interface People {
/**
* 找对象
*/
public void Zhaoduixiang();
}


张三

public class ZhangSan implements People{
public void Zhaoduixiang() {
System.out.println(this.getClass().getName()+":要求不多,人美胸大活好!");
}
}


张三他爸

public class ZhangSanFather implements People{
/**
* 被代理对象的引用,相当于他爸拿着张三的照片
*/
private ZhangSan zhangSan;

public ZhangSanFather(){

}
public ZhangSanFather(ZhangSan zhangSan){
this.zhangSan = zhangSan;
}

/**
* 代理
*/
public void Zhaoduixiang() {
before();
this.zhangSan.Zhaoduixiang();
after();
}

/**
*  想跟我儿子好,我先要审核下
*/
private void before(){
System.out.println(this.getClass().getName()+":要求人品好,家境好!");
}

/**
* 评价这个菇凉
*/
private void after(){
System.out.println(this.getClass().getName()+":这个菇凉阔以!");
}
}


测试

@Test
public void testStaticProxy(){
People zhangSanFather = new ZhangSanFather(new ZhangSan());
zhangSanFather.Zhaoduixiang();
}


proxy.staticProxy.ZhangSanFather:要求人品好,家境好!
proxy.staticProxy.ZhangSan:要求不多,人美胸大活好!
proxy.staticProxy.ZhangSanFather:这个菇凉阔以!


重要特点:

- 代理类持有被代理类的引用

动态代理

张三没结婚,请个保姆做饭,洗碗,张三自己只用张嘴吃饭就行。保姆就是张三的代理。

- 接口

public interface People {
/**
* 吃东西
*/
void eat(String food) throws Throwable;
}


张三

public class ZhangSan implements  People{
/**
* 吃
* @param food 食物
*/
public void eat(String food) {
System.out.println(this.getClass().getName()+":吃"+food+"!");
}
}


代理

public class ProxyHandler implements InvocationHandler{
/**
* 被代理对象的引用
*/
private People people;

public ProxyHandler(People people){
this.people = people;
}

/**
* 代理方法
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
method.invoke(people,args);
after();
return null;
}

/**
* 执行在代理方法之前
*/
private void before(){
System.out.println(this.getClass().getName()+":做饭!");
}

/**
* 执行在代理方法之后
*/
private void after(){
System.out.println(this.getClass().getName()+":洗碗!");
}
}


测试

@Test
public void testJDKDynamicProxy() throws Throwable {
//创建代理
People people = (People) Proxy.newProxyInstance(
People.class.getClassLoader(),
new Class[]{People.class},
new ProxyHandler(new ZhangSan()));
System.out.println(people.getClass().getName());
//执行操作
people.eat("炸鸡,喝啤酒
4000
!");
}


com.sun.proxy.$Proxy0
proxy.dynamicProxy.ProxyHandler:做饭!
proxy.dynamicProxy.ZhangSan:吃炸鸡,喝啤酒!!
proxy.dynamicProxy.ProxyHandler:洗碗!


到这里,效果虽然看到了,但是为什么要使用动态代理呢?

保姆给张三做饭、洗碗,大多时候不会像张三他爸那样长期固定,可能会因各种原因辞职,而重新帮另外的人做饭、洗碗。她代理的具体事情没有变化,变化的只是被代理的对象,如果使用静态代理,那么每次都要重新定义代理(不符合开闭原则),或者新增代理(代码冗余,不宜维护)。

那么jdk动态代理又是如何工作的呢?

前面代码中

System.out.println(people.getClass().getName());


打印的结果是

com.sun.proxy.$Proxy0


这就是jdk动态生成的,处于内存中的代理。还是一头雾水,必须要看看他具体的内容是什么

/**
* 生成代理的Class文件
*/
public static void createProxyClassFile(){
//生成代理类的字节流
byte[] $Proxy0s = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{People.class});
//生成文件
try {
FileOutputStream fout = new FileOutputStream("src/main/java/proxy/dynamicProxy/$Proxy0.class");
fout.write($Proxy0s);
fout.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}


重新运行程序,生成代理文件

@Test
public void testJDKDynamicProxy() throws Throwable {
//创建代理
People people = (People) Proxy.newProxyInstance(
People.class.getClassLoader(),
new Class[]{People.class},
new ProxyHandler(new ZhangSan()));
System.out.println(people.getClass().getName());
//执行操作
people.eat("炸鸡,喝啤酒!");
createProxyClassFile();
}




如上图,这个$Proxy0.class就是jdk动态生成的,反编译打开该文件,真相就在里面!

public final class $Proxy0 extends Proxy implements People {
private static Method m1;
private static Method m0;
private static Method m3;
private static Method m2;

public $Proxy0(InvocationHandler var1) throws  {
super(var1);
}

public final boolean equals(Object var1) throws  {
...
}

public final int hashCode() throws  {
...
}

public final void eat(String var1) throws Throwable {
super.h.invoke(this, m3, new Object[]{var1});
}

public final String toString() throws  {
...
}

static {
try {
...
m3 = Class.forName("proxy.dynamicProxy.People").getMethod("eat", new Class[]{Class.forName("java.lang.String")});
...
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}


从源码可以看出$Proxy0的真实身份:

1、它实际上实现了People接口,所以可以具体实现eat(String var1)方法。

2、而且它继承了Proxy类,在jdk源码中可以看到它有一个成员变量InvocationHandler,是不是很眼熟,这就是我们自定义代码ProxyHandler实现的接口。

public class Proxy implements java.io.Serializable{
...
protected InvocationHandler h;
...
}


3、这里逻辑就很清晰了,eat(String var1)的具体实现就是调用InvocationHandler接口的invoke方法,并把真实的eat方法反射生成通过参数传入invoke方法,所以我们才能在ProxyHandler中调用method.invoke(people,args),从而实现动态代理。

动态代理完美的诠释的开闭原则,即对扩展开放,对修改关闭。非常适用于程序扩展,非常非常重要,在各种框架中广泛应用,如大名鼎鼎的SpirngAOP。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  jdk 代理模式 java