动态代理总结
什么是代理?
什么是代理?我觉得去饭店吃饭,服务员与厨师之间的关系就是代理,首先服务员类与厨师
类必须是属于同一家饭店(实现了同一个接口),比如说你去饭店吃饭,本来是要直接找厨师
的说:“我要吃宫保鸡丁”,然后就调用厨师的做饭方法。但是有了服务员之后呢,你一进
餐厅就找服务员,服务员也有给你做菜的功能,但是她的这个功能调用厨师的,然后另外它
还有一些别的功能,比如说给你拿一些餐具啊或者给你结账啊什么的功能。
这是现实生活中的代理,在java中的代理,代理类和被代理的类要实现了同一个接口,也就
是被代理类中有的方法代理类中都得有,如果他的某个方法你没有,那你还代理什么啊是吧。
所以在java重要写一个代理类的话,就要自定义一个类去实现与被代理类相同的接口,然后
再在代理类中添加一些别的功能,比如说系统日志啊什么的,这就是静态代理,意思就是手
工写一个代理类然后javac运行出一个字节码文件。
总之就是,代理类中的方法不能比被代理类中的方法少,就是 你还没人家懂得多呢,碰到个
事你不能帮人家解决,人家还找你代理干什么呢?
★ 什么是动态代理?
所谓的动态代理就是java虚拟机在运行过程中,自动生成了一份字节码,这份字节码文件通
常就被用作代理类。但是JVM自动生成的动态类必须实现了一个或多个接口,而自动生成的这
个类只能用作与其实现了相同接口的类的代理类。
但是如果一个类它没有实现接口,那怎么给它弄一个代理类呢,这时可以使用CGLIB库,CGLIB
库可以自动生成一个目标类的子类。一个类的子类也可以被用作该类的代理。
★然后就是,JVM是怎么自动生成的代理类呢?我们来分析一下详细的步骤:
查看API知道java.lang.reflect包中的Proxy类中有一个静态方法getProxyClass方法返回的是代理
类的 java.lang.Class 对象。然后还知道自动生成的类必须实现了接口,所以在调用getProxyClass
方法时是不是要把生成的代理类实现了那个接口当参数给传进去啊?然后每个字节码文件被加载
到内存时都需要一个类加载器,所以也要把一个类加载器当参数传给getProxyClass。通常用的
是和接口对应的类加载器。
例如:
Class clazzProxy = Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);
然后这个clazzProxy字节码文件就可以被当做实现了Collection接口的类的代理类了。
然后可以知道自动生成的这个代理类的字节码文件中就有一个带参数的构造函数,参数
为InvocationHandler类型的对象。
★上面已经获得了JVM自动生成的代理类的字节码文件了,再然后是不是就用该 通过反射
来创建代理类的实例对象啦?
因为字节码文件中就一个带参数的构造函数,所以运用反射的话就要用构造函数
的newInstance方法啦。就是
Constructor constructor
= clazzProxy.getConstructor(InvocationHandler.class);
Collection proxy
= (Collection)constructor.newInstance(new InvocationHandler());
因为构造函数的参数是InvocationHandler类型的,所以在newInstance的时候要传一个
具体的InvocationHandler对象当参数。所以还要创建一个InvocationHandler类型的对象了。
再看API发现InvocationHandler是一个接口,所以上面在创建对象的时候,传了一个参数为new InvocationHandler()是错误的。应该创建一个类实现InvocationHandler接口。
那就应该实现接口里面的所有方法。而InvocationHandler接口中只有一个invoke方法,
所以:
class MyInvocationHander implements InvocationHandler
{
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
{
return null;
}
}
因为InvocationHandler是接口,所以传InvocationHandler参数的时候,
也可以用匿名内部类的方方式。
★上面的步骤就是动态生成代理类的过程,总结一下就是:
用Proxy类的getProxyClass方法获得动态类的字节码文件,传入的参数有类
加载器和实现的接口,然后就是利用反射获得构造函数再创建对象,构造函数
的的参数是InvocationHandler类型的对象,InvocationHandler是接口,所以要
创建个类实现这个接口,复写里面的invoke方法。
另外Proxy类中还有一个静态方法:
newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h),
就是把上面的步骤合成一步了,参数还是那三个参数,只不过是以下都传进来罢了。