重拾-Spring AOP-自動代理

概述

在上一篇 重拾-Spring AOP 中咱們會發現 Spring AOP 是經過類 ProxyFactoryBean 建立代理對象,其有個缺陷就是隻能代理一個目標對象 bean, 當代理目標類過多時,配置文件臃腫不方便管理維護,所以 Spring 提供了可以實現自動建立代理的類 BeanNameAutoProxyCreator , DefaultAdvisorAutoProxyCreator ;下面咱們看下兩者是如何實現自動代理的。java

BeanNameAutoProxyCreator

BeanNameAutoProxyCreator 是經過判斷當前 bean name 是否匹配,只有匹配的 bean 纔會建立代理。

使用示例

  • Spring xml 配置
<bean id="userService" class="org.springframework.aop.UserServiceImpl" />
<bean id="demoService" class="org.springframework.aop.DemoServiceImpl" />

<bean id="userBeforeAdvice" class="org.springframework.aop.UserBeforeAdvice" />
<bean id="userAfterAdvice" class="org.springframework.aop.UserAfterAdvice" />

<bean id="beanNameAutoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
    <!-- 配置要代理的 bean -->
    <property name="beanNames">
        <list>
            <value>userService</value>
            <value>demoService</value>
        </list>
    </property>
    <!-- 配置 interceptor, advice, advisor -->
    <property name="interceptorNames">
        <list>
            <value>userAfterAdvice</value>
            <value>userBeforeAdvice</value>
        </list>
    </property>
</bean>
  • 測試
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/org/springframework/aop/aop.xml");

UserService userService = (UserService) ctx.getBean("userService");

userService.say();

DemoService demoService = (DemoService) ctx.getBean("demoService");

demoService.demo();
  • 運行結果
do before advice ....
do say method
do after return advice ....
do before advice ....
do demo.
do after return advice ....

實現分析

類結構

BeanNameAutoProxyCreator

如上圖 BeanNameAutoProxyCreator 類結構能夠看出,其實現了接口 BeanPostProcessor ; 那麼咱們能夠大概猜想出其自動代理的實現原理與自動注入相似,都是在 bean 實例化後進行特殊的處理,下面就讓咱們看下源碼驗證下吧。正則表達式

