学习代理反射
最近从网上学习了动态代理,写篇博客来加深一下理解,学习了https://www.zhihu.com/question/20794107 这篇文章
从静态代理开始说起
假设现在项目经理有一个需求:在项目现有所有类的方法前后打印日志。
这个可以通过静态代理来实现
1.为现有的每一个类都编写一个对应的代理类,并且让它实现和目标类相同的接口(假设都有)
2.在创建代理对象时,通过构造器塞入一个目标对象,然后在代理对象的方法内部调用目标对象同名方法,并在调用前后打印日志。也就是说,代理对象 = 增强代码 + 目标对象(原对象)。有了代理对象后,就不用原对象了
接口
public interface UserService { //新增用户,为了简化不体现参数 void addUser(); //接口方法不能体现{},即body(类体) //修改用户,为了简化不体现参数 void editUser(); }
实现类
public class UserServiceImpl implements UserService { @Override public void addUser() { System.out.println("新增一个用户"); } @Override public void editUser() { System.out.println("对用户进行修改"); }
代理类
public class UserServiceProxy implements UserService{ private UserService realSubject; @Override public void addUser() { System.out.println("代理类UserServiceProxy addUser方法的调用前执行..."); realSubject.addUser(); System.out.println("代理类UserServiceProxy addUser方法的调用后执行..."); } @Override public void editUser() { System.out.println("代理类UserServiceProxy editUser方法的调用前执行..."); realSubject.editUser(); System.out.println("代理类UserServiceProxy editUser方法的调用后执行..."); } public UserServiceProxy() { } public UserServiceProxy(UserService realSubject) { this.realSubject = realSubject; } }
但是我们会发现静态代理有他的缺陷,一个是要为每一个方法编写他的代理类,工作量太大,另外后续再接口和实现类中如果添加了方法,也要在代理中添加,太麻烦,于是我们就要想办法来解决
我们知道通过反射能够通过类的class对象,创建出对应的实例,那我们能否不写代理类,而是通过代理类的class对象直接创建代理实例呢,这时候有人可能就会提出疑问了,如果不写代理类,哪里来的class对象呢,那些构造器,方法去哪里找呢?
实际上,代理类和目标类理应实现同一组接口。之所以实现相同接口,是为了尽可能保证代理对象的内部结构和目标对象一致,这样我们对代理对象的操作最终都可以转移到目标对象身上,代理对象只需专注于增强代码的编写。所以我们可以从接口那里获取到本来应该由代理类提供的信息,而构造方面则可以通过java.lang.reflect.InvocationHandler接口和 java.lang.reflect.Proxy类互相配合来实现
先说说Proxy,Proxy有个静态方法:getProxyClass(ClassLoader, interfaces),只要你给它传入类加载器和一组接口,它就给你返回对应接口的代理Class对象。简单来说就是在获得接口的属性的同时,可以通过反射实例化为对象了.
再来说说InvocationHandler,根据代理Class的构造器创建对象时,需要传入InvocationHandler。每次调用代理对象的方法,最终都会调用InvocationHandler的invoke()方法:
class ProxyTest { public static void main(String[] args) throws Throwable { //目标对象 CalculatorImpl target = new CalculatorImpl(); //传入目标对象 //目的:1.根据它实现的接口生成代理对象 2.代理对象调用目标对象方法 Calculator calculatorProxy = (Calculator) getProxy(target); calculatorProxy.add(1, 2); calculatorProxy.subtract(2, 1); } //用来返回代理对象的方法 private static Object getProxy(final Object target) throws Exception { //参数1:随便找个类加载器给它, 参数2:目标对象实现的接口,让代理对象实现相同接口 //得到的是代理对象的class Class proxyClazz = Proxy.getProxyClass(target.getClass().getClassLoader(), target.getClass().getInterfaces()); //传入InvocationHandler,得到有参构造器 Constructor constructor = proxyClazz.getConstructor(InvocationHandler.class); //通过构造器反射获得代理对象 Object proxy = constructor.newInstance(new InvocationHandler() { //InvocationHandler接口的invoke方法每次调用代理对象方法,最终都会执行invoke @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(method.getName() + "方法开始执行..."); //通过反射执行目标对象方法,两个参数一个是目标对象,一个是本来方法的参数,来应对重构参数不一样的同名方法 Object result = method.invoke(target, args); System.out.println(result); System.out.println(method.getName() + "方法执行结束..."); //返回方法执行结果 return result; } }); //返回代理对象 return proxy; } }
不过实际编程中,一般不用getProxyClass(),而是使用Proxy类的另一个静态方法:Proxy.newProxyInstance(),直接返回代理实例,连中间得到代理Class对象的过程都帮你隐藏.
最后来回顾一下学到的
最后动态代理生成的代理对象,最终都可以用接口接收,和目标对象一起形成了多态,可以随意切换展示不同的功能。但是切换的同时,与用实现类接受相比只能使用该接口定义的方法。