1. 程式人生 > >aop原始碼解析三-postProcessAfterInitialization

aop原始碼解析三-postProcessAfterInitialization

aop原始碼解析二:postProcessBeforeInstantiation博文中 我們花了很長的一個篇幅介紹一個方法shouldSkip方法 雖然看名字很簡單 但裡面確實做了很多的事情 雖然最終也是沒有去建立代理 但這些工作並不是白做的 解析完以後快取起來了 後面就可以直接使用了 這次我們看下AnnotationAwareAspectJAutoProxyCreator##postProcessAfterInitialization(Object bean, String beanName)方法

    public Object postProcessAfterInitialization
(Object bean, String beanName) throws BeansException { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (!this.earlyProxyReferences.containsKey(cacheKey)) { return wrapIfNecessary(bean, beanName, cacheKey); } } return
bean; }

在這裡看到了我們一直想看到的代理相關的程式碼 因為建立代理是一件耗時的事情 要做很多的解析 轉換等工作 所以根據beanClass``beanName作為快取的key

    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        // 因為尋找切面方法等操作是很浪費時間的操作 所以如果不必操作的話或者已經建立過代理的話 儘量提前返回
        //已經建立過
        if (beanName != null && this
.targetSourcedBeans.containsKey(beanName)) { return bean; } //本身是切面類 if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { return bean; } if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } // 可以看出作者的思路是很清晰的 幾句程式碼概括了要做的事情 然後在方法裡面展開 // advice advisor pointcut 等之間的名次的區別與聯絡 // advice : action to take at a joinpoint // 1. 尋找適合此bean的切面方法 // 2. 建立代理 Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); //建立代理物件 java動態代理或者是基於cglib的代理 cglib代理更高效 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; }

我們又看到了shouldSkip(bean.getClass(), beanName)方法 跟我們上篇博文介紹的是同一個方法
這裡主要看下getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null)方法
其實這個方法的主要作用是對於尋找到的所有的切面對beanClass做過濾 涉及到切點的匹配 例如正則表示式 註解等

    @Override
    protected Object[] getAdvicesAndAdvisorsForBean(Class beanClass, String beanName, TargetSource targetSource) {
        // 尋找勝任的或者說適合此bean的advior
        List advisors = findEligibleAdvisors(beanClass, beanName);
        if (advisors.isEmpty()) {
            return DO_NOT_PROXY;
        }
        return advisors.toArray();
    }


        protected List<Advisor> findEligibleAdvisors(Class beanClass, String beanName) {
        //Advisor.class
        //尋找所有的候選項
        // 這裡注意 findCandidateAdvisors() 方法在AnnotationAwareAspectJAutoProxyCreator類有繼承
        List<Advisor> candidateAdvisors = findCandidateAdvisors();
        //從候選項中挑出適合此bean的advisor
        List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
        extendAdvisors(eligibleAdvisors);
        if (!eligibleAdvisors.isEmpty()) {
            //對advisor排序 按照順序:
            eligibleAdvisors = sortAdvisors(eligibleAdvisors);
        }
        return eligibleAdvisors;
    }

findCandidateAdvisors()方法 我們在介紹shouldSkip(xxx)方法的時候就已經講解過 不熟悉的讀者可以多讀幾遍 這個方法解析出了所有的advisor advisor包括advice``pointcut資訊

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

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

        public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
        if (candidateAdvisors.isEmpty()) {
            return candidateAdvisors;
        }
        List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
        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;
            }
            //todo 寫註釋
            if (canApply(candidate, clazz, hasIntroductions)) {
                eligibleAdvisors.add(candidate);
            }
        }
        return eligibleAdvisors;
    }

        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;
        }
    }

        public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
        Assert.notNull(pc, "Pointcut must not be null");
        if (!pc.getClassFilter().matches(targetClass)) {
            return false;
        }

        MethodMatcher methodMatcher = pc.getMethodMatcher();
        IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
        if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
            introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
        }
        // 類繼承關係中 只要有一個類的一個方法被匹配 就返回true
        Set<Class> classes = new LinkedHashSet<Class>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
        classes.add(targetClass);
        for (Class<?> clazz : classes) {
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                if ((introductionAwareMethodMatcher != null &&
                        introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
                        methodMatcher.matches(method, targetClass)) {
                    return true;
                }
            }
        }

        return false;
    }

最重要的還是最後一個canApply方法 根據pointcut資訊去做匹配 判斷是否符合要求 因為這裡是返回類的代理 所以只要有一個方法被匹配到 就人為滿足條件 至於是怎麼做匹配的 感興趣的讀者可以深入閱讀 我們這裡只要知道做了什麼就可以了
回到wrapIfNecessary方法 繼續往下看 下面的就是我們一直想看到的方法了 建立代理
看一下aop的相關配置