分析
public Object postProcessAfterInitialization(Object bean, String name) throws BeansException {
    // Check for special cases. We don't want to try to autoproxy a part of the autoproxying
    // infrastructure, lest we get a stack overflow.
    if (isInfrastructureClass(bean, name) || shouldSkip(bean, name)) {
        logger.debug("Did not attempt to autoproxy infrastructure class '" + bean.getClass() + "'");
        return bean;
    }
    
    TargetSource targetSource = getTargetSource(bean, name);
    
    Object[] specificInterceptors = getInterceptorsAndAdvisorsForBean(bean, name);
    
    // proxy if we have advice or if a TargetSourceCreator wants to do some
    // fancy stuff such as pooling
    if (specificInterceptors != DO_NOT_PROXY || !(targetSource instanceof SingletonTargetSource)) {

        // handle prototypes correctly
        // 獲取容器中配置的 advisors 
        Advisor[] commonInterceptors = resolveInterceptorNames();

        List allInterceptors = new ArrayList();
        if (specificInterceptors != null) {
            allInterceptors.addAll(Arrays.asList(specificInterceptors));
            if (commonInterceptors != null) {
                if (this.applyCommonInterceptorsFirst) {
                    allInterceptors.addAll(0, Arrays.asList(commonInterceptors));
                }
                else {
                    allInterceptors.addAll(Arrays.asList(commonInterceptors));
                }
            }
        }
        if (logger.isInfoEnabled()) {
            int nrOfCommonInterceptors = commonInterceptors != null ? commonInterceptors.length : 0;
            int nrOfSpecificInterceptors = specificInterceptors != null ? specificInterceptors.length : 0;
            logger.info("Creating implicit proxy for bean '" +  name + "' with " + nrOfCommonInterceptors +
                                    " common interceptors and " + nrOfSpecificInterceptors + " specific interceptors");
        }
        ProxyFactory proxyFactory = new ProxyFactory();

        // copy our properties (proxyTargetClass) inherited from ProxyConfig
        proxyFactory.copyFrom(this);
        
        if (!getProxyTargetClass()) {
            // Must allow for introductions; can't just set interfaces to
            // the target's interfaces only.
            // 添加設置代理的接口
            Class[] targetsInterfaces = AopUtils.getAllInterfaces(bean);
            for (int i = 0; i < targetsInterfaces.length; i++) {
                proxyFactory.addInterface(targetsInterfaces[i]);
            }
        }
        
        for (Iterator it = allInterceptors.iterator(); it.hasNext();) {
            Advisor advisor = GlobalAdvisorAdapterRegistry.getInstance().wrap(it.next());
            // 添加 advisor
            proxyFactory.addAdvisor(advisor);
        }
        proxyFactory.setTargetSource(getTargetSource(bean, name));
        
        // 建立代理對象,依舊採用的 jdk 動態代理; 由於上面設置了代理的 interface
        return proxyFactory.getProxy();
    }
    else {
        return bean;
    }
}
protected Object[] getInterceptorsAndAdvisorsForBean(Object bean, String beanName) {
    if (this.beanNames != null) {
        // bean name 包含在配置的名稱列表中,說明須要代理
        if (this.beanNames.contains(beanName)) {
            return PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS;
        }
        for (Iterator it = this.beanNames.iterator(); it.hasNext();) {
            String mappedName = (String) it.next();
            // bean name 匹配通配符,說明須要代理
            if (isMatch(beanName, mappedName)) {
                return PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS;
            }
        }
    }
    // 說明 bean 不須要代理
    return DO_NOT_PROXY;
}
protected boolean isMatch(String beanName, String mappedName) {
    // bean name 匹配通配符
    return (mappedName.endsWith("*") && beanName.startsWith(mappedName.substring(0, mappedName.length() - 1))) ||
            (mappedName.startsWith("*") && beanName.endsWith(mappedName.substring(1, mappedName.length())));
}

BeanNameAutoProxyCreator 的源碼大概總結其自動代理流程:spring

  • 判斷當前 bean name 是否匹配配置
  • 加載配置的 advisor, 也就是配置的 interceptorNames
  • 採用 jdk 動態代理建立 bean 的代理對象

DefaultAdvisorAutoProxyCreator

DefaultAdvisorAutoProxyCreator 會搜索 BeanFactory 容器內部全部可用的 Advisor; 併爲容器中匹配的 bean 建立代理。 在上一篇 重拾-Spring AOP 中咱們知道 Spring AOP 會默認建立實例爲 DefaultPointcutAdvisorAdvisor; 那麼在分析 DefaultAdvisorAutoProxyCreator 以前,咱們看下 Spring AOP 還爲咱們提供了哪些內置的 Advisor

NameMatchMethodPointcutAdvisor

NameMatchMethodPointcutAdvisor 是按 method name 匹配,只有當目標類執行方法匹配的時候,纔會執行 Advice
public class NameMatchMethodPointcut extends StaticMethodMatcherPointcut {

    // 配置攔截的 method name
    private String[] mappedNames = new String[0];

    public boolean matches(Method m, Class targetClass) {
        for (int i = 0; i<this.mappedNames.length; i++) {
            String mappedName = this.mappedNames[i];
            // 目標方法是否與配置的 method name 相等;或者匹配通配符
            if (mappedName.equals(m.getName()) || isMatch(m.getName(), mappedName)) {
                return true;
            }
        }
        return false;
    }

    // 是否以 * 開頭或結束並匹配
    protected boolean isMatch(String methodName, String mappedName) {
        return (mappedName.endsWith("*") && methodName.startsWith(mappedName.substring(0, mappedName.length() - 1))) ||
                (mappedName.startsWith("*") && methodName.endsWith(mappedName.substring(1, mappedName.length())));
    }

}

RegexpMethodPointcutAdvisor

