深入理解Spring源码之剖析AOP(注解配置方式)(一)

简介: 深入理解Spring源码之剖析AOP(注解配置方式)

先贴出整篇文章的测试代码:

import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.atguigu.aop.MathCalculator;
import com.atguigu.bean.Boss;
import com.atguigu.bean.Car;
import com.atguigu.bean.Color;
import com.atguigu.bean.AwareBean;
import com.atguigu.config.MainConfigOfAOP;
import com.atguigu.config.MainConifgOfAutowired;
import com.atguigu.dao.BookDao;
import com.atguigu.service.BookService;
public class IOCTest_AOP {
  @Test
  public void test01(){
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAOP.class);
    MathCalculator mathCalculator = applicationContext.getBean(MathCalculator.class);
    mathCalculator.div(1, 0);
    applicationContext.close();
  }
}
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.springframework.aop.Advisor;
import org.springframework.aop.Pointcut;
import org.springframework.aop.framework.AopInfrastructureBean;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import com.atguigu.aop.MathCalculator;
@EnableAspectJAutoProxy
@Configuration
public class MainConfigOfAOP {
  //业务逻辑类加入容器中
  @Bean
  public MathCalculator calculator(){
    return new MathCalculator();
  }
  //切面类加入到容器中
  @Bean
  public LogAspects logAspects(){
    return new LogAspects();
  }
}
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
/**
 * 切面类
 * 
 * @Aspect: 告诉Spring当前类是一个切面类
 *
 */
@Aspect
public class LogAspects {
  //抽取公共的切入点表达式
  //1、本类引用
  //2、其他的切面引用
  @Pointcut("execution(public int com.atguigu.aop.MathCalculator.*(..))")
  public void pointCut(){};
  //@Before在目标方法之前切入;切入点表达式(指定在哪个方法切入)
  @Before("pointCut()")
  public void logStart(JoinPoint joinPoint){
    Object[] args = joinPoint.getArgs();
    System.out.println(""+joinPoint.getSignature().getName()+"运行。。。@Before:参数列表是:{"+Arrays.asList(args)+"}");
  }
  @After("com.atguigu.aop.LogAspects.pointCut()")
  public void logEnd(JoinPoint joinPoint){
    System.out.println(""+joinPoint.getSignature().getName()+"结束。。。@After");
  }
  //JoinPoint一定要出现在参数表的第一位
  @AfterReturning(value="pointCut()",returning="result")
  public void logReturn(JoinPoint joinPoint,Object result){
    System.out.println(""+joinPoint.getSignature().getName()+"正常返回。。。@AfterReturning:运行结果:{"+result+"}");
  }
  @AfterThrowing(value="pointCut()",throwing="exception")
  public void logException(JoinPoint joinPoint,Exception exception){
    System.out.println(""+joinPoint.getSignature().getName()+"异常。。。异常信息:{"+exception+"}");
  }
}
public class MathCalculator {
  public int div(int i,int j){
    System.out.println("MathCalculator...div...");
    return i/j; 
  }
}

AOP的基本概念和使用步骤

AOP:【动态代理】
         指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式;
 1、导入aop模块;Spring AOP:(spring-aspects)
 2、定义一个业务逻辑类(MathCalculator);在业务逻辑运行的时候将日志进行打印(方法之前、方法运行结束、方法出现异常,xxx)
 3、定义一个日志切面类(LogAspects):切面类里面的方法需要动态感知MathCalculator.div运行到哪里然后执行;
         通知方法:
             前置通知(@Before):logStart:在目标方法(div)运行之前运行
             后置通知(@After):logEnd:在目标方法(div)运行结束之后运行(无论方法正常结束还是异常结束)
             返回通知(@AfterReturning):logReturn:在目标方法(div)正常返回之后运行
             异常通知(@AfterThrowing):logException:在目标方法(div)出现异常以后运行
             环绕通知(@Around):动态代理,手动推进目标方法运行(joinPoint.procced())
 4、给切面类的目标方法标注何时何地运行(通知注解);
 5、将切面类和业务逻辑类(目标方法所在类)都加入到容器中;
 6、必须告诉Spring哪个类是切面类(给切面类上加一个注解:@Aspect)
 7、给配置类中加 @EnableAspectJAutoProxy 【开启基于注解的aop模式】
         在Spring中很多的 @EnableXXX;
 三步:
     1)、将业务逻辑组件和切面类都加入到容器中;告诉Spring哪个是切面类(@Aspect)
     2)、在切面类上的每一个通知方法上标注通知注解,告诉Spring何时何地运行(切入点表达式)
     3)、开启基于注解的aop模式;@EnableAspectJAutoProxy