<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>

這行程式碼註冊了BPP 另外還指定了proxy-target-class屬性 值為true的意思是強制使用cglib建立代理 還有另外一種java jdk提供的代理 需要被代理的類的方法繼承自某個介面方法才可以 cglib並沒有這個限制 推薦使用cglib的方式 執行速度更快
但我們下面是以jdk建立代理為例講解 這種方式更簡單 讀者也可能更熟悉這種方式 那麼就把這行xml配置改成下面這樣

<aop:aspectj-autoproxy proxy-target-class="false" expose-proxy="true"/>

對java提供的動態代理實現不瞭解的讀者 建議先看這篇部落格java動態代理介紹 這樣下面的代理就更容易理解
我們看下createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean))方法

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

        ProxyFactory proxyFactory = new ProxyFactory();
        // Copy our properties (proxyTargetClass etc) inherited from ProxyConfig.
        proxyFactory.copyFrom(this);

        //使用java動態代理
        if (!shouldProxyTargetClass(beanClass, beanName)) {
            // Must allow for introductions; can't just set interfaces to
            // the target's interfaces only.
            Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, this.proxyClassLoader);
            for (Class<?> targetInterface : targetInterfaces) {
                proxyFactory.addInterface(targetInterface);
            }
        }

        //
        Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
        for (Advisor advisor : advisors) {
            proxyFactory.addAdvisor(advisor);
        }

        proxyFactory.setTargetSource(targetSource);
        customizeProxyFactory(proxyFactory);

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

        return proxyFactory.getProxy(this.proxyClassLoader);
    }

ProxyFactory是一個很重要的類 裡面有各種與切面相關的屬性,方法等

public Object getProxy(ClassLoader classLoader) {
        return createAopProxy().getProxy(classLoader);
    }

getProxy(ClassLoader classLoader)方法返回的就是目標類的代理類 分成兩大步完成

  • 返回要使用的AopProxy CglibAopProxy或者JdkDynamicAopProxy
  • 建立代理
    protected final synchronized AopProxy createAopProxy() {
        if (!this.active) {
            activate();
        }
        //預設是DefaultAopProxyFactory
        return getAopProxyFactory().createAopProxy(this);
    }

    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        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.");
            }
            if (targetClass.isInterface()) {
                return new JdkDynamicAopProxy(config);
            }
            return CglibProxyFactory.createCglibProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }

預設使用的建立AopProxy的是DefaultAopProxyFactory 是一個工廠類 類的實現很簡單 根據條件返回對應的AopProxy 當然都把proxyFactory作為引數傳遞了進去
我們接下來看JdkDynamicAopProxy##getProxy方法

public Object getProxy(ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
        }
        Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
        //尋找equals hashCode方法
        findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
    }

completeProxiedInterfaces(AdvisedSupport advised) 方法在代理類要實現的所有介面中加入了兩個介面SpringProxyAdvised介面 SpringProxy是一個標誌介面 沒有什麼方法需要去實現 Advised提供的介面方法就比較多了 裡面有代理相關的所有配置屬性
下面就是建立代理了 相信讀者如果閱讀了java動態代理介紹這篇部落格的話 讀者應該理解做了什麼事情 在下一篇部落格中我們看一下方法的呼叫過程

相關推薦

aop原始碼解析-postProcessAfterInitialization

在aop原始碼解析二:postProcessBeforeInstantiation博文中 我們花了很長的一個篇幅介紹一個方法shouldSkip方法 雖然看名字很簡單 但裡面確實做了很多的事情 雖然最終也是沒有去建立代理 但這些工作並不是白做的 解析完以後快取起

AOP原始碼解析)增強器的獲取

普通增強器的獲取邏輯通過getAdvisor方法實現,實現步驟包括對切點的註解的獲取及根據註解資訊生成增強。 public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectIns

Elastic-Job原始碼解析()之分片定時任務執行

通過本篇的閱讀你將學會了解Elastic-Job的定時時機,及如何通過分片方式做一個分散式的定時任務框架。瞭解常用的三種分片策略,及如何自定義分散式分片策略 目錄 Elastic-Job如何通過SpringJobScheduler啟動定時 Ela

spring cloud Config原始碼解析()

    前面說完我們如何從github上面去取資料,這裡說說server端剩餘的類。ConfigServerEncryptionConfiguration類。從類的名字我們可以看出主要是加解密相關的配置類,進入類中可以看到定義了EncryptionController encry

iOS總結-網路框架-AFNetworking原始碼解析()

參考https://www.jianshu.com/p/f32bd79233da 有關解析的主要是AFHTTPResponseSerializer: NSObject,其他的都是繼承AFHTTPResponseSerializer AFURLResponseSerialization的協議方

