一、前言
最近在看Spring的源碼,其中有牽扯到cglib的相關內容,遂簡單記錄下cglib是如何使用的
二、原理(節選自網絡)
CGLIB原理:動態生成一個要代理類的子類,子類重寫要代理的類的所有不是final的方法。在子類中採用方法攔截的技術攔截所有父類方法的調用,順勢織入橫切邏輯。它比使用java反射的JDK動態代理要快。
CGLIB底層:使用字節碼處理框架ASM,來轉換字節碼並生成新的類。不鼓勵直接使用ASM,因爲它要求你必須對JVM內部結構包括class文件的格式和指令集都很熟悉。
CGLIB缺點:對於final方法,無法進行代理。
三、一個核心類:Enhancer
Enhancer是CGLib的字節碼增強器,可以方便的對非final類進行擴展,它動態創建了給定類型的子類但是攔截了所有非final的方法。
3.1、簡單使用Enhancer
如何簡單的使用Enhancer:,看下面的幾個方法:
//設置需要代理的類
public void setSuperclass(Class superclass);
//設置一個Callback
public void setCallback(Callback callback);
Callback可以理解爲我們需要對原始類方法做的增強處理。
看下面的一段代碼:
public class CglibTest4 {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(QService.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("my callback");
methodProxy.invokeSuper(o,objects);
return o;
}
});
QService service = (QService) enhancer.create();
System.out.println("test1 方法~~~~~~~~~~~~~~");
service.test1();
System.out.println("test2 方法~~~~~~~~~~~~~~");
service.test2();
System.out.println("getClass 方法~~~~~~~~~~~~~~");
service.getClass();
System.out.println("hashCode 方法~~~~~~~~~~~~~~~");
service.hashCode();
}
}
class QService {
public void test1() {
System.out.println("test1");
}
public void test2() {
System.out.println("test2");
}
}
結果:
注意:
Enhancer 生成的代理類攔截了所有非final的方法(上面的例子中,攔截了test1方法、test2方法、hashCode方法,卻未攔截getClass方法)
3.2、只攔截部分方法
Enhancer會攔截原始類的所有非final方法,但是我們通常情況下只需要對部分方法(例如業務方法)進行一些增強。這個時候可以給Enhancer設置CallbackFilter。
還是用上面的例子,這次我們只對test1方法和test2方法進行增強。
public class CglibTest4 {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(QService.class);
Callback[] callbacks = new Callback[2];
callbacks[0] = NoOp.INSTANCE;
callbacks[1] = (MethodInterceptor) (o, method, objects, methodProxy) -> {
System.out.println("my callback");
methodProxy.invokeSuper(o,objects);
return o;
};
enhancer.setCallbackFilter(method -> {
if (method.getName().equals("test1") || method.getName().equals("test2")){
return 1;
}else {
return 0;
}
});
enhancer.setCallbacks(callbacks);
QService service = (QService) enhancer.create();
System.out.println("調用test1 方法~~~~~~~~~~~~~~");
service.test1();
System.out.println("");
System.out.println("調用test2 方法~~~~~~~~~~~~~~");
service.test2();
System.out.println("");
System.out.println("調用getClass 方法~~~~~~~~~~~~~~");
service.getClass();
System.out.println("");
System.out.println("調用hashCode 方法~~~~~~~~~~~~~~~");
service.hashCode();
}
}
class QService {
public void test1() {
System.out.println("test1");
}
public void test2() {
System.out.println("test2");
}
}
結果:
注意以下幾點:
1、設置了兩個Callback
2、設置了一個CallbackFilter
Enhancer支持設置多個Callback,進行多種不同方式的增強,而CallbackFilter的作用就是幫助代理類找到某個方法需要的Callback。上面的例子中,第一個Callback什麼也不會做,而第二個Callback則會針對test1方法和test2方法做一些增強。CallbackFilter是如何爲方法匹配所需的Callback呢?是通過CallbackFilter的 int accept(Method var1)方法的返回值確定的,這個返回值對應Callback[]中的下標。
四、幾種Callback
4.1、MethodInterceptor
MethodInterceptor,這是一個功能很強大的接口,它可以實現類似於AOP編程中的環繞增強(around-advice)。
第一次使用:
public class CglibTest {
public static void main(String[] args){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MyService.class);
Callback[] callbacks = new Callback[3];
callbacks[0] = new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("default callback");
methodProxy.invoke(o,objects);
return o;
}
};
callbacks[1] = new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("callback1");
methodProxy.invoke(o,objects);
return o;
}
};
callbacks[2] = new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("callback2");
methodProxy.invoke(o,objects);
return o;
}
};
enhancer.setCallbacks(callbacks);
enhancer.setCallbackFilter(new CallbackFilter() {
@Override
public int accept(Method method) {
if(method.getName().equals("method1")){
return 1;
}else if(method.getName().equals("method2")){
return 2;
}
return 0;
}
});
MyService myService = (MyService) enhancer.create();
myService.method1();
myService.method2();
}
}
class MyService {
public void method1() {
System.out.println("method1 execute");
}
public void method2() {
System.out.println("method2 execute");
}
}
本意是創建三個callback,method1用callback[1]來增強,method2用callback[2]來增強,但是卻出現了這樣的結果:
原因是出現了循環調用:callback1()->intercept()->callback1()->intercept()->… 最後導致棧溢出
後來修改成使用invokeSuper()正常運行:
public class CglibTest {
public static void main(String[] args){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MyService.class);
Callback[] callbacks = new Callback[3];
callbacks[0] = new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("default callback");
methodProxy.invokeSuper(o,objects);
return o;
}
};
callbacks[1] = new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("callback1");
methodProxy.invokeSuper(o,objects);
return o;
}
};
callbacks[2] = new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("callback2");
methodProxy.invokeSuper(o,objects);
return o;
}
};
enhancer.setCallbacks(callbacks);
enhancer.setCallbackFilter(new CallbackFilter() {
@Override
public int accept(Method method) {
if(method.getName().equals("method1")){
return 1;
}else if(method.getName().equals("method2")){
return 2;
}
return 0;
}
});
MyService myService = (MyService) enhancer.create();
myService.method1();
myService.method2();
}
}
class MyService {
public void method1() {
System.out.println("method1 execute");
}
public void method2() {
System.out.println("method2 execute");
}
}
注意:MethodInterceptor的實現方法中應該調用invokeSuper方法,避免出現循環調用
4.2、FixedValue
使用FixedValue可以很容易的替換掉方法的返回值。
public class CglibTest2 {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MService.class);
enhancer.setCallback(new FixedValue() {
@Override
public Object loadObject() throws Exception {
return "new method1";
}
});
MService mService = (MService) enhancer.create();
System.out.println(mService.method1());
}
}
class MService {
public String method1() {
return "method1";
}
}
結果:
4.3、NoOp
啥都不幹,只是調用原來的方法
public class CglibTest2 {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MService.class);
enhancer.setCallback(NoOp.INSTANCE);
MService mService = (MService) enhancer.create();
System.out.println(mService.method1());
}
}
class MService {
public String method1() {
return "method1";
}
}
4.4、LazyLoader
它提供了一個方法:Object loadObject() throws Exception;,loadObject()方法會在第一次被代理類的方法調用時觸發,它返回一個代理類的對象,這個對象會被存儲起來然後負責所有被代理類方法的調用,就像它的名字說的那樣,一種lazy模式。如果被代理類或者代理類的對象的創建比較麻煩,而且不確定它是否會被使用,那麼可以選擇使用這種lazy模式來延遲生成代理。
public class CglibTest3 {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Person.class);
enhancer.setCallback((LazyLoader) () -> {
try{
System.out.println(" before lazyloader");
return new Person("lg");
}finally {
System.out.println(" after lazyloader");
}
});
Person person = (Person) enhancer.create();
System.out.println(person.getName());
System.out.println(person.getName());
System.out.println(person.getName());
}
}
class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public Person(){
}
}
結果:
如果把下面的代碼注掉,則Person不會實例化:
//System.out.println(person.getName());
//System.out.println(person.getName());
//System.out.println(person.getName());
結果:
4.5、Dispatcher
Dispatcher和LazyLoader接口相同,也是提供了loadObject()方法,這個方法同樣地返回一個代理對象,這個對象同樣可以代理原方法的調用。不過它們之間不同的地方在於,Dispatcher的loadObject()方法在每次發生對原方法的調用時都會被調用並返回一個代理對象來調用原方法。也就是說Dispatcher的loadObject()方法返回的對象並不會被存儲起來,可以類比成Spring中的Prototype類型,而LazyLoader則是lazy模式的Singleton。
public class CglibTest3 {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Person.class);
enhancer.setCallback((Dispatcher) () -> {
try{
System.out.println(" before lazyloader");
return new Person("lg");
}finally {
System.out.println(" after lazyloader");
}
});
Person person = (Person) enhancer.create();
System.out.println(person);
System.out.println(person);
}
}
class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public Person(){
}
}
結果:
即,通過Dispatcher生成的對象,每次調用的時候,代理都會重新再執行一次loadObject方法