一、@EnableAspectJAutoProxy和AnnotationAwareAspectJAutoProxyCreator分析

AOP原理:【看给容器中注册了什么组件,这个组件什么时候工作,这个组件的功能是什么?】

@EnableAspectJAutoProxy;

1、@EnableAspectJAutoProxy是什么?

        @Import(AspectJAutoProxyRegistrar.class):给容器中导入AspectJAutoProxyRegistrar

        利用AspectJAutoProxyRegistrar自定义给容器中注册bean;BeanDefinetion

        internalAutoProxyCreator=AnnotationAwareAspectJAutoProxyCreator

        给容器中注册一个AnnotationAwareAspectJAutoProxyCreator;

       

2、 AnnotationAwareAspectJAutoProxyCreator

         AnnotationAwareAspectJAutoProxyCreator

             ->AspectJAwareAdvisorAutoProxyCreator

                 ->AbstractAdvisorAutoProxyCreator

                     ->AbstractAutoProxyCreator

                         implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware

                         关注后置处理器(在bean初始化完成前后做事情)、自动装配BeanFactory

       

        AbstractAutoProxyCreator.setBeanFactory()

        AbstractAutoProxyCreator.有后置处理器的逻辑;

       

        AbstractAdvisorAutoProxyCreator.setBeanFactory()-》initBeanFactory()

       

        AnnotationAwareAspectJAutoProxyCreator.initBeanFactory()

=======创建和注册AnnotationAwareAspectJAutoProxyCreator的过程========
1)、传入配置类,创建ioc容器
2)、注册配置类,调用refresh()刷新容器;
3)、registerBeanPostProcessors(beanFactory);注册bean的后置处理器来方便拦截bean的创建;
    3.1)、先获取ioc容器已经定义了的需要创建对象的所有BeanPostProcessor
    3.2)、给容器中加别的BeanPostProcessor
    3.3)、优先注册实现了PriorityOrdered接口的BeanPostProcessor;
    3.4)、再给容器中注册实现了Ordered接口的BeanPostProcessor;
    3.5)、注册没实现优先级接口的BeanPostProcessor;
    3.6)、注册BeanPostProcessor,实际上就是创建BeanPostProcessor对象,保存在容器中;
                      创建internalAutoProxyCreator的BeanPostProcessor【AnnotationAwareAspectJAutoProxyCreator】
           3.6.1)、创建Bean的实例
           3.6.2)、populateBean;给bean的各种属性赋值
           3.6.3)、initializeBean:初始化bean;
                      3.6.3.1)、invokeAwareMethods():处理Aware接口的方法回调
                      3.6.3.2)、applyBeanPostProcessorsBeforeInitialization():应用后置处
                                 理器的postProcessBeforeInitialization()             
                      3.6.3.3)、invokeInitMethods();执行自定义的初始化方法
                      3.6.3.4)、applyBeanPostProcessorsAfterInitialization();执行后置处
                                理器的postProcessAfterInitialization();
           3.6.4)、BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)创建成
                         功;--》aspectJAdvisorsBuilder
    3.7)、把BeanPostProcessor注册到BeanFactory中;
                 beanFactory.addBeanPostProcessor(postProcessor);    
=======AnnotationAwareAspectJAutoProxyCreator执行时机========
AnnotationAwareAspectJAutoProxyCreator 继承了 InstantiationAwareBeanPostProcessor
4)、finishBeanFactoryInitialization(beanFactory);完成BeanFactory初始化工作;创建剩下的单实
例bean
    4.1)、遍历获取容器中所有的Bean,依次创建对象getBean(beanName); getBean->doGetBean()-
        >getSingleton()->
    4.2)、创建bean【AnnotationAwareAspectJAutoProxyCreator在所有bean创建之前会有一个拦
            截,InstantiationAwareBeanPostProcessor,会调用      
           【postProcessBeforeInstantiation()】                               
            4.2.1)、先从缓存中获取当前bean,如果能获取到,说明bean是之前被创建过的,直接使用,否则再创建;只要创建好的Bean都会被缓存起来
            4.2.2)、createBean();创建bean;AnnotationAwareAspectJAutoProxyCreator 会在任
                    何bean创建之前先尝试返回bean的实例【BeanPostProcessor是在Bean对象创建完成    
                    初始化 前后调用的】【InstantiationAwareBeanPostProcessor是在创建Bean实例    
                    之前先尝试用后置处理器返回对象的     
                    4.2.2.1)、resolveBeforeInstantiation(beanName, mbdToUse);解析                                                   
                            BeforeInstantiation希望后置处理器在此能返回一个代理对象;如果能返
                            回代理对象就使用,如果不能就继续
                            4.2.2.1.1)、后置处理器先尝试返回对象;bean = 
                                        applyBeanPostProcessorsBeforeInstantiation():
                                        拿到所有后 置处理器,如果是     
                                        InstantiationAwareBeanPostProcessor;就执行
                                        postProcessBeforeInstantiation                                   
                                        if (bean != null) {
                                       bean =         
                                           applyBeanPostProcessorsAfterInitialization(bean, beanName);                                       
                                         }
                   4.2.2.2)、doCreateBean(beanName, mbdToUse, args);真正的去创建一个bean
                             例;和3.6流程一样;

