博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring 源码分析(三) —— AOP(二)Spring AOP 整体架构
阅读量:6203 次
发布时间:2019-06-21

本文共 9142 字,大约阅读时间需要 30 分钟。

hot3.png

Spring AOP 架构

        先是生成代理对象,然后是拦截器的作用,最后是编织的具体实现。这是AOP实现的三个步骤,当然Spring AOP也是一样。

        而从Spring AOP整体架构上看其核心都是建立在代理上的。当我们建立增强实例时,我们必须先使用 ProxyFactory 类加入我们需要织入该类的所有增强,然后为该类创建代理。一般而言,AOP实现代理的方法有三种,而 Spring 内部用到了其中的两种方法:动态代理和静态代理,而动态代理又分为JDK动态代理和CGLIB代理。而前面提到的 ProxyFactory 类控制着 Spring AOP 中的织入和创建代理的过程。在真正创建代理之前,我们必须指定被增强对象或者目标对象。我们是通过 setTarget() 方法来完成这个步骤的。而 ProxyFactory 内部将生成代理的过程全部转给 DefaultAopProxyFactory 对象来完成,然后根据设置转给其他的类来完成。下面是 Spring AOP 生成代理的原理图:

231946_FGbT_2528735.png

        而特别需要注意的是,在 Spring AOP 中,一个 Advisor(通知者,也翻作 通知器或者增强器)就是一个切面,他的主要作用是整合切面增强设计(Advice)和切入点设计(Pointcut)Advisor有两个子接口:IntroductionAdvisor 和 PointcutAdvisor,基本所有切入点控制的 Advisor 都是由 PointcutAdvisor 实现的。而下面的是Spring AOP的整体架构图

041131_h0UQ_2528735.jpg

        乍一看,非常的复杂,但如果结合上面的Spring AOP生成代理的原理图一起看,也就那么回事,只是丰富的许多属性了,我们会慢慢介绍的。

Spring AOP 使用范例

        项目结构

        分析 Spring AOP 源码的话,直接从最简单业务代码入手,一步步的对源码进行解析。希望借此能看清楚 Spring AOP 纵向和横向代码之间的联系,而且思维也不会太跳跃。下面就是 Spring AOP 测试程序 Test  项目的结构图:

225844_I4vr_2528735.png

        注:Spring 源码版本是 Spring-4.1.6,我的源码分析也是基于这一版本的,JDK 版本是 1.8,aspect 是用的 aspectjrt-1.7.4 和 aspectjweaver-1.7.4。

        创建用于拦截的Bean

        在实际项目中,Bean应该可以算是核心逻辑了,其中的 printTest 方法中可能会封装整个的核心业务逻辑,如此重要的地方我们想在printTest方法前后加入日志来进行跟踪或者调试也是很自然地想法,但是如果直接修改源码是不符合面向对象的设计原则的,它并不符合面向对象的设计方式,而且随意的改动原有的代码也会造成一定的风险,这里就体现 AOP 的优越性了。

package test;/** * 测试Bean * * @Auther kay * @Date   2016-03-08 */public class TestBean {    private String testStr = "testStr";    public String getTestStr(){        return testStr;    }    public void setTestStr(String testStr){        this.testStr = testStr;    }    public void printTest(){        System.out.println("test Bean");    }}

        创建Advisor

        Spring AOP 实现的核心,这里采用了@AspectJ注释对POJO进行标注,使AOP的工作大大简化。

package test;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.*;/** * Aspect切面 * * @Auther kay * @Date   2016-03-08 */@Aspectpublic class AspectTest {    /**     * 配置切入点,主要为方便同类中其他方法使用此处配置的切入点     */    private final String POINT_CUT = "execution(* test.TestBean.*(..))";    /**     * 配置前置通知,使用在方法aspect()上注册的切入点     * 同时接受JoinPoint切入点对象,可以没有该参数     */    @Before(POINT_CUT)    public void beforeTest(){        System.out.println("before Test");    }    /**     * 配置后置通知,使用在方法aspect()上注册的切入点     */    @After(POINT_CUT)    public void afterTest(){        System.out.println("after Test");    }    /**     * 配置环绕通知,使用在方法aspect()上注册的切入点     *     * @param point JoinPoint的支持接口     * @return Object     */    @Around(POINT_CUT)    public Object arountTest(ProceedingJoinPoint point){        System.out.println("before1");        Object object = null;                try{            object = point.proceed();        }        catch (Throwable e){            e.printStackTrace();        }        System.out.println("after1");        return object;    }}

        配置文件

        XML是Spring的基础,尽管Spring一再简化配置,并且大有使用注解取代XML配置之势,但无论如何XML还是Spring的基石。

    
    
    
    
    

        测试

        测试程序,验证效果。