RegexpMethodPointcutAdvisor 是按照正則表達式匹配方法,可以精肯定位到須要攔截的方法。
public class RegexpMethodPointcut extends StaticMethodMatcherPointcut implements ClassFilter { 

    public boolean matches(Method m, Class targetClass) { 
        // TODO use target class here?
        // 拼接表達式
        String patt = m.getDeclaringClass().getName() + "." + m.getName();
        for (int i = 0; i < this.compiledPatterns.length; i++) {
            // 正則匹配
            boolean matched = this.matcher.matches(patt, this.compiledPatterns[i]);
            if (logger.isDebugEnabled()) {
                logger.debug("Candidate is: '" + patt + "'; pattern is " + this.compiledPatterns[i].getPattern() +
                             "; matched=" + matched);
            }
            if (matched) {
                return true;
            }
        }
        return false;
    }

    public boolean matches(Class clazz) {
        // TODO do with regexp
        return true;
    }
    
    public ClassFilter getClassFilter() {
        return this;
    }

}

使用示例

  • xml 配置
<beans>

    <bean id="userService" class="org.springframework.aop.UserServiceImpl" />
    <bean id="demoService" class="org.springframework.aop.DemoServiceImpl" />

    <bean id="userBeforeAdvice" class="org.springframework.aop.UserBeforeAdvice" />
    <bean id="userAfterAdvice" class="org.springframework.aop.UserAfterAdvice" />

    <!-- 按方法名稱匹配 -->
    <bean id="nameMatchMethodPointcutAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
        <property name="mappedNames">
            <!-- 匹配 save 開頭的方法 -->
            <value>save*</value>
        </property>
        <property name="advice">
            <ref bean="userBeforeAdvice" />
        </property>
    </bean>

    <bean id="regexpMethodPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name="pattern">
            <!-- 匹配以 del 開頭的方法 -->
            <value>org.springframework.aop.*.del*.*</value>
        </property>
        <property name="advice">
            <ref bean="userAfterAdvice" />
        </property>
    </bean>

    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />
</beans>
  • 測試
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/org/springframework/aop/aop.xml");

UserService userService = (UserService) ctx.getBean("userService");

userService.saveUser();

userService.delUser();

DemoService demoService = (DemoService) ctx.getBean("demoService");

demoService.saveDemo();

demoService.delDemo();
  • 測試結果
do before advice ....
do save user ......
do del user ......
do after return advice ....
do before advice ....
do save demo ......
do del demo ......
do after return advice ....
從測試結果能夠看出,經過配置不一樣 Advisor 匹配不一樣的 Method 採用相應的 Advice 進行處理。

實現分析

類結構

DefaultAdvisorAutoProxyCreator

從上圖 DefaultAdvisorAutoProxyCreator 類結構,咱們知道其實現與 BeanNameAutoProxyCreator 相似;都是經過實現接口 BeanPostProcessor 在 bean 完成實例化後進行自動代理處理。app

分析

DefaultAdvisorAutoProxyCreatorBeanNameAutoProxyCreator 都繼承了類 AbstractAutoProxyCreator ,因此從源碼中咱們能夠發現兩者都重寫了方法 getInterceptorsAndAdvisorsForBean ,也就是在獲取當前 bean 所匹配的 Advisor 邏輯不同以外其餘處理一致; 那麼下面針對 DefaultAdvisorAutoProxyCreator 的實現咱們主要看下方法 getInterceptorsAndAdvisorsForBean 的處理。源碼分析

protected Object[] getInterceptorsAndAdvisorsForBean(Object bean, String name) {
    // 查找與當前 bean 匹配的 advisor
    List advices = findEligibleAdvisors(bean.getClass());
    if (advices.isEmpty()) {
        return DO_NOT_PROXY;
    }
    // 對 advisor 集合排序
    advices = sortAdvisors(advices);
    return advices.toArray();
}
  • 查找匹配的 Advisor
