spring实现切面编程
Spring两大特性
- IOC(控制反转)
IOC:通俗点来讲,就是把对象的创建交给Spring容器来管理,不用我们手动new
- AOP(面向切面编程)
AOP:定义一个切面,在切面中执行特定代码,实现代码增强,常用于日志打印,异常处理,性能耗时计算,事务处理,安全验证等等,
用AOP和不用AOP做日志记录的区别
- 不用AOP:
每个方法都要写记录日志的代码,代码多,工作量大
- 使用AOP:
日志统一交给某个类(切面配置类)来记录,这样做的好处是业务代码简洁了,也能达到一定的解耦等等
AOP实战之旅(基于注解)
- 实战环境:jdk8、SpringBoot2.4.3、maven3.6。
- @Pointcut: 切点。
- @Before: 切面方法执行之前执行。
- @After: 切面方法执行之后执行 。
- @AfterRunning: 切面方法成功返回结果之后执行
- @AfterThrowing: 切面方法抛出异常后执行
1、SpringBoot项目搭建,并加入AOP依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2、创建切面类SystemLogAspect
@Aspect //标识为一个切面类
@Component //交给Spring管理
public class SystemLogAspect {
//@Pointcut("@annotation(com.demo.aop.joggle.SystemLogInterface)") //应用在有@SystemLogInterface注解的方法上
public void controllerAspect(){
System.out.println("我是切点");
}
@Before("controllerAspect()")
public void doBefore(JoinPoint joinPoint) {
// 线程绑定变量(该数据只有当前请求的线程可见)
Date beginTime = new Date();
System.out.println("方法执行前通知");
//doSomething
}
@AfterReturning("controllerAspect()")
public void after(JoinPoint joinPoint) {
System.out.println("方法返回结果后执行");
//doSomething
}
@AfterThrowing("controllerAspect()")
public void afterThrow(JoinPoint joinPoint){
System.out.println("方法发生异常后执行");
//doSomething
}
}
3、创建自定义注解SystemLogInterface
/**
* @description:切面注解
* @author: Mosey
* @time: 2021/3/6 14:50
*/
@Target(ElementType.METHOD)//作用于参数或方法上
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SystemLogInterface {
}
4、创建TestController,并在test方法处使用自定义的SystemLogInterface注解
@RequestMapping(value = "/test")
@Slf4j
@RestController
public class TestController {
@SystemLogInterface
@GetMapping(value = "/aop")
public String test(){
System.out.println("方法正在执行中。。。");
return "访问成功";
}
}
5、运行项目。访问localhost:8080/test/aop会打印出下面的结果
方法执行前通知
方法正在执行中。。。
方法返回结果后执行
!>注意:如果方法运行时发生异常,会进入到@AfterThrowing中,如果我们有在test()方法中捕获异常,则不会进入@AfterThrowing。
AOP实战之旅(不基于注解)
-
上面的切面类SystemLogAspect的切点用到的是@Pointcut("@annotation(com.demo.aop.joggle.SystemLogInterface)")基于注解实现AOP的。
-
其实我们也可以用@Pointcut("execution(* com.demo.aop.controller..test())")来进行切点。
-
execution(* com.demo.aop.controller..test())意思是:com.demo.aop.controller包下的所有类的test方法(),括号里边的..表示任何参数。
-
PS:切点换成execution表达式后,要把controller层的@SystemLogInterface注解注释掉。
-
运行结果和上面的一样
方法执行前通知
方法正在执行中。。。 方法返回结果后执行
execution表达式
更多有关于execution表达式的,可以看下面的截图,execution也可以用连接符&& || 和!等匹配多个表达式,具体问题具体分析
补充
1.当
@Before
、@After
、@Around
、@AfterReturning
、@AfterThrowing
同时使用并切点相同时
-
@Around
中没有调用pjp.proceed()
方法时,不执行切点对应的目标方法 -
如果有调用
pjp.proceed()
方法时,开始执行切点对应的目标方法(无异常情况),顺序:@Around =》@Before =》pjp.proceed()执行切点方法 =》@AfterReturning =》@After -
如果有调用
pjp.proceed()
方法时,开始执行切点对应的目标方法(有异常情况),顺序:@Around =》@Before =》目标方法发生异常,不返回数据 =》@AfterThrowing拦截 =》@After
2.如果有多个切面,使用@Order(Integer)注解多个切面,Integer越小,优先级越高。