package test;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;/** * 测试程序 * * @Auther kay * @Date   2016-03-08 */public class Test {    public static void main(String[] arge){        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");        TestBean bean = context.getBean("test", TestBean.class);        bean.printTest();    }}

        打印结果

        控制台打印如下代码:

225750_LohV_2528735.png

        Spring AOP是如何实现AOP的?首先我们知道,Spring是否支持注释的AOP是由一个配置文件来控制的,也就是<aop:aspectj-autoproxy />,下面我们分析就从这句配置开始。

Spring AOP 的入口

        BeanDefinition 的解析

        首先spring-config.xml配置文件里的<aop:aspectj-autoproxy>进行解析,发现其如果不是bean标签,则会用不同的类来解析。解析AOP的是:http\://www.springframework.org/schema/aop=org.springframeworl.aop.config.AopNamespaceHandler。也就是  AopNamespaceHandler 类来解析,我们对 AopNamespaceHandler 类进行分析

004313_Ah28_2528735.png

        发现 AopNamespaceHandler 的一段代码是 <aop:aspectj-autoproxy> 解析的入口

AopNamespaceHandler.java

        registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());

                

        BeanDefinition 的载入

        由此,我们知道只要配置文件中出现“aspectj-autoproxy”的注解时就会使用解析器对 AspectJAutoProxyBeanDefinitionParser 进行解析

004611_KAlY_2528735.png

        所有解析器,都是由 BeanDefinitionParser 接口的统一实现,入口都是从 parse函数开始的,AspectJAutoProxyBeanDefinitionParser 的 parse 函数如下:

AspectJAutoProxyBeanDefinitionParser .java

public BeanDefinition parse(Element element, ParserContext parserContext) {   // 注册 AnnotationAwareAspectJAutoProxyCreator   AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);      // 对于注释中子类进行处理   extendBeanDefinition(element, parserContext);   return null;}

        其中的 registerAspectJAnnotationAutoProxyCreatorIfNecessary 函数是 AnnotationAwareAspectJAutoProxyCreator 注册的地方,也是注册的主要逻辑实现的地方。

AopNamespaceUtils.java

public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(      ParserContext parserContext, Element sourceElement) {   // 注册或升级 AutoProxyCreator 定义 beanName 为 org.Springframework.aop.config.internalAutoProxyCreator的   // BeanDefinition   BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(         parserContext.getRegistry(), parserContext.extractSource(sourceElement));            // 对于 proxy-target-class 以及 expose-proxy 属性的处理   useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);      // 注册组件并通知,便于监听器作进一步处理   // 其中 beanDefinition 的 className 为 AnnotationAwareAspectJAutoProxyCreator   registerComponentIfNecessary(beanDefinition, parserContext);}

        BeanDefinition 的注册

        在对于AOP实现上,基本上都是靠 AnnotationAwareAspectJAutoProxyCreator 去完成的,它可以根据@Point 注解定义的切点来自动代理相匹配的 bean。但是为了配置简便,Spring 使用了自定义配置来帮我们自动注册 AnnotationAwareAspectJAutoProxyCreator。具体实现如下

AopNamespaceUtils.java

public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {   return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);}

AopConfigUtils.java

private static BeanDefinition registerOrEscalateApcAsRequired(Class
 cls, BeanDefinitionRegistry registry, Object source) {   Assert.notNull(registry, "BeanDefinitionRegistry must not be null");      // 如果已经存在自动代理创建器且存在的自动代理创建器与现在的不一致那么需要根据优先级来判断到底需要任何使用   if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {      // AUTO_PROXY_CREATOR_BEAN_NAME="org.springframework.aop.config.internalAutoProxyCreator"      BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);      if (!cls.getName().equals(apcDefinition.getBeanClassName())) {         int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());         int requiredPriority = findPriorityForClass(cls);         if (currentPriority < requiredPriority) {            // 改变bean 最重要的就是改变bean 所对应的 className 属性            apcDefinition.setBeanClassName(cls.getName());         }      }      // 如果已经存在自动代理创建器并且与将要创建的一致,那么无需再此创建      return null;   }   RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);   beanDefinition.setSource(source);   beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);   beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);   // AUTO_PROXY_CREATOR_BEAN_NAME="org.springframework.aop.config.internalAutoProxyCreator"   registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);   return beanDefinition;}

        以上代码中实现了自动注册 AnnotationAwareAspectJAutoProxyCreator 类的功能,同时这里还涉及了一个优先级的问题,如果已经存在了自动代理创建器,而且存在的自动代理创建器与现在的不一致,那么就需要根据优先级来判断到底需要使用哪一个?    

            

属性处理

        一般而言,Spring AOP 内部使用 JDK 动态代理或者 CGLIB 来为目标对象创建代理。如果被代理的目标对象实现了至少一个接口,则会使用 JDK 动态代理。所有该目标类型实现的接口都将被代理。若该目标对象没有实现任何接口,则创建一个 CGLIB 代理。一般情况下,使用 CGLIB 需要考虑增强(advise)Final 方法不能被复写以及需要指定 CGLIB 包的位置,尽管 CGLIB 效率更高,但还是推荐使用 JDK 动态代理。

        而 AOP 配置中的 prioxy-target-class 和 expose-proxy 属性在代理生成中起到了至关重要的。prioxy-target-class 主要负责上面两种代理的实现,而 expose-proxy 则负责自我调用切面中的增强。

AopNamespaceUtils.java

private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, Element sourceElement) {   if (sourceElement != null) {      // 对于 proxy-target-class 属性的处理      boolean proxyTargetClass = Boolean.valueOf(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));      if (proxyTargetClass) {         AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);      }      // 对于 expose-proxy 属性的处理      boolean exposeProxy = Boolean.valueOf(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));      if (exposeProxy) {         AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);      }   }}

AopConfigUtils.java

public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {   // 强制使用,其实也是一个属性设置   if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {      BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);      definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);   }}static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) {   if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {      BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);      definition.getPropertyValues().add("exposeProxy", Boolean.TRUE);   }}

        这些,基本就是Spring AOP部分的实现框架了,下节就要对AOP的主要实现类 AnnotationAwareAspectJAutoProxyCreator 进行分析。

——水门(2016年3月于杭州

转载于:https://my.oschina.net/kaywu123/blog/632486

你可能感兴趣的文章
量子通信和大数据最有市场突破前景
查看>>
我国5G处于从技术到标准化过渡阶段
查看>>
2020年全球ESL市场规模将增长到200亿
查看>>
Gartner:企业在移动应用研发上的投入过少
查看>>
思科公司提出监测数据中心的计划
查看>>
Salesforce斥资7亿美元收购营销数据初创企业Krux
查看>>
微软推出Windows Hello 或将帮助用户摆脱密码
查看>>
docker部署spring cloud项目
查看>>
华尔街分析师:苹果将陷入长达10年瓶颈期
查看>>
20年来的管中一窥:FBI究竟是如何窃听你的计算机的?
查看>>
揭秘Pure Storage即将推出的高端阵列
查看>>
管理员权限的凭证安全漏洞
查看>>
大数据产业规划出炉 未来五年复合增长率达30%
查看>>
摩尔定律堪称不死神话: IBM开发5纳米芯片
查看>>
阿里云境外将设数据中心 抢占微软国际市场份额
查看>>
为“华为们”的专利逆袭叫好
查看>>
香港电讯将构建光纤超高速公路
查看>>
银行业大数据变现的三大关键
查看>>
虚拟化迷宫:应用分层放哪好?
查看>>
HPE公布2Q17财报 服务器收入大幅下滑
查看>>