文章目录
- 一. AOP介绍
- 二. AOP使用
- Spring AOP核心概念
- 切点
- 连接点
- 通知
- 切面
- 通知类型
- @PointCut
- 切面优先级@Order
- 切点表达式
- execution(....)
- @annotation
- 自定义注解
- 使用@annotation
- 添加自定义注解
- 三. 代理模式
一. AOP介绍
Spring框架两大核心:
- IoC
- AOP
二. AOP使用
- 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
- 编写AOP程序
记录Controller中每个方法的执行时间
@Slf4j
@Aspect
@Component
public class TimeAspect {
@Around("execution(* com.example.demo.controller.*.*(..))")
public Object recordTime(ProceedingJoinPoint pjp) throws Throwable {
//记录方法执行开始时间
long begin = System.currentTimeMillis();
//执行方法
Object result = pjp.proceed();
//记录执行方法结束时间
long end = System.currentTimeMillis();
//记录方法执行耗时
log.info(pjp.getSignature() + "耗时: {} ms",end-begin);
return result;
}
}
Spring AOP核心概念
切点
这就是切点表达式
连接点
@RequestMapping("/test")
@RestController
public class TestController {
@RequestMapping("/t1")
public String t1() {
return "t1";
}
@RequestMapping("/t2")
public Boolean t2() {
return true;
}
@RequestMapping("/t3")
public Integer t3() {
return 1;
}
}
这些方法都是连接点
通知
切面
切⾯所在的类,我们⼀般称为切⾯类(被@Aspect注解标识的类)
通知类型
@Slf4j
@Aspect
@Component
public class TimeAspect {
@Around("execution(* com.example.demo.controller.*.*(..))")
public Object recordTime(ProceedingJoinPoint pjp) throws Throwable {
//记录方法执行开始时间
long begin = System.currentTimeMillis();
//执行方法
Object result = pjp.proceed();
//记录执行方法结束时间
long end = System.currentTimeMillis();
//记录方法执行耗时
log.info(pjp.getSignature() + "耗时: {} ms",end-begin);
return result;
}
@Before("execution(* com.example.demo.controller.*.*(..))")
public void doBefore(){
log.info("执行before方法");
}
@After("execution(* com.example.demo.controller.*.*(..))")
public void doAfter(){
log.info("执行After方法");
}
@AfterReturning("execution(* com.example.demo.controller.*.*(..))")
public void doAfterReturning(){
log.info("执行AfterReturning方法");
}
@AfterThrowing("execution(* com.example.demo.controller.*.*(..))")
public void doAfterThrowing(){
log.info("执行AfterThrowing方法");
}
@Around("execution(* com.example.demo.controller.*.*(..))")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("Around方法开始前");
Object object = joinPoint.proceed();
log.info("Around方法开始后");
return object;
}
}
测试:
发生异常:
@PointCut
@Pointcut("execution(* com.example.demo.controller.*.*(..))")
public void pt(){}
@Before("pt()")
public void doBefore(){
log.info("执行before方法");
}
切面优先级@Order
现有两个切面类, 分别有before和after方法, 那么方法的执行顺序为:
切点表达式
execution(…)
execution() 是最常⽤的切点表达式,⽤来匹配⽅法,语法为:
execution(<访问修饰符> <返回类型> <包名.类名.⽅法(⽅法参数)> <异常>)
其中:访问修饰符和异常可以省略
@annotation
可是使用自定义注解和@annotation的方式来描述哪些方法需要切面
自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAspect {
}
使用@annotation
使用@annotation来描述自定义注解, 放在切点表达式中
表示, 只有加了自定义注解的方法, 才是连接点
@Aspect
@Component
@Slf4j
public class MyAspectDemo {
@Around("@annotation(com.example.demo.aspect.MyAspect)")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("方法前");
Object object = joinPoint.proceed();
log.info("方法后");
return object;
}
}
添加自定义注解
只给t3加注解, 测试:
三. 代理模式
使用代理前:
使用代理后:
静态代理:在程序运⾏前,代理类的.class⽂件就已经存在了
相⽐于静态代理来说,动态代理更加灵活
Spring AOP是基于动态代理实现的
Java也对动态代理进⾏了实现,并给我们提供了⼀些API,常⻅的实现⽅式有两种:
JDK动态代理有⼀个最致命的问题是其只能代理实现了接⼝的类