基于注解配置的AOP
注解方式AOP的基本使用
- Spring的AOP也提供了注解方式配置,使用相应的注解替代之前的xml配置,xml配置AOP时,我们主要配置了三部分:目标类被Spring容器管理(注解使用@Service)、通知类被Spring容器管理(注解使用@Component)、通知与切点的织入(切面)(使用注解@Aspect,不同的通知使用不同对应的注解),如下
使用注解具体示例代码
package com.example.advice;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
// 自定义增强类,内部提供增强方法
@Component
@Aspect // 告诉Spring容器该类是一个切面
public class MyAdvice {
@Before("execution(void com.example.Service.ServiceImpl.UserServiceImpl.*(..))")
// todo 前置通知
public void beforeAdvice() {
System.out.println("前置通知");
}
// todo 后置通知
@AfterReturning("execution(void com.example.Service.ServiceImpl.UserServiceImpl.*(..))")
public void afterAdvice() {
System.out.println("后置通知");
}
// todo 环绕通知
@Around("execution(void com.example.Service.ServiceImpl.UserServiceImpl.*(..))")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
// 前置通知
System.out.println("环绕通知:前置通知");
Object res = proceedingJoinPoint.proceed(); // 执行目标方法
System.out.println("环绕通知中目标方法执行了");
// 后置通知
System.out.println("环绕通知:后置通知");
return res;
}
// todo 异常通知
@AfterThrowing(pointcut = "execution(void com.example.Service.ServiceImpl.UserServiceImpl.*(..))", throwing = "throwable")
public void afterThrowingAdvice(Throwable throwable) {
System.out.println("异常抛出通知...出现异常才会执行");
}
// todo 最终通知
@After("execution(void com.example.Service.ServiceImpl.UserServiceImpl.*(..))")
public void endAdvice() {
System.out.println("最终通知....怎么样都会通知");
}
}
只需要在配置文件中指定组件扫描范围即可
测试类代码如下
package com.example.Test;
import com.example.Service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestMyAOP {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext3.xml");
UserService userService = applicationContext.getBean(UserService.class);
userService.show1();
}
}
运行结果如下
可以参考java web专栏中的AOP相关文章:内容管理-CSDN创作中心
注解方式AOP配置详解
package com.example.advice;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
// 自定义增强类,内部提供增强方法
@Component
@Aspect // 告诉Spring容器该类是一个切面
public class MyAdvice {
// todo 切点表达式的抽取
@Pointcut("execution(void com.example.Service.ServiceImpl.UserServiceImpl.*(..))")
public void MyPointCut() {
}
@Before("MyAdvice.MyPointCut()")
// todo 前置通知
public void beforeAdvice() {
System.out.println("前置通知");
}
// todo 后置通知
@AfterReturning("MyAdvice.MyPointCut()")
public void afterAdvice() {
System.out.println("后置通知");
}
// todo 环绕通知
@Around("MyAdvice.MyPointCut()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
// 前置通知
System.out.println("环绕通知:前置通知");
Object res = proceedingJoinPoint.proceed(); // 执行目标方法
System.out.println("环绕通知中目标方法执行了");
// 后置通知
System.out.println("环绕通知:后置通知");
return res;
}
// todo 异常通知
@AfterThrowing(pointcut = "MyAdvice.MyPointCut()", throwing = "throwable")
public void afterThrowingAdvice(Throwable throwable) {
System.out.println("异常抛出通知...出现异常才会执行");
}
// todo 最终通知
@After("MyAdvice.MyPointCut()")
public void endAdvice() {
System.out.println("最终通知....怎么样都会通知");
}
}
同样的,可以使用配置类来代替上述的配置文件实现全注解开发
package com.example.Config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
//注解配置类替代配置文件,实现纯注解开发
@Configuration // 表示该类是一个核心配置类,同时将该类交给Spring容器管理(内置了@Component注解)
@ComponentScan({"com.example"})//<context:component-scan base-package="com.example"/>
public class SpringConfig {
}
测试类中的获取Spring容器的方法也要改变一下。
注解方式AOP原理解析
如果使用xml配置文件+注解的方式:
还是通过标签中的命名空间对应的命名空间处理器,在MATE-INF文件夹下的spring.handlers文件夹中,进行查找
不同命名空间对应不同解析器,然后一步步追溯源码,最终还是落脚到实现BeanPostProcessor接口,重写其中的postProcessAfterInitialization方法,向容器中注册bean对象。
如果使用配置类的方式(全注解)
查看注解类中的关键注解@EnableAspectJAutoProxy,
导入了相关类,来注册bean对象
图示三种方法都是底层源码最中都是使用红色框中自动代理构造器创建proxy对象。