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不可以