Spring AOP拦截规则的两种定义方式
AOP的根本目的就是解耦,分开业务代码与系统共用代码,例如打印日志。
Spring支持AspectJ的注解式切面编程,主要包含4个部分,分别是
- 使用@Aspect声明切面
- 使用@After、@Before、@Around等定义建言
- 定义拦截规则,即切点,作为建言参数。使用@PointCut抽取为公共切点。
- 连接点JointPoint:符合拦截条件的位置
使用注解切面必须要引入Aspectj的依赖,否则会报创建Bean失败。
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
使用注解定义拦截规则
定义一个注解,作为元数据,用于反射。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Action {
String name();
}
使用注解
@Service
public class DemoAnnotationService {
@Action(name = "拦截add操作")
public void add() {
}
}
定义切面
@Component
@Aspect
public class LogAspect {
@Pointcut("@annotation(com.lzp.springboot.p1.aop.Action)")
public void annotationPointCut() {
}
@After("annotationPointCut()")
public void after(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Action annotation = method.getAnnotation(Action.class);
System.out.println("注解名:" + annotation.name());
}
}
使用execution定义拦截规则
使用execution就不需要自定义注解,需要指定扫描的包、类、方法。
@Service
public class DemoMethodService {
public void add() {
}
}
定义切面
@Component
@Aspect
public class LogAspect {
@Before("execution(* com.lzp.springboot.p1.aop.DemoMethodService.*(..))")
public void before(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
System.out.println("方法规则:" + method.getName());
}
}
测试
开启Spring对AspectJ的支持,需要使用@EnableAspectJAutoProxy注解。
@Configuration
@ComponentScan("com.lzp.springboot.p1.aop")
@EnableAspectJAutoProxy
public class AopConfig {
}
测试
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AopConfig.class);
DemoAnnotationService demoAnnotationService = context.getBean(DemoAnnotationService.class);
DemoMethodService demoMethodService = context.getBean(DemoMethodService.class);
demoAnnotationService.add();
demoMethodService.add();
context.close();
}
}
一个打印Service执行时间的示例
/**
* 监控Service运行时间
*
* @author lzp
* @date 2020/7/30
*/
@Aspect
@Component
public class ServiceLogAspect {
/**
* AOP通知主要有5种
* 1. 前置通知@Before:方法调用前执行
* 2. 后置通知@AfterReturning:方法调用后执行
* 3. 环绕通知@Around:方法调用前后通知
* 4. 异常通知@AfterThrowing:方法发生异常通知
* 5. 最终通知@After:最终final通知,不管是否异常都会通知
*/
private static final Logger logger =
LoggerFactory.getLogger(ServiceLogAspect.class);
/**
* AOP切面表达式
* execution 代表所要执行的表达式主体
* 第一处 * 代表方法返回类型 *代表所有类型
* 第二处 包名代表aop监控的类所在的包
* 第三处 .. 代表该包以及其子包下的所有类方法
* 第四处 * 代表类名,*代表所有类
* 第五处 *(..) *代表类中的方法名,(..)表示方法中的任何参数
*/
@Around("execution(* com.lzp.service.impl..*.*(..))")
public Object recordServiceTimeLog(ProceedingJoinPoint joinPoint) throws Throwable {
logger.info("====== 开始执行{}.{} ======",
joinPoint.getTarget().getClass(), // service 实现类
joinPoint.getSignature().getName()); // 方法签名
long begin = System.currentTimeMillis();
// 执行目标方法
Object result = joinPoint.proceed();
long end = System.currentTimeMillis();
logger.warn("====== 执行结束,耗时{}毫秒 ======", (end - begin));
return result;
}
}