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包:
- asm-2.2.3.jar
- cglib-nodep-2.2.jar
沒有jar包的可以去1積分下載:
https://download.csdn.net/download/qq_34598667/10746196
修改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。