cglib代理的使用

擁抱心中的夢想發表於2018-03-28

一、什麼是CGLIB?

總的來說,無論是cglib、jdk動態代理又或者是aop面向切面程式設計,都運用到了一個最重要的設計模式--代理模式!萬變不離其終,學好代理模式,打遍天下無敵手!

cglib就是一個位元組碼生成轉換的庫嘛!這倒是不難理解,它主要被AOP,測試,資料訪問框架用來生成動態代理物件攔截欄位訪問

今天我們就來說說cglib在代理方面的應用!

二、CGLIB原始碼粗略解讀!

首先我們來看看cglib原始碼的包結構:

cglib代理的使用

從cglib核心包中可以看到有個proxy的包,我們一起去探個究竟!展開proxy可以發現:

cglib代理的使用

在該包中的Enhancer類和MethodInterceptor介面是整個包的核心所在!Enhancer就是“增強”的意思嘛!主要用於生成動態子類以啟用方法攔截,什麼意思?這樣子講!cglib類代理的基本思想就是對被代理類生成一個新的類(proxy),該類是繼承自被代理類的,並對被代理類方法執行前後執行一些操作,這些操作的通常就是一些回撥操作,可以是MethodInterceptorLazyLoader,CallbackFilter,其中MethodInterceptor是最常用的。

所有被Enhancer關聯的物件預設都是實現Factory介面的,該介面提供了一組可以設定物件回撥型別的方法,你可以通過呼叫setUseFactory(false)取消此特性!

需要注意的是,cglib是無法代理final修飾的方法的,因為這是java語言規範決定的!

MethodInterceptor是一個提供環繞通知的通用回撥介面!Aop中有這樣的術語,那就是前置通知後置通知環繞通知,非常好理解,就是一個在方法執行前的通知,一個方法執行後的通知,另外一個就是方法執行前後都通知。

該介面只有一個intercept()方法:

 public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
                               MethodProxy proxy) throws Throwable;
複製程式碼

所有對被代理類方法的執行都會跳轉到這個方法上面來,而原來的方法則通過反射得到的Method物件或者MethodProxy物件進行呼叫。

三、老規矩,來個栗子一起團圓!

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

class Student {

	private String name = "zhangsan";

	public String getStuName() {
		return name;
	}

}

public class CglibMethodInterceptTest {

	public static void main(String[] args) {
		//建立一個Enhancer物件
		Enhancer enchaner = new Enhancer();
		//設定被代理的類
		enchaner.setSuperclass(Student.class);
		//建立一個回撥介面
		Callback interceptor = new MethodInterceptor() {

			@Override
			public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
					throws Throwable {
				System.err.println("原方法名是 : " + method.getName());
				System.err.println("原方法宣告的類為 " + method.getDeclaringClass());
				System.err.println("我是 " + (String) proxy.invokeSuper(obj, args));
				System.err.println("我呼叫結束了");
				return null;
			}
		};
		enchaner.setCallback(interceptor);
		Student student = (Student) enchaner.create();
		student.getStuName();

	}
}
複製程式碼

輸出的結果為:

原方法名是 : getStuName
原方法宣告的類為 class wokao666.test.Student
我是 zhangsan
我呼叫結束了
複製程式碼

過濾器的使用

package wokao666.test;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import net.sf.cglib.proxy.NoOp;

class Student {

	private String name = "zhangsan";
	private String rename = "rename";

	public String getStuName() {
		return name;
	}

	public String getRename() {
		return rename;
	}

}

public class CglibMethodInterceptTest {

	public static void main(String[] args) {
		//建立一個Enhancer物件
		Enhancer enchaner = new Enhancer();
		//設定被代理的類
		enchaner.setSuperclass(Student.class);

		//建立一個回撥介面
		Callback interceptor = new MethodInterceptor() {

			@Override
			public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
					throws Throwable {
				System.err.println("原方法名是 : " + method.getName());
				System.err.println("原方法宣告的類為 " + method.getDeclaringClass());
				System.err.println("我是 " + (String) proxy.invokeSuper(obj, args));
				System.err.println("我呼叫結束了");
				return proxy.invokeSuper(obj, args);
			}
		};
		CallbackFilter callbackFilter = new CallbackFilter() {

			@Override
			public int accept(Method method) {
				int flag = 0;
				if ("getStuName".equals(method.getName())) {
					System.err.println("我將此方法過濾掉了,不對該方法進行攔截");
					return 1;
				}
				return 0;
			}
		};
		Callback[] callbacks = new Callback[] { interceptor, NoOp.INSTANCE };
		enchaner.setCallbackFilter(callbackFilter);
		enchaner.setCallbacks(callbacks);
		Student student = (Student) enchaner.create();
		System.err.println(student.getStuName());
		System.err.println(student.getRename());
	}
}
複製程式碼
我將此方法過濾掉了,不對該方法進行攔截
zhangsan
原方法名是 : getRename
原方法宣告的類為 class wokao666.test.Student
我是 rename
我呼叫結束了
rename
複製程式碼

NoOp.INSTANCE:這個NoOp表示no operator,即什麼操作也不做,代理類直接呼叫被代理的方法不進行攔截。

getStuName對應的CallbackFilter中定義的索引1,在Callback[]陣列中使用的過濾為NoOp,因此直接執行了被代理方法。

getRename對應CallbackFilter中定義的索引0,在Callback[]陣列中使用的過濾為interceptor,因此執行了方法攔截器進行攔截。

我寫的只是一點皮毛,建議大家在此基礎上多動手,結合原始碼多寫寫一些例子,寫多了,懂得就多了!

相關文章