Spring系列之AOP分析之獲取Advisor的過程(三)

我們在這篇文章中接着上一篇文章的分析。我們在上一篇文章中分析了創建AspectJProxyFactory,並向AspectJProxyFactory中添加目標對象和獲取目標對象的過程。我們在這一篇文章中分析調用addAspect方法添加切面的過程。
在AspectJProxyFactory中有兩個addAspect重載方法,一個入參是切面實例對象,一個入參是切面類對象。他們兩個的區別是:傳入實例對象的方法會將實例對象封裝爲一個單例不再進行切面對象的場景,傳入切面類對象的方法需要創建切面對象實例。我們分析入參爲切面類對象的方法。代碼如下:

    public void addAspect(Class<?> aspectClass) {
        //全限定類名
        String aspectName = aspectClass.getName();
        //根據切面對象創建切面元數據類
        AspectMetadata am = createAspectMetadata(aspectClass, aspectName);
        //根據傳入的切面類創建 切面實例 將切面實例封裝爲切面實例工廠
        MetadataAwareAspectInstanceFactory instanceFactory = createAspectInstanceFactory(am, aspectClass, aspectName);
        //從切面實例工廠中獲取Advisor。
        addAdvisorsFromAspectInstanceFactory(instanceFactory);
    }

上面的代碼只調用了createAspectMetadata、createAspectInstanceFactory、addAdvisorsFromAspectInstanceFactory這三個方法,但是這個過程卻是很複雜的。我們先看createAspectMetadata這個方法。
我們先看看AspectMetadata 這個類是個什麼東西。

public class AspectMetadata implements Serializable {

    /**
    * 切面的名字 可能是類的全限定類名 也可能是Spring容器中bean的名字
    */
    private final String aspectName;

    /**
    * 切面類 指帶有切面註解的類
    */
    private final Class<?> aspectClass;
    /**
    * 類的類型 這個是AspectJ中定義的類  存儲了aspectClass類的類相關信息
    * 實現類爲 AjTypeImpl
    */
    private transient AjType<?> ajType;

    /**
    * Spring AOP 中的切點表達式
    */
    private final Pointcut perClausePointcut;
}

AspectMetadata這個類中主要存儲了切面類的名字、切面類對象和AspectJ中定義的存儲切面類Class對象的類以及SpringAOP中的切點表達式。
createAspectMetadata方法的內容如下:

    private AspectMetadata createAspectMetadata(Class<?> aspectClass, String aspectName) {
        //直接調用 AspectMetadata的構造函數  創建對象 入參爲:切面類和切面類的全限定類名
        AspectMetadata am = new AspectMetadata(aspectClass, aspectName);
        //如果切面類不是切面則拋出異常
        //這裏判斷我們傳入的切面類是不是切面很簡單,即判斷切面類上是否存在@Aspect註解。
        //這裏判斷一個類是不是切面類是這樣進行判斷的:如果我們傳入的切面類上沒有@Aspect註解的話,則去查找它的父類上
        //是否存在@Aspect註解。一直查到父類爲Object。如果一直沒有找到帶有@Aspect註解的類,則會拋出異常。
        if (!am.getAjType().isAspect()) {
            throw new IllegalArgumentException("Class [" + aspectClass.getName() + "] is not a valid aspect type");
        }
        return am;
    }

AspectMetadata的構造函數: 在這個構造函數裏主要是查找帶有@Aspect註解的類。獲取@Aspect類的PerClause類型。正常都是SINGLETON。

    public AspectMetadata(Class<?> aspectClass, String aspectName) {
        //傳入的切面類名直接賦值
        this.aspectName = aspectName;

        Class<?> currClass = aspectClass;
        AjType<?> ajType = null;
        //這裏循環查找 帶有Aspect的類,一直找到父類爲Object
        while (currClass != Object.class) {
            AjType<?> ajTypeToCheck = AjTypeSystem.getAjType(currClass);
            if (ajTypeToCheck.isAspect()) {
                //這裏的AjType所持有的aspectClass爲帶有@Aspect註解的類。
                //可能是我們傳入的類,也可能是我們的傳入類的父類 父父類。。。
                ajType = ajTypeToCheck;
                break;
            }
            //查找父類
            currClass = currClass.getSuperclass();
        }
        //如果傳入的類 沒有@Aspect註解 則拋出異常
        if (ajType == null) {
            throw new IllegalArgumentException("Class '" + aspectClass.getName() + "' is not an @AspectJ aspect");
        }
        //這裏是檢查AspectJ的註解 這裏一般我們也不會用到 可以忽略掉。
        if (ajType.getDeclarePrecedence().length > 0) {
            throw new IllegalArgumentException("DeclarePrecendence not presently supported in Spring AOP");
        }
        //帶有@Aspect註解的類。
        this.aspectClass = ajType.getJavaClass();
        this.ajType = ajType;
        //正常我們的Aspect類 都是SINGLETON
        //其他的是AspectJ提供的一些高級的用法 我們這裏先不展開
        switch (this.ajType.getPerClause().getKind()) {
            case SINGLETON:
                this.perClausePointcut = Pointcut.TRUE;
                return;
            ..............
            省略

        }
    }

