对于反射的一些总结之动态代理
1、为什么需要使用代理?
1.1、 首先我们需要了解一下什么是横切关注点?
横切关注点: 跨越应用程序多个模块的功能.
1.2、非模块化的横切关注点将会导致的问题:
① 、代码混乱: 每个方法在处理核心逻辑的同时还必须兼顾其他多个关注点.
② 、代码分散: 以日志需求为例, 只是为了满足这个单一需求, 就不得不在多个模块里多次重相复同的日志代码. 如果日志需求发生变化, 必须修改所有模块. 这些类似的需求包括验证, 事务等.
③ 、代码膨胀:越来越多的非业务需求(日志和验证)加入后, 原有的方法急剧膨胀.
1.3、如何解决:
- AOP(Aspect Orient Program, 面向切面编程)(Spring 框架就是使用AOP)
- 使用动态代理模块化横切关注点:代理设计模式的原理:使用一个代理将对象包装起来, 然后用该代理对象取代原始对象. 任何对原始对象的调用都要通过代理. 代理对象决定是否以及何时将方法调用转到原始对象上.
1.4、如何创建动态代理:
① 、Proxy 提供用于创建动态代理类和代理对象的静态方法, 它也是所有动态代理类的父类.
② 、Proxy 提供了两个方法来创建动态代理类和动态代理实例
我们通过代码来进行理解:
接口的代码:
1 package cn.vincent.porxy; 2 3 public interface ArithmeticCalculator { 4 5 int add(int i, int j); 6 7 int sub(int i, int j); 8 9 void mul(int i, int j); 10 11 void div(int i, int j); 12 13 14 }
接口实现类的代码:
1 package cn.vincent.porxy; 2 3 public class ArithmeticCalculatorImpl implements ArithmeticCalculator { 4 5 @Override 6 7 public int add(int i, int j) { 8 9 return i + j; 10 11 } 12 13 @Override 14 15 public int sub(int i, int j) { 16 17 return i - j; 18 19 } 20 21 @Override 22 23 public void mul(int i, int j) { 24 25 System.out.println(i * j); 26 27 } 28 29 @Override 30 31 public void div(int i, int j) { 32 33 System.out.println(i / j); 34 35 } 36 37 }
动态代理类实现的代码:
1 package cn.vincent.porxy; 2 3 import java.lang.reflect.InvocationHandler; 4 5 import java.lang.reflect.Method; 6 7 import java.lang.reflect.Proxy; 8 9 import org.junit.Test; 10 11 12 public class TestPorxy { 13 14 /** 15 16 * 关注点: 17 18 * 19 20 * 1.获取需要被代理的目标对象。 21 22 * 23 24 * 2. Proxy.newProxyInstance(loader, interfaces, h); 25 26 * public static Object newProxyInstance(ClassLoader loader, 27 28 * Class<?>[] interfaces, 29 30 * InvocationHandler h) 31 32 * 通过使用代理类调用 静态方法 newProxyInstance 返回一个指定接口的代理类实例, 33 34 * 一般情况下其返回的是被代理的目标对象接口类型的!该接口可以将方法调用指派到指* 定的调用处理程序。 35 36 * 37 38 * 对于 newProxyInstance 方法中参数都代表什么? 39 40 * ClassLoader loader : 类加载器通常使用和被代理的对象的类加载器相同. 41 42 * 43 44 * Class<?>[] interfaces:该参数必须是一个接口类型的数组.其中若代理对象不需要 45 46 * 额外实现被代理的目标对象实现的接口外的 47 48 * 额外的接口类型,可以使用 target.getClass().getInterfaces(),来表示目标对象*实现的接口 49 50 * 51 52 * InvocationHandler h :通常使用匿名内部类的方式。 53 54 * 对于 匿名内部类InvocationHandler中的invoke 方法 中的参数。 55 56 * public Object invoke(Object proxy, Method method, Object[] args) *throws Throwable { 57 58 * 59 60 * Object result = method.invoke(target, args); 61 62 * return result; 63 64 * } 65 66 * Object proxy : 指的是正在被返回的那个代理对象,一般不使用! 67 68 * Object[] args : 传入的参数。 69 70 * Method method : 通过调用 invoke(Object obj, Object ... args),将被代 71 72 * 理的对象及参数传入, 73 74 * method.invoke(target, args); 75 76 * 实现动态代理。 77 78 */ 79 80 @Test 81 82 public void test() { 83 84 85 ArithmeticCalculator target = new ArithmeticCalculatorImpl(); 86 87 Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), 88 89 // new Class[]{ArithmeticCalculator.class}, 90 91 target.getClass().getInterfaces(), 92 93 new InvocationHandler() { 94 95 @Override 96 97 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 98 99 Object result = method.invoke(target, args); 100 101 return result; 102 103 } 104 105 }); 106 107 ArithmeticCalculator arithmeticCalculator = (ArithmeticCalculator) proxy; 108 109 int result = arithmeticCalculator.add(1, 1); 110 111 112 System.out.println(result); 113 114 } 115 116 }