解析動(dòng)態(tài)代理jdk的Proxy與spring的CGlib(包括區(qū)別介紹)
1. 為什么要使用動(dòng)態(tài)代理?
動(dòng)態(tài)代理:在不改變?cè)写a的情況下上進(jìn)行對(duì)象功能增強(qiáng) 使用代理對(duì)象代替原來(lái)的對(duì)象完成功能 進(jìn)而達(dá)到拓展功能的目的
2.JDK Proxy 動(dòng)態(tài)代理面向接口的動(dòng)態(tài)代理
特點(diǎn):
- 一定要有接口和實(shí)現(xiàn)類的存在 代理對(duì)象增強(qiáng)的是實(shí)現(xiàn)類 在實(shí)現(xiàn)接口的方法重寫的方法
- 生成的代理對(duì)象只能轉(zhuǎn)換成 接口的不能轉(zhuǎn)換成 被代理類
- 代理對(duì)象只能增強(qiáng)接口中定義的方法 實(shí)現(xiàn)類中其他和接口無(wú)關(guān)的方法是無(wú)法增強(qiáng)的
- 代理對(duì)象只能讀取到接口中方法上的注解 不能讀取到實(shí)現(xiàn)類方法上的注解
- 使用方法:
public class Test1 { public static void main(String[] args) { Dinner dinner=new Person("張三"); // 通過Porxy動(dòng)態(tài)代理獲得一個(gè)代理對(duì)象,在代理對(duì)象中,對(duì)某個(gè)方法進(jìn)行增強(qiáng) // ClassLoader loader,被代理的對(duì)象的類加載器 ClassLoader classLoader = dinner.getClass().getClassLoader(); // Class<?>[] interfaces,被代理對(duì)象所實(shí)現(xiàn)的所有接口 Class[] interaces= dinner.getClass().getInterfaces(); // InvocationHandler h,執(zhí)行處理器對(duì)象,專門用于定義增強(qiáng)的規(guī)則 InvocationHandler handler = new InvocationHandler(){ // invoke 當(dāng)我們讓代理對(duì)象調(diào)用任何方法時(shí),都會(huì)觸發(fā)invoke方法的執(zhí)行 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // Object proxy, 代理對(duì)象 // Method method,被代理的方法 // Object[] args,被代理方法運(yùn)行時(shí)的實(shí)參 Object res=null; if(method.getName().equals("eat")){ System.out.println("飯前洗手"); // 讓原有的eat的方法去運(yùn)行 res =method.invoke(dinner, args); System.out.println("飯后刷碗"); }else{ // 如果是其他方法,那么正常執(zhí)行就可以了 res =method.invoke(dinner, args); } return res; } }; Dinner dinnerProxy =(Dinner) Proxy.newProxyInstance(classLoader,interaces,handler); //dinnerProxy.eat("包子"); dinnerProxy.drink(); } } interface Dinner{ void eat(String foodName); void drink(); } class Person implements Dinner{ private String name; public Person(String name) { this.name = name; } @Override public void eat(String foodName) { System.out.println(name+"正在吃"+foodName); } @Override public void drink( ) { System.out.println(name+"正在喝茶"); } } class Student implements Dinner{ private String name; public Student(String name) { this.name = name; } @Override public void eat(String foodName) { System.out.println(name+"正在食堂吃"+foodName); } @Override public void drink( ) { System.out.println(name+"正在喝可樂"); } }
3.CGlib動(dòng)態(tài)代理
cglib動(dòng)態(tài)代理模式是面向父類
特點(diǎn):
面向父類的和接口沒有直接關(guān)系
2.不僅可以增強(qiáng)接口中定義的方法還可以增強(qiáng)其他方法
3.可以讀取父類中方法上的所有注解
使用實(shí)例
public class Test1 { @Test public void testCglib(){ Person person =new Person(); // 獲取一個(gè)Person的代理對(duì)象 // 1 獲得一個(gè)Enhancer對(duì)象 Enhancer enhancer=new Enhancer(); // 2 設(shè)置父類字節(jié)碼 enhancer.setSuperclass(person.getClass()); // 3 獲取MethodIntercepter對(duì)象 用于定義增強(qiáng)規(guī)則 MethodInterceptor methodInterceptor=new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { /*Object o, 生成之后的代理對(duì)象 personProxy Method method, 父類中原本要執(zhí)行的方法 Person>>> eat() Object[] objects, 方法在調(diào)用時(shí)傳入的實(shí)參數(shù)組 MethodProxy methodProxy 子類中重寫父類的方法 personProxy >>> eat() */ Object res =null; if(method.getName().equals("eat")){ // 如果是eat方法 則增強(qiáng)并運(yùn)行 System.out.println("飯前洗手"); res=methodProxy.invokeSuper(o,objects); System.out.println("飯后刷碗"); }else{ // 如果是其他方法 不增強(qiáng)運(yùn)行 res=methodProxy.invokeSuper(o,objects); // 子類對(duì)象方法在執(zhí)行,默認(rèn)會(huì)調(diào)用父類對(duì)應(yīng)被重寫的方法 } return res; } }; // 4 設(shè)置methodInterceptor enhancer.setCallback(methodInterceptor); // 5 獲得代理對(duì)象 Person personProxy = (Person)enhancer.create(); // 6 使用代理對(duì)象完成功能 personProxy.eat("包子"); } } class Person { public Person( ) { } public void eat(String foodName) { System.out.println("張三正在吃"+foodName); } }
原理區(qū)別:
java動(dòng)態(tài)代理是利用反射機(jī)制生成一個(gè)實(shí)現(xiàn)代理接口的匿名類,在調(diào)用具體方法前調(diào)用InvokeHandler來(lái)處理。
而cglib動(dòng)態(tài)代理是利用asm開源包,對(duì)代理對(duì)象類的class文件加載進(jìn)來(lái),通過修改其字節(jié)碼生成子類來(lái)處理。
1、如果目標(biāo)對(duì)象實(shí)現(xiàn)了接口,默認(rèn)情況下會(huì)采用JDK的動(dòng)態(tài)代理實(shí)現(xiàn)AOP
2、如果目標(biāo)對(duì)象實(shí)現(xiàn)了接口,可以強(qiáng)制使用CGLIB實(shí)現(xiàn)AOP
3、如果目標(biāo)對(duì)象沒有實(shí)現(xiàn)了接口,必須采用CGLIB庫(kù),spring會(huì)自動(dòng)在JDK動(dòng)態(tài)代理和CGLIB之間轉(zhuǎn)換
如何強(qiáng)制使用CGLIB實(shí)現(xiàn)AOP?
(1)添加CGLIB庫(kù),SPRING_HOME/cglib/*.jar
(2)在spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>
JDK動(dòng)態(tài)代理和CGLIB字節(jié)碼生成的區(qū)別?
(1)JDK動(dòng)態(tài)代理只能對(duì)實(shí)現(xiàn)了接口的類生成代理,而不能針對(duì)類
(2)CGLIB是針對(duì)類實(shí)現(xiàn)代理,主要是對(duì)指定的類生成一個(gè)子類,覆蓋其中的方法
因?yàn)槭抢^承,所以該類或方法最好不要聲明成final
兩個(gè)動(dòng)態(tài)代理的區(qū)別
- JDK動(dòng)態(tài)代理是面向接口的,只能增強(qiáng)實(shí)現(xiàn)類中接口中存在的方法。CGlib是面向父類的,可以增強(qiáng)父類的所有方法
- JDK得到的對(duì)象是JDK代理對(duì)象實(shí)例,而CGlib得到的對(duì)象是被代理對(duì)象的子類 生活雖然苦悶,但跑起來(lái)總是帶風(fēng)!
到此這篇關(guān)于解析動(dòng)態(tài)代理jdk的Proxy與spring的CGlib的文章就介紹到這了,更多相關(guān)動(dòng)態(tài)代理jdk的Proxy與spring的CGlib內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
HTTP協(xié)議簡(jiǎn)介_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了HTTP協(xié)議簡(jiǎn)介,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來(lái)看看吧2017-07-07vscode入門教程之頁(yè)面啟動(dòng)與代碼調(diào)試
VScode是微軟推出的一款輕量級(jí)的編輯器,采用了和VS相同的UI界面。今天小編給大家?guī)?lái)一篇如何使用vscode來(lái)進(jìn)行最基本的工作的小教程,希望大家能夠喜歡2020-01-01