目录
2. 定义 AOP 切面类(建议新建 aop 包,不要忘了加上 @Aspect、@Component 注解):
一:AOP 切面的快速引用
1. 引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2. 定义 AOP 切面类(建议新建 aop 包,不要忘了加上 @Aspect、@Component 注解):
package com.example.springbootaop.aop;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogAspect {
/**
* 日志打印
*/
private Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 使用Pointcut给这个方法定义切点,即UserService中全部方法均为切点。<br>
* 这里在这个log方法上面定义切点,然后就只需在下面的Before、After等等注解中填写这个切点方法"log()"即可设置好各个通知的切入位置。
* 其中:
* <ul>
* <li>execution:代表方法被执行时触发</li>
* <li>*:代表任意返回值的方法</li>
* <li>com.example.springbootaop.service.impl.UserServiceImpl:这个类的全限定名</li>
* <li>(..):表示任意的参数</li>
* </ul>
*/
@Pointcut("execution(* com.example.springbootaop.service.impl.UserServiceImpl.*(..))")
public void log() {
}
/**
* 前置通知:在被代理方法之前调用
*/
@Before("log()")
public void doBefore() {
logger.warn("调用方法之前:");
logger.warn("接收到请求!");
}
/**
* 后置通知:在被代理方法之后调用
*/
@After("log()")
public void doAfter() {
logger.warn("调用方法之后:");
logger.warn("打印请求内容完成!");
}
/**
* 返回通知:被代理方法正常返回之后调用
*/
@AfterReturning("log()")
public void doReturning() {
logger.warn("方法正常返回之后:");
logger.warn("完成返回内容!");
}
/**
* 异常通知:被代理方法抛出异常时调用
*/
@AfterThrowing("log()")
public void doThrowing() {
logger.error("方法抛出异常!");
}
}
引入代码后,将 log 方法的 @Pointcut 注解中的参数:
com.example.springbootaop.service.impl.UserServiceImpl
改为自己的 Impl 包的全限定名
3. 编写核心业务代码:
Controller:
@GetMapping("/testAOP")
public void testAOPController() {
log.info("执行业务代码");
userService.testAOPService();
log.info("业务代码执行完毕");
}
Service:
/**
* 测试 AOP 引入
*/
void testAOPService();
ServiceImpl:
@Override
public void testAOPService() {
log.info("核心业务代码......");
}
调用 Controller 的接口后的效果:
只有框中的代码是我们在 业务代码编写的,其余都是经过了 AOP 切面添加。
二:AOP 切面的环绕通知
在 aop 切面类中加入环绕通知的方法:
aop 类:
/**
* 环绕通知
*/
@Around("log()")
public void around(ProceedingJoinPoint joinPoint) {
logger.warn("执行环绕通知之前:");
try {
joinPoint.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
logger.warn("执行环绕通知之后");
}
通知方法中有一个 ProceedingJoinPoint
类型参数,通过其 proceed
方法来调用原方法。需要注意的是环绕通知是会覆盖原方法逻辑的,如果上面代码不执行joinPoint.proceed();
这一句,就不会执行原被织入方法。因此环绕通知一定要调用参数的 proceed
方法,这是通过反射实现对被织入方法调用。
再次测试如下:
已经在使用已有的原通知前引入了环绕通知引入了
三:通知方法传参
上面每个通知方法是没有参数的。下面我们实现在前置通知方法中植入的参数
/**
* 前置通知:在被代理方法之前调用
*/
@Before("log() && args(num)")
public void doBefore(Integer num) {
logger.warn("调用方法之前:");
logger.info("引入的参数 num:" + num);
logger.warn("接收到请求!");
}
可见在注解后面加一个 args
选项,里面写参数名即可。
需要注意的是,通知方法的参数必须和被植入方法参数一一对应例如:
业务代码的改动:
Controller:
@GetMapping("/testAOP")
public void testAOPController() {
log.info("执行业务代码");
userService.testAOPService(1);
log.info("业务代码执行完毕");
}
Service:
/**
* 测试 AOP 引入
*/
void testAOPService(int num);
ServiceImpl:
@Override
public void testAOPService(int num) {
log.info("核心业务代码......");
}
再次测试结果:
当然,传入 aop 方法中的参数也可以是非基本数据类型,只需要把传入参数的类型改一下就好
转载来自:
https://juejin.cn/post/6999570632409088008