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.开始测试:

Spring BOOT中AOP的实现以及【通知顺序】测试

①.正常执行的效果:看控制台输出

Spring BOOT中AOP的实现以及【通知顺序】测试

②异常执行的效果:看控制台输出

Spring BOOT中AOP的实现以及【通知顺序】测试

③使用默认的@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==自定义==后置通知执行了》》》");
    }

异常执行,看输出台:

Spring BOOT中AOP的实现以及【通知顺序】测试

正常执行,看输出台:

Spring BOOT中AOP的实现以及【通知顺序】测试

6.测试结果分析:

不自定义情况下,无异常的aop执行流程:环绕前置==》前置==》程序执行==》环绕后置==》后置==》返回

不自定义情况下,有异常的aop执行流程:环绕前置==》前置==》程序执行==》环绕后置==》后置==》异常返回

自定义情况下,无异常的aop执行流程:自定义环绕前置==》前置==》程序执行==》自定义环绕返回==》自定义环绕后置==》自定义环绕返回==》后置==》返回

自定义情况下,无异常的aop执行流程:自定义环绕前置==》前置==》程序执行==》自定义环绕异常==》自定义环绕后置==》自定义环绕返回==》后置==》返回

结论:

①@around环绕通知很强大,可以自定义通知,私人定制。

②环绕前置通知优先前置通知先执行;

③后置通知在环绕后置通知后执行(在不自定义@around的情况下,返回和异常只返回其中一个);

欢迎留言补充,第一次写博客,写的稀烂。。。