AOP

面向切面编程

在程序运行过程中将某段代码切入到指定方法/指定位置运行的编程方式

==底层:动态代理==

功能测试

  1. 导入aop模块:spring-aspects

  2. 定义一个业务逻辑类(MathCalculator),要求在业务逻辑种进行日志打印(方法运行之前、方法运行之后、出现异常……)

  3. 定义一个日志切面类(LogAspect),里面的方法需要动态感知 MathCalculator.div 运行到哪里并打印相关信息

    通知方法:

    • 前置通知( @Before ):LogStart
    • 后置通知( @After ):LogEnd
    • 返回通知( @AfterReturning ):LogReturn
    • 异常通知( @AfterThrowing ):LogException
    • 环绕通知( @Around ):动态代理,手动推进目标方法运行( joinPoint.proceed() )
  4. 给日志切面类标注通知注解

  5. 业务逻辑类日志切面类 都加入到容器中

  6. 告诉Spring哪个类是切面类:给切面类上加注解@Aspect

  7. 在配置类上加注解@EnableAspectJAutoProxy

    Spring中很多的 @Enablexxx

直接上代码!

1
2
3
4
5
6
7
//业务逻辑类
public class MathCalculator {

public int div(int i, int j) {
return i / j;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//切面类
@Aspect
public class LogAspect {

//设置切入点
@Pointcut("execution(public int com.wxs.aop.MathCalculator.*(..))")
public void pointCut() {
}

@Before("pointCut()")
/*
注意!!!
参数joinPoint参数必须放在第一位Spring才能识别
*/
public void logStart(JoinPoint joinPoint) {
System.out.println("除法运行参数是:" + joinPoint.getArgs().toString());
}

@After("pointCut()")
public void logEnd(JoinPoint joinPoint) {
System.out.println(joinPoint.getSignature().getName() + "正常运行");
}

@AfterReturning(value = "pointCut()",returning = "result")
public void logReturn(Object result) {
System.out.println("除法的结果是:"+ result);
}

@AfterThrowing(value = "pointCut()",throwing = "e")
public void logException(Exception e) {
System.out.println("出现的异常" + e);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//主配置类
@EnableAspectJAutoProxy
@Configuration
public class MainConfigOfAOP {

@Bean
public MathCalculator mathCalculator() {
return new MathCalculator();
}

@Bean
public LogAspect logAspect() {
return new LogAspect();
}
}

AOP原理

==源码分析:看给容器中注册了什么组件、组件如何工作、有什么功能==

  1. @EnalbleAspectJAutoProxy

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

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

    给容器中注册一个名叫 internalAutoProxyCreator

    类型为 AnnotationAwareAspectJAutoProxyCreator 的组件

  2. 接下来我们就详细看一下 AnnotationAwareAspectJAutoProxyCreator 是个什么东西:

    先康康继承树:

    image-20200415122727767

    重点关注 后置处理器自动装配BeanFactory

    我们从上往下慢慢看!

    1.先从父类 AbstractAutoProxyCreator 开始看!
    1
    2
    3
    4
    5
    6
    7
    8
    //实现BeanFactory的方法
    public void setBeanFactory(BeanFactory beanFactory)

    //有后置处理器相关的方法
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName)
    public boolean postProcessAfterInstantiation(Object bean, String beanName)
    public Object postProcessBeforeInitialization(Object bean, String beanName)
    public Object postProcessAfterInitialization(@Nullable Object bean, String beanName)
    2.再看 AbstractAdvisorAutoProxyCreator
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //重写了setBeanFactory方法
    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
    super.setBeanFactory(beanFactory);
    if (...) {
    ...
    } else {
    //内部调用了initBeanFactory方法
    this.initBeanFactory((ConfigurableListableBeanFactory)beanFactory);
    }
    }
    3.继续向下看子类 AspectJAwareAdvisorAutoProxyCreator
    1
    //emmmmm,方法都跟 后置处理器 或者 自动装配BeanFactory 没啥关系,就不看了
    4.最后看 AnnotationAwareAspectJAutoProxyCreator !
    1
    2
    3
    //又重写了该方法,相当于父类 setBeanFactory() 时还得调它
    @Override
    protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory)

    流程:

    image-20200415125231299

    1.传入主配置类,创建ioc容器

    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfigOfAOP.class);

    2.注册配置类,调用 refresh(),刷新容器(就跟初始化容器一样)
    3. refresh() 中:image-20200415124948236

    注册 bean 的后置处理器来方便拦截 bean 的创建,进去康康

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // 1.先获取ioc容器中需要创建对象的所有 BeanPostProcessor
    String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);

    // 2.给容器中加别的BeanPostProcessor
    beanFactory.addBeanPostProcessor
    (new PostProcessorRegistrationDelegate.BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));

    // 3.优先注册实现了接口 PriorityOrdered 的 BeanPostProcessor
    if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
    pp = (BeanPostProcessor)beanFactory
    .getBean(ppName,BeanPostProcessor.class);
    priorityOrderedPostProcessors.add(pp);
    }
    // 4.再注册实现了 Ordered 接口的 BeanPostProcessor
    else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
    orderedPostProcessorNames.add(ppName);
    }
    // 5.最后注册没实现优先级接口的 BeanPostProcessor
    else {
    nonOrderedPostProcessorNames.add(ppName);
    }
    // 6.注册BeanPostProcessor,实际上是创建 BeanPostProcessor 对象,保存在容器中

    创建 internalAutoProxyCreatorBeanPostProcessor

     1. 创建 Bean 的实例
    
    1. populateBean():给 bean 的各种属性赋值

    2. initializeBean():初始化 bean

      1. invokeAwareMethods():处理Aware接口的方法回调
      2. applyBeanPostProcessorsBeforeInitialization():应用后置处理器的 beforeInitialization()
      3. invokeInitMethods():执行自定义初始化方法
      4. applyBeanPostProcessorsAfterInitialization():应用后置处理器的 afterInitialization()
    3. BeanPostProcessor 创建成功

      把 BeanPostProcessor 注册到 BeanFactory中:beanFactory.addBeanPostProcessor(postProcessor)

    4.finishBeanFactoryInitialization(beanFactory):完成 BeanFactory初始化工作
    1. 遍历获取容器中所有的 Bean ,依次创建对象

      getBean()—>doGetBean()—>getSingleton()

    2. 创建 bean

      1. 先去缓存中康康,如果没有再创建 bean

      2. createBean():创建 bean

        1. resolveBeforeInstantiation(beanName, mbd)

          希望后置处理器在此处能返回一个代理对象,如果能返回代理对象就使用,不能就继续执行下面的操作

        2. doCreateBean(beanName, mbdToUse,args):真正去创建一个bean实例