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()方法的三个参数:

  1. loader,指定代理对象的类加载器;
  2. interfaces,代理对象需要实现的接口,可以同时指定多个接口;
  3. 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之间转换

参照原码 Github