spring aop實現 -- 未完待續

a 一. AOP介紹

 AOP(Aspect Oriented Programming) 面向切面編程, 是OOP(Object Oriented Programming)面向對象編程的 補充,是一種思想。 OOP的特性(封裝,繼承,多態,抽象),主要強調的 對象或者類之間的縱向關係, 而橫向關係卻無法得到確保。 AOP正是補充的橫向關係。

     一個非常簡單的小例子,例如我們當前有三個類(User,Admin,Employee),都有findOne查詢, 我們需要統計查詢消耗的時間,在只有OOP的時候,我們就要這麼寫(如下圖)。  亦或是根據繼承,可以自行在BaseService裏封裝兩個方法,一個beforeExecute(),一個afterExecute(), 執行時候換成 super.beforeExecute()和 super.afterExecute()。大同小異。  

import ..... // 省略
  
@Service
public class AdminService extends BaseService{
  @Resource
  private AdminDAO adminDAO;
  
  // Slf4j日誌
  private final Logger logger = LoggerFactory.getLogger(ScheduledService.class);
  
  public Admin findOne() {
     logger.info("查詢開始了" + System.currentTimeMillis());
     return adminDAO.findOne(); // "SELECT * FROM admin limit 1"
     logger.info("查詢結束了" + System.currentTimeMillis());
  }
}

Employee和User同理,不重複了

可以看到,每一個實現最起碼要調用一次,(大項目成百上千的model,service),重複量就很大, 如果是想修改其中的邏輯,就需要每一個都修改一次,重複工作量很大。

   AOP利用 "橫切"思想,橫向擴展類,並將公共行爲封裝到一起(所謂的Aspect,切面),減少系統的重複代碼,降低模塊之間的耦合度,提升可維護度。   二. AOP一些常用概念

   1. advice : 直譯 爲 "增強",或者"通知"。 是aop執行的具體邏輯操作, spring定義了 前置(beforeAdvice),後置(afterAdvice),異常(ThrowsAdvice),引入(DynamicIntroductionAdvice)四種,具體目錄爲org.springframework.aop。 需要注意的是, 第五種, 環繞(AspectJAroundAdvice)不是spring定義的,而是AspectJ定義的,具體路徑爲org.springframework.aop.aspectj。 

   2. join point : 連接點。 是在邏輯執行時可插入切面的一個點。這個點可以是調用方法時、拋出異常時或者其他行爲。切面代碼可以利用這些點插入到應用的正常流程之中,並添加新的行爲。

   3. pointcut : 切點。切點的定義會匹配通知所要織入的一個或多個連接點。我們通常使用明確的類和方法名稱,或是通過表達式匹配的一組類或方法。

 關於切點和連接點,查資料查的我一度非常混淆這兩個甚至覺得這就是一個概念。 在官方搜索的一個小博客裏,看到了這樣一句話 "pointcut that allows selecting join points in beans with a matching name pattern". 大概我自己的理解就是 切點一定是連接點,而連接點不一定是切點。

   4.aspect : 切面。 是一個大的概念,切面應該是pointcut + advice ,是一個整體的東西。

   5.weaving : 織入。 構建新對象(代理對象)的方法,spring選擇在運行時動態注入,而aspectj選擇在編譯器或者類裝載時,根據類的修飾決定是jdk代理還是cglib代理。一般都是由ProxyFactory來完成

  總的來說,AOP通過切點進行織入, 通過代理的方式構造出代理對象,來替代原對象進行邏輯的執行。

三. aop的源碼分析

   使用過springboot的童鞋們都知道,利用註解@EnableAspectJAutoProxy則可以開啓aop模式(記得加載aop的jar包)。 而早些版本的spring則需要在xml文件中配置 <aop:config>或者是<aop:aspectj-autoproxy>來開啓。

   我們從@EnableAspectJAutoProxy 追溯進去,到達註解類的源碼。

package org.springframework.context.annotation; // 具體的包位置 有興趣的可以看看

import ...// import省略


@Target({ElementType.TYPE}) // 元註解之一, 表示該註解的修飾範圍
@Retention(RetentionPolicy.RUNTIME) // 元註解之一, 表示該類保留的階段
@Documented // 元註解之一,標記爲公衆api,可以javadoc文檔話
@Import({AspectJAutoProxyRegistrar.class}) //  導入註解,類似於組合,將參數類(可以爲多個)注入到當前容器中
public @interface EnableAspectJAutoProxy {
    // 代理類標誌,爲true代表直接代理類,需要利用cglib代理。 爲false則直接使用傳統的jdk代理方式
    boolean proxyTargetClass() default false;