我們在看createAspectInstanceFactory這個方法的內容:

    private MetadataAwareAspectInstanceFactory createAspectInstanceFactory(
            AspectMetadata am, Class<?> aspectClass, String aspectName) {

        MetadataAwareAspectInstanceFactory instanceFactory;
        //前面我們分析過 我們在使用 @Aspect註解的時候 都是直接在類上添加@Aspect註解
        if (am.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
            // Create a shared aspect instance.
            //根據傳入的切面類創建 切面對象 是一個單例 要求有無參構造函數
            //這個獲取 單例 切面對象的方式可以學習一下
            Object instance = getSingletonAspectInstance(aspectClass);
            //將上一步創建的切面對象 封裝到SingletonMetadataAwareAspectInstanceFactory中
            //從名字我們也可以看出來 這是一個單例的帶有切面元數據的切面實例工廠
            instanceFactory = new SingletonMetadataAwareAspectInstanceFactory(instance, aspectName);
        }
        else {
            // Create a factory for independent aspect instances.
            //這裏創建一個 SimpleMetadataAwareAspectInstanceFactory 傳入切面類和切面名字
            instanceFactory = new SimpleMetadataAwareAspectInstanceFactory(aspectClass, aspectName);
        }
        return instanceFactory;
    }

這個方法主要是創建了一個MetadataAwareAspectInstanceFactory 的子類。用來組合切面實例對象和切面元數據。面向接口編程的一個很好的體現(依賴倒轉)。MetadataAwareAspectInstanceFactory有很多子類,在不同的場景下創建不同用途的實例。其UML類圖如下:
MetadataAwareAspectInstanceFactory
我們先看創建SingletonMetadataAwareAspectInstanceFactory的構造函數:

    public SingletonMetadataAwareAspectInstanceFactory(Object aspectInstance, String aspectName) {
        //將切面實例傳入到父類構造函數中
        super(aspectInstance);
        //創建切面元數據 和之前的過程一下
        this.metadata = new AspectMetadata(aspectInstance.getClass(), aspectName);
    }

addAdvisorsFromAspectInstanceFactory這個方法,應該是我們這次要分析的重點方法了,獲取Advisor的邏輯都在這個方法中。其代碼如下:

    private void addAdvisorsFromAspectInstanceFactory(MetadataAwareAspectInstanceFactory instanceFactory) {
        //使用ReflectiveAspectJAdvisorFactory從MetadataAwareAspectInstanceFactory中獲取Advisor
        List<Advisor> advisors = this.aspectFactory.getAdvisors(instanceFactory);
        //從中挑出適用於目標對象的Advisor
        advisors = AopUtils.findAdvisorsThatCanApply(advisors, getTargetClass());
        AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(advisors);
        //對獲取到的Advisor進行排序
        AnnotationAwareOrderComparator.sort(advisors);
        //將獲取到Advisor添加到advisors集合中
        addAdvisors(advisors);
    }

