概述
使用過spring aop的人應(yīng)該都知道擎值,spring是通過動(dòng)態(tài)代理來實(shí)現(xiàn)的逐抑。而動(dòng)態(tài)代理聽過的有jdk的動(dòng)態(tài)代理以及cglib的動(dòng)態(tài)代理鸠儿。究竟這兩種代理方式有什么區(qū)別厕氨,好奇研究了下。
jdk動(dòng)態(tài)代理示例
這里舉個(gè)簡單的例子田晚,普通人要買票国葬,但是自己買票一般都買不到的贤徒,于是汇四,可以讓黃牛代為買票。
public interface BuyTicket {
/**
* 買票
*/
void buyTicket();
}
普通人買票
public class CommonPerson implements BuyTicket {
@Override
public void buyTicket() {
System.out.println("買到票了序宦!");
}
}
黃牛代理買票
public class HuangNiu implements InvocationHandler{
private final CommonPerson target;
public HuangNiu(CommonPerson target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("黃牛幫忙代購");
Object res = method.invoke(target, args);
return res;
}
}
來買票了
public static void main(String[] args) {
//需要被代理的類
CommonPerson commonPerson = new CommonPerson();
//代理類
HuangNiu huangNiu = new HuangNiu(commonPerson);
//生成代理對象
BuyTicket buyTicket = (BuyTicket) Proxy.newProxyInstance(CommonPerson.class.getClassLoader(), new Class[]{BuyTicket.class}, huangNiu);
//調(diào)用代理對象的方法
buyTicket.buyTicket();
}
這個(gè)例子很簡單背苦,需要注意的有幾點(diǎn)
1、被代理的類需要實(shí)現(xiàn)某個(gè)接口
行剂,比如這里的CommonPerson類實(shí)現(xiàn)了BuyTicket接口。
2腌巾、代理某個(gè)方法需要實(shí)現(xiàn)InvocationHandler
接口
3、通過Proxy.newProxyInstance
生成代理對象
所以所有的實(shí)現(xiàn)都在Proxy.newProxyInstance里面了壤躲。
分析下Proxy.newProxyInstance源碼
ProxyClassFactory.apply方法
從上面分析知道备燃,動(dòng)態(tài)代理類的生成,最終是在ProxyClassFactory里實(shí)現(xiàn)的漏麦。這里截取些比較重要的方法
總結(jié)下:
1)生成的類名叫做com.sun.proxy.$Proxy+自增數(shù)字
2)在ProxyGenerator.generateProxyClass里生成字節(jié)碼
3)最后使用類加載器加載生成的類
那么jdk的動(dòng)態(tài)代理究竟幫我們生成了怎么樣的類呢?繼續(xù)跟下ProxyGenerator.generateProxyClass方法
方法有點(diǎn)長撕贞,這里就不列出來了,感興趣的可以看下sun.misc.ProxyGenerator#generateClassFile這個(gè)方法
簡單說明下秧均,生成的字節(jié)碼:
1)添加hashCode、equals目胡、toString方法
2)實(shí)現(xiàn)了接口(比如這里的BuyTicket接口)所有的實(shí)現(xiàn)都代理給了InvocationHandler.invoke方法
3)生成一個(gè)帶有InvocationHandler參數(shù)的構(gòu)造函數(shù)
繪制類圖
使用cglib實(shí)現(xiàn)動(dòng)態(tài)代理
CommonPerson
使用cglib無需聲明一個(gè)接口了
public class CommonPerson {
public void buyTicket() {
System.out.println("買到票了链快!");
}
}
代理類需要實(shí)現(xiàn)MethodInterceptor接口
public class HuangNiu implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("黃牛幫忙代購");
Object res = methodProxy.invokeSuper(o, objects);
return res;
}
}
public static void main(String[] args) {
HuangNiu huangNiu = new HuangNiu();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(CommonPerson.class);
enhancer.setCallback(huangNiu);
CommonPerson person = (CommonPerson) enhancer.create();
person.buyTicket();
}
代理類的生成邏輯在Enhancer.create方法里。這里分析的代碼就不在貼出來了巨双。
繪制下生成類圖
和jdk動(dòng)態(tài)代理不一樣的是霉祸,cglib生成的方法會繼承被代理類
(jdk動(dòng)態(tài)代理是實(shí)現(xiàn)同一個(gè)接口),然后生成的方法也和jdk的一樣脉执,會調(diào)用MethodInterceptor也就是這里的HuangNiu的intercept方法。
總結(jié)
jdk的動(dòng)態(tài)代理和cglib的動(dòng)態(tài)代理,都是通過運(yùn)行時(shí)動(dòng)態(tài)生成字節(jié)碼
的方式來實(shí)現(xiàn)代理的迅细。