AOP底層原理之CGlib

else發表於2021-09-09

什麼是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, 實現代理.

  1. 實現一個帶攔截器的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;
    }
    
}
  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();
    }
  1. 返回相同的值

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);
}
  1. 不同方法實現不同的攔截

實現程式碼如下:

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);

}

  1. 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) {
	
}

}

  1. 建立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/,如需轉載,請註明出處,否則將追究法律責任。

相關文章