對於上面的代碼我們一步一步的分析。先從this.aspectFactory.getAdvisors這裏開始。這裏的aspectFactory爲ReflectiveAspectJAdvisorFactory。在SpringAOP中從Aspect中獲取Advisor都是使用的ReflectiveAspectJAdvisorFactory這個類。這個類是AspectJAdvisorFactory的子類,他們的關係如下:
AspectJAdvisorFactory
AbstractAspectJAdvisorFactory和ReflectiveAspectJAdvisorFactory中很重要的類。他們的重要性在我們後面的分析中會慢慢的體現出來。
ReflectiveAspectJAdvisorFactory中的getAdvisors方法內容如下:

    public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
        //切面類  這個我們在上說過  是一個帶有Aspect註解的類。不一定就是我們調用addAspect傳入的類 可能是其父類
        Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
        //切面類名字
        String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
        //校驗切面類
        validate(aspectClass);

        //validate方法的內容如下 主要是校驗 帶有Aspect註解的類查看它的父類是否也有Aspect註解並且不是抽象類
        //校驗我們的切面類是否帶有Aspect註解
        public void validate(Class<?> aspectClass) throws AopConfigException {
        //如果我們的帶有@Aspect註解的類的父類也帶有@Aspect註解並且其還不是抽象類 則拋出異常
        if (aspectClass.getSuperclass().getAnnotation(Aspect.class) != null &&
                !Modifier.isAbstract(aspectClass.getSuperclass().getModifiers())) {
            throw new AopConfigException("[" + aspectClass.getName() + "] cannot extend concrete aspect [" +
                    aspectClass.getSuperclass().getName() + "]");
        }
        AjType<?> ajType = AjTypeSystem.getAjType(aspectClass);
        //再次校驗 切面類是否帶有 @Aspect註解
        if (!ajType.isAspect()) {
            throw new NotAnAtAspectException(aspectClass);
        }
        //下面這兩個正常開發中一般遇到不
        if (ajType.getPerClause().getKind() == PerClauseKind.PERCFLOW) {
            throw new AopConfigException(aspectClass.getName() + " uses percflow instantiation model: " +
                    "This is not supported in Spring AOP.");
        }
        if (ajType.getPerClause().getKind() == PerClauseKind.PERCFLOWBELOW) {
            throw new AopConfigException(aspectClass.getName() + " uses percflowbelow instantiation model: " +
                    "This is not supported in Spring AOP.");
        }
    }

繼續getAdvisors方法中下面的內容

    //將我們上一步獲取的MetadataAwareAspectInstanceFactory實例又包裝爲LazySingletonAspectInstanceFactoryDecorator
    //裝飾模式的一個使用
    //確保只能獲取到一個切面實例
    MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
                new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);

        List<Advisor> advisors = new LinkedList<Advisor>();
        //調用getAdvisorMethods方法來獲取 切面類中 所有不包含Pointcut註解的方法。
        for (Method method : getAdvisorMethods(aspectClass)) {
            //得到Advisor
            Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
            if (advisor != null) {
                advisors.add(advisor);
            }
        }

    //我們來看看getAdvisorMethods方法的內容
    private List<Method> getAdvisorMethods(Class<?> aspectClass) {
        final List<Method> methods = new LinkedList<Method>();
        //回調  ReflectionUtils.doWithMethods這個方法我們在Spring的代碼中會經常看到
        ReflectionUtils.doWithMethods(aspectClass, new ReflectionUtils.MethodCallback() {
            //ReflectionUtils.MethodCallback的匿名實現
            @Override
            public void doWith(Method method) throws IllegalArgumentException {
                // Exclude pointcuts
                //不帶Pointcut註解的方法 添加到methods集合中
                if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
                    methods.add(method);
                }
            }
        });
        Collections.sort(methods, METHOD_COMPARATOR);
        return methods;
    }
    //ReflectionUtils.doWithMethods方法的內容
    public static void doWithMethods(Class<?> clazz, MethodCallback mc, MethodFilter mf) {
        // Keep backing up the inheritance hierarchy.
        //獲取類中所有的方法
        //這裏需要注意的是 這裏支持JDK1.8中的新特性 可以獲取到接口中的default方法
        Method[] methods = getDeclaredMethods(clazz);
        for (Method method : methods) {
            //如果傳入的MethodFilter 不等於null的話,則調用它的matches方法 根據匹配規則進行匹配
            if (mf != null && !mf.matches(method)) {
                continue;
            }
            try {
                //回調前面我們定義的doWith方法
                mc.doWith(method);
            }
            catch (IllegalAccessException ex) {
                throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex);
            }
        }
        //父類中的方法
        if (clazz.getSuperclass() != null) {
            doWithMethods(clazz.getSuperclass(), mc, mf);
        }
        //接口中的方法
        else if (clazz.isInterface()) {
            for (Class<?> superIfc : clazz.getInterfaces()) {
                doWithMethods(superIfc, mc, mf);
            }
        }
    }

剩下的內容我們下一章繼續分析。

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