1.spring aop的相关注解
Spring AOP(面向切面编程)通过一系列注解来实现切面的定义和织入,从而达到将横切关注点(比如日志记录、事务管理、安全性检查等)与核心业务逻辑分离的目的。以下是Spring AOP中一些关键的注解及其用途
@Aspect | 切面声明,标注在类、接口(包括注解类型)或枚举上。这个类中包含了切面的逻辑定义,如通知(Advice)和切点(Pointcut)。 |
@Pointcut | 切入点声明,即切入到哪些目标类的目标方法。既可以用 execution 切点表达式, 也可以是 annotation 指定拦截拥有指定注解的方法. value 属性指定切入点表达式,默认为 "",用于被通知注解引用,这样通知注解只需要关联此切入点声明即可,无需再重复写切入点表达式 |
@Before | 前置通知, 在目标方法(切入点)执行之前执行。 value 属性绑定通知的切入点表达式,可以关联切入点声明,也可以直接设置切入点表达式 注意:如果在此回调方法中抛出异常,则目标方法不会再执行,会继续执行后置通知 -> 异常通知。 |
@After | 后置通知, 在目标方法(切入点)执行之后执行 |
@AfterReturning | 返回通知, 在目标方法(切入点)返回结果之后执行. pointcut 属性绑定通知的切入点表达式,优先级高于 value,默认为 "" |
@AfterThrowing | 异常通知, 在方法抛出异常之后执行, 意味着跳过返回通知 pointcut 属性绑定通知的切入点表达式,优先级高于 value,默认为 "" 注意:如果目标方法自己 try-catch 了异常,而没有继续往外抛,则不会进入此回调函数 |
@Around | 环绕通知:目标方法执行前后分别执行一些代码,类似拦截器,可以控制目标方法是否继续执行。 通常用于统计方法耗时,参数校验等等操作。 |
正常流程:【环绕通知-前】-> 【前置通知】-> 【返回通知】-> 【后置通知】->【环绕通知-后】
@Aspect 快速入门
- @Aspect 常见用于记录日志、异常集中处理、权限验证、Web 参数校验、事务处理等等
- 要想把一个类变成切面类,只需3步:
- 在类上使用 @Aspect 注解使之成为切面类
- 切面类需要交由 Sprign 容器管理,所以类上还需要有 @Service、@Repository、@Controller、@Component 等注解
- 在切面类中自定义方法接收通知
/**
* 通知类,横切逻辑
* @Author ZhaoShuHao
* @Date 2024/4/29 14:47
*/
@Component
@Aspect
public class Advices {
// @Before("execution(public int com.zsh.aop.Math.*(..))")
@Before("execution(* com.zsh.aop.Math.*(..))")
public void before(JoinPoint jp){
System.out.println("----------前置通知----------"+jp.getSignature().getName()+"方法执行了,参数为"+ Arrays.toString(jp.getArgs()));
}
@AfterReturning(value = "execution(public int com.zsh.aop.Math.*(..))",returning = "result")
public void after(JoinPoint jp,Object result){
System.out.println("----------最终通知----------"+jp.getSignature().getName()+"方法执行了,结果为:"+result);
}
}
2.execution 切点表达式 拦截指定类的方法
在Spring AOP中,`execution`是切点表达式中最常用的一种形式,用于匹配方法的执行。它的语法结构如下:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
各部分说明如下:
- `modifiers-pattern`:访问修饰符,如`public`、`protected`、`private`、`*`(表示任何访问修饰符)。
- `ret-type-pattern`:返回值类型模式,例如`void`、全限定类名、`*`(表示任何返回类型)。
- `declaring-type-pattern`:声明该方法的类或接口的全限定名模式,可省略,表示任意类或接口。
- `name-pattern`:方法名模式,可以使用通配符`*`,如`set*`匹配所有以`set`开头的方法。
- `param-pattern`:参数模式,指定方法参数的类型,用逗号分隔。例如,`(int, String)`匹配具有一个`int`和一个`String`参数的方法。`..`表示任意数量和类型的参数。
- `throws-pattern`:抛出异常的类型列表,很少使用,一般用`*`表示任何异常。
以下是一些示例,展示如何使用`execution`来拦截指定类的方法:
execution(public * com.example.service..*(..)) | 拦截com.example.service包下所有类的任意公共方法 |
execution(* com.example.service.MyService.*(..)) | 拦截com.example.service.MyService类的所有方法 |
execution(* com.example.service.MyService.save*(..)) | 仅拦截com.example.service.MyService中的save*(...)方法,方法名以save开始,参数不限 |
execution(void com.example.service..*.*()) | 拦截com.example.service包下的所有类的void类型、无参数的方法 |
execution(String com.example.service.MyService.getDetails(..)) | 拦截com.example.service.MyService中返回类型为String且名为getDetails的方法,不考虑参数 |
execution(* com.wmx.aspect.EmpServiceImpl.findEmpById(Integer)) | 匹配 com.wmx.aspect.EmpService 类中的 findEmpById 方法,且带有一个 Integer 类型参数。 |
execution(* com.wmx.aspect.EmpServiceImpl.findEmpById(*)) | 匹配 com.wmx.aspect.EmpService 类中的 findEmpById 方法,且带有一个任意类型参数。 |
execution(* com.wmx.aspect.EmpServiceImpl.findEmpById(..)) | 匹配 com.wmx.aspect.EmpService 类中的 findEmpById 方法,参数不限。 |
execution(* grp.basic3.se.service.SEBasAgencyService3.editAgencyInfo(..)) || execution(* grp.basic3.se.service.SEBasAgencyService3.adjustAgencyInfo(..)) |
匹配 editAgencyInfo 方法或者 adjustAgencyInfo 方法 |
@Pointcut("(execution(* grp.basic3..*Controller*.*(..)) && !execution(* grp.basic3.BaseExceptionController*.*(..)))") | 匹配 grp.basic3包及其子包下面名称包含 'Controller' 类中的全部方法,但是排除掉其中的以 BaseExceptionController 开头的类。 |
execution(* com.wmx.aspect.EmpService.*(..)) | 匹配 com.wmx.aspect.EmpService 类中的任意方法 |
execution(* com.wmx.aspect.*.*(..)) | 匹配 com.wmx.aspect 包(不含子包)下任意类中的任意方法 |
execution(* com.wmx.aspect..*.*(..)) | 匹配 com.wmx.aspect 包及其子包下任意类中的任意方法 |
execution(* grp.pm..*Controller.*(..)) | 匹配 grp.pm 包下任意子孙包中以 "Controller" 结尾的类中的所有方法 |
通过组合这些模式,你可以非常灵活地定义想要拦截的方法集。