AOP與動態代理淺析


AOP是Spring最重要的功能之一,通過切面實現對象增強,降低對業務的侵入,把核心業務代碼和周邊公共代碼解耦。

比如,在方法上加上@Transactional注解,就能夠使用Spring的默認事務機制,通過TransactionInterceptor實現切面,在before時提供調用點,決定業務方法是否需要開啟一個新的獨立事務,在after時確定事務被提交、回滾、還是繼續運行。這就不需要在每個方法里都重復寫一遍事務操作的相關代碼。

 

AOP通過動態創建代理對象實現,動態代理的方式分為JDK動態代理和CGLIB,以下簡述這兩種代理方式的使用

JDK動態代理

先看使用,需要通過Proxy類的 newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法來生成指定類型的代理對象,該類必須有實現的接口。

 

 使用舉例:

public interface IUserInfoService {
Integer saveUserInfo(UserInfo userInfo);
}

 

public class UserInfoServiceImpl implements IUserInfoService {
@Override
public Integer saveUserInfo(UserInfo userInfo) {
System.out.println(userInfo.getUserName());
return userInfo.getId();
}
}
public static void main(String[] arg) {
UserInfo userInfo = new UserInfo();
userInfo.setId(1);
userInfo.setUserName("小明");

IUserInfoService userInfoService = new UserInfoServiceImpl();
//創建代理對象
IUserInfoService userInfoServiceProxy = (IUserInfoService) Proxy.newProxyInstance(userInfoService.getClass().getClassLoader(),
       //傳入要代理的接口
userInfoService.getClass().getInterfaces(),
       //傳入InvocationHandler接口實現類,提供切面邏輯
(proxy, method, args) -> {
          //實現增強
System.out.println("方法執行前");
Object obj = method.invoke(userInfoService, args);
System.out.println("方法執行后");
return obj;
});
System.out.println("Id:" + userInfoServiceProxy.saveUserInfo(userInfo));
}

 

主要原理是,通過代理類生成器ProxyGenerator直接生成代理類的Class文件而不需要再通過虛擬機再加載。

生成過程是根據Class文件結構拼接而成的,源碼分析可以參考:https://www.cnblogs.com/liuyun1995/p/8157098.html

生成的代理類默認繼承Porxy類,實現傳入的接口。

這也就解釋了被代理的類需要實現接口,才能讓代理類收集到需要代理的方法。

拿到Class文件后,通過getConstrucator()方法創建只有InvocationHandler類型參數的構造函數,然后通過newInstance()方法生成代理對象實例。

代理方法會調用InvocationHandler的invoke()方法,為被代理類的方法實現增強。

 

CGLIB

CGLIB全稱Code Generation Library,意為代碼生成庫。

CGLIB代理也是通過生成字節碼文件生成新的代理對象,它的思想是通過繼承代理類的非final方法,然后在重寫方法時寫入增強。

 

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserInfoServiceImpl.class);
enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
  //實現增強
System.out.println("方法執行前");
Object obj = methodProxy.invokeSuper(o, objects);
System.out.println("方法執行后");
return obj;
});
//創建被代理對象
UserInfoServiceImpl userInfoService = (UserInfoServiceImpl) enhancer.create();
System.out.println("Id:" + userInfoService.saveUserInfo(userInfo));

Enhancer相當於JDK代理中的Proxy,但它既能代理普通類,也能夠代理接口。

 

這兩種代理相比之下,一般認為CGLIB代理方式創建慢執行快,JDK動態代理創建快執行較慢。

AOP

Spring根據策略判斷使用JDK動態代理和還是CGLIB

@Override
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    // config.isOptimize()是否強制使用優化代理策略,默認false

    // config.isProxyTargetClass()  目標類本身被代理而不是目標類的接口

    // hasNoUserSuppliedProxyInterfaces() 是否存在代理接口

        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            Class<?> targetClass = config.getTargetClass(); if (targetClass == null) { throw new AopConfigException("TargetSource cannot determine target class: " + "Either an interface or a target is required for proxy creation."); }
       //目標是接口或是代理類時,使用JDK動態代理 if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { return new JdkDynamicAopProxy(config); } return new ObjenesisCglibAopProxy(config); } else {
       //默認使用JDK動態代理 return new JdkDynamicAopProxy(config); } }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM