AOP底層原理之CGlib
什麼是CGlib
Cglib的官方解釋: Byte Code Generation Library is high level API to generate and transform Java byte code. It is used by AOP, testing, data access frameworks to generate dynamic proxy objects and intercept field access.
所以, cglib是一個功能強大、高效能和高質量的程式碼生成庫,它用於擴充套件JAVA類並在執行時實現介面。
AOP, SPring AOP, Hibernate等都在使用CGlib, EasyMock和jMock是使用模擬物件測試Java程式碼的庫。它們都使用CGLIB庫為沒有介面的類建立模擬物件。
CGLIB庫使用ASM(一個小型但快速的位元組碼操作框架)轉換現有的位元組碼並生成新類。除了CGLIB庫,指令碼語言(如Groovy和BeanShell)也使用ASM生成Java位元組碼。ASM使用類似SAX的解析器機制來實現高效能。
圖中顯示了CGLIB庫相關框架和語言之間的關係。注意,一些框架,如Spring AOP和Hibernate,經常同時使用CGLIB庫和JDK動態代理來滿足它們的需求。Hibernate使用JDK動態代理為WebShere應用伺服器實現事務管理器介面卡;預設情況下,Spring AOP使用JDK動態代理來代理介面,除非強制使用CGLIB代理。
下面舉例介紹下CGLib的典型應用:
準備工作, 建立一個Bean類:
package com.xqljj;
public class Dao {
public Dao() {
update();
}
public void update() {
System.out.println("PeopleDao.update()");
}
public void select() {
System.out.println("PeopleDao.select()");
}
}
1.簡單的代理模式實現
我們首先實現一個DaoProxy
package com.xqljj;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class DaoProxy implements MethodInterceptor {
@Override
public Object intercept(Object object, Method method, Object[] objects, MethodProxy proxy) throws Throwable {
System.out.println("Before Method Invoke");
proxy.invokeSuper(object, objects);
System.out.println("After Method Invoke");
return object;
}
}
那麼在呼叫的時候,直接呼叫
public static void testCglib() {
DaoProxy daoProxy = new DaoProxy();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Dao.class);
enhancer.setCallback(daoProxy);
Dao dao = (Dao)enhancer.create();
dao.update();
dao.select();
}
透過setCallback, 實現代理.
- 實現一個帶攔截器的Proxy
package com.xqljj;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class DaoAnotherProxy implements MethodInterceptor {
@Override
public Object intercept(Object object, Method method, Object[] objects, MethodProxy proxy) throws Throwable {
System.out.println("StartTime=[" + System.currentTimeMillis() + "]");
//method.invoke(object, objects);
proxy.invokeSuper(object, objects);
System.out.println("EndTime=[" + System.currentTimeMillis() + "]");
return object;
}
}
在Enhancer裡面set filter, filter可以根據需求來定製:
package com.xqljj;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.CallbackFilter;
public class DaoFilter implements CallbackFilter {
@Override
public int accept(Method method) {
if ("select".equals(method.getName())) {
return 0;
}
return 1;
}
}
- 建構函式不攔截方法
那麼在enhancer.setInterceptDuringConstruction(false);
public static void testCglib3() {
DaoProxy daoProxy = new DaoProxy();
DaoAnotherProxy daoAnotherProxy = new DaoAnotherProxy();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Dao.class);
enhancer.setCallbacks(new Callback[]{daoProxy, daoAnotherProxy, NoOp.INSTANCE});
enhancer.setCallbackFilter(new DaoFilter());
enhancer.setInterceptDuringConstruction(false);
Dao dao = (Dao)enhancer.create();
dao.update();
dao.select();
}
- 返回相同的值
Bean類:
package com.xqljj;
public class PersonService {
public String sayHello(String name) {
return "Hello " + name;
}
public Integer lengthOfName(String name) {
return name.length();
}
}
對於上面的類,我們建立一個攔截器,來攔截SayHello方法.Cglib裡面Enhancer,我們可以利用它來建立一個動態代理. enhancer.setSuperclass(PersonService.class);
我們想要每次都返回相同的值,實現方法如下:
public static void fixvalue() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PersonService.class);
enhancer.setCallback((FixedValue) () -> "Hello Tom!");
PersonService proxy = (PersonService) enhancer.create();
String res = proxy.sayHello("HAHA");
System.out.println(res);
}
- 不同方法實現不同的攔截
實現程式碼如下:
public static void methodDepend() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PersonService.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
if (method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) {
return “Hello Tom!”;
} else {
return proxy.invokeSuper(obj, args);
}
});
PersonService proxy = (PersonService) enhancer.create();
System.out.println(proxy.sayHello(null));
//assertEquals("Hello Tom!", proxy.sayHello(null));
int lengthOfName = proxy.lengthOfName("Mary");
//assertEquals(4, lengthOfName);
System.out.println(lengthOfName);
}
- Cglib實現BeanGenerator
public static void beanGenerator() {
BeanGenerator beanGenerator = new BeanGenerator();
beanGenerator.addProperty("name", String.class);
Object myBean = beanGenerator.create();
try {
Method setter = myBean.getClass().getMethod("setName", String.class);
setter.invoke(myBean, "some string value set by a cglib");
Method getter = myBean.getClass().getMethod("getName");
System.out.println(getter.invoke(myBean));
} catch (Exception e) {
}
}
- 建立Mixin
mixin 就是可以merge多個物件成一個,Cglib支援把多個物件合併成一個, 當然,這些物件需要實現Interface.
舉例如下:
public interface Interface1 {
String first();
}
public interface Interface2 {
String second();
}
public class Class1 implements Interface1 {
@Override
public String first() {
return "first behaviour";
}
}
public class Class2 implements Interface2 {
@Override
public String second() {
return "second behaviour";
}
}
為了合併這兩個Interface, 我們需要定義一個mergeinterface
public interface MixinInterface extends Interface1, Interface2 { }
合併後, 呼叫Mixin.create
public static void testMiXin() {
Mixin mixin = Mixin.create(
new Class[]{ Interface1.class, Interface2.class, MixinInterface.class },
new Object[]{ new Class1(), new Class2() }
);
MixinInterface mixinDelegate = (MixinInterface) mixin;
System.out.println(mixinDelegate.first());
System.out.println(mixinDelegate.second());
}
執行,列印的結果是:
first behaviour
second behaviour
總結:
Cglib廣泛用於Spring,Hibernate,Mock等,是一個高效能的程式碼生成庫,還有很多使用場景,例如Java Security, Java序列化等,需要仔細摸索.
另外,以上程式碼,好多沒有寫列印結果,但作者都一一編譯執行過了,你不妨也試試,可以有很深的體會.
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2318/viewspace-2822758/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 15-Spring AOP的底層實現原理JDKProxy&CGLIBSpringJDKCGLib
- 【Spring AOP】AOP 底層實現原理 —— 動態代理類的建立(JDK、CGlib)、工廠如何加工原始物件SpringJDKCGLib物件
- php底層原理之函式PHP函式
- php底層原理之變數(二)PHP變數
- php底層原理之變數(一)PHP變數
- PHP 底層原理之類和物件PHP物件
- 分散式之系統底層原理分散式
- Android之Context底層原理AndroidContext
- OC底層探索(十六) KVO底層原理
- Spring AOP 實現原理與 CGLIB 應用SpringCGLib
- synchronized底層原理synchronized
- php底層原理之垃圾回收機制PHP
- php底層原理之陣列實現PHP陣列
- Spring AOP概述、底層實現Spring
- 面試必備之MYSQL索引底層原理分析面試MySql索引
- RunLoop底層原理探究OOP
- RabbitMq底層原理分析MQ
- HashMap原理底層剖析HashMap
- HashMap的底層原理HashMap
- iOS底層原理-CategoryiOSGo
- ArrayList集合底層原理
- ConcurrentHashMap底層原理HashMap
- HashMap原理詳解,包括底層原理HashMap
- HashMap底層實現原理HashMap
- 理解PHP底層原理(一)PHP
- 底層原理面試彙總面試
- 初步理解 JavaScript 底層原理JavaScript
- Netty的底層原理Netty
- Spring Cloud底層原理SpringCloud
- NSDictionary底層實現原理
- Vue中的底層原理Vue
- ArrayList底層原理淺析
- HashMap的底層原理分析HashMap
- 底層原理探究(二)RunLoopOOP
- AutoreleasePool底層實現原理
- iOS底層原理探究-RunloopiOSOOP
- golang select底層原理Golang
- InnoDB索引與底層原理索引