Spring注解(AOP)
底层动态代理
程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式
导入aop的相关模块
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.1.6.RELEASE</version> </dependency>
动态感知业务类运行的状态
通知: 前置 @Before 后置 @After 环绕 @Around 异常@AfterThrowing 返回 @AfterReturning
补充环绕通知: 动态代理,手动推进目标方法运行(joinPoint.proceed() )
步骤:
编写切面类
在切面类里面 把每个方法 何时何地切入都指定好
将切面类和业务逻辑类 都添加到容器中
告诉容器 哪个是切面类@Aspect
在 xml中 <aop: aspectj-autoproxy> </aop:aspectj-autoproxy> 开启基于注解版的切面功能
注解中: 在当前的配置类加入 @EnableAspectJAutoProxy 启用基于注解的aop模式
正常代码:
@Component public class Mathcalculator { public int div(int i , int j) { System.out.println("Mathcalculator--div被调用"); return i/j; } }
切面类:
@EnableAspectJAutoProxy @Configuration public class AOPConfig { @Bean //业务逻辑类注册到容器 public Mathcalculator mathcalculator() { return new Mathcalculator(); } @Bean // 切面类注册到容器中 public LogAspects logAspects() { return new LogAspects(); } }
测试:
public class test { @Test public void test01(){ //使用无参构造器 创建applicationContext (详情自己研究下有参构造器的方法) AnnotationConfigApplicationContext applicationContext= new AnnotationConfigApplicationContext(AOPConfig.class); Mathcalculator bean = applicationContext.getBean(Mathcalculator.class); bean.div(1, 1); //设置需要激活的环境 applicationContext.getEnvironment().setActiveProfiles("dev","test"); //可以设置多个 //注册主配置类 applicationContext.register(Profile.class); //启动刷新容器 applicationContext.refresh(); } }
结果:
升级:
切面类
@Aspect public class LogAspects { //抽取公共的切入点表达式 本类的 如果是别的类的 写全名就OK了 @Pointcut("execution(public int com.toov5.config.Mathcalculator .*(..))" ) public void pointCut() { } // @Before("public int com.toov5.config.Mathcalculator.div(int , int)") @Before( "pointCut()" ) public void logStart(JoinPoint joinpoint) { //获取签名 获取方法 Object[] args = joinpoint.getArgs(); //目标方法 运行时候需要的参数表 System.out.println("除法运行@Before"+joinpoint.getSignature().getName()+"运行时参数表"+args ); } @After( "pointCut()" ) //运行异常与否 都要执行的 public void logEnd(JoinPoint joinPoint) { System.out.println(joinPoint.getSignature().getName()+"除法结束@After"); } @AfterReturning(value="pointCut()", returning="result") public void logReturn(JoinPoint joinpoint,Object result ) { //result 接受返回值 System.out.println(joinpoint.getSignature().getName()+"除法正常运行@AfterReturning,结果:"+result); } @AfterThrowing(value = "pointCut()", throwing="e") public void logException(JoinPoint joinpoint, Exception e) { System.out.println( joinpoint.getSignature().getName()+"除法异常@AfterThrowing"+e.getMessage()); } }
配置:
@EnableAspectJAutoProxy @Configuration public class AOPConfig { @Bean //业务逻辑类注册到容器 public Mathcalculator mathcalculator() { return new Mathcalculator(); } @Bean // 切面类注册到容器中 public LogAspects logAspects() { return new LogAspects(); } }
测试类:
public class test { @Test public void test01(){ //使用无参构造器 创建applicationContext (详情自己研究下有参构造器的方法) AnnotationConfigApplicationContext applicationContext= new AnnotationConfigApplicationContext(AOPConfig.class); Mathcalculator bean = applicationContext.getBean(Mathcalculator.class); bean.div(1, 1); //设置需要激活的环境 applicationContext.getEnvironment().setActiveProfiles("dev","test"); //可以设置多个 //注册主配置类 applicationContext.register(Profile.class); //启动刷新容器 applicationContext.refresh(); } }
结果:
JointPoint 要写一定要写在 参数的首位!
三步走:
1 将业务逻辑组件和切面类都加入到容器,告诉Spring哪个是切面类(@Aspect)
2 在切面类上的每一个通知方法上标注 通知注解,告诉Spring合适何地运行(切入点表达式)
3 开启基于注解的aop模式 与config上
总结:
aop的原理
加入@EnableAspectJAutoProxy 才有AOP
继续点开看
利用AspectJAutoProxyRegistrar 给自定义容器中注册bean
给容器注册一个AnnotationAwareAspectJAutoProxyCreator 自动代理创建器
可以一直追踪其父类:
实现了 SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
后置处理器
关注后置处理器的工作 Bean初始化完成前后做的事情
BeanFactory 自动注入
AbstractAutoProxyCreator :
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
然后是与后置处理器有关的:
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName)
有后置处理器的逻辑
AbstractAutoProxyCreator 类
AnnotationAwareAspectJAutoProxyCreator 类 initBeanFactory
启动后
1、 传入主配置类 创建 IOC容器
2、 注册配置类 调用refresh() 刷新容器
3、 registerBeanPostProcessors(beanFactory) 注册bean的后置处理器 方便拦截bean的创建
1先获取ioc容器中已经定义了需要创建对象的所有BeanPostProcessor
2给容器加别的BeanPostProcessor
3优先注册实现了PriorityOrdered接口的BeanPostProcessor
4在给容器中注册实现了Ordered接口的BeanPostProcessor
5注册没实现优先级接口的BeanPostProcessor
创建internalAutoProxyCreator的BeanPostProcessor
创建Bean的实例
populateBean 初始化Bean
1、 invokeAwareMethods() 处理Aware接口的方法回调
2、 applyBeanPostProcessorsBeforeInitialization() 应有后置处理器的BeforeInitialization( )
3、invokeInitMethods() 自定义的初始化方法
4、applyBeanPostProcessorsAfterInitialization () 执行后置处理器的 postProcessAfterInitialization
4、BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)创建成功; aspectJAdvisorBuilder
6 把beanPostProcessor注册到BeanFactory中 调用addBeanPostProcessor(postProcessor)