资讯专栏INFORMATION COLUMN

重拾-Spring AOP-自动代理

Mr_houzi / 1431人阅读

摘要:是通过判断当前是否匹配,只有匹配的才会创建代理。实现分析类结构从上图类结构,我们知道其实现与类似都是通过实现接口在完成实例化后进行自动代理处理。

概述

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

BeanNameAutoProxyCreator
BeanNameAutoProxyCreator 是通过判断当前 bean name 是否匹配,只有匹配的 bean 才会创建代理。
使用示例

Spring xml 配置








    
    
        
            userService
            demoService
        
    
    
    
        
            userAfterAdvice
            userBeforeAdvice
        
    

测试

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 类结构可以看出,其实现了接口 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
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 配置



    
    

    
    

    
    
        
            
            save*
        
        
            
        
    

    
        
            
            org.springframework.aop.*.del*.*
        
        
            
        
    

    

测试

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 类结构,我们知道其实现与 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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/73539.html

相关文章

  • 重拾-Spring Transaction

    摘要:当存在挂起的事务时,执行恢复挂起的事务将挂起的事务绑定的重新绑定到当前上下文事务的就是将挂起的事务重新绑定到当前上下文中。 问题 面试中是不是有时经常会被问到 Spring 事务如何管理的了解吗? ,Spring 事务的传播性有哪些,能聊聊它们的使用场景吗?, 事务回滚的时候是所有异常下都会回滚吗?; 下面我们就带着这些问题来看看 Spring 事务是如何实现的吧。 实现分析 首先我们...

    fasss 评论0 收藏0
  • 重拾-Spring-AOP

    摘要:简单点说也就是当前切面将会拦截哪些类下的哪些方法,拦截过程中会采用哪些增强处理前置通知,返回通知,异常通知。切面链,是一系列的切面的集合。 AOP 术语 关于 AOP 的概念描述及相关术语可以参考 彻底征服 Spring AOP 之 理论篇 总结的很好; 本文将着重分析下 AOP 的实现过程。 使用示例 定义接口 public interface UserService { v...

    468122151 评论0 收藏0
  • 从源码入手,一文带你读懂Spring AOP面向切面编程

    摘要:,,面向切面编程。,切点,切面匹配连接点的点,一般与切点表达式相关,就是切面如何切点。例子中,注解就是切点表达式,匹配对应的连接点,通知,指在切面的某个特定的连接点上执行的动作。,织入,将作用在的过程。因为源码都是英文写的。 之前《零基础带你看Spring源码——IOC控制反转》详细讲了Spring容器的初始化和加载的原理,后面《你真的完全了解Java动态代理吗?看这篇就够了》介绍了下...

    wawor4827 评论0 收藏0
  • 架构~微服务 - 收藏集 - 掘金

    摘要:它就是史上最简单的教程第三篇服务消费者后端掘金上一篇文章,讲述了通过去消费服务,这篇文章主要讲述通过去消费服务。概览和架构设计掘金技术征文后端掘金是基于的一整套实现微服务的框架。 Spring Boot 配置文件 – 在坑中实践 - 后端 - 掘金作者:泥瓦匠链接:Spring Boot 配置文件 – 在坑中实践版权归作者所有,转载请注明出处本文提纲一、自动配置二、自定义属性三、ran...

    church 评论0 收藏0
  • Spring AOP就是这么简单啦

    摘要:是一种特殊的增强切面切面由切点和增强通知组成,它既包括了横切逻辑的定义也包括了连接点的定义。实际上,一个的实现被拆分到多个类中在中声明切面我们知道注解很方便,但是,要想使用注解的方式使用就必须要有源码因为我们要 前言 只有光头才能变强 上一篇已经讲解了Spring IOC知识点一网打尽!,这篇主要是讲解Spring的AOP模块~ 之前我已经写过一篇关于AOP的文章了,那篇把比较重要的知...

    Jacendfeng 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<