Java静态代理、动态代理与CGLib代理
java的動態代理舉足輕重,它同反射原理一直是許多框架的底層實現。今天嘮一下。
一、代理模式
代理模式是一種設計模式,提供了對目標對象額外的訪問方式,即通過代理對象訪問目標對象,這樣可以在不修改原目標對象的前提下,提供額外的功能操作,擴展目標對象的功能。簡言之,代理模式就是設置一個中間代理來控制訪問原目標對象,以達到增強原對象的功能和簡化訪問方式。代理分靜態代理和動態代理兩種。
示意圖:
二、靜態代理
我們平常去電影院看電影的時候,在電影開始的階段是不是經常會放廣告呢?
電影是電影公司委托給影院進行播放的,但是影院可以在播放電影的時候,產生一些自己的經濟收益,比如賣爆米花、可樂等,然后在影片開始結束時播放一些廣告。
現在用代碼來進行模擬。
首先得有一個接口,通用的接口是代理模式實現的基礎。這個接口我們命名為 Movie,代表電影播放的能力。
然后,我們要有一個真正的實現這個 Movie 接口的類,和一個只是實現接口的代理類。
public class RealMovie implements Movie { @Overridepublic void play() {// TODO Auto-generated method stubSystem.out.println("您正在觀看電影 《肖申克的救贖》");} }這個表示真正的影片。它實現了 Movie 接口,play() 方法調用時,影片就開始播放。那么 Proxy 代理呢?
public class Cinema implements Movie { RealMovie movie;public Cinema(RealMovie movie) {super();this.movie = movie;}@Overridepublic void play() {guanggao(true); movie.play(); guanggao(false);} public void guanggao(boolean isStart){if ( isStart ) {System.out.println("電影馬上開始了,爆米花、可樂、口香糖9.8折,快來買啊!");} else {System.out.println("電影馬上結束了,爆米花、可樂、口香糖9.8折,買回家吃吧!");}} }Cinema 就是 Proxy 代理對象,它有一個 play() 方法。不過調用 play() 方法時,它進行了一些相關利益的處理,那就是廣告?,F在,我們編寫測試代碼。
public class ProxyTest {public static void main(String[] args) {RealMovie realmovie = new RealMovie();Movie movie = new Cinema(realmovie);movie.play();} }然后觀察結果:
電影馬上開始了,爆米花、可樂、口香糖9.8折,快來買啊! 您正在觀看電影 《肖申克的救贖》 電影馬上結束了,爆米花、可樂、口香糖9.8折,買回家吃現在可以看到,代理模式可以在不修改被代理對象的基礎上,通過擴展代理類,進行一些功能的附加與增強。值得注意的是,代理類和被代理類應該共同實現一個接口,或者是共同繼承某個類。
上面介紹的是靜態代理的內容,為什么叫做靜態呢?
因為它的類型是事先預定好的,比如上面代碼中的 Cinema 這個類。
三、動態代理所涉及的一些接口、類及方法
java動態代理的主要類和接口有:
java.lang.reflect.Proxy,java.lang.reflect.InvocationHandler
1、public static InvocationHandler getInvocationHandler(Object var0) 該方法用于獲取指定代理對象所關聯的調用處理器
2、public static Class<?> getProxyClass(ClassLoader var0, Class… var1) 該方法用于獲取關聯于指定類裝載器和一組接口的動態代理類的類對象
3、public static Object newProxyInstance(ClassLoader var0, Class<?>[]var1, InvocationHandler var2) 方法用于為指定類裝載器,一組接口及調用處理器生成動態代理類實例
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
將類的字節碼裝載到 Java 虛擬機(JVM)中并為其定義類對象,然后該類才能被使用。Proxy類與普通類的唯一區別就是其字節碼是由 JVM 在運行時動態生成的而非預存在于任何一個 .class 文件中。
四、動態代理例子(基于反射技術實現的)
1.方法接口
public interface SayGoodbye {void sayGoodbye(String name); } public interface SayHello {void sayHello(String name); }2.接口實現
public class goodByeImpl implements SayGoodbye {@Overridepublic void sayGoodbye(String name) {System.out.println("GoodBye"+name);} } public class HelloImpl implements SayHello{@Overridepublic void sayHello(String name) {System.out.println("Hello"+name);} }3.代理實例類
public class ServiceProxy implements InvocationHandler {private Object target;public void InvokeBefore(){System.out.println("調用前");}public void InvokeAfter(){System.out.println("調用后");}public Object getInstance(Object target) {this.target = target;//target.getClass().getClassLoader() 類加載器,動態代理的類加載器必然和被代理的對象在同一個加載器,所以直接使用//target.getClass().getInterfaces() 實現的接口//this 該方法只負責實例化代理,至于代理業務的流程規范,由InvocationHandler(this)來定義return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),this);}@Override//Object proxy代理類//Method method要攔截(優化)的方法//Object[] args方法的參數public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object result;//反射方法前調用InvokeBefore();//反射執行方法 相當于調用target.sayHelllo;result = method.invoke(target,args);//反射方法后調用.InvokeAfter();return result;} }4.測試類
public class ProxyTest {public static void main(String[] args) {//生成proxy類ServiceProxy proxy = new ServiceProxy();//獲取SayGoodbye代理對象,此處需要傳入goodByeImpl實例給getInstance方法,最終被newProxyInstance調用SayGoodbye sgServiceProxy = (SayGoodbye) proxy.getInstance(new goodByeImpl());//通過代理對象調用方法sgServiceProxy.sayGoodbye(" lisi");//獲取SayHello代理對象SayHello shServiceProxy=(SayHello)proxy.getInstance(new HelloImpl());//調用對象方法shServiceProxy.sayHello(" zhangsan");} }5.運行結果
調用前 GoodBye lisi 調用后 調用前 Hello zhangsan 調用后總結:動態代理如果需求改變,只需修改接口方法以及實現類,而無需修改代理的實例,實現了解耦。
五、動態代理源碼解析
1.Proxy類的靜態方法newProxyInstance方法
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException{//驗證傳入的InvocationHandler是否為空Objects.requireNonNull(h);//克隆代理類實現的接口final Class<?>[] intfs = interfaces.clone();//獲得安全管理器final SecurityManager sm = System.getSecurityManager();//檢查創建代理類所需的權限if (sm != null) {checkProxyAccess(Reflection.getCallerClass(), loader, intfs);}/** Look up or generate the designated proxy class.* 查找或者生成特定的代理類(如果緩存中存在,則直接獲取)*/Class<?> cl = getProxyClass0(loader, intfs);/** Invoke its constructor with the designated invocation handler.*/try {//權限校驗if (sm != null) {checkNewProxyPermission(Reflection.getCallerClass(), cl);}//獲取參數類型是InvocationHandler.class的代理類構造器final Constructor<?> cons = cl.getConstructor(constructorParams);final InvocationHandler ih = h;//如果代理類是不可訪問的, 就使用特權將它的構造器設置為可訪問if (!Modifier.isPublic(cl.getModifiers())) {AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {cons.setAccessible(true);return null;}});}//傳入InvocationHandler實例去構造一個代理類的實例,所有代理類都繼承自Proxy,而Proxy構造方法需要InvocationHandler實例作為參數return cons.newInstance(new Object[]{h});} catch (IllegalAccessException|InstantiationException e) {throw new InternalError(e.toString(), e);} catch (InvocationTargetException e) {Throwable t = e.getCause();if (t instanceof RuntimeException) {throw (RuntimeException) t;} else {throw new InternalError(t.toString(), t);}} catch (NoSuchMethodException e) {throw new InternalError(e.toString(), e);}}從newProxyInstance方法看出,產生代理類核心代碼在getProxyClass0
2.Proxy類的靜態方法getProxyClass0方法
private static Class<?> getProxyClass0(ClassLoader loader,Class<?>... interfaces) {if (interfaces.length > 65535) {throw new IllegalArgumentException("interface limit exceeded");}//如果由實現給定接口的代理類存在,這將簡單地返回緩存的副本;否則,將通過ProxyClassFactory創建代理類return proxyClassCache.get(loader, interfaces);}getProxyClass0通過類加載器和接口集合去緩存里面獲取,如果能找到代理類就直接返回,否則就會調用ProxyClassFactory這個工廠去生成一個代理類,下面我們看下Proxy的靜態內部類ProxyClassFactory
3.ProxyClassFactory
1 private static final class ProxyClassFactory2 implements BiFunction<ClassLoader, Class<?>[], Class<?>>3 {4 // prefix for all proxy class names 代理類名稱前綴5 private static final String proxyClassNamePrefix = "$Proxy";6 7 // next number to use for generation of unique proxy class names, 用原子類來生成代理類的序號, 保證序號唯一8 private static final AtomicLong nextUniqueNumber = new AtomicLong();9 10 @Override11 public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {12 13 Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);14 for (Class<?> intf : interfaces) {15 /*16 * Verify that the class loader resolves the name of this17 * interface to the same Class object.18 */19 Class<?> interfaceClass = null;20 try {21 interfaceClass = Class.forName(intf.getName(), false, loader);22 } catch (ClassNotFoundException e) {23 }24 //intf是否可以由指定的類加載進行加載25 if (interfaceClass != intf) {26 throw new IllegalArgumentException(27 intf + " is not visible from class loader");28 }29 /*30 * Verify that the Class object actually represents an31 * interface.32 * intf是否是一個接口33 */34 if (!interfaceClass.isInterface()) {35 throw new IllegalArgumentException(36 interfaceClass.getName() + " is not an interface");37 }38 /*39 * Verify that this interface is not a duplicate.40 * intf在數組中是否有重復41 */42 if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {43 throw new IllegalArgumentException(44 "repeated interface: " + interfaceClass.getName());45 }46 }47 // package to define proxy class in 生成代理類的包名48 String proxyPkg = null;49 // 代理類的訪問標志, 默認是public final50 int accessFlags = Modifier.PUBLIC | Modifier.FINAL;51 52 /*53 * Record the package of a non-public proxy interface so that the54 * proxy class will be defined in the same package. Verify that55 * all non-public proxy interfaces are in the same package.56 * 驗證所有非公共代理接口都在同一個包中57 */58 for (Class<?> intf : interfaces) {59 //獲取接口的訪問標志60 int flags = intf.getModifiers();61 //如果接口的訪問標志不是public, 那么生成代理類的包名和接口包名相同62 if (!Modifier.isPublic(flags)) {63 //生成的代理類的訪問標志設置改為final64 accessFlags = Modifier.FINAL;65 String name = intf.getName();66 int n = name.lastIndexOf('.');67 String pkg = ((n == -1) ? "" : name.substring(0, n + 1));68 if (proxyPkg == null) {69 proxyPkg = pkg;70 } else if (!pkg.equals(proxyPkg)) {71 //代理類如果實現不同包的接口, 并且接口都不是public的, 那么就會在這里報錯72 throw new IllegalArgumentException(73 "non-public interfaces from different packages");74 }75 }76 }77 78 if (proxyPkg == null) {79 // if no non-public proxy interfaces, use com.sun.proxy package 如果沒有非公共代理接口,那生成的代理類都放到默認的包下:com.sun.proxy80 proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";81 }82 83 /*84 * Choose a name for the proxy class to generate.85 * 生成代理類的全限定名, 包名+前綴+序號, 例如:com.sun.proxy.$Proxy086 */87 long num = nextUniqueNumber.getAndIncrement();88 String proxyName = proxyPkg + proxyClassNamePrefix + num;89 90 /*91 * Generate the specified proxy class.92 * 這里是核心, 用ProxyGenerator來生成字節碼, 該類放在sun.misc包下93 */94 byte[] proxyClassFile = ProxyGenerator.generateProxyClass(95 proxyName, interfaces, accessFlags);96 //根據二進制文件生成相應的Class實例97 try {98 return defineClass0(loader, proxyName,99 proxyClassFile, 0, proxyClassFile.length); 100 } catch (ClassFormatError e) { 101 /* 102 * A ClassFormatError here means that (barring bugs in the 103 * proxy class generation code) there was some other 104 * invalid aspect of the arguments supplied to the proxy 105 * class creation (such as virtual machine limitations 106 * exceeded). 107 */ 108 throw new IllegalArgumentException(e.toString()); 109 } 110 } 111 }大boss終于出現了,下面我們分ProxyGenerator.generateClassFile
5.ProxyGenerator.generateClassFile
動態代理的本質:通過類加載器獲取類字節碼,通過類實現的接口反射獲得該類的屬性,方法等,并生成新的字節碼文件
6.代理類字節碼文件分析 $Proxy0.class
1 package com.sun.proxy;2 3 import com.doubibi.framework.util.proxy.HelloService;4 import java.lang.reflect.InvocationHandler;5 import java.lang.reflect.Method;6 import java.lang.reflect.Proxy;7 import java.lang.reflect.UndeclaredThrowableException;8 9 public final class $Proxy0 extends Proxy10 implements HelloService11 {12 //equals方法13 private static Method m1;14 //HelloService 的sayHello方法15 private static Method m3;16 //toString方法17 private static Method m2;18 //hashCode方法19 private static Method m0;20 21 //構造方法22 public $Proxy0(InvocationHandler paramInvocationHandler)23 throws 24 {25 super(paramInvocationHandler);26 }27 28 public final boolean equals(Object paramObject)29 throws 30 {31 try32 {33 return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();34 }35 catch (Error|RuntimeException localError)36 {37 throw localError;38 }39 catch (Throwable localThrowable)40 {41 throw new UndeclaredThrowableException(localThrowable);42 }43 }44 45 //調用了invocationHandler的invoke方法,invoke執行了HelloService 的sayHello方法46 public final void sayHello()47 throws 48 {49 try50 {51 this.h.invoke(this, m3, null);52 return;53 }54 catch (Error|RuntimeException localError)55 {56 throw localError;57 }58 catch (Throwable localThrowable)59 {60 throw new UndeclaredThrowableException(localThrowable);61 }62 }63 64 public final String toString()65 throws 66 {67 try68 {69 return (String)this.h.invoke(this, m2, null);70 }71 catch (Error|RuntimeException localError)72 {73 throw localError;74 }75 catch (Throwable localThrowable)76 {77 throw new UndeclaredThrowableException(localThrowable);78 }79 }80 81 public final int hashCode()82 throws 83 {84 try85 {86 return ((Integer)this.h.invoke(this, m0, null)).intValue();87 }88 catch (Error|RuntimeException localError)89 {90 throw localError;91 }92 catch (Throwable localThrowable)93 {94 throw new UndeclaredThrowableException(localThrowable);95 }96 }97 98 static99 { 100 try 101 { 102 m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); 103 m3 = Class.forName("com.doubibi.framework.util.proxy.HelloService").getMethod("sayHello", new Class[0]); 104 m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); 105 m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); 106 return; 107 } 108 catch (NoSuchMethodException localNoSuchMethodException) 109 { 110 throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); 111 } 112 catch (ClassNotFoundException localClassNotFoundException) 113 { 114 throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); 115 } 116 } 117 }源碼分析參考自
六、CGLib代理
七、CGLib原理粗略解析
CGLIB類庫結構
先看cglib包里有一個主要的包proxy包如下:
在該包中的Enhancer類和MethodInterceptor接口是整個包的核心所在!Enhancer就是“增強”的意思!主要用于生成動態子類以啟用方法攔截,什么意思?這樣子講!cglib類代理的基本思想就是對被代理類生成一個新的類(proxy),該類是繼承自被代理類的,并對被代理類方法執行前后執行一些操作,這些操作的通常就是一些回調操作,可以是MethodInterceptor,LazyLoader,CallbackFilter,其中MethodInterceptor是最常用的。所有被Enhancer關聯的對象默認都是實現Factory接口的,該接口提供了一組可以設置對象回調類型的方法,你可以通過調用setUseFactory(false)取消此特性!需要注意的是,cglib是無法代理final修飾的方法的,因為這是java語言規范決定的!MethodInterceptor是一個提供環繞通知的通用回調接口!Aop中有這樣的術語,那就是前置通知,后置通知,環繞通知,非常好理解,就是一個在方法執行前的通知,一個方法執行后的通知,另外一個就是方法執行前后都通知。該接口只有一個intercept()方法:
所有對被代理類方法的執行都會跳轉到這個方法上面來,而原來的方法則通過反射得到的Method對象或者MethodProxy對象進行調用。
八、CGLib代理例子
- 有一個沒有實現任何接口的實體類
- 實現一個MethodInterceptor,方法調用會被轉發到該類的intercept()方法。
- 然后在需要使用實體類的時候,通過CGLIB動態代理獲取代理對象。
實體類:
public class MyEntity {public void method() {System.out.println("method");} }實現一個MethodInterceptor:
import java.lang.reflect.Method; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class MyMethodInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy) throws Throwable {System.out.println("before:"+method.getName());Object object = proxy.invokeSuper(obj, arg);System.out.println("after:"+method.getName());return object;} }測試類:
public class Testcglib {public static void main(String[] args) {//該設置用于輸出cglib動態代理產生的類 //Enhancer用于操作底層字節碼,生成虛擬子類//System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"D:\\proxy"); Enhancer enhancer = new Enhancer();//繼承被代理類enhancer.setSuperclass(MyEntity .class);//設置回調enhancer.setCallback(new MyMethodInterceptor());//生成代理類對象MyEntity myEntity= (MyEntity)enhancer.create();//在調用代理類中方法時會被我們實現的方法攔截器進行攔截myEntity.method();} }詳細的CGLib源碼分析
九、JDK動態代理和CGLIB代理的區別
JDK動態代理模式只能代理接口,如果要代理類那么就不行了。而CGLIB則是代理類。因此,Spring AOP 會這樣子來進行切換,因為Spring AOP 同時支持 CGLIB、ASPECTJ、JDK動態代理,當你的真實對象有實現接口時,Spring AOP會默認采用JDK動態代理,否則采用cglib代理。同時CGLIB也有其缺陷,那就是必須目標類必須是可以繼承的,如果目標類不可繼承,那么我們就無法使用CGLIB來增強該類,我們可以稱JDK動態代理實現的AOP為面向接口的動態增強,將CGLIB實現的AOP稱為面向子類的動態增強。
在spring中:
總結
以上是生活随笔為你收集整理的Java静态代理、动态代理与CGLib代理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2021年中国人力资源服务行业研究报告
- 下一篇: 2021当代青年婚恋状态研究报告