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依赖
<dependencies>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.4</version>
</dependency>
</dependencies>
//实现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的动态代理则没有这些限制。
总结
- JDK基于实现接口实现动态代理,CGlib基于创建子类实现动态代理。
- JDK动态代理不能代理没有继承接口的类,CGlib可以。
- JDK可以代理Final类和Final方法,CGlib不可以