俗話說:Coder不知?jiǎng)討B(tài)代理瓤逼,走在路上沒人理A鳌!霸旗!
所以本文嘗試說明白java代理模式贷帮,代理中的靜態(tài)代理和動(dòng)態(tài)代理,java的動(dòng)態(tài)代理如何寫诱告,動(dòng)態(tài)代理的底層原理撵枢,spring aop中使用的cglib如何實(shí)現(xiàn),以及底層原理,cglib和jdk的動(dòng)態(tài)代理的區(qū)別锄禽,javassist的使用方法及底層實(shí)現(xiàn)原理
目錄結(jié)構(gòu)
1潜必、靜態(tài)代理
2、動(dòng)態(tài)代理
2.1沟绪、jdk的動(dòng)態(tài)代理
2.2刮便、cglib
2.3、javassist
1绽慈、靜態(tài)代理
為了便于理解動(dòng)態(tài)理解恨旱,將常見的硬編碼代理模式成為靜態(tài)代理
代理模式的定義是給某一個(gè)對象提供一個(gè)代理對象,并由代理對象控制對原對象的引用坝疼。類似于實(shí)際生活中的中介搜贤,想必都知道,這里不贅述钝凶。
代理模式的結(jié)構(gòu)圖如下:
如圖仪芒,我們使用JavaProgramer類實(shí)現(xiàn)Programer接口,ProxyJavaProgramer類也實(shí)現(xiàn)Programer接口耕陷,同時(shí)調(diào)用JavaProgramer類掂名,對其code進(jìn)行封裝和改寫。
具體代碼如下:
//Programer 接口
public interface Programer {
void coding();
}
//JavaProgramer 類
public class JavaProgramer implements Programer {
public void coding() {
System.out.println("java");
}
}
//ProxyJavaProgramer 代理類
public class ProxyJavaProgramer implements Programer{
public void coding() {
JavaProgramer javaProgramer = new JavaProgramer();
System.out.println("proxy start...");
javaProgramer.coding();
System.out.println("proxy end...");
}
}
//測試類
public class Main {
public static void main(String[] args) {
Programer programer = new ProxyJavaProgramer();
programer.coding();
}
}
輸出:
proxy start...
java
proxy end...
代理模式的作用:
1哟沫、隔離具體實(shí)現(xiàn)類饺蔑,用戶直接使用的是代理類,不需要知道具體實(shí)現(xiàn)類的實(shí)現(xiàn)細(xì)節(jié)
2嗜诀、通過代理類對具體實(shí)現(xiàn)類進(jìn)行增強(qiáng)猾警,增加一些自定義功能
2、動(dòng)態(tài)代理
上面講述了靜態(tài)代理隆敢,靜態(tài)代理的缺點(diǎn)也是很明顯的发皿,1是需要程序員硬編碼,2是如果一個(gè)接口有10個(gè)實(shí)現(xiàn)類拂蝎,使用代理模式則需要編寫10個(gè)代理模式穴墅,類數(shù)量劇增。動(dòng)態(tài)代理則更加靈活一些温自,通過底層的實(shí)現(xiàn)玄货,程序員只需要將需要代理的對象傳進(jìn)來,即可實(shí)現(xiàn)對應(yīng)的代理類捣作。將代理類的生成交給程序誉结。
java中動(dòng)態(tài)代理的實(shí)現(xiàn)方式有多種鹅士,這里我們只介紹三種券躁。
分別是JDK自帶動(dòng)態(tài)代理,開源項(xiàng)目cglib和開源項(xiàng)目javassist。
下面分別講述這三種如何實(shí)現(xiàn)動(dòng)態(tài)代理及底層實(shí)現(xiàn)的原理
2.1JDK自帶動(dòng)態(tài)代理
java的自帶動(dòng)態(tài)代理實(shí)現(xiàn)的基本思想是:1:顯示Invokhandler接口也拜,2:使用Proxy類生成代理類對象以舒,3:利用反射執(zhí)行代理類的指定方法
直接上代碼,一看就明白
//IProgramer接口
public interface IProgramer {
void coding();
}
//Programer實(shí)現(xiàn)類
public class Programer implements IProgramer{
public void coding() {
System.out.println("coding");
}
}
//統(tǒng)一的代理類(傳入需要代理的類實(shí)例慢哈,即可)
public class JdkProxy implements InvocationHandler {
//代理對象
private Object target;
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("start");
method.invoke(target,args);
System.out.println("start");
return null;
}
public JdkProxy(Object target) {
this.target = target;
}
}
//測試類
public class JdkProxyMain {
public static void main(String[] args) {
Programer programer = new Programer();
JdkProxy jdkProxy = new JdkProxy(programer);
IProgramer proxyProgramer =
(IProgramer) Proxy.newProxyInstance(
programer.getClass().getClassLoader(),
programer.getClass().getInterfaces(),
jdkProxy);
proxyProgramer.coding();
}
}
輸出結(jié)果:
start
coding
start
根據(jù)代碼蔓钟,大家肯定很清楚動(dòng)態(tài)代理是怎么回事了,下面先不著急了解底層實(shí)現(xiàn)的邏輯卵贱。我們緊接著看一下其他兩種實(shí)現(xiàn)動(dòng)態(tài)代理的方式:cglib和javassist
2.2cglib
cglib是一個(gè)開源的項(xiàng)目滥沫,使用cglib也可以實(shí)現(xiàn)動(dòng)態(tài)代理。具體怎么實(shí)現(xiàn)键俱,cglib的實(shí)現(xiàn)思路是兰绣,1:導(dǎo)入cglib依賴包,2:代理類實(shí)現(xiàn)MethodInterceptor编振,3:使用Enhance類得到動(dòng)態(tài)代理的實(shí)例缀辩,并執(zhí)行指定方法。
具體實(shí)現(xiàn)直接上代碼踪央,一看就懂
//被代理類
public class Programer {
void codeing(){
System.out.println("i am coding");
}
}
//代理類
public class Hacker implements MethodInterceptor {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)
throws Throwable {
System.out.println("begin");
methodProxy.invokeSuper(o,objects);
System.out.println("end");
return null;
}
}
//測試類
public class CglibMain {
public static void main(String[] args) {
Programer programer = new Programer();
Hacker hacker = new Hacker();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(programer.getClass());
enhancer.setCallback(hacker);
Programer programerProxy = (Programer) enhancer.create();
programerProxy.codeing();
}
}
輸出結(jié)果:
begin
i am coding
end
使用cglib的實(shí)現(xiàn)動(dòng)態(tài)代理的過程更為簡單臀玄。那么cglib和JDK動(dòng)態(tài)代理有什么區(qū)別呢?區(qū)別主要有:
JDK動(dòng)態(tài)代理和CGLIB字節(jié)碼生成的區(qū)別畅蹂?
1健无、JDK動(dòng)態(tài)代理只能對實(shí)現(xiàn)了接口(注意必須有接口)的類生成代理,而不能針對類
2魁莉、CGLIB是針對類實(shí)現(xiàn)代理睬涧,主要是對指定的類生成一個(gè)子類,覆蓋其中的方法旗唁。因?yàn)槭抢^承畦浓,所以該類或方法最好不要聲明成final
3、cglib效率比JDK動(dòng)態(tài)代理要差一點(diǎn)點(diǎn)检疫,具體原因后面分析底層的實(shí)現(xiàn)的時(shí)候講解
在Spring aop中使用了JDK動(dòng)態(tài)代理和cglib讶请,使用情況是,如果實(shí)現(xiàn)了接口則使用JDK動(dòng)態(tài)代理屎媳,如果沒有實(shí)現(xiàn)接口則使用cglib夺溢。
2.3javassist
javassist是一個(gè)字節(jié)碼指令庫,利用javassist可以修改.class文件的結(jié)構(gòu)烛谊,以實(shí)現(xiàn)對類的修改和創(chuàng)建
下面直接上代碼风响,看下javassist是符合修改方法和創(chuàng)建方法的
//被修改的類
public class Programer {
public void coding(){
System.out.println("coding");
}
}
//修改后的類
public class JavassistProgramer {
/**
* 使用javassist修改coding方法
*/
public void updateCoding() throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("com.chanyi.ssist.Programer");
CtMethod updateCoding = cc.getDeclaredMethod("coding");
updateCoding.insertBefore("System.out.println(\"start\");");
updateCoding.insertAfter("System.out.println(\"end\");");
Object proxyClass = cc.toClass().newInstance();
Method execute = proxyClass.getClass().getMethod("coding");
execute.invoke(proxyClass);
}
/**
* 使用javassist新增一個(gè)方法
*/
public void newMethod()throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("com.chanyi.ssist.Programer");
CtMethod ctMethod = new CtMethod(CtClass.voidType, "newMethod", new CtClass[]{}, cc);
ctMethod.setModifiers(Modifier.PUBLIC);
ctMethod.setBody("{System.out.println(\"i am new method \");}");
cc.addMethod(ctMethod);
Object proxyClass = cc.toClass().newInstance();
Method personFlyMethod = proxyClass.getClass().getMethod("newMethod");
personFlyMethod.invoke(proxyClass);
}
}
//測試類
public class JavassistMain {
public static void main(String[] args) {
JavassistProgramer javassistProgramer = new JavassistProgramer();
try {
javassistProgramer.updateCoding();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
輸出結(jié)果:
start
coding
end
以上介紹了三種動(dòng)態(tài)修改.class字節(jié)碼,從而達(dá)到修改方法實(shí)現(xiàn)的方法丹禀。實(shí)際上修改.class字節(jié)碼的方法還有很多状勤。下面貼一張網(wǎng)上結(jié)構(gòu)圖鞋怀,一看就懂
很明顯這些方式的底層實(shí)現(xiàn)都是利用自己維護(hù)的指令類,修改和創(chuàng)建.class文件持搜。
如果有想深入了解的可以學(xué)習(xí)一下ASM密似。這里不再贅述
參數(shù)資料:
1、https://www.cnblogs.com/rickiyang/p/11336268.html
2葫盼、https://blog.csdn.net/chenchaofuck1/article/details/51727605