理解Java动态代理(一)

Java中动态代理实现方式主要有两种,一种是JDK官方提供的基于接口的动态代理,另一种是CGLib提供的基于类的的动态代理。在Spring Aop框架中,默认是是实现了接口的类使用JDK动态代理,没有实现接口的类使用CGlib动态代理,也可以设置强制全部都使用CGlib。

JDK提供的基于接口的动态代理
//定义接口Animal
public interface Animal {
    void eat();
}

//定义类Human,实现接口Animal
public class Human implements Animal {
    public void eat() {
        System.out.println("吃肉");
    }
}

//实现InvocationHandler,动态代理的新增逻辑都写在具体的InvocationHandler实现里。
//需要注意的一点是invoke方法中的proxy参数是动态代理产生的代理对象,而不是被代理的对象。在invoke方法里调用proxy.invoke(human,args)会无限递归,导致StackOverFlow。
//执行被代理对象的方法需要将被代理对象的引用传到InvocationHandler中。
public class HumanInvocationHandler implements InvocationHandler {
    private Human human;
    public HumanInvocationHandler(Human human){
        this.human = human;
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("把肉煮熟");
        return method.invoke(human,args);
    }
}

//Main方法
public class Main {
    public static void main(String[] args){
        Human human = new Human();
        HumanInvocationHandler humanInvocationHandler = new HumanInvocationHandler(human);

        Animal animal = (Animal) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), Human.class.getInterfaces(), humanInvocationHandler);
        System.out.println("类名: " + animal.getClass());
        System.out.println("父类名: " + animal.getClass().getSuperclass());
        for(Class c:animal.getClass().getInterfaces()){
            System.out.println("接口名:" + c);
        }
        animal.eat();

        System.out.println("类名: " +human.getClass());
        System.out.println("父类名: " + human.getClass().getSuperclass());
        for(Class c:human.getClass().getInterfaces()) {
            System.out.println("接口名:" + c);
        }
        human.eat();
    }
}
输出结果
代理对象
类名: class com.sun.proxy.$Proxy0
父类名: class java.lang.reflect.Proxy
接口名:interface intefaces.Animal
把肉煮熟
吃肉

原始对象
类名: class intefaces.classes.Human
父类名: class java.lang.Object
接口名:interface intefaces.Animal
吃肉

从输出结果可以看出来生成的代理对象的类型是com.sun.proxy.$Proxy0,这是运行时动态生成的类型,它的父类是java.lang.reflect.Proxy,并且实现了接口 intefaces.Animal。执行这个类实现的接口的方法时,会转发到InvocationHandler里来处理。
java.lang.reflect.Proxy这个类是所有JDK动态代理生成的代理类的父类,这个设计决定了JDK动态代理只能实现基于接口的动态代理。对普通的类做代理的话,生成的代理类必然是这个类的子类,因为JAVA语言规范里规定了一个类只能有一个父类,这就和所有代理类的父类都是java.lang.reflect.Proxy相冲突了。JAVA官方选择同一个类作为所有代理类的父类肯定是经过深思熟虑的,具体的原因以后有空了去了解一下,不过说不定以后JAVA就原生支持基于类的动态代理了。

CGlib提供的基于类的动态代理
//引入CGlib依赖
    
        
            cglib
            cglib
            3.2.4
        
    
//实现InvocationHandler接口,此InvocationHandler非JDK中的InvocationHandler,是CGlib提供的接口,继承了Callback接口
//Callback接口的子接口有多种,我们这里选用了和JDK中定义一样的InvocationHandler来实现Callback
public class HumanInvocationHandlerCGlib implements InvocationHandler {
    private Human human;

    public HumanInvocationHandlerCGlib(Human human){
        this.human = human;
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("把肉煮熟");
        return method.invoke(human,args);
    }
}

//main方法
public class Main {
    public static void main(String[] args){
        Human human = new Human();
        Enhancer enhancer = new Enhancer();
        HumanInvocationHandlerCGlib humanInvocationHandlerCGlib = new HumanInvocationHandlerCGlib(human);
        enhancer.setSuperclass(Human.class);
        enhancer.setCallback(humanInvocationHandlerCGlib);
        Human proxy = (Human) enhancer.create();

        System.out.println("类名: " + proxy.getClass());
        System.out.println("父类名: " + proxy.getClass().getSuperclass());
        for(Class c:proxy.getClass().getInterfaces()){
            System.out.println("接口名:" + c);
        }
        proxy.eat();

        System.out.println("类名: " +human.getClass());
        System.out.println("父类名: " + human.getClass().getSuperclass());
        for(Class c:human.getClass().getInterfaces()) {
            System.out.println("接口名:" + c);
        }
        human.eat();
    }
}
输出结果
代理对象
类名: class intefaces.classes.Human$$EnhancerByCGLIB$$6fa4b36a
父类名: class intefaces.classes.Human
接口名:interface net.sf.cglib.proxy.Factory
把肉煮熟
吃肉

原始对象
类名: class intefaces.classes.Human
父类名: class java.lang.Object
接口名:interface intefaces.Animal
吃肉

我们可以看到代理对象的类名是intefaces.classes.Human$$EnhancerByCGLIB$$6fa4b36a,它的父类是intefaces.classes.Human,同时实现了CGlib提供的net.sf.cglib.proxy.Factory接口。使用CGlib时需要注意,CGlib无法对Final类生成代理类,无法对Final方法进行代理。因为CGlib使用的是创建子类来实现代理,JAVA的语言规范里子类对以上例子都是无能修改的,JDK的动态代理则没有这些限制。

总结
  1. JDK基于实现接口实现动态代理,CGlib基于创建子类实现动态代理。
  2. JDK动态代理不能代理没有继承接口的类,CGlib可以。
  3. JDK可以代理Final类和Final方法,CGlib不可以

你可能感兴趣的:(理解Java动态代理(一))