SpringAOP+源碼解析,切就完事了

本文是對近期學習知識的一個總結,附帶源碼註釋及流程圖,若有不足之處,還望評論區批評指正。html

此處感謝javadoop的源碼解析,收益匪淺:https://javadoop.com/post/spring-aop-introjava

1、AOP、SpringAOP、AspectJ的區別

AOP爲Aspect Oriented Programming的縮寫,意爲:面向切面編程,經過預編譯方式和運行期間動態代理實現程序功能的統一維護的一種技術。利用AOP能夠對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度下降,提升程序的可重用性,同時提升了開發的效率。web

文縐縐的,沒用過確實很懵,可是用過以後,不說清晰,起碼有內意思了。spring

關於SpringAOP和AspectJ,參考Javadoop老師的解釋:https://javadoop.com/post/spring-aop-intro編程

Spring AOP:數組

  • 它基於動態代理來實現。默認地,若是使用接口的,用 JDK 提供的動態代理實現,若是沒有接口,使用 CGLIB 實現。你們必定要明白背後的意思,包括何時會不用 JDK 提供的動態代理,而用 CGLIB 實現。
  • Spring 3.2 之後,spring-core 直接就把 CGLIB 和 ASM 的源碼包括進來了,這也是爲何咱們不須要顯式引入這兩個依賴
  • Spring 的 IOC 容器和 AOP 都很重要,Spring AOP 須要依賴於 IOC 容器來管理。
  • 若是你是 web 開發者,有些時候,你可能須要的是一個 Filter 或一個 Interceptor,而不必定是 AOP。
  • Spring AOP 只能做用於 Spring 容器中的 Bean,它是使用純粹的 Java 代碼實現的,只能做用於 bean 的方法。
  • Spring 提供了 AspectJ 的支持,後面咱們會單獨介紹怎麼使用,通常來講咱們用純的 Spring AOP 就夠了。
  • 不少人會對比 Spring AOP 和 AspectJ 的性能,Spring AOP 是基於代理實現的,在容器啓動的時候須要生成代理實例,在方法調用上也會增長棧的深度,使得 Spring AOP 的性能不如 AspectJ 那麼好。

AspectJ:安全

  • AspectJ 出身也是名門,來自於 Eclipse 基金會,link:https://www.eclipse.org/aspectjapp

  • 屬於靜態織入,它是經過修改代碼來實現的,它的織入時機能夠是:eclipse

    • Compile-time weaving:編譯期織入,如類 A 使用 AspectJ 添加了一個屬性,類 B 引用了它,這個場景就須要編譯期的時候就進行織入,不然無法編譯類 B。
    • Post-compile weaving:也就是已經生成了 .class 文件,或已經打成 jar 包了,這種狀況咱們須要加強處理的話,就要用到編譯後織入。
    • Load-time weaving:指的是在加載類的時候進行織入,要實現這個時期的織入,有幾種常見的方法。一、自定義類加載器來幹這個,這個應該是最容易想到的辦法,在被織入類加載到 JVM 前去對它進行加載,這樣就能夠在加載的時候定義行爲了。二、在 JVM 啓動的時候指定 AspectJ 提供的 agent:-javaagent:xxx/xxx/aspectjweaver.jar
  • AspectJ 能幹不少 Spring AOP 幹不了的事情,它是 AOP 編程的徹底解決方案。Spring AOP 致力於解決的是企業級開發中最廣泛的 AOP 需求(方法織入),而不是力求成爲一個像 AspectJ 同樣的 AOP 編程徹底解決方案。ide

  • 由於 AspectJ 在實際代碼運行前完成了織入,因此你們會說它生成的類是沒有額外運行時開銷的。

2、AOP關鍵術語

  • 切面(Aspect):也就是咱們定義的專一於提供輔助功能的模塊,好比安全管理,日誌信息等。

  • 鏈接點(JoinPoint):切面代碼能夠經過鏈接點切入到正常業務之中,圖中每一個方法的每一個點都是鏈接點。

  • 切入點(PointCut):一個切面不須要通知全部的鏈接點,而在鏈接點的基礎之上增長切入的規則,選擇須要加強的點,最終真正通知的點就是切入點。

  • 通知方法(Advice):就是切面須要執行的工做,主要有五種通知:before,after,afterReturning,afterThrowing,around。

  • 織入(Weaving):將切面應用到目標對象並建立代理對象的過程,SpringAOP選擇再目標對象的運行期動態建立代理對

  • 引入(introduction):在不修改代碼的前提下,引入能夠在運行期爲類動態地添加方法或字段。

3、通知的五種類型

  • 前置通知Before:目標方法調用以前執行的通知。
  • 後置通知After:目標方法完成以後,不管如何都會執行的通知。
  • 返回通知AfterReturning:目標方法成功以後調用的通知。
  • 異常通知AfterThrowing:目標方法拋出異常以後調用的通知。
  • 環繞通知Around:能夠看做前面四種通知的綜合。

4、切入點表達式

