深入理解Sping AOP(二)

語言: CN / TW / HK

theme: cyanosis

持續創作,加速成長!這是我參與「掘金日新計劃 · 6 月更文挑戰」的第15天,點選檢視活動詳情

1.開啟AOP

@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AspectJAutoProxyRegistrar.class) public @interface EnableAspectJAutoProxy {

基於AspectJ註解形式開啟AOP需要使用@EnableAspectJAutoProxy註解,進入這個註解,它通過@Import標籤向容器當中匯入了一個註冊器AspectJAutoProxyRegistrar。

``` private static BeanDefinition registerOrEscalateApcAsRequired( Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {

    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");

    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
        BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
        if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
            int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
            int requiredPriority = findPriorityForClass(cls);
            if (currentPriority < requiredPriority) {
                apcDefinition.setBeanClassName(cls.getName());
            }
        }
        return null;
    }

    RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
    beanDefinition.setSource(source);
    beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
    beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
    return beanDefinition;
}

```

然後AspectJAutoProxyRegistrar類實現了ImportBeanDefinitionRegistrar 介面,熟悉IOC容器的話應該能理解實現了在ImportBeanDefinitionRegistrar .registerBeanDefinitions()方法中可以動態的往容器裡面新增Bean的配置(BeanDefinition)資訊,也就是向BeanFactory中註冊AnnotationAwareAspectJAutoProxyCreator物件的BeanDefinition資訊。

觀察繼承關係圖,因為它是後置處理器,所有在容器啟動的Refresh()方法中會執行registerBeanPostProcessors(beanFactory) 方法時候會建立它。

打斷點能看到未執行registerBeanPostProcessors方法前BeanDefinitionMap中是存在這個BeanDefinition的。

單例池中和BeanPostProcessors是沒有生成這個bean的。

執行該方法後能夠看到單例池和BeanPostProcessors中存在了這個後置處理器的Bean。

以上都是IOC的內容。

上圖是AnnotationAwareAspectJAutoProxyCreator後置處理器的註冊和建立階段的流程圖。

到此為止,我們已經知道Refresh()方法中的registerBeanPostProcessors() 方法已經初始化這個Bean了,在singletonObjects(即單例池)中是能看到這個bean的。

那麼這個後置處理器是如何操作的呢?

在Refresh()方法中的registerBeanPostProcessors() 後面有一個方法finishBeanFactoryInitialization() ,會初始化所有的單例物件,而後置處理器就是在例項化和初始化這些物件的過程中發揮作用的。

2.切面通知載入

AnnotationAwareAspectJAutoProxyCreator的父類實現了InstantiationAwareBeanPostProcessor,而該介面繼承了後置處理器BeanPostProcessor介面,他的父類有這麼幾個實現方法;

//bean例項化前的回撥操作 @Override public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException { } //bean例項化後的回撥操作 @Override public boolean postProcessAfterInstantiation(Object bean, String beanName) { } //bean例項化完成後初始化之前回調操作 @Override public Object postProcessBeforeInitialization(Object bean, String beanName) { } //bean例項化完成初始化之後回撥操作 @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { }

這裡提前介紹說明一下:postProcessBeforeInstantiation方法中會載入切面及通知。

SpringIOC的refresh()方法包含了許多邏輯,其中在finishBeanFactoryInitialization()方法中,開始瞭解析AnnotationAwareAspectJAutoProxyCreator的工作。

熟悉一下IOC初始化bean的過程:

preInstantiateSingletons() -----> getBean() -----> doGetBean() ----->getSingleton(嘗試從快取中獲取)---->lamda內部類方法getObject----->CreatBean();

在CreatBean方法中有一個方法:resolveBeforeInstantiation(beanName, mbdToUse);

該方法就會遍歷所有後置處理器,呼叫InstantiationAwareBeanPostProcessor型別的後置處理器的postProcessBeforeInstantiation方法。

正好容器中@EnableAspectJAutoProxy為我們添加了該型別的後置處理器。所以每次單例項bean建立的時候都會呼叫該方法。

``` try { // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance. Object bean = resolveBeforeInstantiation(beanName, mbdToUse); if (bean != null) { return bean; } } catch (Throwable ex) { throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, "BeanPostProcessor before instantiation of bean failed", ex); }

    try {
        Object beanInstance = doCreateBean(beanName, mbdToUse, args);
        if (logger.isTraceEnabled()) {
            logger.trace("Finished creating instance of bean '" + beanName + "'");
        }
        return beanInstance;
    }

```

會先嚐試返回一個代理物件,如果返回不成功,那麼就執行doCreatBean方法。

@Nullable protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) { Object bean = null; if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) { // Make sure bean class is actually resolved at this point. if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { Class<?> targetType = determineTargetType(beanName, mbd); if (targetType != null) { // 呼叫AnnotationAwareAspectJAutoProxyCreator的postProcessorsBeforeInstantiation() // 其實是父類AbstractAutoProxyCreator中的 bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName); if (bean != null) { //呼叫AnnotationAwareAspectJAutoProxyCreator的postProcessAfterInitialization() bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); } } } mbd.beforeInstantiationResolved = (bean != null); } return bean; }

在看postProcessBeforeInstantiation()之前先看下我寫的切面類,

``` @Component @Aspect public class Aop {

    /**
     * 切面
     */
    @Pointcut("execution(* com.yang.service..*.*(..))")
    public void pointcut() {
    }


    @Before("pointcut()")
    public void before() {
            System.out.println("增強-------------------------------------------");
    }

} ```

我的demo中切面類叫AOP,接下來就看postProcessBeforeInitialization()是如何解析@Aspect修飾的類的;

@Override public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) { //省略程式碼---------------------------------------- if (targetSource != null) { if (StringUtils.hasLength(beanName)) { this.targetSourcedBeans.add(beanName); } Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource); Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } return null; }

getAdvicesAndAdvisorsForBean方法,該方法的目的是獲取並生成Advisor Bean。其中包含了掃描通過@Aspect註解配置且與Bean方法的匹配的Advice。

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

    List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
    if (advisors.isEmpty()) {
        return DO_NOT_PROXY;
    }
    return advisors.toArray();
}

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    //獲取到所有切面通知方法
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    //匹配到符合當前物件的通知方法
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    //特殊處理,這裡不贅述
    extendAdvisors(eligibleAdvisors);
    //對通知方法集合進行排序
    if (!eligibleAdvisors.isEmpty()) {
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}

