下面我们就来详细的介绍一下切入点表达式的具体写法。
切入点表达式:
-
描述切入点方法的一种表达式
-
作用:主要用来决定项目中的哪些方法需要加入通知
-
常见形式:
execution(……):根据方法的签名来匹配
execution主要根据方法的返回值、包名、类名、方法名、方法参数等信息来匹配,语法为:
execution(访问修饰符? 返回值 包名.类名.?方法名(方法参数) throws 异常?)
其中带?
的表示可以省略的部分
-
访问修饰符:可省略(比如: public、protected)
-
包名.类名: 可省略
-
throws 异常:可省略(注意是方法上声明抛出的异常,不是实际抛出的异常)
示例:
@Before("execution(void com.Demo.service.impl.DeptServiceImpl.delete(java.lang.Integer))")
可以使用通配符描述切入点
-
*
:单个独立的任意符号,可以通配任意返回值、包名、类名、方法名、任意类型的一个参数,也可以通配包、类、方法名的一部分 -
..
:多个连续的任意符号,可以通配任意层级的包,或任意类型、任意个数的参数
切入点表达式的语法规则:
-
方法的访问修饰符可以省略
-
返回值可以使用
*
号代替(任意返回值类型) -
包名可以使用
*
号代替,代表任意包(一层包使用一个*
) -
使用
..
配置包名,标识此包以及此包下的所有子包 -
类名可以使用
*
号代替,标识任意类 -
方法名可以使用
*
号代替,表示任意方法 -
可以使用
*
配置参数,一个任意类型的参数 -
可以使用
..
配置参数,任意个任意类型的参数
切入点表达式示例
-
省略方法的修饰符号
-
execution(void com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))
-
使用
*
代替返回值类型 -
execution(* com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))
-
使用
*
代替包名(一层包使用一个*
) -
使用
..
省略包名 -
execution(* com..DeptServiceImpl.delete(java.lang.Integer))
-
使用
*
代替类名 -
execution(* com..*.delete(java.lang.Integer))
-
使用
*
代替方法名 -
execution(* com..*.*(java.lang.Integer))
-
使用
*
代替参数 -
execution(* com.itheima.service.impl.DeptServiceImpl.delete(*))
-
使用
..
省略参数 -
execution(* com..*.*(..))
注意事项:
-
根据业务需要,可以使用 且(&&)、或(||)、非(!) 来组合比较复杂的切入点表达式。
execution(* com.itheima.service.DeptService.list(..)) || execution(* com.itheima.service.DeptService.delete(..))
切入点表达式的书写建议:
-
所有业务方法名在命名时尽量规范,方便切入点表达式快速匹配。如:查询类方法都是 find 开头,更新类方法都是update开头
//业务类
@Service
public class DeptServiceImpl implements DeptService {
public List<Dept> findAllDept() {
//省略代码...
}
public Dept findDeptById(Integer id) {
//省略代码...
}
public void updateDeptById(Integer id) {
//省略代码...
}
public void updateDeptByMoreCondition(Dept dept) {
//省略代码...
}
//其他代码...
}
//匹配DeptServiceImpl类中以find开头的方法
execution(* com.itheima.service.impl.DeptServiceImpl.find*(..))
描述切入点方法通常基于接口描述,而不是直接描述实现类,增强拓展性
execution(* com.itheima.service.DeptService.*(..))
在满足业务需要的前提下,尽量缩小切入点的匹配范围。如:包名匹配尽量不使用 ..,使用 * 匹配单个包
execution(* com.itheima.*.*.DeptServiceImpl.find*(..))
@annotation
已经学习了execution切入点表达式的语法。那么如果我们要匹配多个无规则的方法,比如:list()和 delete()这两个方法。这个时候我们基于execution这种切入点表达式来描述就不是很方便了。而在之前我们是将两个切入点表达式组合在了一起完成的需求,这个是比较繁琐的。
我们可以借助于另一种切入点表达式annotation来描述这一类的切入点,从而来简化切入点表达式的书写。
实现步骤:
-
编写自定义注解
-
在业务类要做为连接点的方法上添加自定义注解
自定义注解:MyLog
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
}
业务类:DeptServiceImpl
@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;
}
@Override
@MyLog //自定义注解(表示:当前方法属于目标方法)
public void delete(Integer id) {
//1. 删除部门
deptMapper.delete(id);
}
@Override
public void save(Dept dept) {
dept.setCreateTime(LocalDateTime.now());
dept.setUpdateTime(LocalDateTime.now());
deptMapper.save(dept);
}
@Override
public Dept getById(Integer id) {
return deptMapper.getById(id);
}
@Override
public void update(Dept dept) {
dept.setUpdateTime(LocalDateTime.now());
deptMapper.update(dept);
}
}
切面类
@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 切入点表达式
-
基于注解的方式来匹配切入点方法。这种方式虽然多一步操作,我们需要自定义一个注解,但是相对来比较灵活。我们需要匹配哪个方法,就在方法上加上对应的注解就可以了
-
(本文转载自:黑马程序员提供的笔记
本文只用来学习参考,如有侵权,请联系删除)