spring aop execution AspectJ切入点表达式 http://www.mekau.com/3675.html
AspectJ切入点@Pointcut语法详解 http://sishuok.com/forum/posts/list/281.html 非常详细
分类pointcuts 遵循特定的语法用于捕获每一个种类的可使用连接点。 主要的种类:
方法执行:execution(MethodSignature)
方法调用:call(MethodSignature)
构造器执行:execution(ConstructorSignature)
构造器调用:call(ConstructorSignature)
类初始化:staticinitialization(TypeSignature)
属性读操作:get(FieldSignature)
属性写操作:set(FieldSignature)
例外处理执行:handler(TypeSignature)
对象初始化:initialization(ConstructorSignature)
对象预先初始化:preinitialization(ConstructorSignature)
Advice执行:adviceexecution()
切入点指示符用来指示切入点表达式目的,,在Spring AOP中目前只有执行方法这一个连接点,Spring AOP支持的AspectJ切入点指示符如下:
execution:用于匹配方法执行的连接点;
within:用于匹配指定类型内的方法执行;
this:用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配;
target:用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配;
args:用于匹配当前执行的方法传入的参数为指定类型的执行方法;
@within:用于匹配所以持有指定注解类型内的方法;
@target:用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;
@args:用于匹配当前执行的方法传入的参数持有指定注解的执行;
@annotation:用于匹配当前执行方法持有指定注解的方法;
bean:Spring AOP扩展的,AspectJ没有对于指示符,用于匹配特定名称的Bean对象的执行方法;
reference pointcut:表示引用其他命名切入点,只有@ApectJ风格支持,Schema风格不支持。
来了解下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
举例说明:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
任意公共方法的执行: execution(public * *(..)) 任何一个以“set”开始的方法的执行: execution(* set*(..)) AccountService 接口的任意方法的执行: execution(* com.xyz.service.AccountService.*(..)) 定义在service包里的任意方法的执行: execution(* com.xyz.service.*.*(..)) 定义在service包和所有子包里的任意类的任意方法的执行: execution(* com.xyz.service..*.*(..)) 定义在pointcutexp包和所有子包里的JoinPointObjP2类的任意方法的执行: execution(* com.test.spring.aop.pointcutexp..JoinPointObjP2.*(..))") ***> 最靠近(..)的为方法名,靠近.*(..))的为类名或者接口名,如上例的JoinPointObjP2.*(..)) pointcutexp包里的任意类. within(com.test.spring.aop.pointcutexp.*) pointcutexp包和所有子包里的任意类. within(com.test.spring.aop.pointcutexp..*) 实现了Intf接口的所有类,如果Intf不是接口,限定Intf单个类. this(com.test.spring.aop.pointcutexp.Intf) ***> 当一个实现了接口的类被AOP的时候,用getBean方法必须cast为接口类型,不能为该类的类型. 带有@Transactional标注的所有类的任意方法. @within(org.springframework.transaction.annotation.Transactional) @target(org.springframework.transaction.annotation.Transactional) 带有@Transactional标注的任意方法. @annotation(org.springframework.transaction.annotation.Transactional) ***> @within和@target针对类的注解,@annotation是针对方法的注解 参数带有@Transactional标注的方法. @args(org.springframework.transaction.annotation.Transactional) 参数为String类型(运行是决定)的方法. |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
// @Aspect不再能修饰接口,而只能是类 // 访问aspect实例时,不再能使用aspectOf()和hasAspect() // 而应以aspect的类作为参数,使用由org.aspectj.lang.Aspects提供的静态方法aspectOf()与hasAspect() @Aspect("perthis|pertarget|percflow|percflowbelow(Pointcut) | pertypewithin(TypePattern)") // 定义aspect的优先顺序,需要使用完全的限定名,这在@AspectJ中很普遍,也是由Java编译器决定的 // AspectJ的未来版本可能提供string[]类型的参数支持 @DeclarePrecedence("ajia.HomeSecurityAspect, ajia.SaveEnergyAspect") public abstract static class AspectName extends class_or_aspect_name implements interface_list { // 使用@Pointcut配合一个占位用的方法声明来定义一个pointcut // 抽象pointcut依旧只有名字、参数,没有实际的joinpoint定义 @Pointcut public abstract void pointcut_name(Type args); // pointcut定义时仍要注意使用全限定名 // 方法只是占位符,方法体除了采用类似条件编译时的if()切入方式外都置空 // 若方法会抛出异常,则同样要在方法原型加上throws声明 // 切记要开启编译器选项-g:vars,让编译器预留参数名(建设采用这种方式) @Pointcut("execution(public * ajia.banking.domain.Account.*(float)) && this(account) && args(amount)") public void accountOperation(Account account, float amount) {} // 或者利用Annotation的属性,建立参数名与pointcut之间的关联 // 但这样得自己维护argNames与方法参数表的一致性,所以不推荐 @Pointcut(value="execution(public * ajia.banking.domain.Account.*(float)) && this(account) && args(amount)", argNames="account, amount") public void accountOperation(Account account, float amount) {} // advice的定义类似传统语法, // before-advice必须是public与void的 // 方式一:匿名pointcut @Before("execution(* *(..)) && !within(ajia.monitoring.*)") public void beatHeart() { heartBeatListener.beat(); } // 方式二:命名的pointcut @Pointcut("execution(* *.*(..)) && !within(ajia.monitoring.*)") public void aliveOperation() {} @Before("aliveOperation()") public void beatHeart() { heartBeatListener.beat(); } // advice仍旧支持经由类JoinPoint的反射获取上下文 // JoinPoint对象本身定义动态部分 // JoinPoint.StaticPart定义静态部分 // JoinPoint.EnclosingStaticpart定义包裹静态信息的部分 // 同时,advice仍旧支持target/this/args @Pointcut("call(void Account.credit(float)) && target(account) && args(amount)") public void creditOperation(Account account, float amount) {} @Before("creditOperation(account, amount)" ) public void beforeCreditOperation(JoinPoint.StaticPart jpsp, JoinPoint.EnclosingStaticPart jpesp, Account account, float amount) { System.out.println("Crediting " + amount + " to " + account); } // after-advice的实现同样直观 @Pointcut("call(* java.sql.Connection.*(..)) && target(connection)") public void connectionOperation(Connection connection) {} @After("connectionOperation(connection)") public void monitorUse(Connection connection) { System.out.println("Just used " + connection); } @AfterReturning(value="connectionOperation(connection)", returning="ret") public void monitorSuccessfulUse(Connection connection, Object ret) { System.out.println("Just used " + connection + " successfully which returned " + ret); } @AfterThrowing(value="connectionOperation(connection)", throwing="ex") public void monitorFailedUse(Connection connection, Exception ex) { System.out.println("Just used " + connection + " but met with a failure of kind " + ex); } // around-advice的实现稍显复杂 // 需要参考JoinPoint反射的方式,为around-advice的方法传入一个ProceedingJoinPoint参数 // 该对象有方法proceed()及其重载版本proceed(Object[]),可以执行被切入的方法 // 这个Object[]数组中,依次为this-target-args // 分别经由ProceedingJoinPoint对象的方法this()、target()与getArgs()获取 @Around("pointcut_xxx()") public Object measureTime(ProceedingJoinPoint pjp) { Object[] context = formProceedArguments(pjp.this(), pjp.target(), pjp.getArgs()); Object result = proceed(context); return result; } // 可以用下面这个方法获取该Object[]数组 public static Object[] formProceedArguments(Object thiz, Object target, Object[] arguments) { int argumentsOffset = 0; if(thiz != null) { argumentsOffset++; } if(target != null) { argumentsOffset++; } Object[] jpContext = new Object[arguments.length + argumentsOffset]; int currentIndex = 0; if(thiz != null) { jpContext[currentIndex++] = thiz; } if(target != null) { jpContext[currentIndex++] = target; } System.arraycopy(arguments, 0,jpContext, argumentsOffset, arguments.length); return jpContext; } // 声明Error与Warning @DeclareError("callToUnsafeCode()") static final String unsafeCodeUsageError = "This third-party code is known to result in a crash"; @DeclareWarning("callToBlockingOperations()") static final String blockingCallFromAWTWarning = "Please ensure you are not calling this from the AWT thread"; // @AspectJ提供了@DeclareParents,但很少使用,而更多使用下面的@DeclareMixin作为替代 // @AspectJ实现的Mix-in,本质是一个返回proxy对象的工厂方法,用于返回一个包裹了aspect的proxy // 下面的代码等价于:declare parents: ajia.banking.domain.* implements Serializable; // 由于返回null,因此只能作为对被切入类的都有Serializable的一个标记 @DeclareMixin("ajia.banking.domain.*") public Serializable serializableMixin() { return null; } // @DeclareMixin支持一个参数,模仿依赖注入的方式,把被切入的对象传递给工厂 // AuditorImp是接口Auditor的一个实现,即对Object的一个代理 @DeclareMixin("ajia.banking.domain.*") public Auditor auditorMixin(Object mixedIn) { return new AuditorImpl(mixedIn); } // 要mix-in若干个接口,则需要在interfaces里依次放上要添附的接口 @DeclareMixin(value="ajia.banking.domain.*", interfaces="{Auditor.class, MonitoringAgent.class}") public AuditorMonitoringAgent mixin() { return new AuditorMonitoringAgentImpl(); } } |