```

什麼是Advisor? 首先,Advice是增強方法,即@Around, @Before等註解修飾的方法。而Advisor則是在Advice之上再包了一層。例如PointcutAdvisor則包有Advice和Pointcut

findCandidateAdvisors()

protected List<Advisor> findCandidateAdvisors() { // Add all the Spring advisors found according to superclass rules. List<Advisor> advisors = super.findCandidateAdvisors(); // Build Advisors for all AspectJ aspects in the bean factory. if (this.aspectJAdvisorsBuilder != null) { advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors()); } return advisors; }

這裡findCandidateAdvisors在AbstractAdvisorAutoProxyCreator中有實現,同時被AnnotationAwareAspectJAutoProxyCreator重寫了。

不過可以看到重寫的方法中先呼叫了super.findCandidateAdvisor。

this.aspectJAdvisorsBuilder.buildAspectJAdvisors() 從所有Bean中獲取@Aspect配置的Bean並建立Advisor,也是我們關注的內容。

由於這段程式碼比較長,這裡就不細說了,深挖的話還能再寫一篇文章。

主要是通過beanName掃描@Aspect配置並生成Advisor的過程了。

其中this.advisorFactory.getAdvisors(factory)是生成Advisor類的具體內容。

findAdvisorsThatCanApply()

現在我們獲得了所有的候選Advisor,那麼找出和當前Bean匹配的Advisor呢?

``` protected List findAdvisorsThatCanApply( List candidateAdvisors, Class<?> beanClass, String beanName) {

    ProxyCreationContext.setCurrentProxiedBeanName(beanName);
    try {
        return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
    }
    finally {
        ProxyCreationContext.setCurrentProxiedBeanName(null);
    }
}

```

會執行AopUtils.findAdvisorsThatCanApply

public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) { if (candidateAdvisors.isEmpty()) { return candidateAdvisors; } List<Advisor> eligibleAdvisors = new ArrayList<>(); for (Advisor candidate : candidateAdvisors) { if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) { eligibleAdvisors.add(candidate); } } boolean hasIntroductions = !eligibleAdvisors.isEmpty(); for (Advisor candidate : candidateAdvisors) { if (candidate instanceof IntroductionAdvisor) { // already processed continue; } if (canApply(candidate, clazz, hasIntroductions)) { eligibleAdvisors.add(candidate); } } return eligibleAdvisors; }

最後定位到canApply()

public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) { if (advisor instanceof IntroductionAdvisor) { return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass); } else if (advisor instanceof PointcutAdvisor) { PointcutAdvisor pca = (PointcutAdvisor) advisor; return canApply(pca.getPointcut(), targetClass, hasIntroductions); } else { // It doesn't have a pointcut so we assume it applies. return true; } }

可以看出判斷是否是該bean合適的advisor,是通過advisor.getPointcut().getClassFilter().matches(targetClass)方法來判斷的。匹配完class以後下面還有MethodMatcher來匹配method。回想我們在配置pointcut的時候不僅僅有class的規則,也有method的規則。

當然,再深入matches方法進去的話就是pointcut的匹配語法實現了。有興趣的可以自行閱讀。

補充:代理物件建立流程連結深入理解Sping AOP(一)