代理模式是項目中常用的一種設計模式。提供了間接訪問目標對象的一種方式;即通過代理對象訪問目標對象。這樣做的好處是,可以在不改變原有目標對象的基礎上,對目標對象增加額外的擴展功能。
什么是代理模式
代理模式是項目中常用的一種設計模式。提供了間接訪問目標對象的一種方式;即通過代理對象訪問目標對象。
這樣做的好處是,可以在不改變原有目標對象的基礎上,對目標對象增加額外的擴展功能。
代理模式又分為靜態代理、jdk動態代理、cglib動態代理三種實現方式。
三種實現方式各有優點,以及適用的場景。
一、靜態代理
被代理對象與代理對象需要實現相同的接口或者是繼承相同父類,因此要定義一個接口或抽象類。
/**代理接口*/
public interface IHello {
String hi(String key);
}
/**代理接口實現類*/
public class HelloImpl implements IHello {
@Override
public String hi(String key) {
String str = "hello:" + key;
System.out.println("HelloImpl! " + str);
return str;
}
}
/**靜態代理類*/
public class HelloStaticProxy implements IHello {
private IHello hello;
public HelloStaticProxy(IHello hello) {
this.hello = hello;
}
@Override
public String hi(String key) {
System.out.println(">>> static proxy start");
String result = hello.hi(key);
System.out.println(">>> static proxy end");
return result;
}
}
/**測試*/
public class DemoTest {
public static void main(String[] args) {
IHello helloProxy = new HelloStaticProxy(new HelloImpl());
helloProxy.hi("world");
}
}
輸出結果:
>>> static proxy start
HelloImpl! hello:world
>>> static proxy end
二、jdk動態代理
jdk動態代理是基于接口的一種代理方式,目標對象一定要實現接口。
原理是,利用反射機制,動態生成匿名類繼承Proxy類并且實現了要代理的接口,由于java不支持多繼承,所以JDK動態代理不能代理類。
/**代理接口*/
public interface IHello {
String hi(String key);
}
/**代理接口實現類*/
public class HelloImpl implements IHello {
@Override
public String hi(String key) {
String str = "hello:" + key;
System.out.println("HelloImpl! " + str);
return str;
}
}
/**jdk動態代理類*/
public class JdkProxy implements InvocationHandler {
private Object target;
public JdkProxy(Object target) {
this.target = target;
}
/**
* 獲取被代理接口實例對象
*
* @param <T>
* @return
*/
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(">>> JdkProxy start");
Object result = method.invoke(target, args);
System.out.println(">>> JdkProxy end");
return result;
}
}
/**測試*/
public class Demo2Test {
public static void main(String[] args) {
JdkProxy proxy = new JdkProxy(new HelloImpl());
IHello helloProxy = proxy.getProxy();
helloProxy.hi(" jdk proxy !");
}
}
輸出結果:
>>> JdkProxy start
HelloImpl! hello: jdk proxy !
>>> JdkProxy end
三、cglib動態代理
目標對象可以不用實現接口,不能針對final類進行代理。
原理是,動態生成class繼承目標對象。
使用cglib必須引入對應的jar包。
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.7</version>
</dependency>
/**目標類*/
public class HelloImpl {
public String hi(String key) {
String str = "hello:" + key;
System.out.println("HelloImpl! " + str);
return str;
}
}
/**cglib代理類*/
public class CglibProxy implements InvocationHandler {
private Object target;
/**
* 獲取被代理接口實例對象
*/
public <T> T getProxy() {
//1創建增強器對象
Enhancer e = new Enhancer();
//2設置增強器的類加載器
e.setClassLoader(target.getClass().getClassLoader());
//3設置代理對象父類類型
e.setSuperclass(target.getClass());
//4設置回調函數
e.setCallback(this);
//5創建代理對象
return (T) e.create();
}
public CglibProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(">>> cglib start");
Object obj = method.invoke(target, args);
System.out.println(">>> cglib end");
return obj;
}
}
/**測試*/
public class Demo3Test {
public static void main(String[] args) {
HelloImpl hello = new HelloImpl();
CglibProxy cglibProxy = new CglibProxy(hello);
HelloImpl proxy = cglibProxy.getProxy();
proxy.hi(" cglib ");
}
}
輸出結果:
>>> cglib start
HelloImpl! hello: cglib
>>> cglib end
四、總結
靜態代理,代理類必須非常明確,所以無法做到通用,但是效率也是最高的。
jdk動態代理,必須基于接口代理,有一定局限性;動態生成字節碼文件,可以用于通用業務(性能日志等)。
cglig動態代理,也是動態生成字節碼文件,生成的代理類繼承了目標對象。
spring aop默認代理策略是:如果目標對象實現了接口,則使用jdk動態代理,否則使用cglib代理。
jdk8之后,jdk動態代理效率要高于cglib代理?。