切入点表达式
切入点表达式:描述切入点方法的一种表达式
作用:主要用来决定项目中的哪些方法需要加入通知
常见形式:
- execution(……):根据方法的签名来匹配
2. @annotation(……) :根据注解匹配
execution
execution主要根据方法的返回值、包名、类名、方法名、方法参数等信息来匹配,语法为:
execution(访问修饰符? 返回值 包名.类名.?方法名(方法参数) throws 异常?)
其中带`?`的表示可以省略的部分
- 访问修饰符:可省略(比如: public、protected)
- 包名.类名: 可省略
- throws 异常:可省略(注意是方法上声明抛出的异常,不是实际抛出的异常)
示例:
~~~java
@Before("execution(void com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))")
~~~
可以使用通配符描述切入点
- `*` :单个独立的任意符号,可以通配任意返回值、包名、类名、方法名、任意类型的一个参数,也可以通配包、类、方法名的一部分
- `..` :多个连续的任意符号,可以通配任意层级的包,或任意类型、任意个数的参数
切入点表达式的语法规则:
1. 方法的访问修饰符可以省略
2. 返回值可以使用`*`号代替(任意返回值类型)
3. 包名可以使用`*`号代替,代表任意包(一层包使用一个`*`)
4. 使用`..`配置包名,标识此包以及此包下的所有子包
5. 类名可以使用`*`号代替,标识任意类
6. 方法名可以使用`*`号代替,表示任意方法
7. 可以使用 `*` 配置参数,一个任意类型的参数
8. 可以使用`..` 配置参数,任意个任意类型的参数
切入点表达式示例
省略方法的修饰符号
~~~java
execution(void com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))
~~~
使用`*`代替返回值类型
~~~java
execution(* com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))
~~~
使用`*`代替包名(一层包使用一个`*`)
~~~java
execution(* com.itheima.*.*.DeptServiceImpl.delete(java.lang.Integer))
~~~
使用`..`省略包名
~~~java
execution(* com..DeptServiceImpl.delete(java.lang.Integer))
~~~
使用`*`代替类名
~~~java
execution(* com..*.delete(java.lang.Integer))
~~~
使用`*`代替方法名
~~~java
execution(* com..*.*(java.lang.Integer))
~~~
使用 `*` 代替参数
```java
execution(* com.itheima.service.impl.DeptServiceImpl.delete(*))
```
使用`..`省略参数
~~~java
execution(* com..*.*(..))
~~~
注意事项:
- 根据业务需要,可以使用 且(&&)、或(||)、非(!) 来组合比较复杂的切入点表达式。
```java
execution(* com.itheima.service.DeptService.list(..)) || execution(* com.itheima.service.DeptService.delete(..))
切入点表达式的书写建议:
- 所有业务方法名在命名时尽量规范,方便切入点表达式快速匹配。如:查询类方法都是 find 开头,更新类方法都是update开头
- 描述切入点方法通常基于接口描述,而不是直接描述实现类,增强拓展性
- 在满足业务需要的前提下,尽量缩小切入点的匹配范围。如:包名匹配尽量不使用 ..,使用 * 匹配单个包
@annotation
如果我们要匹配多个无规则的方法,比如:list()和 delete()这两个方法。这个时候我们基于execution这种切入点表达式来描述就不是很方便了。而在之前我们是将两个切入点表达式组合在了一起完成的需求,这个是比较繁琐的。
我们可以借助于另一种切入点表达式annotation来描述这一类的切入点,从而来简化切入点表达式的书写。
实现步骤:
1. 编写自定义注解
2. 在业务类要做为连接点的方法上添加自定义注解
自定义注解:MyLog
~~~java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
}
~~~
业务类:DeptServiceImpl
~~~java
@Slf4j
@Service
public class DeptServiceImpl implements DeptService {
@Autowired
private DeptMapper deptMapper;
@Override
@MyLog //自定义注解(表示:当前方法属于目标方法)
public List<Dept> list() {
List<Dept> deptList = deptMapper.list();
//模拟异常
//int num = 10/0;
return deptList;
}
切面类
~~~java
@Slf4j
@Component
@Aspect
public class MyAspect6 {
//针对list方法、delete方法进行前置通知和后置通知
//前置通知
@Before("@annotation(com.itheima.anno.MyLog)")
public void before(){
log.info("MyAspect6 -> before ...");
}
//后置通知
@After("@annotation(com.itheima.anno.MyLog)")
public void after(){
log.info("MyAspect6 -> after ...");
}
}
~~~
重启SpringBoot服务,测试查询所有部门数据,查看控制台日志:
总结:
execution切入点表达式
- 根据我们所指定的方法的描述信息来匹配切入点方法,这种方式也是最为常用的一种方式
- 如果我们要匹配的切入点方法的方法名不规则,或者有一些比较特殊的需求,通过execution切入点表达式描述比较繁琐
annotation 切入点表达式
- 基于注解的方式来匹配切入点方法。这种方式虽然多一步操作,我们需要自定义一个注解,但是相对来比较灵活。我们需要匹配哪个方法,就在方法上加上对应的注解就可以了