Spring BOOT中AOP的实现以及【通知顺序】测试
直接上干货!
开发环境:IDEA+SpringBoot(2.1.3.RELEASE)+jdk1.8
测试时间:2019.3.15
目的:Spring BOOT中AOP的实现,并测试五钟通知模式的执行顺序
1.pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--lombok idea需要下载插件才能支持-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--热启动,自动检测更新最新代码-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<!--aop核心启动依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--boot web 依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--boot测试依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
application.yml
server:
port: 8080
spring:
aop:
proxy-target-class: true
2.声明一个自定义注解@Intercept,作为连接点(这只是我个人做法,当然可以声明其他连接点)
package com.example.model.inter;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)//运行时声明周期
@Target({ElementType.METHOD,ElementType.TYPE})//注解可以添加在哪个位置(这里是方法和类上)
public @interface Intercept {
}
3.声明一个切面类DemoAspectJ
@Aspect //开启aspect注解
@Slf4j //日志输出
@Component // 注入sprin 容器
public class DemoAspectJ {
private final Logger logger = LoggerFactory.getLogger(DemoAspectJ.class);
@Autowired
private ExceptionLogMapper exceptionLogMapper;
@Autowired
private IExceptionLogService iExceptionLogService;
@Pointcut("@annotation(com.example.model.inter.Intercept)")
public void pointCut() {
}
@Before(value = "pointCut()")
public void before(JoinPoint joinPoint) {
logger.info("@Before前置通知进来了》》》");
}
@Around(value = "pointCut()")
public void around(ProceedingJoinPoint pjp) throws Throwable{
// logger.info("@Around环绕通知进来了》》》");
// pjp.proceed();
// logger.info("@Around环绕通知出去了》》》");
try {
logger.info("@Around==自定义==前置通知执行了》》》");
pjp.proceed();
logger.info("@Around==自定义==返回通知执行了》》》");
}catch(Exception e){
logger.info("@Around==自定义==异常通知执行了》》》");
}
logger.info("@Around==自定义==后置通知执行了》》》");
}
@AfterReturning(value = "pointCut()")
public void afterReturning(JoinPoint joinPoint) {
logger.info("@AfterReturning返回通知执行了》》》");
}
@AfterThrowing(value = "pointCut()")
public void afterThrowing(JoinPoint joinPoint) {
logger.info("@AfterThrowing异常通知执行了》》》");
}
@After(value = "pointCut()")
public void after(JoinPoint joinPoint) {
logger.info("@After后置通知执行了》》》");
}
}
千万注意!!!
@EnableAspectJAutoProxy //主启动类千万别忘了开启支持AOP
@SpringBootApplication
public class ModelApplication {
public static void main(String[] args) {
SpringApplication.run(ModelApplication.class, args);
}
}
4.声明Controller请求来测试(boot关于mvc快速启动web工程的需要自行掌握,五秒部署)。
@RestController
@RequestMapping("/demo")
@Slf4j
public class DemoController {
@Autowired
private IExceptionLogService iExceptionLogService;
private final Logger logger = LoggerFactory.getLogger(DemoController.class);
@RequestMapping("/one")
@Intercept //自定义注解(被拦截)
public String doSomething(ModelAndView mv) throws Exception {
try {
int i = 2 / 1;//正常执行
logger.info("【正常执行】");
// int j = 2 / 1;//异常执行
// logger.info("【异常执行】");
} catch (Exception e) {
throw new Exception("人造异常");
}
return "hello aop!";
}
}
5.开始测试:
①.正常执行的效果:看控制台输出
②异常执行的效果:看控制台输出
③使用默认的@around,不在@around中做自定义操作,注释掉自定义的内容
@Around(value = "pointCut()")
public void around(ProceedingJoinPoint pjp) throws Throwable{
logger.info("@Around环绕通知进来了》》》");
pjp.proceed();
logger.info("@Around环绕通知出去了》》》");
// try {
// logger.info("@Around==自定义==前置通知执行了》》》");
// pjp.proceed();
// logger.info("@Around==自定义==返回通知执行了》》》");
// }catch(Exception e){
// logger.info("@Around==自定义==异常通知执行了》》》");
// }
// logger.info("@Around==自定义==后置通知执行了》》》");
}
异常执行,看输出台:
正常执行,看输出台:
6.测试结果分析:
①不自定义情况下,无异常的aop执行流程:环绕前置==》前置==》程序执行==》环绕后置==》后置==》返回
②不自定义情况下,有异常的aop执行流程:环绕前置==》前置==》程序执行==》环绕后置==》后置==》异常返回
③自定义情况下,无异常的aop执行流程:自定义环绕前置==》前置==》程序执行==》自定义环绕返回==》自定义环绕后置==》自定义环绕返回==》后置==》返回
④自定义情况下,无异常的aop执行流程:自定义环绕前置==》前置==》程序执行==》自定义环绕异常==》自定义环绕后置==》自定义环绕返回==》后置==》返回
结论:
①@around环绕通知很强大,可以自定义通知,私人定制。
②环绕前置通知优先前置通知先执行;
③后置通知在环绕后置通知后执行(在不自定义@around的情况下,返回和异常只返回其中一个);
欢迎留言补充,第一次写博客,写的稀烂。。。