Spring AOP切点表达式使用及说明
作者:ZerahMu
切点表达式
在Spring AOP中,连接点始终代表方法的执行。切入点是与连接点匹配的谓词,切入点表达语言是以编程方式描述切入点的方式。
切点表达式是除过AOP逻辑之外我们开发主要关注的东西,本小结对各种表达式作以说明,spring aop中目前有9种切入点表达式的写法
execute
within
this
target
args
@target
@within
@annotation
@args
一、execute表达式
拦截任意公共方法
execution(public * *(..))
拦截以set开头的任意方法
execution(* set*(..))
拦截类或者接口中的方法
拦截AccountService(类、接口)中定义的所有方法 execution(* com.xyz.service.AccountService.*(..))
拦截包中定义的方法,不包含子包中的方法
拦截com.xyz.service包中所有类中任意方法,**不包含**子包中的类 execution(* com.xyz.service.*.*(..))
拦截包或者子包中定义的方法
拦截com.xyz.service包或者子包中定义的所有方法 execution(* com.xyz.service..*.*(..))
二、within表达式
表达式格式:包名.* 或者 包名…*
拦截包中任意方法,不包含子包中的方法
拦截service包中任意类的任意方法 within(com.xyz.service.*)
拦截包或者子包中定义的方法
拦截service包及子包中任意类的任意方法 within(com.xyz.service..*)
三、this表达式
代理对象为指定的类型会被拦截
目标对象使用aop之后生成的代理对象必须是指定的类型才会被拦截,注意是目标对象被代理之后生成的代理对象和指定的类型匹配才会被拦截 this(com.xyz.service.AccountService)
this表达式的使用,可能不是很好理解,借用示例说明一下:
package com.ms.aop.jthis.demo1; public interface IService { void m1(); } package com.ms.aop.jthis.demo1; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @Slf4j @Component public class ServiceImpl implements IService { @Override public void m1() { log.info("切入点this测试!"); } } package com.ms.aop.jthis.demo1; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; @Aspect @Component @Slf4j public class Interceptor1 { @Pointcut("this(com.ms.aop.jthis.demo1.ServiceImpl)") public void pointcut() { } @Around("pointcut()") public Object invoke(ProceedingJoinPoint invocation) throws Throwable { log.info("方法执行之前"); Object result = invocation.proceed(); log.info("方法执行完毕"); return result; } } package com.ms.aop.jthis.demo1; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.EnableAspectJAutoProxy; @ComponentScan(basePackageClasses = {Client.class}) @EnableAspectJAutoProxy @Slf4j public class Client { public static void main(String[] args) { AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Client.class); IService service = annotationConfigApplicationContext.getBean(IService.class); service.m1(); log.info("{}", service instanceof ServiceImpl); } }
执行结果
10:27:12.277 [main] INFO com.ms.aop.jthis.demo1.ServiceImpl - 切入点this测试!
10:27:12.277 [main] INFO com.ms.aop.jthis.demo1.Client - false
- @EnableAspectJAutoProxy:表示若spring创建的对象如果实现了接口,默认使用jdk动态代理,如果没有实现接口,使用cglib创建代理对象 所以 service 是使用jdk动态代理生成的对象,service instanceof ServiceImpl 为 false
- @Pointcut(“this(com.ms.aop.jthis.demo1.ServiceImpl)”) 表示被spring代理之后生成的对象必须为com.ms.aop.jthis.demo1.ServiceImpl才会被拦截,但是service不是ServiceImpl类型的对象了【这是因为默认采用的JDK动态代理,所以AOP生成的是代理对象,因此也service就不是ServiceImpl类型的对象】,所以不会被拦截
修改代码
@EnableAspectJAutoProxy(proxyTargetClass = true) proxyTargetClass=true表示强制使用cglib来生成代理对象
执行结果:
10:34:50.755 [main] INFO com.ms.aop.jthis.demo1.ServiceImpl - 切入点this测试!
10:34:50.756 [main] INFO com.ms.aop.jthis.demo1.Interceptor1 - 方法执行完毕
10:34:50.756 [main] INFO com.ms.aop.jthis.demo1.Client - true
service 为 ServiceImpl类型的对象,所以会被拦截,因为熟悉CGLIB理论知识的同学都知道,CGLIB生成的代理对象是源类型的子类,因此service肯定是ServiceImpl类型的对象了,因为多态属于关系。
四、target表达式
目标对象为指定的类型被拦截
target(com.xyz.service.AccountService) 目标对象为AccountService类型的会被代理
this 和 target 的不同点
- this作用于代理对象,target作用于目标对象
- this表示目标对象被代理之后生成的代理对象和指定的类型匹配会被拦截,匹配的是代理对象
- target表示目标对象和指定的类型匹配会被拦截,匹配的是目标对象
五、args 表达式
匹配方法中的参数
匹配只有一个参数,且类型为 com.ms.aop.args.demo1.UserModel@Pointcut("args(com.ms.aop.args.demo1.UserModel)")
- 匹配多个参数 args(type1,type2,typeN)
匹配任意多个参数
匹配第一个参数类型为com.ms.aop.args.demo1.UserModel的所有方法, .. 表示任意个参数 @Pointcut("args(com.ms.aop.args.demo1.UserModel,..)")
六、@target表达式
匹配的目标对象的类有一个指定的注解
目标对象中包含com.ms.aop.jtarget.Annotation1注解,调用该目标对象的任意方法都会被拦截 @target(com.ms.aop.jtarget.Annotation1)
七、@within表达式
指定匹配必须包含某个注解的类里的所有连接点
声明有com.ms.aop.jwithin.Annotation1注解的类中的所有方法都会被拦截 @within(com.ms.aop.jwithin.Annotation1)
@target 和 @within 的不同点
- @target(注解A):判断被调用的目标对象中是否声明了注解A,如果有,会被拦截
- @within(注解A): 判断被调用的方法所属的类中是否声明了注解A,如果有,会被拦截
- @target关注的是被调用的对象,@within关注的是调用的方法所在的类
八、@annotation表达式
匹配有指定注解的方法(注解作用在方法上面)
@annotation(com.ms.aop.jannotation.demo2.Annotation1)
被调用的方法包含指定的注解
九、@args表达式
方法参数所属的类型上有指定的注解,被匹配
注意:是方法参数所属的类型上有指定的注解,不是方法参数中有注解
匹配1个参数,且第1个参数所属的类中有Anno1注解
- @args(com.ms.aop.jargs.demo1.Anno1)
- 匹配多个参数,且多个参数所属的类型上都有指定的注解
- @args(com.ms.aop.jargs.demo1.Anno1,com.ms.aop.jargs.demo1.Anno2)
- 匹配多个参数,且第一个参数所属的类中有Anno1注解
- @args(com.ms.aop.jargs.demo2.Anno1,…)
切点表达式组合
另外,可以使用&&、||、!、三种运算符来组合切点表达式,表示与或非的关系。
@Pointcut("@target(org.springframework.stereotype.Repository)") public void repositoryMethods() {} @Pointcut("execution(* *..create*(Long,..))") public void firstLongParamMethods() {} @Pointcut("repositoryMethods() && firstLongParamMethods()") public void entityCreationMethods() {}
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。