上面提到:鏈接點增長切入規則就至關於定義了切入點,固然切入點表達式分爲兩種:within和execution,這裏主要學習execution表達式。

  • 寫法:execution(訪問修飾符 返回值 包名.包名……類名.方法名(參數列表))
  • 例:execution(public void com.smday.service.impl.AccountServiceImpl.saveAccount())
  • 訪問修飾符能夠省略,返回值可使用通配符*匹配。
  • 包名也可使用*匹配,數量表明包的層級,當前包可使用..標識,例如* *..AccountServiceImpl.saveAccount()
  • 類名和方法名也均可以使用*匹配:* *..*.*()
  • 參數列表使用..能夠標識有無參數都可,且參數可爲任意類型。

全通配寫法:* *…*.*(…)

一般狀況下,切入點應當設置再業務層實現類下的全部方法:* com.smday.service.impl.*.*(..)

5、AOP應用場景

  • 記錄日誌
  • 監控性能
  • 權限控制
  • 事務管理

6、AOP源碼分析

SpringBean的生命週期

寫了好多篇文章,每次都要來回顧一下SpringBean的生命週期,可見它真的十分重要。

Spring的Aop又是在哪完成的對目標對象的代理呢?咱們大概也可以想到,其實就是在執行回調的時候。按照慣例,先複習一下,從getBean開始到返回Bean經歷了什麼:

回顧完SpringBean的建立流程以後,咱們以註解方式@EnableAspectJAutoProxy配置Aop開啓@Aspectj爲例,進行一波AOP的流程總結:

AOP的流程總結

經過源碼能夠發現,實際上是經過@EnableAspectJAutoProxy註解注入了一個AnnotationAwareAspectJAutoProxyCreator,但這個類中其實並無重寫postProcessAfterInitialization(),最終實現實際上是在AbstractAutoProxyCreator中。

具體乾的事情,我已經經過一張圖總結出來了,若是想要了解更加具體的信息,不妨打開源碼,能夠看的更加清晰一些。

AnnotationAwareAspectJAutoProxyCreator的註冊

首先是對AnnotationAwareAspectJAutoProxyCreator的註冊環節:【在此不做贅述】

class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {

    @Override
    @Nullable
     // 1. 註冊proxy creator
    public BeanDefinition parse(Element element, ParserContext parserContext) { 
   AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
        extendBeanDefinition(element, parserContext);
        return null;
    }

}

applyBeanPostProcessorsAfterInitialization入口

AbstractAutowireCapableBeanFactory.java

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    //若是bean實現了BeanNameAware、BeanClassLoaderAware、BeanFactoryAware接口, 回調
    invokeAwareMethods(beanName, bean);


    Object wrappedBean = bean;
    //aop在init-method以前並無進行操做, 目前仍是原來那個對象
    if (mbd == null || !mbd.isSynthetic()) {
        //BeanPostProcessor 的 postProcessBeforeInitialization 回調
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }
     //處理bean中定義的init-method或 bean實現了InitializingBean ,調用afterPropertiesSet() 方法
    invokeInitMethods(beanName, wrappedBean, mbd);

    if (mbd == null || !mbd.isSynthetic()) {
        //BeanPostProcessor 的 postProcessAfterInitialization 回調 注意這裏!
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }
    return wrappedBean;
}
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
    throws BeansException {

    Object result = existingBean;
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        //AnnotationAwareAspectJAutoProxyCreator
        Object current = processor.postProcessAfterInitialization(result, beanName);
        if (current == null) {
            return result;
        }
        result = current;
    }
    return result;//返回[可能代理後的]結果
}

AbstractAutoProxyCreator的主要方法

//SpringAop在IOC容器建立bean實例的最後對bean進行處理,進行代理加強, AbstractAutoProxyCreato	
@Override 
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            return wrapIfNecessary(bean, beanName, cacheKey);//這個方法將返回代理類
        }
    }
    return bean;
}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {

    //返回匹配當前bean 的全部的advisor, advice, interceptor
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        //在這裏建立代理
        Object proxy = createProxy(
            bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }

    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

createProxy過程

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {

	//建立ProxyFactory實例
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.copyFrom(this);
	//在schema-based配置方式裏,能夠將 proxy-target-class="true",這樣無論有沒有接口都使用cglib
    if (!proxyFactory.isProxyTargetClass()) {
        if (shouldProxyTargetClass(beanClass, beanName)) {
            proxyFactory.setProxyTargetClass(true);
        }
        else {
            evaluateProxyInterfaces(beanClass, proxyFactory);
        }
    }
	//返回當前bean的advisors數組
    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    proxyFactory.addAdvisors(advisors); //設置advisors數組
    proxyFactory.setTargetSource(targetSource);//targetSource 攜帶了真實實現的信息
    customizeProxyFactory(proxyFactory);

    proxyFactory.setFrozen(this.freezeProxy);
    if (advisorsPreFiltered()) {
        proxyFactory.setPreFiltered(true);
    }

    return proxyFactory.getProxy(getProxyClassLoader());//getProxy(getProxyClassLoader())這一步建立代理
}

JDK動態代理和CGLIB動態代理什麼時候使用

這一步產生分歧的地方在ProxyFactory的getProxy方法,在getProxy以前,首先須要執行createAopProxy,而createAopProxy方法又被這個AopProxyFactory調用:

protected final synchronized AopProxy createAopProxy() {
    if (!this.active) {
        activate();
    }
    //建立AopProxy以前,須要一個AopProxyFactory
    return getAopProxyFactory().createAopProxy(this);
}

// ProxyCreatorSupport
//這個aopProxyFactory用於建立aopProxy, 以後能夠用aopProxy.getProxy(classLoader)建立代理
public ProxyCreatorSupport() {
    this.aopProxyFactory = new DefaultAopProxyFactory();
}

也就是最後會走到DefaultAopProxyFactory中

@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    
    if (!IN_NATIVE_IMAGE && 
        (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
        Class<?> targetClass = config.getTargetClass();
        if (targetClass == null) {
            throw new AopConfigException();
        }
        //若是要代理的類自己就是接口,使用JDK動態代理
        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
        }
        //jdk動態代理基於接口,只有接口中的方法纔會被加強, cglib基於類繼承,若是方法使用了final或者private修飾,也不能加強
        return new ObjenesisCglibAopProxy(config);
    }
    else {
        // 若是有接口,會跑到這個分支
        return new JdkDynamicAopProxy(config);
    }
}

