目录
[1. 根据方法签名匹配编写切点表达式](#1. 根据方法签名匹配编写切点表达式)
[1.1 具体语法](#1.1 具体语法)
[1.2 通配符表达规范](#1.2 通配符表达规范)
[2. 根据注解匹配编写切点表达式](#2. 根据注解匹配编写切点表达式)
[2.1 实现步骤](#2.1 实现步骤)
[2.2 元注解及其常用取值含义](#2.2 元注解及其常用取值含义)
[2.3 使用自定义注解](#2.3 使用自定义注解)
[2.3.1 编写自定义注解@MyAspect](#2.3.1 编写自定义注解@MyAspect)
[2.3.2 编写切面类MyAspectDemo](#2.3.2 编写切面类MyAspectDemo)
[2.3.3 编写测试类及测试方法](#2.3.3 编写测试类及测试方法)
在前文使用注解编写AOP程序时,对于每一个切面,都是由切点和通知构成。
对于切点,有两种方式可以指明需要功能增强的类或类的方法:
(1)直接使用@Around、@Before、@After、@AfterReturning、@AfterThrowing等注解直接指明需要功能增强的类或方法,形如:
java
@Around("execution(* com.example.bookmanagementsystem.controller.*.*(..))")
(2)通过@Pointcut注解指明公共切点,在其他通知前仍采取五个注解指明类或方法,但可通过@Pointcut标注的通知的方法名来简化编写,形如:
java
@Pointcut("execution(* com.zhouyou.demos.controller.*.*(..))")
public void pc(){ }
@Around("pc()")
但对于方式1的每一种切点声明方式和方式2的@Pointcut声明切点方式,都是采用方法的签名来匹配。
实际上切点表达式有两种编写方式,可根据方法的签名匹配,也可根据注解匹配。
1. 根据方法签名匹配编写切点表达式
1.1 具体语法

1.2 通配符表达规范
1、*匹配诶任意字符,值匹配一个元素,包名使用*表示任意包,类名使用*表示任意类,返回值使用*表示任意返回值,方法名使用*表示任意方法,参数使用*表示一个任意类型的参数;
2、..匹配多个连续的任意符号。..配置包名表示此包及其所有子包,..配置参数表示任意个任意类型的参数;
如:
java
execution(* com.zhouyou.demos.controller.*.*(..)
表示controller包下所有的类的所有方法;
2. 根据注解匹配编写切点表达式
除以上采用方法签名的方式外,还可以使用@annotation注解的方式编写切点表达式,这种方式通常应用在某个切面类需要匹配多个controller类中的部分方法的情况;
2.1 实现步骤
1、编写自定义注解;
2、使用@annotation表达式来描述切点;
3、在连接点的方法上添加自定义注解;
2.2 元注解及其常用取值含义
- @Target:表示注解所修饰的对象范围:

2、@Retention:表示注解的生命周期:

2.3 使用自定义注解
2.3.1 编写自定义注解@MyAspect
在aspect包下创建MyAspect注解类:

在MyAspect类中使用元注解指明其修饰对象范围和生命周期:
java
package com.zhouyou.demos.aspect;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 表示该注解用于标注方法
@Target(ElementType.METHOD)
// 表示该注解声明周期在程序执行时有效
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAspect {
}
2.3.2 编写切面类MyAspectDemo
java
package com.zhouyou.demos.aspect;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
@Slf4j
public class MyAspectDemo {
@Before("@annotation(com.zhouyou.demos.aspect.MyAspect)")
public void doBefore(){
log.info("MyAspectDemo doBefore");
}
@After("@annotation(com.zhouyou.demos.aspect.MyAspect)")
public void doAfter(){
log.info("MyAspectDemo doAfter");
}
}
2.3.3 编写测试类及测试方法
在controller包下创建TestCotroller1和TestController2两个测试类,分别设置3个和2个不同返回值的测试方法,并在TestController1的test12方法和TestController2的test22方法前增加自定义的@MyAspect注解。
- TestController1:
java
package com.zhouyou.demos.controller;
import com.zhouyou.demos.aspect.MyAspect;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/test1")
public class TestController1 {
@RequestMapping("/test11")
public String test11(){
return "aop";
}
@MyAspect
@RequestMapping("/test12")
public Integer test12(){
return 12;
}
@RequestMapping("/test13")
public boolean test13(){
return true;
}
}
- TestController2:
java
package com.zhouyou.demos.controller;
import com.zhouyou.demos.aspect.MyAspect;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/test2")
public class TestController2 {
@RequestMapping("/test21")
public String test21(){
return "hello";
}
@MyAspect
@RequestMapping("/test22")
public Integer test22(){
return 22;
}
}
启动项目,依次按顺序访问test1/test11至test1/test13,和test2/test21、test2/test22,控制台日志如下:
只有添加了自定义注解@MyAspect修饰的TestController1的test12方法和TestController2的test22方法执行时,切面通知doBefore和doAfter执行,访问其余方法时,切面通知均未执行。