日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

动态代理之Cglib浅析

發(fā)布時間:2023/12/13 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 动态代理之Cglib浅析 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

什么是Cglib

Cglib是一個強大的,高性能,高質(zhì)量的代碼生成類庫。它可以在運行期擴展JAVA類與實現(xiàn)JAVA接口。其底層實現(xiàn)是通過ASM字節(jié)碼處理框架來轉(zhuǎn)換字節(jié)碼并生成新的類。大部分功能實際上是ASM所提供的,Cglib只是封裝了ASM,簡化了ASM操作,實現(xiàn)了運行期生成新的class。

Cglib的原理

運行時動態(tài)的生成一個被代理類的子類(通過ASM字節(jié)碼處理框架實現(xiàn)),子類重寫了被代理類中所有非final的方法。在子類中采用方法攔截的技術(shù)攔截所有父類方法的調(diào)用,順勢植入橫切邏輯。

Cglib優(yōu)缺點

優(yōu)點:JDK動態(tài)代理要求被代理的類必須實現(xiàn)接口,當需要代理的類沒有實現(xiàn)接口時Cglib代理是一個很好的選擇。另一個優(yōu)點是Cglib動態(tài)代理比使用java反射的JDK動態(tài)代理要快(Cglib的FastClass機制,解析參考http://www.cnblogs.com/cruze/p/3865180.html#lable3)

缺點:對于被代理類中的final方法,無法進行代理,因為子類中無法重寫final函數(shù)

Cglib類庫

net.sf.cglib.core:底層字節(jié)碼處理類,他們大部分與ASM有關(guān)系。

net.sf.cglib.transform:編譯期或運行期類和類文件的轉(zhuǎn)換

net.sf.cglib.proxy:實現(xiàn)創(chuàng)建代理和方法攔截器的類

net.sf.cglib.reflect:實現(xiàn)快速反射和C#風格代理的類

net.sf.cglib.util:集合排序等工具類

net.sf.cglib.beans:JavaBean相關(guān)的工具類

最主要的一個接口CallBack接口,即回調(diào)接口,下面的攔截器、過濾器、延遲加載都繼承于它,結(jié)構(gòu)圖如下:

?

Cglib的攔截器和過濾器

攔截器:實現(xiàn)MethodInterceptor接口的類,在intercept方法中實現(xiàn)對代理目標類的方法攔截。但同時Cglib為簡化和提高性能提供了一些專門的回調(diào)類型如FixedValue(可以在實現(xiàn)的方法loadObject中指定返回固定值,而不調(diào)用目標類函數(shù))、NoOp(把對回調(diào)方法的調(diào)用直接委派到這個方法的父類,即不進行攔截)

過濾器:實現(xiàn)CallbackFilter接口的類,通過accept方法返回一個下標值,用于指定調(diào)用哪個攔截器進行攔截處理

/*** @author longe* 被代理類*/ public class TDao {public void create() {System.out.println("create() is running !");}public void query() {System.out.println("query() is running !");}public void update() {System.out.println("update() is running !");}public void delete() {System.out.println("delete() is running !");} }/*** @author longe* 過濾器*/ public class TDaoCglibFilter implements CallbackFilter {public final static int NO_RESTRICTION = 0;public final static int RESTRICTION_CREATE = 1;/* (non-Javadoc)* 根據(jù)調(diào)用的方法返回使用的callbacks下標*/@Overridepublic int accept(Method method) {if(method.getName().startsWith("create")){return RESTRICTION_CREATE;}return NO_RESTRICTION;} }/*** @author longe* 攔截器*/ public class TDaoCglibProxy implements MethodInterceptor {private String name;private Object objT;public TDaoCglibProxy(String name) {this.name = name;}public TDaoCglibProxy(Object obj,String name) {this.name = name;this.objT = obj;}@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {if(!name.equals("張三")){System.out.println("沒有權(quán)限!");return null;} // return method.invoke(objT, args); //通過反射進行調(diào)用return proxy.invokeSuper(obj, args); //使用Cglib代理調(diào)用 }public static void main(String[] args) {Enhancer en = new Enhancer();en.setSuperclass(TDao.class);TDaoCglibProxy callBack = new TDaoCglibProxy("張三1"); // 指定兩個callback,通過callBackFilter返回的下標值控制調(diào)用使用那一個callback處理en.setCallbacks(new Callback[] { NoOp.INSTANCE, callBack }); // 方法過濾器,根據(jù)過濾器返回不同的值回調(diào)對應(yīng)下標的Callback方法en.setCallbackFilter(new TDaoCglibFilter());TDao dao = (TDao) en.create();dao.create();dao.query();dao.update();dao.delete();}} 代碼示例 執(zhí)行輸出如下:沒有權(quán)限! query() is running ! update() is running ! delete() is running !

可以看到只用create方法的調(diào)用被TDaoCglibProxy攔截器攔截了,從而判斷出沒有權(quán)限。而其他方法沒有被TDaoCglibProxy攔截器攔截,是因為在Filter過濾器中返回的索引為0,即指定的回調(diào)callback是NoOp,索引直接調(diào)用了父類的實現(xiàn)方法(即被代理類的實現(xiàn))

Cglib的延遲加載

LazyLoader:當實際對象需要延遲加載時,可以使用LazyLoader回調(diào)。一旦實際對象被裝載,它將被每一個調(diào)用代理對象的方法使用(即實際對象被訪問時才調(diào)用此回調(diào)callback的loadObject方法)

Dispatcher:和LazyLoader回調(diào)具有相同的特點,區(qū)別是當代理方法被調(diào)用時,裝載對象的方法也總是要被調(diào)用

ProxyRefDispatcher:與Dispatcher一樣,只不過可以把代理對象作為參數(shù)進行傳遞

public class CglibLazyTest extends TestCase {public void testLazyBean() {TB tb = new TB();System.out.println("***************************************");System.out.println("cid's value: " + tb.getCid());System.out.println("***************************************");System.out.println("bean's username value: " + tb.getBean()); // LazyLoader 這里將返回同一個對象System.out.println("bean's username value: " + tb.getBean());System.out.println("***************************************");System.out.println("bean2's username value: " + tb.getBean2()); // 這里將返回一個新的對象System.out.println("bean2's username value: " + tb.getBean2());}/*** @author longe* 通過實現(xiàn)LazyLoader接口延遲加載*/class Lazy implements LazyLoader {@Overridepublic Object loadObject() throws Exception {TestBean tb = new TestBean();System.out.println("lazy load ========= ");tb.setUserName("longe lazy");return tb;}}/*** @author longe* 通過實現(xiàn)Dispatcher接口延遲加載*/class DispacherTest implements Dispatcher {@Overridepublic Object loadObject() throws Exception {TestBean2 tb2 = new TestBean2();System.out.println("dispatcher load =========== ");tb2.setNo("longe dispatcher lazy");return tb2;}}/*** @author longe* 與Dispatcher一樣,只不過可以把代理對象作為參數(shù)進行傳遞*/class ProxyRefDispacherTest implements ProxyRefDispatcher {@Overridepublic Object loadObject(Object proxy) throws Exception {return null;}}class TB {private String cid;private TestBean bean;private TestBean2 bean2;LazyLoader lazy = new Lazy();Dispatcher dis = new DispacherTest();public TB() {cid = "no lazy";// Enhancer en = new Enhancer();// en.setSuperclass(TestBean.class);// en.setCallback(lazy);// bean = (TestBean) en.create(new Class[]{CglibLazyTest.class},new// Object[]{new CglibLazyTest()});bean = (TestBean) Enhancer.create(TestBean.class, lazy);bean2 = (TestBean2) Enhancer.create(TestBean2.class, dis);System.out.println("TB construct end...");}public TestBean getBean() {return bean;}public String getCid() {return cid;}public void setCid(String cid) {this.cid = cid;}public void setBean(TestBean bean) {this.bean = bean;}public TestBean2 getBean2() {return bean2;}public void setBean2(TestBean2 bean2) {this.bean2 = bean2;}}}class TestBean {public TestBean() {System.out.println("TestBean construct end...");}private String userName;public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;} }class TestBean2 {public TestBean2() {System.out.println("TestBean2 construct end...");}private String no;public String getNo() {return no;}public void setNo(String no) {this.no = no;} } 代碼示例 執(zhí)行輸出如下:

TestBean construct end...
TestBean2 construct end...
TB construct end...
***************************************
cid's value: no lazy
***************************************
TestBean construct end...
lazy load =========
bean's username value: practices.model.proxy.cglib.TestBean@77521871
bean's username value: practices.model.proxy.cglib.TestBean@77521871
***************************************
TestBean2 construct end...
dispatcher load ===========
bean2's username value: practices.model.proxy.cglib.TestBean2@2ec2dfea
TestBean2 construct end...
dispatcher load ===========
bean2's username value: practices.model.proxy.cglib.TestBean2@7bfa93a1

上面的示例中TestBean對象使用了LazyLoader回調(diào),TestBean2對象使用了Dispatcher回調(diào)。

分析:

  • 在TB的構(gòu)造函數(shù)中創(chuàng)建了TestBean和TestBean2對象,此時這兩個對象分別被創(chuàng)建,但是是空對象。
  • 當tb.getBean().getUserName()第一次訪問TestBean對象時,此時LazyLoader回調(diào)被調(diào)用,TestBean裝載并進行初始化賦值,最終返回裝載的新對象。
  • 當tb.getBean().getUserName()第二次訪問TestBean對象時,TestBean已經(jīng)被裝載過,此時LazyLoader回調(diào)不被調(diào)用。
  • 當tb.getBean2().getNo()第一次訪問TestBean2對象時,此時Dispatcher回調(diào)被調(diào)用,TestBean2裝載并進行初始化賦值,最終返回裝載的新對象。
  • 當tb.getBean2().getNo()第二次訪問TestBean2對象時,此時Dispatcher回調(diào)還是會被調(diào)用,TestBean2裝載并進行初始化賦值,最終返回一個新裝載的新對象
  • Cglib中的Mixin

    net.sf.cglib.proxy.Mixin允許多個對象被綁定到一個單個的大對象。在代理中對方法的調(diào)用委托到下面對應(yīng)的對象中。

    通過指定代理類型和實際的代理對象參數(shù)即可直接創(chuàng)建代理。可以綁定多個,但需要注意類型數(shù)組與對象數(shù)組的一一對應(yīng)。如:

    public static void testCglibMixin() {System.out.println(MixinIfA.class.getName());Class[] ints = new Class[]{MixinIfA.class,MixinIfB.class};Object[] objs = new Object[]{new MixinAImpl(),new MixinBImpl()};Object obj = Mixin.create(ints, objs);MixinIfA a = (MixinIfA) obj;MixinIfB b = (MixinIfB) obj;a.methodA();b.methodB();}

    ?public static Mixin create(Class[] interfaces, Object[] delegates)中第一個參數(shù)必須是接口類型,第二個參數(shù)是接口實現(xiàn)類對象實例

    OK,到這Cglib代理了解的差不多了,從Cglib的動態(tài)代理知道它可以動態(tài)的生成java類,那么這里順帶貼上一段簡單的Cglib生成bean的代碼.(供后續(xù)復習用...)

    /*** @author longe* 動態(tài)生成bean*/ public class CglibBean {public Object object;public BeanMap beanMap;public CglibBean() {}public CglibBean(Map<String, Class> propertyMap) {this.object = generateBean(propertyMap);this.beanMap = BeanMap.create(this.object);}public void putValue(String property, Object value) {beanMap.put(property, value);}public Object getValue(String property) {return beanMap.get(property);}public Object getObject() {return this.object;}public Object generateBean(Map<String, Class> propertyMap) {BeanGenerator generator = new BeanGenerator();Set<String> keySet = propertyMap.keySet();for (String key : keySet) {generator.addProperty(key, propertyMap.get(key));}return generator.create();}public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {Map<String, Class> propertyMap = new HashMap<>();propertyMap.put("id", Class.forName("java.lang.Integer"));propertyMap.put("name", Class.forName("java.lang.String"));propertyMap.put("address", Class.forName("java.lang.String"));CglibBean cglibBean = new CglibBean(propertyMap);cglibBean.putValue("id", 101);cglibBean.putValue("name", "longe");cglibBean.putValue("address", "beijing");System.out.println(cglibBean.getValue("id"));System.out.println(cglibBean.getValue("name"));System.out.println(cglibBean.getValue("address"));Object object = cglibBean.getObject();Method[] ms = object.getClass().getDeclaredMethods();for (Method method : ms) {System.out.println(method.getName());if (method.getName().startsWith("get")) {System.out.println("=====" + method.invoke(object, null));}}} } View Code

    Cglib異常

    java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given at net.sf.cglib.proxy.Enhancer.emitConstructors(Enhancer.java:721) at net.sf.cglib.proxy.Enhancer.generateClass(Enhancer.java:499) at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25) at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216) at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377) at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:285) at practices.model.proxy.cglib.CglibLazyTest.testLazyBean1(CglibLazyTest.java:21) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at junit.framework.TestCase.runTest(TestCase.java:176) at junit.framework.TestCase.runBare(TestCase.java:141) at junit.framework.TestResult$1.protect(TestResult.java:122) at junit.framework.TestResult.runProtected(TestResult.java:142) at junit.framework.TestResult.run(TestResult.java:125) at junit.framework.TestCase.run(TestCase.java:129) at junit.framework.TestSuite.runTest(TestSuite.java:252) at junit.framework.TestSuite.run(TestSuite.java:247) at org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:86) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

    原因:

    • 代理目標對象不能是內(nèi)部類(因為內(nèi)部類的創(chuàng)建依賴外部類),如果是內(nèi)部類,cglib代理內(nèi)部會獲取到一個有參構(gòu)造函數(shù)(參數(shù)是外部類對象,如果實在需要代理一個內(nèi)部類,可以通過傳遞構(gòu)造參數(shù)實現(xiàn))
    • Cglib代理默認創(chuàng)建一個缺省構(gòu)造函數(shù)的目標對象,如果目標對象存在有參構(gòu)造函數(shù),Cglib進行代理時需要指定構(gòu)造函數(shù)的參數(shù),或者在目標對象上必須存在缺省構(gòu)造函數(shù),否則拋出異常(可以通過傳遞構(gòu)造參數(shù)創(chuàng)建代理類)

    轉(zhuǎn)載于:https://www.cnblogs.com/mr-long/p/5889054.html

    總結(jié)

    以上是生活随笔為你收集整理的动态代理之Cglib浅析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。