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