@PointCut 表达式详解
PointCut
:切入点,指哪些方法需要被执行AOP
,PointCut表达式可以有以下几种方式:
格式:@ 注解(value=“表达标签 ( 表达式格式)”)
如:@Pointcut (value=“execution(* com.cn.spring.aspectj.NotVeryUsefulAspectService.*(…))”)
表达式标签
- execution():用于匹配方法执行的连接点
- args(): 用于匹配当前执行的方法传入的参数为指定类型的执行方法
- this(): 用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配;
- target(): 用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配;
- within(): 用于匹配指定类型内的方法执行;
- @args():于匹配当前执行的方法传入的参数持有指定注解的执行;
- @target():用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;
- @within():用于匹配所以持有指定注解类型内的方法;
- @annotation:用于匹配当前执行方法持有指定注解的方法;
其中execution 是用的最多的,
execution
execution格式:
execution(
modifier-pattern?
ret-type-pattern
declaring-type-pattern?
name-pattern(param-pattern)
throws-pattern?
)
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)
其中带 ?号的 modifiers-pattern?,declaring-type-pattern?,hrows-pattern?是可选项
ret-type-pattern,name-pattern, parameters-pattern是必选项;
- modifier-pattern? 修饰符匹配,如public 表示匹配公有方法
- ret-type-pattern 返回值匹配,* 表示任何返回值,全路径的类名等
- declaring-type-pattern? 类路径匹配
- name-pattern 方法名匹配,* 代表所有,set*,代表以set开头的所有方法
- (param-pattern) 参数匹配,指定方法参数(声明的类型),
(..)代表所有参数,
()代表一个参数,
(,String)代表第一个参数为任何值,第二个为String类型.
- throws-pattern? 异常类型匹配
例子:
- execution(public * *(..)) 定义任意公共方法的执行
- execution(* set*(..)) 定义任何一个以"set"开始的方法的执行
- execution(* com.xyz.service.AccountService.*(..)) 定义AccountService 接口的任意方法的执行
- execution(* com.xyz.service.*.*(..)) 定义在service包里的任意方法的执行
- execution(* com.xyz.service ..*.*(..)) 定义在service包和所有子包里的任意类的任意方法的执行
- execution(* com.test.spring.aop.pointcutexp…JoinPointObjP2.*(…)) 定义在pointcutexp包和所有子包里的JoinPointObjP2类的任意方法的执行:
说明:最靠近(..)的为方法名,靠近.∗(..))的为类名或者接口名,如上例的JoinPointObjP2.∗(..))
AspectJ类型匹配的通配符:
- * 匹配任何数量字符;
- . . 匹配任何数量字符的重复,如在类型模式中匹配任何数量子包;而在方法参数模式中匹配任何数量参数。
- +:匹配指定类型的子类型;仅能作为后缀放在类型模式后边。
如:
- java.lang.String 匹配String类型;
- java.*.String 匹配java包下的任何“一级子包”下的String类型;如匹配java.lang.String,但不匹配java.lang.ss.String
- java..* 匹配java包及任何子包下的任何类型; 如匹配java.lang.String、java.lang.annotation.Annotation
- java.lang.*ing 匹配任何java.lang包下的以ing结尾的类型;
- java.lang.Number+ 匹配java.lang包下的任何Number的自类型;如匹配java.lang.Integer,也匹配java.math.BigInteger
within和@within
- within(com.test.spring.aop.pointcutexp.*) pointcutexp包里的任意类.
- within(com.test.spring.aop.pointcutexp…*) pointcutexp包和所有子包里的任意类.
- @within(org.springframework.transaction.annotation.Transactional) 带有@Transactional标注的所有类的任意方法.
this
- this(com.test.spring.aop.pointcutexp.Intf) 实现了Intf接口的所有类,如果Intf不是接口,限定Intf单个类.
当一个实现了接口的类被AOP的时候,用getBean方法必须cast为接口类型,不能为该类的类型.
@target
- @target(org.springframework.transaction.annotation.Transactional) 带有@Transactional标注的所有类的任意方法.
@annotation
- @annotation(org.springframework.transaction.annotation.Transactional) 带有@Transactional标注的任意方法.
@within和@target针对类的注解,@annotation是针对方法的注解
args 和 @args
- @args(org.springframework.transaction.annotation.Transactional) 参数带有@Transactional标注的方法.
- args(String) 参数为String类型(运行是决定)的方法.
Pointcut定义时,还可以使用&&、∣∣、!运算符
代码演示:
@Aspect
@Component
public class LogAspects {
//抽取公共的切入点表达式
//1、本类引用
//2、其他的切面引用
@Pointcut("execution(public int com.springboot.test.aop.MyCalculator.*(..))")
public void pointCut(){};
//@Before在目标方法之前切入;切入点表达式(指定在哪个方法切入)
@Before("pointCut()")
public void logStart(JoinPoint joinPoint){
Object[] args = joinPoint.getArgs();
System.out.println(""+joinPoint.getSignature().getName()+"运行。。。@Before:参数列表是:{"+ Arrays.asList(args)+"}");
}
@After("pointCut()")
public void logEnd(JoinPoint joinPoint){
System.out.println(""+joinPoint.getSignature().getName()+"结束。。。@After");
}
//JoinPoint一定要出现在参数表的第一位
@AfterReturning(value="pointCut()",returning="result")
public void logReturn(JoinPoint joinPoint,Object result){
System.out.println(""+joinPoint.getSignature().getName()+"正常返回。。。@AfterReturning:运行结果:{"+result+"}");
}
@AfterThrowing(value="pointCut()",throwing="exception")
public void logException(JoinPoint joinPoint,Exception exception){
System.out.println(""+joinPoint.getSignature().getName()+"异常。。。异常信息:{"+exception+"}");
}
@Around(value="pointCut()")
public Object logAround(ProceedingJoinPoint joinPoint) {
System.out.println("@Around before proceed");
Object proceed = null;
try {
proceed = joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("@Around after proceed ");
return proceed;
}
}
注意点:
1.环绕通知的入参一定是ProceedingJoinPoint。和其它的几个通知不一样。
2.环绕通知的方法的返回值一定不要是void,最好是object(即使你的方法是没有返回的,那么void的时候返回为null,不会报错)。