    // 是否將該代理對象暴露爲線程共享,爲true則暴露,使用threadLocal方式,爲false則忽略
    boolean exposeProxy() default false;
}

      ps: 關於元註解,其實是修飾註解的註解,大概所有@Enable開頭的註解類都有這幾個修飾。 可以參考這篇文章 : java元註解

      ps_2: 關於@Import,個人更容易理解爲組合的模式,沒有繼承或者實現的關係,更像是一種內部類的感覺,類似於php的trait,或是python的imp.load_source。

   看一下AspectJAutoProxyRegisterar的源碼,發現只有一個方法

package org.springframework.context.annotation;

import ... // import省略

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    AspectJAutoProxyRegistrar() {
    }

    /*
     *  @param AnnotationMetadata  導入數據的配置類
     *  @param BeanDefinitionRegistry 一種bean的定義格式,包含一些註冊的接口,通常爲beanFactory
     */
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
     
     // 註冊成spring使用的beanDefinition格式 --- 1
     AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

     // 將配置數據註冊到  AnnotationAttributes格式(封裝的特殊map) 的屬性列表
     AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, 
     EnableAspectJAutoProxy.class);
        // 判斷配置數據的map
        if (enableAspectJAutoProxy != null) {
            // 判斷對應的屬性值(該屬性值上文已講), 爲true則直接使用cglib代理 ---2
            if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
            }
            // 判斷對應的屬性值(該屬性值上文已講), 爲true則直接使用暴露性接口 ---3
            if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
            }
        }

    }
}

重點就是 1的方法,註冊成beanDefinitition,包含了整體的核心邏輯。 其中 最重要的則是被調用的重載方法, 將一個很核心的類AnnotationAwareAspectJAutoProxyCreator注入。 

public abstract class AopConfigUtils {
    //外部調用的註冊方法,實際調用的是內部對應的重載方法
    @Nullable // 參數可爲null的註解
    public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
        return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, (Object)null);
    }

    // 被調用的重載方法,此步是重點
    @Nullable
    public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, @Nullable Object source) {
       // 在此地方注入AnnotationAwareAspectJAutoProxyCreator 這個類,是aop的關鍵類
        return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
    }

    // 執行註冊
    @Nullable
    private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
        Assert.notNull(registry, "BeanDefinitionRegistry must not be null");

        // 實際註冊的org.springframework.aop.config.internalAutoProxyCreator
        if (registry.containsBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator")) {
            // 獲取internalAutoProxyCreator的定義
            BeanDefinition apcDefinition = registry.getBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator");
            // 與我們傳入的類名做對比, 如果一致則不做處理。 如果不一致,則需要決定優先級問題
            if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
                // 獲取優先級並進行對比
                int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
                int requiredPriority = findPriorityForClass(cls);
                if (currentPriority < requiredPriority) {
                    // 優先級被更高的取代時, 設置beanClassName。 定義的名稱,不等同於最終的beanName
                    apcDefinition.setBeanClassName(cls.getName());
                }
            }

            return null;
        } else {
            // else分支表示目前沒有internalAutoProxyCreator的定義,則直接新建
            RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
            beanDefinition.setSource(source);
            beanDefinition.getPropertyValues().add("order", -2147483648);
            beanDefinition.setRole(2);
            // 註冊定義
            registry.registerBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator", beanDefinition);
            return beanDefinition;
        }
    }
}

    // 優先級隊列獲取,可以看到,這個類維護了一個優先級的數組,使用static代碼塊進行初始化,越靠後的優先級就越高
    // 直接根據index的索引定優先級也是很暴力的操作。
    private static int findPriorityForClass(Class<?> clazz) {
        return APC_PRIORITY_LIST.indexOf(clazz);
    }
    
    // 靜態代碼塊初始化
    static {
        APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);
        APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);
        APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class);
    }

   補充一點 : 在registerOrEscalateApcAsRequired方法中 註冊了"org.springframework.aop.config.internalAutoProxyCreator"這個類。查了一些資料沒有什麼有價值的, 我翻了一下老版本的源碼的發現是個預定義的常量,名爲"AUTO_PROXY_CREATOR_BEAN_NAME"。  然而 "org.springframework.aop.config.internalAutoProxyCreator"只是BeanDefintion的定義的className而已,並非真的類名.其對應的真實類是org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator。 具體在哪裏進行的alias別名沒有找到,暫時留一個坑吧。

   如果通讀這段邏輯會發現,我們明明註冊的是"AnnotationAwareAspectJAutoProxyCreator",在首次進入的時候,卻直接註冊"org.springframework.aop.config.internalAutoProxyCreator"。(第一個if判斷的時候已經走的else)

   我打開了AnnotationAwareAspectJAutoProxyCreator類的繼承圖,說明先註冊的是父類,所以並沒有什麼問題,但是具體爲什麼這麼設計就不得而知了。即使再次註冊到子類也會因爲優先級的原因,將類變爲對應的子類。

   繼而我們可以繼續分析其源碼,打開AnnotationAwareAspectJAutoProxyCreator,發現只有簡單的幾個屬性和一些setter。 

