创建测试工程
请参照任何一个spring boot的工程创建。在此不在赘述。
- pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.platform</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- 定义一个注解
package com.platform.demo;
import java.lang.annotation.Documented;
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)
@Documented
public @interface LogAnno {
}
- 定义个Controller类,方便测试
package com.platform.demo;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MainController {
@GetMapping("/test")
@ResponseBody
@LogAnno
public String test() {
return "success";
}
}
- 定义切面类,先来看一个简单的定义
package com.platform.demo;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class LogAspect {
@Before("@annotation(com.platform.demo.LogAnno)")
public void before() {
System.out.println("before.............");
}
@After("@annotation(com.platform.demo.LogAnno)")
public void after() {
System.out.println("after.............");
}
}
-
当我们通过postman发送请求。
可以看到对应的日志输出如下:
before.............
after.............
从上述上我们基本可以了解下AOP的基本用法,就是像刀子一样能在对应的调用链路上切开一道口子,以实现对应的逻辑。接下来我们重点讲几个关于AOP的具体实现。
用法说明
- @Pointcut
在理解这个注解前,先来看一个具体示例,还是以上面的例子说明。大家有没有发现@Before的注解的值和@After注解的值是一样的,有重复的嫌疑。哪么针对这种情况,我们怎么处理呢,是否有类似别名或者其他替代的方式呢。答案是有的。请看下面的示例:
package com.platform.demo;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class LogAspect {
@Pointcut("@annotation(com.platform.demo.LogAnno)")
public void piontCutLog() {
}
@Before("piontCutLog()")
public void before() {
System.out.println("before.............");
}
@After("piontCutLog()")
public void after() {
System.out.println("after.............");
}
}
我们发现@Pointcut在此处充当了别名的作用,所以从这里按照字面意思理解,就是切面点定义。
- @Before
在切入点前执行逻辑,上面介绍过用法,下面的示例代码和结果可以进一步说明结论。 - @After
在切入点后执行逻辑,上面介绍过用法,下面的示例代码和结果可以进一步说明结论。
package com.platform.demo;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MainController {
@GetMapping("/test")
@ResponseBody
@LogAnno
public String test() {
System.out.println("test running.............");
return "success";
}
}
before.............
test running.............
after.............
- @Arround
Aop中最重要的注解,因为功能强大,所以处理起来也足够强大。
@Component
@Aspect
public class LogAspect {
@Pointcut("@annotation(com.platform.demo.LogAnno)")
public void piontCutLog() {
}
@Before("piontCutLog()")
public void before() {
System.out.println("before.............");
}
@After("piontCutLog()")
public void after() {
System.out.println("after.............");
}
@Around("piontCutLog()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("around advise before");
Object result = pjp.proceed();
System.out.println("around advise end " + result);
return result;
}
}
展示结果如下:
around advise before
before.............
test running.............
after.............
around advise end success
通过上面的列子我们发现环绕通知可以获取运行的结果,甚至于说让不让运行,都可以左右。
未完待补充
附录:切点指示符 (9类)
- execution
- within
- this、
- target、
- args、
- @target、
- @args、* @within、
- @annotation