Srping全注解开发---AOP模块

2021-11-15 15:04:23 浏览数 (1)

AOP模块

  • 回顾五个通知注解
  • 使用演示
    • 1.切面类(@Aspect)
    • 业务逻辑类
    • 在配置类中将切面类和业务逻辑类都加入到容器中
      • 切记给配置类上加上@EnableAspectJAutoProxy,开启基于注解的aop模式
    • 测试
      • 注意:spring中很多Enablexxx都是开启某一项功能
    • 在切面类的方法中获取切入方法的相关信息
      • joinpoint必须出现在方法参数的首位
      • JoinPoint对象详细使用方法,参考下面这篇文章
    • AOP注解开发三部曲
  • AOP原理剖析部分
    • 创建和注册AnnotationAwareAspectJAutoProxyCreator的流程:
  • 总结
  • 完整流程图

回顾五个通知注解


使用演示

1.切面类(@Aspect)

代码语言:javascript复制
@Aspect//表明是一个切面类
public class MyAspect
{
    //抽取公共的切入点表达式
    //1.本类引用:直接写方法名()
    //2.其他切面引用:方法的全类名()
    @Pointcut("execution(public int com.math.calculator.*(..))")
    public void point(){};
    //在目标方法之前切入,切入点表达式(指定切入哪个方法)
    //本类方法引用切点表达式演示:
    @Before("point()")
    public void logStart()
    {
        System.out.println("目标方法运行前...");
    }
    //外部方法引用切点表达式演示:
    @After("com.aop.MyAspect.point()")
    public void logAfter()
    {
        System.out.println("目标方法结束后...");
    }

    @AfterReturning("point()")
    public void logReturning()
    {
        System.out.println("目标方法正常返回后...");
    }

    @AfterThrowing("point()")
    public void logThrowing()
    {
        System.out.println("目标方法出现异常后...");
    }
}

业务逻辑类

代码语言:javascript复制
//计算器类
public class calculator
{
    public int add(int i,int j)
    {
        return i j;
    }
}

在配置类中将切面类和业务逻辑类都加入到容器中

切记给配置类上加上@EnableAspectJAutoProxy,开启基于注解的aop模式

代码语言:javascript复制
@EnableAspectJAutoProxy
@Configuration
public class MyConfig
{
    @Bean
    public MyAspect myAspect()
    {
        return new MyAspect();
    }
    @Bean
    public calculator calculator()
    {
        return new calculator();
    }
}

测试

代码语言:javascript复制
public class Main
{
  //传入的是配置类的位置,一开始是加载配置类,之前是加载配置文件的位置
  private static AnnotationConfigApplicationContext ioc= new AnnotationConfigApplicationContext(MyConfig.class);

  public static void main(String[] args)
  {
    calculator bean = ioc.getBean(calculator.class);
    System.out.println(bean.add(1,2));
  }
}

注意:spring中很多Enablexxx都是开启某一项功能


在切面类的方法中获取切入方法的相关信息

joinpoint必须出现在方法参数的首位

代码语言:javascript复制
@Aspect//表明是一个切面类
public class MyAspect
{
    //抽取公共的切入点表达式
    //1.本类引用:直接写方法名()
    //2.其他切面引用:方法的全类名()
    @Pointcut("execution(public int com.math.calculator.*(..))")
    public void point(){};
    //在目标方法之前切入,切入点表达式(指定切入哪个方法)
    //本类方法引用切点表达式演示:
    @Before("point()")
    public void logStart(JoinPoint joinPoint)
    {
        System.out.println("目标方法运行前  " "方法名:" joinPoint.getSignature().getName());
    }
    //外部方法引用切点表达式演示:
    @After("com.aop.MyAspect.point()")
    public void logAfter()
    {
        System.out.println("目标方法结束后...");
    }

    //joinpoint必须出现在方法参数的首位
    @AfterReturning(value = "point()",returning = "res")
    public void logReturning(JoinPoint js,Object res)
    {
        System.out.println(js.getSignature().getName() "方法正常返回后  " "方法返回值:" res);
    }

    //joinpoint必须出现在方法参数的首位
    @AfterThrowing(value = "point()",throwing = "ex")
    public void logThrowing(JoinPoint js,Exception ex)
    {
        System.out.println(js.getSignature().getName() "方法出现异常后  " "异常是:" ex);
    }
}

JoinPoint对象详细使用方法,参考下面这篇文章

SpringAop中JoinPoint对象的使用方法


AOP注解开发三部曲

1.将业务逻辑组件和切面类加入到容器中,告诉spring容器哪一个是切面类(@Aspect)

2.在切面类上的每一个通知方法上标注通知注解,告诉Spring何时何地运行(切入点表达式)

3.开启基于注解的aop模式


AOP原理剖析部分

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

@EnableAspectJAutoProxy:

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

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

==>给容器中注册一个AnnotationAwareAspectJAutoProxyCreator(AspectJ自动代理创建器)


下面展示AspectJ自动代理创建器往上的父类层级关系: AnnotationAwareAspectJAutoProxyCreator –>AspectJAwareAdvisorAutoProxyCreator —>AbstractAdvisorAutoProxyCreator —>AbstractAutoProxyCreator implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware 后置处理器,自动装配 BeanFactory


创建和注册AnnotationAwareAspectJAutoProxyCreator的流程:

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

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

代码语言:javascript复制
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
    this();
    this.register(annotatedClasses);//注册配置类
    this.refresh();//刷新容器
}

3. refresh方法中===> this.registerBeanPostProcessors(beanFactory);

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

代码语言:javascript复制
      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]  
          (1).创建bean的实例
          (2).populateBean:给Bean的各种属性赋值
          (3).initializeBean:初始化Bean
                (3.1).invokeAwareMethods():处理Aware接口的方法回调
                (3.2).applyBeanPostProcessorsBeforeInitialization:应用后置处理器的postProcessBeforeInitialization
                (3.3).invokeInitMethods():执行自定义的初始化方法
                (3.4).applyBeanPostProcessorsAfterInitialization执行后置处理器的postProcessAfterInitialization
          (4).BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)创建成功


总结:如果我们需要对某个业务逻辑组件进行增强,那么最后我们从容器中拿到的这个业务逻辑组件实际是一个被增强了的代理对象




总结


完整流程图

0 人点赞