Java 动态代理
1、前言
java中代理方式分为静态代理和动态代理,静态代理的代理关系在编译时就确定了,它需要为每一个目标类创建一个代理类,在代理类数量较少时可以选择使用。当代理类较多时,需要使用动态代理,动态代理相对来说提供了很大的灵活性,以下讲解下动态代理的两种实现方式,即JDK原生动态代理和CGLIB动态代理。
2、JDK原生动态代理
2.1 示例代码
定义接口:
public interface Fruit { String name(final String name); double price(double price); }
实现接口的具体代理对象类:
public class Apple implements Fruit { @Override public String name(String name) { System.out.println("this is " + name); return name; } @Override public double price(double price) { System.out.println("Apple price is " + price); return price; } }
定义实现接口InvocationHandler的类:
public class FruitDynamicProxy implements InvocationHandler { private Fruit fruit; public FruitDynamicProxy(Fruit fruit) { this.fruit = fruit; } /** * 获取代理对象实例 * newProxyInstance()会返回一个实现了指定接口的代理对象,对该对象的所有方法调用都会转发给InvocationHandler.invoke()方法 * * @return */ public Object getProxyInstance() { return Proxy.newProxyInstance( //代理对象的类加载器 fruit.getClass().getClassLoader(), //代理对象需要实现的接口,可以是多个 fruit.getClass().getInterfaces(), //方法调用的实际对象, this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("FruitDynamicProxy方法名 " + method.getName() + " ---参数 " + Arrays.toString(args)); //代理对象调用相应方法的返回结果 Object invoke = method.invoke(fruit, args); return invoke; } }
测试:
FruitDynamicProxy fruitDynamicProxy = new FruitDynamicProxy(new Apple()); Fruit banana = (Fruit) fruitDynamicProxy.getProxyInstance(); //调用该方法时转到FruitDynamicProxy类的invoke方法 banana.name("apple"); banana.price(12.6);
2.2 说明
实现接口InvocationHandler类,代理对象在调用方法时就会转到该类的invoke()方法。之后在获取代理对象时使用Proxy的newProxyInstance()方法,该方法会返回一个实现具体接口的代理对象,该代理对象的所有方法调用都会转发到InvocationHandler.invoke()方法,具体是使用了java反射机制。
newProxyInstance()方法的三个参数:
loader
,指定代理对象的类加载器;interfaces
,代理对象需要实现的接口,可以同时指定多个接口;handler
,方法调用的实际处理者,代理对象的方法调用都会转发到这里。
3、CGLIB动态代理
3.1 代码示例
代理对象类:
public class CglibFruit { public String name(String name) { System.out.println("this is " + name); return name; } public double price(double price) { System.out.println("price is " + price); return price; } //final修饰的方法非子类无法访问 public final void hello(String msg) { System.out.println("final method " + msg); } }
实现MethodInterceptor类:
public class CglibMethodInterceptor implements MethodInterceptor { private CglibFruit fruit; public CglibMethodInterceptor(CglibFruit fruit) { this.fruit = fruit; } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("CglibMethodInterceptor method " + method.getName() + " --- 参数 " + objects); Object o1 = methodProxy.invokeSuper(o, objects); return o1; } /** * 获取代理对象实例,通过Enhancer来指定要代理的目标对象、实际处理代理逻辑的对象,最后通过调用create()方法得到代理对象 * @return */ public Object getProxyInstance() { Enhancer enhancer = new Enhancer(); //设置代理对象class字节 enhancer.setSuperclass(this.fruit.getClass()); //设置回调方法 enhancer.setCallback(this); //创建代理对象 Object o = enhancer.create(); return o; } }
测试:
CglibMethodInterceptor cmi = new CglibMethodInterceptor(new CglibFruit()); CglibFruit fruit = (CglibFruit) cmi.getProxyInstance(); fruit.name("orange"); fruit.price(12.666);
3.2 说明
实现MethodInterceptor类,代理对象的方法调用会被转发到该类的intercept()方法。在获取代理对象时通过Enhancer来指定要代理的目标对象、实际处理代理逻辑的对象,最后通过调用create()方法得到代理对象。对这个对象所有非final方法的调用都会转发给MethodInterceptor.intercept()
方法。
4、两者区别
java动态代理是领用反射机制生成一个代理接口匿名类,在调用具体的方法前调用InvocationHandler.invoke()方法来处理。CGLIB动态代理是对代理对象类的class文件加载进来,通过修改器字节码成成子类来处理。
两者使用:
如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
如果目标对象实现了接口,可以强制使用CGLIB实现AOP
如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换