zoukankan      html  css  js  c++  java
  • Spring——AOP原理及源码一【系列完】

    系列介绍

    共分为五篇,按照AOP的运行流程演示并分析springAOP源码,总结流程

    系列流程

    从AOP实例的构建到重要组件分析、基本运行流程、关键方法调用、原理总结等几个方面一步步分解AOP源码

    本篇概述

    为读者演示构建AOP实例及AOP核心组件分析


    一、项目构建

    读者可直接下载示例工程,或复制以下的代码到本地工程开启教程。

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.tlj</groupId>
        <artifactId>spring-test</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aop</artifactId>
                <version>4.3.13.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>4.3.13.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.13</version>
                <scope>test</scope>
            </dependency>
            <!-- https://mvnrepository.com/artifact/javax.inject/javax.inject -->
            <dependency>
                <groupId>javax.inject</groupId>
                <artifactId>javax.inject</artifactId>
                <version>1</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
            <dependency>
                <groupId>com.mchange</groupId>
                <artifactId>c3p0</artifactId>
                <version>0.9.5.2</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.44</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aspects</artifactId>
                <version>4.3.12.RELEASE</version>
            </dependency>
        </dependencies>
    
    
    </project>
    pom.xml
    package config;
    
    import aop.LogAspects;
    import aop.MathCalculator;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.EnableAspectJAutoProxy;
    
    @EnableAspectJAutoProxy
    @Configuration
    public class ConfigOfAOP {
    
        @Bean
        public MathCalculator calculator(){
            return new MathCalculator();
        }
    
        @Bean
        public LogAspects logAspects(){
            return new LogAspects();
        }
    }
    ConfigOfAOP
    package aop;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.*;
    
    import java.util.Arrays;
    
    /**
     * 切面类
     */
    @Aspect
    public class LogAspects {
    
        @Pointcut("execution(public int aop.MathCalculator.*(..))")
        public void poinCut(){}
    
        @Before("poinCut()")
        public void logStart(JoinPoint joinPoint){
            Object[] args = joinPoint.getArgs();
            System.out.println(joinPoint.getSignature().getName()+" 运行。。。@Before "+ Arrays.asList(args));
        }
    
        @After("poinCut()")
        public void  logEnd(){
            System.out.println("除法结束..@After");
        }
    
        @AfterReturning(value = "poinCut()",returning = "result")//获取方法返回值
        public void logReturning(Object result){
            System.out.println("除法正常返回..@AfterReturning "+result);
        }
    
        @AfterThrowing(value = "poinCut()",throwing = "e")
        public void logException(Exception e){
            System.out.println("除法异常..@AfterThrowing "+e);
        }
    }
    LogAspects
    package aop;
    
    public class MathCalculator {
    
        public int div(int i,int j){
            System.out.println("MathCalculator");
            return i/j;
        }
    }
    MathCalculator

    项目目录结构如下:

    到这里,构建了一个简单的切面功能demo


    二、运行测试

    打开测试类,运行测试方法


    最终效果

    总共打印了四行,第二行是业务方法的调用,其他都是调用日志切面类中的方法打印的。

    这就是AOP的使用效果,除了用在日志,还有其他很多用法,这里就不赘述了。

    三、关键组件探究

    为什么AOP能在业务方法调用的前后和发生异常时调用切面方法呢,首先我们需要了解它引入了什么组件。

    为了让AOP起作用,我们需要在配置类上添加@EnableAspectJAutoProxy注解,从字面上看,翻译为启动切面自动代理,那它是怎么启动的呢

    ctrl+鼠标左键进入这个注解,我们可以看到EnableAspectJAutoProxy接口使用@Import注解导入了AspectJAutoProxyRegistrar这个类


    再次ctrl+鼠标左键进入AspectJAutoProxyRegistrar,可以看到,它实现了ImportBeanDefinitionRegistrar接口。

    此接口中的registerBeanDefinitions方法,正是用来向容器中注册组件的。

    接下来来看@EnableAspectJAutoProxy注解到底给容器中注册了什么组件。这是AOP实现的关键。

    四、调试寻找组件

    如下图,我们在ImportBeanDefinitionRegistrar接口的注册方法中打上断点。


     

    点击debug开始调试,程序来到了AspectJAutoProxyRegistrar的registerBeanDefinitions方法

    正在执行的是AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry)

    (字面意思为:注册切面自动代理创造组件如果需要的话)


    接着进入这个方法直到以下这个方法,可以看到返回的是BeanDefinition类型,而BeanDefinition是用来保存bean定义信息的

    上图所示,一进来先进行判断,如果容器中存在AUTO_PROXY_CREATOR_BEAN_NAME定义信息,进行一些操作,最后return null。

    如果不存在,可以看到在125行已经有注册名为AUTO_PROXY_CREATOR_BEAN_NAME的组件的动作,要将定义信息注册到容器中。

    把鼠标放在AUTO_PROXY_CREATOR_BEAN_NAME上,可以看到它名为internalAutoProxyCreator

    接着我们进行下一步,到110行时,显然第一次容器中不存在这个类,所以跳过if{}中的内容

    到121行时,通过bean的各种定义信息,new一个定义bean,用来保存这个bean的各种定义信息

    通过cls的信息,发现注册的internalAutoProxyCreator实际为AnnotationAwareAspectJAutoProxyCreator

    到125行时,已经设置好AnnotationAwareAspectJAutoProxyCreator的各种属性

    并将其命名为internalAutoProxyCreator注册进容器,在126行进行返回。

    注意:这里注册的是BeanDefinition,也就是bean的定义信息,并没有创建bean实例

    总结

    到此为止@EnableAspectJAutoProxy给容器中添加了internalAutoProxyCreator的BeanDefinition组件

    internalAutoProxyCreator中实际又是AnnotationAwareAspectJAutoProxyCreator这个类

    所以我们可以得出AnnotationAwareAspectJAutoProxyCreator就是实现AOP的核心组件

    接下来我们来看AnnotationAwareAspectJAutoProxyCreator的继承关系,看它到底是什么

    五、AnnotationAwareAspectJAutoProxyCreator组件是什么

    进入这个类,发现它继承了AspectJAwareAdvisorAutoProxyCreator

     

    那么AspectJAwareAdvisorAutoProxyCreator又是什么呢,接着进入AspectJAwareAdvisorAutoProxyCreator

    发现AspectJAwareAdvisorAutoProxyCreator又继承了AbstractAdvisorAutoProxyCreator

    我们接着进入AbstractAdvisorAutoProxyCreator中查看

    可以看到AbstractAdvisorAutoProxyCreator继承了AbstractAutoProxyCreator

    再进入AbstractAutoProxyCreator

    可以看到AbstractAutoProxyCreator继承了ProxyProcessorSupport

    并实现了SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware两个接口

    由于接下来继承的ProxyProcessorSupport是一个基础工具类,就不往下分析了


    接下来我们主要看这两个接口

    SmartInstantiationAwareBeanPostProcessor:从名字中带有PostProcessor看出是一个后置处理器接口

    BeanFactoryAware:底层组件接口,和其他XXXAware一样,实现XXXAware接口就可以调用XXX组件

    经过层层的进入,可以得到如下的关系:

    从图得出结论——AnnotationAwareAspectJAutoProxyCreator是一个后置处理器后置处理器原理

    总结

      经过以上五个步骤,我们了解了AOP的使用效果

      找到了AOP的核心组件AnnotationAwareAspectJAutoProxyCreator

      理清了AnnotationAwareAspectJAutoProxyCreator的继承实现关系

      发现了AOP的核心组件本质上是一个后置处理器

    下篇预知:在下一篇中我们将从核心组件在哪发挥作用,何时发挥,以及做了什么,一步步深入原理。

  • 相关阅读:
    sql server执行存储过程慢的问题
    Windows service
    moss网站模板的一些问题
    ACM题解报告——HD1012
    ACM题解报告——HD1496
    ACM题解报告——HD1253
    ACM题解报告——进制数取于余
    ACM题解报告——HD1058
    ACM题解报告——HD1548
    ACM解题报告HD1015
  • 原文地址:https://www.cnblogs.com/Unicron/p/12386776.html
Copyright © 2011-2022 走看看