重拾-Spring AOP-自动代理

概述

在上一篇 重拾-Spring AOP 中我们会发现 Spring AOP 是通过类 ProxyFactoryBean 创建代理对象,其有个缺陷就是只能代理一个目标对象 bean, 当代理目标类过多时,配置文件臃肿不方便管理维护,因此 Spring 提供了能够实现自动创建代理的类 BeanNameAutoProxyCreator , DefaultAdvisorAutoProxyCreator ;下面我们看下二者是如何实现自动代理的。

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 的源码大概总结其自动代理流程:

  • 判断当前 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 完成实例化后进行自动代理处理。

分析

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 的源码分析,可知其自动代理流程大概如下:

  • 从容器中获取所有 Advisor 实例
  • 匹配 bean 所支持的 Advisor
  • 采用 jdk 动态代理创建 bean 的代理对象

小结

BeanNameAutoProxyCreator, DefaultAdvisorAutoProxyCreator 二者的实现可以看出其相同点

  • 都是基于实现接口 BeanPostProcessor 的实现
  • 都是先获取当前 bean 所匹配的 Advisor,后在创建代理对象

二者的不同点在于:

  • 前者是基于 bean name 判断是否判断,后者是通过 Advisor 内部的 Ponitcut 匹配判断
  • 前者的 Advisor 是用户配置的,后者是容器中所有匹配的 Advisor
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容