<--插播一些spring bean生命週期的知識-->  bean在初始化時一般會執行 

postProcessBeforeInstantiation()  -> constructor() -> postProcessPropertyValues() 這幾步,而代理對象一般也在這三步中產生。所以我們向上查找直到AbstractAutoProxyCreator 。 找到了postProcessBeforeInstantiation()方法

查看AbstractAutoProxyCreator的postProcessBeforeInstantiation方法。 AbstractAutoProxyCreator定義許多屬性,有數組,map甚至bool來輔助記錄各種狀態的bean,方便後續使用,不貼這部分的代碼了,有興趣可以去看一下

// AbstractAutoProxyCreator.class


public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
        // 拿取緩存的key
        Object cacheKey = this.getCacheKey(beanClass, beanName);
        if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
            // 如果是增強類。直接忽略
            if (this.advisedBeans.containsKey(cacheKey)) {
                return null;
            }
            // 判斷 特殊bean,判斷是否和切面、切點bean等 aop相關類或實現其接口的類
            // shouldSkip則判斷是否是 original類,也就是原始類
            if (this.isInfrastructureClass(beanClass) || this.shouldSkip(beanClass, beanName)) {
                // 則直接放入屬性advisedBeans(concurrentHashMap)中, 並打上對應的tag,標記其爲特殊類
                this.advisedBeans.put(cacheKey, Boolean.FALSE);
                return null;
            }
        }
        // 判斷自定義的targetSource方法
        TargetSource targetSource = this.getCustomTargetSource(beanClass, beanName);
        // 如果有自定義target方法則加入對應的屬性(map)中
        if (targetSource != null) {
            if (StringUtils.hasLength(beanName)) {
                this.targetSourcedBeans.add(beanName);
            }
            // 獲取增強的方法 --- 重點1
            Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
            // 創建代理對象 --- 重點2
            Object proxy = this.createProxy(beanClass, beanName, specificInterceptors, targetSource);
            // 將對象標記並存入自身屬性(Map)中
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        } else {
            return null;
        }
    }
上述比較重要的方法就是獲取對應的增強getAdvicesAndAdvisorsForBean()方法,和 獲取代理對象的方法 createProxy()。會發現AbstractAutoProxyCreator的getAdvicesAndAdvisorsForBean()是個抽象方法,交由子類實現。 實現地點在子類AbstractAdvisorAutoProxyCreator中(上述類圖的第一個子類)
// AbstractAdvisorAutoProxyCreator.class
    
    @Nullable
    protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
        // 獲取 對應beanClass的所有增強
        List<Advisor> advisors = this.findEligibleAdvisors(beanClass, beanName);
        // DO_NOT_PROXY是一個空數組常量。 如果找不到則返回空數組,否則返回轉到數組狀態的增強類
        return advisors.isEmpty() ? DO_NOT_PROXY : advisors.toArray();
    }

    protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
        // 尋找目前所有的增強類
        List<Advisor> candidateAdvisors = this.findCandidateAdvisors();
        // 所有增強類進行處理,篩選 來確定使用的
        List<Advisor> eligibleAdvisors = this.findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
        // 將增強加入對應的屬性
        this.extendAdvisors(eligibleAdvisors);
        if (!eligibleAdvisors.isEmpty()) { // 進行一下排序
            eligibleAdvisors = this.sortAdvisors(eligibleAdvisors);
        }

        return eligibleAdvisors;
    }

   findCandidateAdvisors由子類進行了實現,擴展了父類的該方法。  算法還是有些複雜的,整體流程還是比較乾淨清晰的。大概流程就是通過遞歸方式遍歷所有的bean,如果有@Aspect修飾就加入到數組中進行標記。

// AnnotationAwareAspectJAutoProxyCreator.class
protected List<Advisor> findCandidateAdvisors() {
        // 調用的實際是 父類的方法
        List<Advisor> advisors = super.findCandidateAdvisors();
        // 擴展後續的邏輯
        if (this.aspectJAdvisorsBuilder != null) {
             // 尋找所有註解爲@ASpect的增強類
            advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
        }

        return advisors;
}


// org.springframework.aop.aspectj.annotation.BeanFactoryAspectJAdvisorsBuilder.class