ReactiveSwift原始碼解析() Signal程式碼的基本實現

上篇部落格我們詳細的聊了ReactiveSwift原始碼中的Bag容器,詳情請參見《》。本篇部落格我們就來聊一下訊號量,也就是Signal的的幾種狀態以及Signal的基本實現。當然本篇部落格所解析的原始碼會用到上篇部落格介紹的Bag容器。本篇部落格我們先通過一個示例來看一下Signal是如何工作的,具體說來

RxDownload2 原始碼解析()

原始碼解析,如需轉載,請註明作者:Yuloran (t.cn/EGU6c76) 前言 造輪子者:Season_zlc 本文主要講述 RxDownload2 的多執行緒斷點下載技術 斷點下載技術前提 伺服器必須支援按 byte-range 下載,也就是支援 Range: bytes=xxx-

Mybatis 原始碼解析、Mapper介面與mapper.xml檔案繫結

     一、流程圖介紹整體過程               1、首先根據MapperScannerConfigurer進行包掃描,掃描Mapper介面,生成Spring特定的描述,並將其交

【進階】RecyclerView原始碼解析()——深度解析快取機制

上一篇部落格從原始碼角度分析了RecyclerView讀取快取的步驟,讓我們對於RecyclerView的快取有了一個初步的理解,但對於RecyclerView的快取的原理還是不能理解。本篇部落格將從實際專案角度來理解RecyclerView的快取原理。

vue2.x原始碼解析——原始碼構建

1.Rollup介紹 1.2rollup和webpack都是區別 webpack更加強大 可以將圖片,css等都解析為js。 rollup 更適合於js庫的編譯,只適用於js部分,別的檔案是不管的,並且更加友好 2.Vue.js 原始碼構建指令碼

java執行緒池(ThreadPoolExecutor)原始碼解析

ctl成員變數 /** * ctl 儲存了兩部分資訊 * workerCount : 執行緒數 * runStatus: 執行緒池的狀態 * * ctl 是一個 AtomicI

Mybatis 原始碼解析()

文章個人學習原始碼所得,若存在不足或者錯誤之處,請大家指出。 Properties配置格式如下: Configuration.xml中: <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE

mybatis原始碼解析()-SqlSession.selectOne類似方法呼叫過程

上篇部落格中介紹了mybatis的載入過程,這篇部落格介紹一下org.apache.ibatis.session.SqlSession的增、刪、改、查方法是怎麼實現的。我們使用mybatis時,基本都是使用Mapper進行增、刪、改、查操作,但是SqlS

aop原始碼解析二:postProcessBeforeInstantiation

我們就不再從bean的載入過程開始看了 在aop原始碼解析一:註冊BPP中 介紹到了一個後置處理器 AnnotationAwareAspectJAutoProxyCreator 我們就從這個類入手aop 先看postProcessBeforeInstantia

Spring AOP原始碼解析(二)獲取增強器

一、方法入口 上一節中,Spring會建立兩個工廠來完成獲取增強方法的功能: AspectMetadata amd = new AspectMetadata(beanType, beanName); if (amd.getAjType().getPerClause()

分散式任務排程平臺XXL-JOB--原始碼解析:xxl-job-admin排程中心原始碼解析之初始化兩個Thread工作執行緒

 xxl-job-admin初始化工作 1.1 啟動時, 載入applicationcontext-xxl-job-admin.xml, 初始化spring容器 <!-- 這個排程中心,在啟動的時候,會做很多初始化的工作 ,比如:執行器資訊,註冊機器列表等資訊 --

Spring Aop原始碼解析

AspectJAwareAdvisorAutoProxyCreator及為Bean生成代理時機分析 上篇文章說了,org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator這

Spring系列(五):Spring AOP原始碼解析

一、@EnableAspectJAutoProxy註解   在主配置類中新增@EnableAspectJAutoProxy註解,開啟aop支援,那麼@EnableAspectJAutoProxy到底做了什麼?接下來分析下:    @EnableAspectJAutoProxy點進去如下: &nb

5.2 spring5原始碼--spring AOP原始碼分析---切面原始碼分析

一. AOP切面原始碼分析 原始碼分析分為三部分 1. 解析切面 2. 建立動態代理 3. 呼叫   原始碼的入口 原始碼分析的入口, 從註解開始: 元件的入口是一個註解, 比如啟用AOP的註解@EnableAspectJAutoProxy. 在註解的實現類裡面, 會有一個@Import("

Spring基於註解形式的 AOP的原理流程及原始碼解析

此篇部落格主要講解Spring如何驗證將要例項化的Bean是否應該被代理,生成代理物件的時機問題。 在第二篇部落格中,Spring對容器內所有的標識了@Aspect註解的的類的切面方法(標識了@Around, @Before, @After, @AfterRe