protected List findEligibleAdvisors(Class clazz) {
    // 查找當前容器中全部定義的 advisor
    List candidateAdvice = findCandidateAdvisors();
    List eligibleAdvice = new LinkedList();
    for (int i = 0; i < candidateAdvice.size(); i++) {
        // Sun, give me generics, please!
        Advisor candidate = (Advisor) candidateAdvice.get(i);
        // 判斷 bean 是否能夠應用 advisor
        if (AopUtils.canApply(candidate, clazz, null)) {
            // 將 advisor 添加到匹配的集合中
            eligibleAdvice.add(candidate);
            logger.info("Candidate Advice [" + candidate + "] accepted for class [" + clazz.getName() + "]");
        }
        else {
            logger.info("Candidate Advice [" + candidate + "] rejected for class [" + clazz.getName() + "]");
        }
    }
    return eligibleAdvice;
}
  • 獲取容器中全部的 Advisor
protected List findCandidateAdvisors() {
    if (!(getBeanFactory() instanceof ListableBeanFactory)) {
        throw new IllegalStateException("Cannot use DefaultAdvisorAutoProxyCreator without a ListableBeanFactory");
    }
    ListableBeanFactory owningFactory = (ListableBeanFactory) getBeanFactory();
    // 從容器中查找全部 bean 定義 type 爲 Advisor 的 bean name
    String[] adviceNames = BeanFactoryUtils.beanNamesIncludingAncestors(owningFactory, Advisor.class);
    List candidateAdvisors = new LinkedList();
    for (int i = 0; i < adviceNames.length; i++) {
        String name = adviceNames[i];
        if (!this.usePrefix || name.startsWith(this.advisorBeanNamePrefix)) {
            // 獲取 advisor 實例
            Advisor advisor = (Advisor) owningFactory.getBean(name);
            candidateAdvisors.add(advisor);
        }
    }
    return candidateAdvisors;
}
  • 判斷 bean 是否匹配 Advisor
public static boolean canApply(Advisor advisor, Class targetClass, Class[] proxyInterfaces) {
    if (advisor instanceof IntroductionAdvisor) {
        return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
    }
    else if (advisor instanceof PointcutAdvisor) {
        PointcutAdvisor pca = (PointcutAdvisor) advisor;
        // 經過 advisor 的 pointcut 判斷 bean 是否匹配
        return canApply(pca.getPointcut(), targetClass, proxyInterfaces);
    }
    else {
        // It doesn't have a pointcut so we assume it applies
        return true;
    }
}


public static boolean canApply(Pointcut pc, Class targetClass, Class[] proxyInterfaces) {
    // 類是否匹配
    if (!pc.getClassFilter().matches(targetClass)) {
        return false;
    }
    
    // 判斷類中的 method 是否匹配
    // 獲取類下全部的method
    Method[] methods = targetClass.getMethods();
    for (int i = 0; i < methods.length; i++) {
        Method m = methods[i];
        // If we're looking only at interfaces and this method
        // isn't on any of them, skip it
        if (proxyInterfaces != null && !methodIsOnOneOfTheseInterfaces(m, proxyInterfaces)) {
            continue;
        }
        // 執行 pointcut 的 method match
        if (pc.getMethodMatcher().matches(m, targetClass))
            return true;
    }
    return false;
}

DefaultAdvisorAutoProxyCreator 的源碼分析,可知其自動代理流程大概以下:post

  • 從容器中獲取全部 Advisor 實例
  • 匹配 bean 所支持的 Advisor
  • 採用 jdk 動態代理建立 bean 的代理對象

小結

BeanNameAutoProxyCreator , DefaultAdvisorAutoProxyCreator 兩者的實現能夠看出其相同點測試

  • 都是基於實現接口 BeanPostProcessor 的實現
  • 都是先獲取當前 bean 所匹配的 Advisor,後在建立代理對象

兩者的不一樣點在於:this

  • 前者是基於 bean name 判斷是否判斷,後者是經過 Advisor 內部的 Ponitcut 匹配判斷
  • 前者的 Advisor 是用戶配置的,後者是容器中全部匹配的 Advisor
相關文章
相關標籤/搜索