二、 AnnotationAwareAspectJAutoProxyCreator注册创建和执行时机

实践:观察AnnotationAwareAspectJAutoProxyCreator注册创建:

notice:eclipse里搜索jar包里的类名快捷键Ctrl+Shift+T 

断点打在AbstractAdvisorAutoProxyCreator类的setBeanFactory方法下,调用过程如下:

对应过程

1)、传入配置类,创建ioc容器

2)、注册配置类,调用refresh()刷新容器;

3)、registerBeanPostProcessors(beanFactory);注册bean的后置处理器来方便拦截bean的创建;

 

断点打在PostProcessorRegistrationDelegate类的registerBeanPostProcessors方法的

beanFactory.getBean(ppName, BeanPostProcessor.class)行上,调用过程如下:

对应过程3)registerBeanPostProcessors(beanFactory);注册bean的后置处理器来方便拦截bean的创建

 

断点打在AbstractAutoProxyCreator的postProcessBeforeInstantiation方法上 调用过程如下:

对应过程4.2)创建bean【AnnotationAwareAspectJAutoProxyCreator在所有bean创建之前会有一个拦截,InstantiationAwareBeanPostProcessor,会调用 【postProcessBeforeInstantiation()】

================================================================================================



相关文章
|
15天前
|
SQL Java 数据库连接
(自用)Spring常用配置
(自用)Spring常用配置
16 0
|
2天前
|
缓存 Java Sentinel
Springboot 中使用 Redisson+AOP+自定义注解 实现访问限流与黑名单拦截
Springboot 中使用 Redisson+AOP+自定义注解 实现访问限流与黑名单拦截
|
2天前
|
XML 人工智能 Java
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
|
8天前
|
存储 安全 Java
第2章 Spring Security 的环境设置与基础配置(2024 最新版)(下)
第2章 Spring Security 的环境设置与基础配置(2024 最新版)(下)
16 0
|
8天前
|
安全 Java 数据库
第2章 Spring Security 的环境设置与基础配置(2024 最新版)(上)
第2章 Spring Security 的环境设置与基础配置(2024 最新版)
33 0
|
9天前
|
安全 Java Spring
Spring Security 5.7 最新配置细节(直接就能用),WebSecurityConfigurerAdapter 已废弃
Spring Security 5.7 最新配置细节(直接就能用),WebSecurityConfigurerAdapter 已废弃
20 0
|
9天前
|
安全 Java 应用服务中间件
江帅帅:Spring Boot 底层级探索系列 03 - 简单配置
江帅帅:Spring Boot 底层级探索系列 03 - 简单配置
25 0
江帅帅:Spring Boot 底层级探索系列 03 - 简单配置
|
9天前
|
Java 关系型数据库 MySQL
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
UWB (ULTRA WIDE BAND, UWB) 技术是一种无线载波通讯技术,它不采用正弦载波,而是利用纳秒级的非正弦波窄脉冲传输数据,因此其所占的频谱范围很宽。一套UWB精确定位系统,最高定位精度可达10cm,具有高精度,高动态,高容量,低功耗的应用。
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
|
14天前
|
JSON Java 数据库连接
属性注入掌握:Spring Boot配置属性的高级技巧与最佳实践
属性注入掌握:Spring Boot配置属性的高级技巧与最佳实践
23 1
|
15天前
|
Java 数据库连接 Spring
简化配置,提高灵活性:Spring中的参数化配置技巧
简化配置,提高灵活性:Spring中的参数化配置技巧
19 0