1.動態代理兩種方式簡述
JDK動態代理:利用反射機制生成一個實現代理接口的匿名類,在調用具體方法前調用InvokeHandler來處理。
CGLib動態代理:利用ASM(開源的Java字節碼編輯庫,操作字節碼)開源包,將代理對象類的class文件加載進來,通過修改其字節碼生成子類來處理。
區別:JDK代理只能對實現接口的類生成代理;CGlib是針對類實現代理,對指定的類生成一個子類,並覆蓋其中的方法,這種通過繼承類的實現方式,不能代理final修飾的類。
2.動態代理的特點
1.字節碼隨用隨創建,隨用隨加載。
2.它與靜態代理的區別也在於此。因為靜態代理是字節碼一上來就創建好,並完成加載。
3.裝飾者模式就是靜態代理的一種體現。
3.動態代理常用的有兩種方式
3.1 基於接口的動態代理
提供者:JDK 官方的 Proxy 類。
要求:被代理類最少實現一個接口。
3.2 基於子類的動態代理
提供者:第三方的 CGLib,如果報 asmxxxx 異常,需要導入 asm.jar。
要求:被代理類不能用 final 修飾的類(最終類)。
4.使用JDK官方的Porxy類創建對象
實體類:
package com.jh.spring13jdk動態代理; import lombok.Data; @Data public class Game implements Open{ //游戲的網速 private int ms = 460; @Override public int openApp() { System.out.println("打開游戲后的網速是:" + this.getMs()); return this.getMs(); } }
接口:
package com.jh.spring13jdk動態代理; public interface Open { int openApp(); }
測試類:
package com.jh.spring13jdk動態代理; import org.junit.Test; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class Spring13Test { //JDK動態代理 省略了代理對象!!! 直接有jdk中的代理類來實現,但是目標類必須有接口 @Test public void test01() { final Game game = new Game(); /** * 代理: * 間接。 * 獲取代理對象: * 要求: * 被代理類最少實現一個接口 * 創建的方式 * Proxy.newProxyInstance(三個參數) * 參數含義: * ClassLoader:和被代理對象使用相同的類加載器。 * Interfaces:和被代理對象具有相同的行為。實現相同的接口。 * InvocationHandler:如何代理。 * 策略模式:使用場景是: * 數據有了,目的明確。 * 如何達成目標,就是策略。 * */ //使用JDK動態類對象,當作迅游加速器的類,代替了靜態的代理類 //Proxy:代理的意思。 newProxyInstancece創建代理對象 Open jdkProxy = (Open) Proxy.newProxyInstance( game.getClass().getClassLoader(), //類的加載器 game.getClass().getInterfaces(), //類的所有接口 new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Integer ms = (Integer) method.invoke(game, args); if (ms != null) { ms = ms - 400; } return ms; } } ); int i = jdkProxy.openApp(); System.out.println("i=" + i); } }
5.使用CGLib的Enhancer類創建代理對象
對於沒有接口的類,如何實現動態代理呢,這就需要CGLib了,CGLib采用了非常底層的字節碼技術,其原理是通過字節碼技術為一個類創建子類,並在子類中采用方法攔截的技術攔截所有父類方法的調用,順勢織入橫切邏輯。但因為采用的是繼承。所以不能對final修飾的類進行代理。
實體類:
package com.jh.spring14cjlib動態代理; import lombok.Data; /** * 目標類 * 父母 */ @Data public class Parents { //成績 private int score = 599; //高考 public int gaoKao(){ System.out.println("父母參加高考,分數是:"+this.getScore()); return this.getScore(); } }
工廠類:
package com.jh.spring14cjlib動態代理; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; //MethodInterceptor:方法上的攔截 public class ParentsFactory implements MethodInterceptor { private Parents parents; public ParentsFactory() { parents = new Parents(); } /** * 基於子類的動態代理 * 要求: * 被代理對象不能是最終類 * 用到的類: * Enhancer * 用到的方法: * create(Class, Callback) * 方法的參數: * Class:被代理對象的字節碼 * Callback:如何代理 */ //增強器 ,把parents創造一個子類 public Parents createParentsSon() { //使用字節碼增強器,去增強我們的父類 Enhancer enhancer = new Enhancer(); //字節碼增強器,可以讀懂class文件 //enhancer 指定一個對象 enhancer.setSuperclass(Parents.class);//反射 //使用工廠,換行(創建子類) enhancer.setCallback(this); //創建子類 Parents son = (Parents) enhancer.create(); //多態 return son; } /** * 執行被代理對象的任何方法,都會經過該方法。在此方法內部就可以對被代理對象的任何 方法進行增強。 * * 參數: * 前三個和基於接口的動態代理是一樣的。 * MethodProxy:當前執行方法的代理對象。 * 返回值: * 當前執行方法的返回值 */ //方法的攔截 @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { Integer score = (Integer) method.invoke(parents, objects); if (score != null) { score = score + 30; } return score; } }
測試類:
package com.jh.spring14cjlib動態代理; import org.junit.Test; public class Spring14Test { @Test public void test01(){ ParentsFactory parentsFactory = new ParentsFactory(); Parents parentsSon = parentsFactory.createParentsSon(); int score = parentsSon.getScore(); System.out.println(score); } }
6.總結:
CGLib創建的動態代理對象比JDK創建的動態代理對象的性能更高,但是CGLib創建對象時所花費的時間卻比JDK多的的。
7.問題:
為什么,要在不改變源代碼的基礎上,去寫一個代理類增強一些功能呢?
因為項目大了,就有主要的功能和次要的功能,要想主要功能和次要功能一起運行,必須用AOP
我再解釋下面向對象和面向切面,面向對象OOP,面向的是主要功能的對象,而AOP是面向對象OOP的一個補充,面向次要功能的對象
目的是為了降低耦合度,提高代碼的復用性。(自己總結的,僅供參考,你懂的,嘻嘻)。
