【Spring AOP】Spring AOP之如何通过注解的方式实现各种通知类型的AOP操作进阶篇(3)
一、切入点表达式的各种类型
切入点表达式的作用:限制连接点的匹配(满足时对应的aspect方法会被执行)
1)execution:用于匹配方法执行连接点。Spring AOP用户可能最经常使用execution切入点指示器
执行表达式的格式如下:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
modifiers-pattern 指定访问修饰符如public
ret-type-pattern 指定返回类型如void;*是最常用的返回类型模式。它匹配任何返回类型
declaring-type-pattern 指定类的全路径名称如com.zbq.springbootdemo.Myservice;如果指定了,通过 . 符号将其连接到name-pattern
name-pattern 指定方法名称如get;您可以使用*通配符作为名称模式的全部或部分
param-pattern 指定参数类型如String;()匹配不带参数的方法,而(..)匹配任意数量(零或更多)的参数。(*)模式匹配接受任意类型参数的方法;(*,String)匹配一个带有两个参数的方法。第一个可以是任何类型,第二个必须是字符串
throws-pattern 指定抛出的异常类型
除了返回类型模式(前面代码段中的rt-type-pattern)、name-pattern和param-pattern之外,其他所有部分都是可选的。
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
// 执行任何公用方法 @Pointcut("execution(public * *(..))") private void pointcut1() { } // 任何以set开头的方法的执行: @Pointcut(" execution(* set*(..))") private void pointcut2() { } // 执行MyService接口定义的任何方法: @Pointcut("execution(* com.zbq.springbootdemo.service.MyService.*(..))") private void pointcut3() { } // 执行service包中定义的任何方法 @Pointcut("execution(* com.zbq.springbootdemo.service..*(..))") private void pointcut4() { } // 执行service包及其子包中定义的任何方法 @Pointcut("execution(* com.zbq.springbootdemo.service..*.*(..))") private void pointcut5() { }
2)within:用于匹配指定类型内的方法执行,限制对某些类型的匹配即满足指定的条件类,使用Spring AOP时匹配的类型中声明的方法被执行。和execution不一样的是,execution针对的是方法而within针对的是类
// 指定具体类中定义的任何方法 @Pointcut("within(com.zbq.springbootdemo.service.MyService)") private void pointcut6_1() { } // 执行service包中定义的任何方法 @Pointcut("within(com.zbq.springbootdemo.service.*)") private void pointcut6() { } // 执行service包及其子包中定义的任何方法 @Pointcut("within(com.zbq.springbootdemo.service..*)") private void pointcut7() { }
3)this:用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配
// 代理实现MyService接口的任何连接点(仅在Spring AOP中执行方法): @Pointcut("this(com.zbq.springbootdemo.service.MyService)") private void pointcut8() { }
4)target:用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配
// 目标对象实现MyService接口的任何连接点(仅在Spring AOP中执行方法) @Pointcut("target(com.zbq.springbootdemo.service.MyService)") private void pointcut9() { }
5)args:用于匹配当前执行的方法传入的参数为指定类型的执行方法·
// 任何接受单个参数的连接点(只有在Spring AOP中才执行方法),并且在运行时传递的参数是可序列化的: // 注意和execution(* *(java.io.Serializable))区分开,args(java.io.Serializable)表示在运行时传递的参数是可序列化的,后者表示参数是Serializable类型的 @Pointcut("args(java.io.Serializable)") private void pointcut10() { }
6)@target:用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解
// 目标对象具有@MyAnnotation注释的任何连接点(仅在Spring AOP中执行方法): @Pointcut("@target(com.zbq.springbootdemo.common.annotation.MyAnnotation))") private void pointcut11() { }
7)@args:用于匹配当前执行的方法传入的参数持有指定注解的执行
// 任何接受单个参数的连接点(仅在Spring AOP中执行方法),其中传递的参数的运行时类型有@classification注解: @Pointcut("@args(com.xyz.security.Classified)") private void pointcut14() { }
8)@within:用于匹配所有持有指定注解类型内的方法,作用和@target类似
// 目标对象的声明类型具有@Transactional注释的任何连接点(仅在Spring AOP中执行方法): @Pointcut("@within(com.zbq.springbootdemo.common.annotation.MyAnnotation)") private void pointcut12() { }
9)@annotation:用于匹配当前执行方法持有指定注解的方法
// 任何连接点(仅在Spring AOP中执行方法),其中执行方法具有@MyAnnotation注解: @Pointcut("@annotation(com.zbq.springbootdemo.common.annotation.MyAnnotation)") private void pointcut13() { }
// 指定注解并获取注解值,指定名称就行 @Pointcut(value = "execution(* com.zbq.springbootdemo.service.MyService..*(..)) && @annotation(myAnnotation)") public void myPointcut4(MyAnnotation myAnnotation) { }
10)bean:Spring AOP扩展的,AspectJ没有对于指示符,用于匹配特定名称的Bean对象的执行方法
// 在名为tradeService的Spring bean上的任何连接点(仅在Spring AOP中执行方法): @Pointcut("bean(tradeService)") private void pointcut15() { } // 在具有匹配通配符表达式*Service的名称的Spring bean上的任何连接点(仅在Spring AOP中执行方法): @Pointcut("bean(*Service)") private void pointcut16() { }
二、切入点表达式的使用
1)命名切入点可以被其他切入点引用,而匿名切入点是不可以的
@Pointcut("execution(* com.zbq.springbootdemo.service.MyService.*(..))") public void myPointcut() { }
@Before("myPointcut()") public void before() { log.info("before "); }
2)可以使用&&、||和!组合切入点表达式。您还可以通过名称引用切入点表达式。下面的例子展示了三个切入点表达式
/** 可以使用&&、||和!组合切入点表达式。您还可以通过名称引用切入点表达式。下面的例子展示了三个切入点表达式: */ /** * 任何公共方法 */ @Pointcut("execution(public * *(..))") private void anyPublicOperation() { } /** * springbootdemo包及其子包的所有方法 */ @Pointcut("within(com.zbq.springbootdemo..*)") private void inTrading() { } @Pointcut("anyPublicOperation() && inTrading()") private void tradingOperation() { }
3)将新的接口(和相应的实现)引入任何被建议的对象
@DeclareParents(value="com.zbq.springbootdemo.service..*", defaultImpl=MyOperationManager.class) public static MyOperation myOperation; @After("execution(* com.zbq.springbootdemo.service.MyService.*(..)) && this(myOperation)" ) public void after(JoinPoint joinPoint, MyOperation myOperation) { myOperation.operation(); log.info("after "); }
// 原本的对象就可以强转为指定的接口 MyOperation myOperation = (MyOperation) context.getBean("myService");