總結:

  • 若是被代理的目標實現了一個或多個自定義的接口,那麼就會使用JDK動態代理。

  • 若是沒有實現任何接口,則使用CGLIB實現代理。

  • 若是設置proxy-target-class=true <property name="proxyTargetClass" value="true"/>則無論有沒有實現接口都會使用CGLIB。

7、JDK動態代理的實現

最終的最終,都會走到真正建立代理對象的流程上:

@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
	//獲取代理接口
    Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
    //查找代理目標的接口是否認義equals和hashcode方法
    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    //使用jdk動態代理建立代理對象
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

第一個參數:classLoader。

第二個參數:實現的接口。

第三個參數:InvocationHandler實例。

而自己JdkDynamicAopProxy本就實現了InvocationHandler,所以傳入this。至此,當調用被代理類的方法的時候,都會最終調用代理類實現的invoke方法,在這個方法中定義橫切的邏輯。

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}
  • proxy:代理對象的引用。
  • method:當前執行的方法。
  • args:當前執行方法所需的參數。
  • return:和被代理對象有相同的返回值。
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 	//當生成的代理類對外提供服務的時候,都會導入到這個invoke方法中
    Object oldProxy = null;
    boolean setProxyContext = false;
	
    TargetSource targetSource = this.advised.targetSource;
    Object target = null;

    try {
        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
            // 對equals方法的代理
            return equals(args[0]);
        }
        else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            //對hashCode()方法的代理
            return hashCode();
        }
        //...

        Object retVal;
		//若是設置了exposeProxy,將proxy放入ThreadLocal中
        if (this.advised.exposeProxy) { 
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }

        // Get as late as possible to minimize the time we "own" the target,
        // in case it comes from a pool.
        target = targetSource.getTarget();
        Class<?> targetClass = (target != null ? target.getClass() : null);

        // 獲取目標方法的攔截鏈,包含全部要執行的 advice
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

        // 檢查一下這個鏈上是否是有advice,若是沒有的話,能夠跳過建立MethodInvocation
        if (chain.isEmpty()) { //chain若是是空的,表示不須要被加強,直接調用目標方法
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
        }
        else {
            // 若是chain裏有advice 執行方法,獲得返回值
            MethodInvocation invocation =
                new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
            //沿着攔截器鏈,執行通知
            retVal = invocation.proceed();
        }

        // 對返回值的處理
        Class<?> returnType = method.getReturnType();
        if (retVal != null && retVal == target &&
            returnType != Object.class && returnType.isInstance(proxy) &&
            !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
            retVal = proxy;
        }
        else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
            throw new AopInvocationException();
        }
        return retVal;
    }
    finally {
        if (target != null && !targetSource.isStatic()) {
            // 釋放目標對象
            targetSource.releaseTarget(target);
        }
        if (setProxyContext) {
            // 存儲代理對象
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}

8、總結

以@AspectJ註解方式爲例

  • 首先,依據<aop:aspectj-autoproxy>@EnableAspectJAutoProxy,Spring會在容器啓動的時候註冊名叫internalAutoProxyCreatorAnnotationAwareAspectJAutoProxyCreator
  • 在bean實例化完成,屬性裝配完成以後,開始執行回調方法,這時取出全部的BeanPostProcessor,執行其postProcessAfterInitialization方法,準備開始對目標對象代理的建立。
  • 首先建立一個代理工廠ProxyFactory,設置一系列的屬性,如全部的通知方法,加強器,攔截器和目標類等注入代理工廠,再調用ProxyFactory.getProxy(classLoader)獲取代理。
  • 經過判斷是用JDK動態代理仍是CGLIB建立不一樣的AopProxy,最後獲取getProxy。

參考資料

相關文章
相關標籤/搜索