1. 程式人生 > >CGLIB實現AOP,MethodInterceptor介面和Enhancer詳解——Spring AOP(四)

CGLIB實現AOP,MethodInterceptor介面和Enhancer詳解——Spring AOP(四)

上一章講到了使用JDK的Proxy實現AOP:
https://blog.csdn.net/qq_34598667/article/details/83380628
這一章我們講另外一種方式,使用CGLIB實現AOP


使用CGLIB實現AOP功能

上一章我們已經說過,要產生某個物件的代理物件,這個物件必須實現一個介面,動態代理技術只能基於介面進行代理。
可是有時候我們在開發時碰到一些物件沒有介面,那想要為它建立一個代理物件是否可以為它建立一個介面呢?那當然是不行的!這些物件可能是伺服器或者容器給我們的,我們怎麼能隨便給它定義一個介面實現呢,不能水錶就給它找個乾爹吧…
這時候我們如果想為它建立代理物件,就要用到另外一種方法了——CGLIB,這個API即使沒有介面也能去建立這個物件的代理物件。

CGLIB產生代理物件的原理

實際上產生的是這個物件的子類,也即我們把一個物件交給CGLIB,它返回出來的似乎是一個代理物件,但其實這個代理物件就是這個物件的子類,利用子類的方式來建立代理物件。


案例講解

本章案例基於上一章案例做:
要使用CGLIB,需要匯入以下jar包:


修改UserServiceImpl類

修改UserServiceImpl類,去掉介面實現:

public class UserServiceImpl{
	//假設有該user是個User物件
	private String user=null;
	public String getUser(){
		return user;
	}
	public UserServiceImpl(){
	}
	public UserServiceImpl(String user){
		this.user=user;
	}
	public void add(String name) {
		System.out.println("我要增加");
	}
	public void delete(int id)
{ System.out.println("我要刪除"); } }

沒有實現介面,若想產生它的代理物件,就要使用CGLIB了
在com.oak.aop下新建一個類:CGLIBProxyFactory用於建立代理物件:

public class CGLIBProxyFactory implements MethodInterceptor{
	
	@Override
	public Object intercept(Object object, Method method, Object[] arg,
			MethodProxy methodProxy) throws Throwable {
		return null;
	}
}

Enhancer

Enhancer類是CGLib中的一個位元組碼增強器,作用用於生成代理物件,跟上一章所學的Proxy類相似,常用方式為:

Enhancer enhancer=new Enhancer();  
//將被代理類ConcreteClassNoInterface設定成父類,

enhancer.setSuperclass(ConcreteClassNoInterface.class);
  
//設定攔截器 回撥物件為本身物件
enhancer.setCallback(this);  

//執行enhancer.create()動態生成一個代理類,並從Object強制轉型成父型別ConcreteClassNoInterface。
ConcreteClassNoInterface ccni=(ConcreteClassNoInterface)enhancer.create(); 

下面我們使用它生成一個代理物件,在CGLIBProxyFactory中新建一個targetObject屬性作為代理的目標物件,新建createProxyInstance(Object targetObject)方法用於生產代理物件:

public class CGLIBProxyFactory implements MethodInterceptor{
	//代理的目標物件
	private Object targetObject;
	//產生一個代理物件
	public Object createProxyInstance(Object targetObject){
		this.targetObject=targetObject;
		//用於生成代理物件
		Enhancer enhancer=new Enhancer();
		//設定目標類為代理物件的父類
		enhancer.setSuperclass(this.targetObject.getClass());
		//設定回撥物件為本身
		enhancer.setCallback(this);
		//生成一個代理類物件
		return enhancer.create();
	}
	@Override
	public Object intercept(Object object, Method method, Object[] arg,
			MethodProxy methodProxy) throws Throwable {
		return null;
	}
}

MethodInterceptor介面–攔截器

在呼叫目標方法時,CGLib會回撥MethodInterceptor介面方法攔截,來實現你自己的代理邏輯,類似於JDK中的InvocationHandler介面。

public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy) throws Throwable
其中引數:
Object:由CGLib動態生成的代理類例項,
Method:上文中實體類所呼叫的被代理的方法引用,
Object[]:引數值列表,
MethodProxy:生成的代理類對方法的代理引用。

返回值

返回:從代理例項的方法呼叫返回的值。

proxy.invokeSuper(obj,arg):

呼叫代理類例項上的proxy方法的父類方法,即真實物件中的方法

編寫CGLIBProxyFactory中回撥的intercept方法

public class CGLIBProxyFactory implements MethodInterceptor{
	//代理的目標物件
	private Object targetObject;
	//產生一個代理物件
	public Object createProxyInstance(Object targetObject){
		this.targetObject=targetObject;
		//用於生成代理物件
		Enhancer enhancer=new Enhancer();
		//設定目標類為代理物件的父類
		enhancer.setSuperclass(this.targetObject.getClass());
		//設定回撥物件為本身
		enhancer.setCallback(this);
		//生成一個代理類物件
		return enhancer.create();
	}
	@Override
	public Object intercept(Object object, Method method, Object[] arg,
			MethodProxy methodProxy) throws Throwable {
		UserServiceImpl service=(UserServiceImpl) this.targetObject;
		Object result = null;
		//在代理真實物件方法執行前我們可以新增一些自己的操作
		System.out.println("Before 真實物件方法執行...");
		if(service.getUser()!=null){//判斷許可權
			result=methodProxy.invoke(targetObject, arg);
		}
	    //在代理真實物件方法執行後我們也可以新增一些自己的操作
		System.out.println("After 真實物件方法執行...");
		return result;
	}
}

測試

新加測試方法testCglibProxy:

@Test
public void testCglibProxy(){
	//UserServiceImpl不設定user值--沒有物件
	UserServiceImpl service=(UserServiceImpl) new CGLIBProxyFactory().
			createProxyInstance(new UserServiceImpl());
	service.add("哈哈");
}

執行檢視控制檯,沒有許可權,真實物件的方法沒有執行:

Before 真實物件方法執行...
After 真實物件方法執行...

給UserServiceImpl的user賦值,給許可權:

@Test
public void testCglibProxy(){
	//UserServiceImpl不設定user值--沒有物件
	UserServiceImpl service=(UserServiceImpl) new CGLIBProxyFactory().
			createProxyInstance(new UserServiceImpl("狗子"));
	service.add("哈哈");
}

測試,控制檯:

Before 真實物件方法執行...
我要增加
After 真實物件方法執行...

已經有了許可權,真實物件執行了add方法。


Spring中的AOP程式設計

Spring裡面有一個AOP程式設計。即面向切面程式設計,其實就是動態代理。當我們交給Spring一個物件,它就會返回代理給我們,它在返回代理物件的時候,首先會檢查我們這個物件有沒有實現一個介面,如果我們這個類有介面,它使用Java的動態代理技術來幫我們構建出代理物件;如果我們這個類沒有實現介面,它會使用CGLIB這套API,採用建立子類的方式來建立代理物件
所以我們所實現AOP動態代理的兩種方式就是JDK的Proxy和CGLIB。