// 尋找註解爲@Aspect的增強器,包含父類的遞歸查詢 
public List<Advisor> buildAspectJAdvisors() {
        // 所有的增強類
        List<String> aspectNames = this.aspectBeanNames;
        if (aspectNames == null) {
            synchronized(this) {
                aspectNames = this.aspectBeanNames;
                // 如果還沒定義切面的beanName
                if (aspectNames == null) {
                    List<Advisor> advisors = new ArrayList();
                    List<String> aspectNames = new ArrayList();
                    // 遞歸方法,從下到上查找所有的bean,
                    String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
                    String[] var18 = beanNames;
                    int var19 = beanNames.length;

                    for(int var7 = 0; var7 < var19; ++var7) {
                        String beanName = var18[var7];
                        // isEligibleBean交由實現類實現,判斷一些類的條件,當前類默認返回true
                        if (this.isEligibleBean(beanName)) {
                            // 直接從beanFactory拿對應的bean
                            Class<?> beanType = this.beanFactory.getType(beanName);
                            // isAspect()判斷該bean是否由 @Aspect註解 有則返回true                            
if (beanType != null && this.advisorFactory.isAspect(beanType)) {
                                // 加入數組中
                                aspectNames.add(beanName);
                                // 創建一個AspectMetadata類(Aspect註解類的實例化模型),判斷是否爲Aspect的實現。 一層一層的遍歷,從子找到父, 直到找到不爲Object子類的 Aspect對象
                                AspectMetadata amd = new AspectMetadata(beanType, beanName);
                                // 判斷切面的實例模式 -- 後邊會引入文章介紹
                                if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                                    // 創建Aspect的元數據
                                    MetadataAwareAspectInstanceFactory factory = new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
                                    // 通過反射,獲取該增強內所有的方法
                                    List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
                                    // 對應不同的模式 進行不同的標記處理
                                    if (this.beanFactory.isSingleton(beanName)) {
                                        this.advisorsCache.put(beanName, classAdvisors);
                                    } else {
                                        this.aspectFactoryCache.put(beanName, factory);
                                    }
                                    // 將獲取到的增強加入List中
                                    advisors.addAll(classAdvisors);
                                } else {
                                    if (this.beanFactory.isSingleton(beanName)) {
                                        throw new IllegalArgumentException("Bean with name '" + beanName + "' is a singleton, but aspect instantiation model is not singleton");
                                    }
                                    // 非單例模式的方法, 直接加入對應的工廠中和數組中,
                                    MetadataAwareAspectInstanceFactory factory = new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
                                    this.aspectFactoryCache.put(beanName, factory);
                                    advisors.addAll(this.advisorFactory.getAdvisors(factory));
                                }
                            }
                        }
                    }

                    this.aspectBeanNames = aspectNames;
                    return advisors;
                }
            }
        }
        // 應對aspectName數組不爲空的時候
        if (aspectNames.isEmpty()) {
            return Collections.emptyList();
        } else {
            // 通過迭代的方法,直接遍歷當前的aspectName
            List<Advisor> advisors = new ArrayList();
            Iterator var3 = aspectNames.iterator();
  
            while(var3.hasNext()) {
                String aspectName = (String)var3.next();
                List<Advisor> cachedAdvisors = (List)this.advisorsCache.get(aspectName);
                // 在Cache的查找 如果沒有則新建 和上述操作差不多
                if (cachedAdvisors != null) {
                    advisors.addAll(cachedAdvisors);
                } else {
                    MetadataAwareAspectInstanceFactory factory = (MetadataAwareAspectInstanceFactory)this.aspectFactoryCache.get(aspectName);
                    advisors.addAll(this.advisorFactory.getAdvisors(factory));
                }
            }

            return advisors;
        }
}


// org.springframework.beans.factory.BeanFactoryUtils.class

// 通過遞歸的方法 從下之上搜索拿去所有的bean
public static String[] beanNamesForTypeIncludingAncestors(ListableBeanFactory lbf, Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
        Assert.notNull(lbf, "ListableBeanFactory must not be null");
        String[] result = lbf.getBeanNamesForType(type, includeNonSingletons, allowEagerInit);
        if (lbf instanceof HierarchicalBeanFactory) {
            HierarchicalBeanFactory hbf = (HierarchicalBeanFactory)lbf;
            if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
                // 遞歸的拿取父節點
                String[] parentResult = beanNamesForTypeIncludingAncestors((ListableBeanFactory)hbf.getParentBeanFactory(), type, includeNonSingletons, allowEagerInit);
                result = mergeNamesWithParent(result, parentResult, hbf);
            }
        }

        return result;
}

 關於amd.getAjType().getPerClause().getKind(). 其實是Aspect的AspectMetadata類提供的一些方式,目前有四種,默認爲singleton(和spring一樣的單例),其他的三種是pertarget、perthis、pertypewithin。找到這麼一篇帖子, 實例化切面模式 

 

// todo

 後面繼續寫 

findAdvisorsThatCanApply

和 getProxy....

 

    

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章