本文已收录 【修炼内功】跃迁之路
在以前的文章中介绍了Spring的IoC( Resource | BeanDefinitionReader | BeanFactory | ApplicationContext),若是说前者是Spring的基石,则本篇要介绍的AOP则能够称做是Spring的点睛之笔,它在整个Spring生态中扮演着重要的角色
Aspect-oriented Programming (AOP) complements Object-oriented Programming (OOP) by providing another way of thinking about program structure. The key unit of modularity in OOP is the class, whereas in AOP the unit of modularity is the aspect. Aspects enable the modularization of concerns that cut across multiple types and objects.
代理能够看做是对调用目标的一个包装,目标代码的调用不是直接发生的而是经过代理完成,以此来让调用者与实现者之间解耦,从而(在无侵入的状况下)在目标代码以外实现一些额外(加强)功能html
实现代理的方法有不少种java
维基百科-面向切面的程序设计Aspect-Oriented Programming,面向切面的程序设计,是计算机科学中的一种程序设计思想,旨在将横切关注点与业务主体进行进一步分离,以提升程序代码的模块化程度。经过在现有代码基础上增长额外的通知(Advice)机制,可以对被声明为“切点(Pointcut)”的代码块进行统一管理与装饰。git
从AOP的定义上来看,其与代理模式十分类似,那能够说AOP就是代理模式的一种实现么?并不是如此,AOP相比于代理模式表现的更为灵活也更细腻,典型的框架如 AspectJgithub
Spring AOP 将面向切面的设计思想引入到了Spring IoC中,但Spring AOP并非AOP的完备实现,其设计目标在于:spring
Spring AOP的做用范围仅限于Spring IoC中,更为通俗的讲,Spring AOP的切面只能做用在Spring IoC所管理的Bean中express
Rather, the aim is to provide a close integration between AOP implementation and Spring IoC, to help solve common problems in enterprise applications.
Spring AOP仅支持方法级的切面,其更像是给目标(切面)方法定义了一个拦截器(method-interceptor)apache
Spring AOP currently supports only method execution join points (advising the execution of methods on Spring beans).
Spring AOP具体的能力及设计目标见 Spring AOP Capabilities And Goals编程
Spring AOP借助了AspectJ的不少能力(注解、切面匹配、拦截器、等),但其织入(Weaving)过程并未使用AspectJ,而是使用了 JDK Proxy/CGLIB 建立代理类,经过反射的方式实现目标方法的调用,Spring AOP及AspectJ在功能上的区别比较见 Comparing Spring AOP and AspectJsegmentfault
Spring框架并未直接引入CGLIB,而是本身维护(repackaging)了一套源码(包括asm在内),可参考 spring-core内的 org.springframework.cglib包及 org.springframework.asm包
相比于AspectJ之类的AOP框架而言,Spring AOP更加简洁,但Spring AOP是如何实现的?这里不妨先留几个疑问,带着问题去理解Spring AOP的实现逻辑缓存
在深刻Spring AOP的实现原理以前,先熟悉几个AOP的基本概念 (AOP Concepts)
Pointcut
切入点,在哪些类的哪些方法上进行切入(拦截),其一般表现为一个表达式(AspectJExpressionPointcut),经过该表达式匹配哪些类(ClassFilter)及哪些方法(MethodMatcher)须要被拦截代理
JoinPoint
链接点,因为Spring AOP仅支持方法,在Spring AOP中能够理解为,经过Pointcut匹配到的须要被拦截的方法
Advice
通知,拦截到链接点后,须要代理执行的代码逻辑,其一共分为 Around、Before、After、AfterReturning、AfterThrowing 五类
Aspect
切面,即 Pointcut + Advice,在哪些类的哪些方法上,代理执行哪些逻辑
Weaving
织入,将切面应用到目标对象,生成代理对象的过程
Target Object
目标(被代理)类对象,链接点所在的类对象
Proxy Object
生成的代理对象
Introduction
引入,在不修改目标类代码的前提下,可在运行期动态的增长一些方法(或属性)
Spring AOP的注册有多种方式,主要以注解及xml配置为主,本篇着重介绍注解方式,xml配置的方式原理相通
为了启用Spring AOP,通常都会使用注解@EnableAspectJAutoProxy,或者xml配置<aop:aspectj-autoproxy>
@Import(AspectJAutoProxyRegistrar.class) public @interface EnableAspectJAutoProxy { boolean proxyTargetClass() default false; boolean exposeProxy() default false; }
proxyTargetClass及exposeProxy参数的做用参见JavaBean Properties,重点在AspectJAutoProxyRegistrar(@Import注解的使用在之后的章节中介绍)实现了ImportBeanDefinitionRegistrar接口(其做用在于动态注册Bean)
跟踪源码(AopConfigUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary)会发现@EnableAspectJAutoProxy注解经过AspectJAutoProxyRegistrar注册器注册了AnnotationAwareAspectJAutoProxyCreator,该Creator实现了BeanPostProcessor
Bean是如何被建立的一文中介绍过,在Bean实例化并初始化完成后,会调用BeanPostProcessor#postProcessAfterInitialization,该方法能够返回一个新的实例对象,AnnotationAwareAspectJAutoProxyCreator对目标对象的代理建立过程,可能就发生在此步骤
// org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization @Override public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { if (bean != null) { // ... 略去 // 切面匹配,若是须要则生成代理bean return wrapIfNecessary(bean, beanName, cacheKey); } return bean; }
看到wrapIfNecessary
是否有豁然开朗的感受?
因此,Spring AOP对目标对象的代理建立过程发生在BeanPostProcessor#postProcessAfterInitialization阶段,即目标Bean实例化且初始化完成以后
AnnotationAwareAspectJAutoProxyCreator实际直接实现了 InstantiationAwareBeanPostProcessor接口,在目标对象建立以前便会有一个短路的操做,可能直接在该步骤生成代理对象而不须要目标对象的实例化,具体逻辑能够参考 AbstractAutoProxyCreator#postProcessBeforeInstantiation
查看wrapIfNecessary的逻辑,总体上并不复杂,找到该bean全部匹配的切面Advisors,并根据所匹配的切面建立代理bean
首选关心的是,如何找到全部匹配的Advisors
AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean
AbstractAdvisorAutoProxyCreator#findEligibleAdvisors
AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors
Advisor的注册有两大途径,直接注册Advisor类型的Bean 或者 注册被@Aspect修饰的Bean
AbstractAdvisorAutoProxyCreator#findCandidateAdvisors
BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans
这一步骤其实并不复杂,在BeanFactory中找到全部注册为Advisor类型的Bean并提早初始化
查看Advisor的源码十分简单,只有一个方法会比较感兴趣
public interface Advisor { Advice getAdvice(); }
而Advice则显得更加神秘,没有任何方法接口定义
public interface Advice {}
留个悬念,客官且看
Q: Advisor是什么?Advice的实现类都有哪些?如何直接注册Advisor的Bean?经过注册Advisor Bean来实现AOP的场景有哪些?
BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors
Spring将全部注册的bean-names拿出遍历,过滤出被@Aspect注解修饰的Bean,经过@Apsect注解及Aspect-Bean信息生成BeanFactoryAspectInstanceFactory,用于后续Advisor的解析(ReflectiveAspectJAdvisorFactory#getAdvisors)
从流程图能够看出,Advisor的解析主要来自方法及属性两部分,方法主要用来解析@Around、@Before、@After、@AfterReturing、@AfterThrowing,属性主要用来解析@DeclareParents
ReflectiveAspectJAdvisorFactory#getAdvisor
Spring会查找被@Around、@Before、@After、@AfterReturing、@AfterThrowing(若是一个方法同时被多个注解修饰,按顺序只会匹配第一个被找到的注解)修饰的方法,将注解的信息封装为AspectJExpressionPointcut,将方法的内部逻辑封装为不一样类型的xxxAdvice,并最终将上述的Pointcut及Advice组合封装为InstantiationModelAwarePointcutAdvisorImpl提供出去
这里出现了开篇提到的几个概念 Pointcut、Advice
Pointcut
Pointcut的实现很是多,深刻以前先浏览下Pointcut的接口定义
public interface Pointcut { ClassFilter getClassFilter(); MethodMatcher getMethodMatcher(); }
不难猜测,Spring AOP就是经过这两个方法来判断哪些类的哪些方法须要被代理,反过来说经过这两个方法判断当前Bean的各个方法有没有以及匹配上了哪些Advice
众多的Pointcut实现中,重点介绍其中的几个
该Pointcut的实现很是简单,不管入参是什么,ClassFilter及MethodMatcher的matches方法总会返回true
该Pointcut只关注目标类上的注解修饰,ClassFilter#matches用来判断目标类上是否存在指定的注解,MethodMatcher#matches总会返回true
AspectJExpressionPointcut
该Pointcut是本篇的重点,一般而言不管使用注解方式仍是xml方式配置AOP,一般在声明Pointcut的时候都会定义一段表达式用来匹配咱们想要拦截/代理的方法,这样的声明方式Spring AOP均会将其封装为AspectJExpressionPointcut,其内部的匹配逻辑比较复杂,在这里很少展开讨论,感兴趣的能够查看 AspectJExpressionPointcut#matches
既然是表达式,便比较关心Spring AOP都支持哪些表达式,从源码中不难看出
public class AspectJExpressionPointcut extends AbstractExpressionPointcut implements ClassFilter, IntroductionAwareMethodMatcher, BeanFactoryAware { private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet<>(); static { // execution() SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION); // args() SUPPORTED_PRIMITIVES.add(PointcutPrimitive.ARGS); // reference() SUPPORTED_PRIMITIVES.add(PointcutPrimitive.REFERENCE); // this() SUPPORTED_PRIMITIVES.add(PointcutPrimitive.THIS); // target() SUPPORTED_PRIMITIVES.add(PointcutPrimitive.TARGET); // within() SUPPORTED_PRIMITIVES.add(PointcutPrimitive.WITHIN); // @annotation() SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ANNOTATION); // @within SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_WITHIN); // @arges() SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ARGS); // @target() SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_TARGET); } }
具体的含义参考 Supported Pointcut Designators,用法参考 AOP Pointcut Examples,使用与、或、非运算参考 Combining Pointcut Expressions
看到composable应该就能猜到这是一个复合的Pointcut,能够将多个Pointcut组合到一块儿进行union、intersection运算
Advice
Advice的定义比较让人摸不到头脑,并无声明任何方法
/** * Tag interface for Advice. Implementations can be any type * of advice, such as Interceptors. */ public interface Advice {}
可是上文已经讲了,Spring AOP会根据不一样的注解生成不一样的Advice实现
注解 | Advice实现 |
---|---|
@Around | AspectJAroundAdvice |
@Before | AspectJMethodBeforeAdvice |
@After | AspectJAfterAdvice |
@AfterReturning | AspectJAfterReturningAdvice |
@AfterThrowing | AspectJAfterThrowingAdvice |
Spring AOP在生成以上任意一种Advice实现时,都会将
candidateAdviceMethod
以上各注解修饰的advice方法
expressionPointcut
上述生成的AspectJExpressionPointcut
aspectInstanceFactory
这是以后定位拦截的方法、生成代理类、执行代理逻辑必须的内容
除了AspectJMethodBeforeAdvice及AspectJAfterReturningAdvice以外,其余三个Advice实现均实现了MethodInterceptor接口
public interface MethodInterceptor extends Interceptor { Object invoke(MethodInvocation invocation) throws Throwable; }
这即是拦截/代理的POINT,查看各Advice对invoke方法的实现逻辑
// AspectJAroundAdvice public Object invoke(MethodInvocation mi) throws Throwable { ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi; // 将目标方法封装为ProceedingJoinPoint ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi); JoinPointMatch jpm = getJoinPointMatch(pmi); // 将目标方法的ProceedingJoinPoint传入advice方法进行调用 return invokeAdviceMethod(pjp, jpm, null, null); }
// AspectJAfterAdvice public Object invoke(MethodInvocation mi) throws Throwable { try { // 调用目标方法 return mi.proceed(); } finally { // 调用advice方法 invokeAdviceMethod(getJoinPointMatch(), null, null); } }
// AspectJAfterThrowingAdvice public Object invoke(MethodInvocation mi) throws Throwable { try { // 调用目标方法 return mi.proceed(); } catch (Throwable ex) { if (shouldInvokeOnThrowing(ex)) { // 调用advice方法 invokeAdviceMethod(getJoinPointMatch(), null, ex); } throw ex; } }
到这里,彷佛对Spring AOP的流程已经有些眉目了,那AspectJMethodBeforeAdvice及AspectJAfterReturningAdvice是如何执行的?
其又分别对应另外两个AdviceInterceptor(无辜脸~)
Advice实现 | 对应的MethodInterceptor实现 |
---|---|
AspectJMethodBeforeAdvice | MethodBeforeAdviceInterceptor |
AspectJAfterReturningAdvice | AfterReturningAdviceInterceptor |
再来看这两种MethodInterceptor的invoke实现
// MethodBeforeAdviceInterceptor public Object invoke(MethodInvocation mi) throws Throwable { // 调用advice方法 this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis()); // 调用目标方法 return mi.proceed(); }
// AfterReturningAdviceInterceptor public Object invoke(MethodInvocation mi) throws Throwable { // 调用目标方法 Object retVal = mi.proceed(); // 调用advice方法 this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis()); return retVal; }
因此,你有看懂@After和@AfterReturning的区别了么?
Q: MethodBeforeAdviceInterceptor及AfterReturningAdviceInterceptor是在何时封装的?
Advisor
没有特别的逻辑在里边,封装了上文提到的Pointcut实现及Advice实现
ReflectiveAspectJAdvisorFactory#getDeclareParentsAdvisor
主要用于对@DeclareParents修饰的属性进行解析,用来支持Introduction
须要加强的接口为@DeclareParents修饰的属性类型,加强的接口实现为@DeclareParents中的defaultImpl参数
AbstractAdvisorAutoProxyCreator#findAdvisorsThatCanApply
AopUtils#findAdvisorsThatCanApply
经过上面的过程(每个节点都是存在缓存的,以上的逻辑只在首次触发)已经拿到了容器中全部已注册的Advisors,如今的流程还处在BeanPostProcessor#postProcessAfterInitialization中,只须要使用每个Advisor中Pointcut的ClassFilter及MethodMatcher去匹配当前的目标bean即可过滤出须要应用到当前Bean上的Advisor
这部分逻辑比较简单,以文字描述其过程
AspectJAwareAdvisorAutoProxyCreator#extendAdvisors
AspectJProxyUtils#makeAdvisorChainAspectJCapableIfNecessary
若是当前的目标Bean存在匹配上的Advisors,则会在在Advisors列表中添加一个特殊的Advisor - ExposeInvocationInterceptor.ADVISOR
该Advisor的实现为DefaultPointcutAdvisor,内部Pointcut为TruePointcut(匹配任何类的任何方法),内部Advice为ExposeInvocationInterceptor,而且Order是最高的
ExposeInvocationInterceptor用于暴露当前线程正在执行的MethodInvocation
AspectJAwareAdvisorAutoProxyCreator#sortAdvisors
Advice会继承Aspect-Bean的Order(经过@Order注解,或实现Ordered或PriorityOrdered),对Advisors的排序则依赖以上(PriorityOrdered优先于Ordered及@Order)
因此,这里有一个问题,若是在一个Aspect-Bean中定义多个Advices,则同一个Aspect-Bean中Advices的顺序是没有办法定义/保障的(官方解释 Advice Ordering)
AbstractAutoProxyCreator#createProxy
Spring AOP会经过proxyTargetClass参数的设置及实际Bean Class的状况判断使用Cglib仍是Jdk Proxy(这里默认您对Cglib及Jdk Proxy的使用有必定的了解)
对Cglib来说,关键在于设置Enhancer的Callback,逻辑能够参考CglibAopProxy#getCallbacks
对JDK Proxy来说,关键在于定义InvocationHandler,逻辑能够参考JdkDynamicAopProxy#invoke
若是一个Bean中的方法匹配上多个Advice,那多个Advice的invoke方法是如何串行执行的?以JdkDynamicAopProxy中的逻辑为例,有两处代码会比较有意思
// org.springframework.aop.framework.JdkDynamicAopProxy#invoke // 找到当前方法匹配到Advices List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); // ... // 将以上Advices封装为ReflectiveMethodInvocation MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); // 触发整条链 retVal = invocation.proceed();
这里相似于Servlet应用中的FilterChain,将advice chain一步步代入,以剥洋葱的方式调用
上文提到的全部Advice实现均会实现MethodInterceptor,内部invoke方法的入参类型为MethodInvocation,注意,ReflectiveMethodInvocation一样实现了MethodInterceptor,简单描述ReflectiveMethodInvocation#proceed的流程以下
关键在于各Advice的实现类中执行invoke方法时,调用的MethodInvocation.proceed既是ReflectiveMethodInvocation#proceed自己
若是存在顺序的三个AroundAdvice、BeforeAdvice、AfterAdvice匹配同一个方法,整个调用过程将会是
Q: Spring AOP还提供了Listener支持(AdvisedSupportListener),如何使用?
@Aspect修饰的类必须同时注册为Bean
@Aspect @Component public class CatAspectJSupport { /* ... */}
拦截/切面/代理的优先级能够经过@Order、Ordered、PriorityOrdered控制
@Aspect @Component @Order(2) public class CatAspectJSupport { /* ... */}
@Aspect @Component public class CatAspectJSupport implements Ordered /** PriorityOrdered **/ { @Override public int getOrder() { return 2; } }
Pointcut能够经过@Pointcut注解声明,也能够直接经过@Around、@Before、@After、@AfterReturing、@AfterThrowing注解声明
经过@Pointcut注解声明能够重复使用,经过Advice注解能够将更多的参数引入Advice函数
@Pointcut("execution(* com.manerfan.springdemo.Cat.barkVoice(..))") public void barkVoiceMethod() {} @Around("barkVoiceMethod()") public Object statistics(ProceedingJoinPoint jp) { /* .. */} @Before("barkVoiceMethod())") public void beforeBark(JoinPoint jp) { /* .. */}
@Around("@annotation(statistics)") public Object statistics(ProceedingJoinPoint jp, Statistics statistics /* 直接将目标方法上的注解对象传入 */) { /* .. */}
For matching method execution join points.
使用强大的表达式拦截方法
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
// 全部包中全部类的全部公共方法 execution(public * *(..)) // com.manerfan.service.AccountServiceImpl类中的全部方法 execution(* com.manerfan.service.AccountServiceImpl.*(..)) // com.manerfan.service包中(包含子包)全部类中返回Long类型的方法 execution(java.lang.Long com.manerfan.service..*.*(..)) // com.manerfan.service.AccountServiceImpl类中全部两个参数且第二个参数是String类型的方法 execution(* com.manerfan.service.AccountServiceImpl.*(,java.lang.String))
注: 全类路径须要是被代理的类路径,非接口类路径
Limits matching to join points within certain types (the execution of a method declared within a matching type when using Spring AOP).
能够看作是execution的子集,用于匹配被拦截方法所在的类
// com.manerfan.service包下全部类中全部方法 within(com.manerfan.service.*) // com.manerfan.service包中(包含子包)全部类中全部方法 within(com.manerfan.service..*) // com.manerfan.service.AccountServiceImpl类中全部方法 within(com.manerfan.service.AccountServiceImpl)
注: 全类路径须要是被代理的类路径,非接口类路径
Limits matching to join points (the execution of methods when using Spring AOP) where the bean reference (Spring AOP proxy) is an instance of the given type.
用于匹配生成的代理对象的类型
// 代理对象的类型为com.manerfan.service.DogImpl this(com.manerfan.service.DogImpl) // 代理对象的类型为com.manerfan.service.Animal接口的全部实现 this(com.manerfan.service.Animal)
注: 只能使用完整类路径,不可以使用通配符,可使用实现类全路径,也可使用接口全路径
初此以外,还可使用参数匹配
// 匹配全部代理对象实现了Animal接口的类 @Before("this(animal)") public void animalBefore(JoinPoint jp, Animal animal /* 这里代入的是生成的代理对象 */) { /* ... */ }
this
还有一个用途即是应用到Introduction上
// 为com.manerfan.service包下的全部类生成代理对象的时候默认实现Poultry接口,实现类是DuckImpl @DeclareParents(value = "com.manerfan.service.*", defaultImpl = DuckImpl.class) public Poultry poultry; // 匹配全部代理对象实现了Poultry接口的类 @Before("this(poultry)") public void animalBefore(JoinPoint jp, Poultry poultry /* 这里代入的是生成的代理对象 */) { /* ... */ }
Limits matching to join points (the execution of methods when using Spring AOP) where the target object (application object being proxied) is an instance of the given type.
与this
相似,但不一样的是,target
用于匹配目标Bean对象的类型
// 目标对象的类型为com.manerfan.service.DogImpl target(com.manerfan.service.DogImpl) // 代理对象的类型为com.manerfan.service.Animal接口的全部实现 target(com.manerfan.service.Animal)
注: 只能使用完整类路径,不可以使用通配符,可使用实现类全路径,也可使用接口全路径
初此以外,还可使用参数匹配
// 匹配全部目标对象实现了Animal接口的类 @Before("target(animal)") public void animalBefore(JoinPoint jp, Animal animal /* 这里代入的是目标Bean对象 */) { /* ... */ }
Limits matching to join points (the execution of methods when using Spring AOP) where the arguments are instances of the given types.
用于匹配方法参数
// 匹配无参的函数 args() // 匹配只有一个参数,且类型为String的函数 args(java.lang.String) // 匹配任意参数的函数 args(..) // 匹配任意参数且第一个参数为String的函数 args(java.lang.String,..) // 匹配任意参数且最后一个参数为String的函数 args(..,java.lang.String)
初此以外,还可使用参数匹配
// 匹配com.manerfan包下全部第一个参数是int类型的方法 @Before("within(com.manerfan..*) && args(index,...)") public void animalBefore(JoinPoint jp, int index /* 这里代入目标方法的参数值 */) { /* ... */ }
Limits matching to join points (the execution of methods when using Spring AOP) where the class of the executing object has an annotation of the given type.
用于匹配目标类上的注解
// 匹配com.manerfan包下全部被Statistics注解修饰的类的全部方法 within(com.manerfan..*) && @target(com.manerfan.support.Statistics)
注: 只能使用注解的全路径 ,不能使用通配符
初此以外,还可使用参数匹配
// 匹配com.manerfan包下全部被Statistics注解修饰的类的全部方法 @Before("within(com.manerfan..*) && @target(st)") public void animalBefore(JoinPoint jp, Statistics st /* 这里代入目标类上的注解,能够获取注解中的值 */) { /* ... */ }
注: 注解只能应用于目标类上,不可应用于接口类,不可应用于方法
Limits matching to join points (the execution of methods when using Spring AOP) where the runtime type of the actual arguments passed have annotations of the given types.
用于匹配方法参数上的注解
// 匹配只有一个参数,且被Statistics注解修饰 @args(com.manerfan.support.Statistics) // 匹配任意参数且第一个参数被Statistics注解修饰 @args(com.manerfan.support.Statistics,..) // 匹配任意参数且最后一个参数被Statistics注解修饰 @args(..,com.manerfan.support.Statistics)
注: 只能使用注解的全路径 ,不能使用通配符
初此以外,还可使用参数匹配
// 匹配com.manerfan包下全部类中全部第一个参数被Statistics注解修饰的方法 @Before("within(com.manerfan..*) && @args(st,..)") public void animalBefore(JoinPoint jp, Statistics st /* 这里代入目标方法参数上的注解,能够获取注解中的值 */) { /* ... */ }
Limits matching to join points within types that have the given annotation (the execution of methods declared in types with the given annotation when using Spring AOP).
用于匹配被拦截方法所在的类上的注解
// 全部被Statistics注解修饰的类的方法 @within(com.manerfan.support.Statistics)
注: 只能使用注解的全路径 ,不能使用通配符
初此以外,还可使用参数匹配
// 匹配com.manerfan包下 全部被Statistics注解修饰的类的方法 @Before("within(com.manerfan..*) && @within(st)") public void animalBefore(JoinPoint jp, Statistics st /* 这里代入目标类的注解,能够获取注解中的值 */) { /* ... */ }
注: 注解只能应用于目标类上,不可应用于接口类,不可应用于方法
Limits matching to join points where the subject of the join point (the method being executed in Spring AOP) has the given annotation.
用于匹配被拦截方法上的注解
// 全部被Statistics注解修饰的方法 @annotation(com.manerfan.support.Statistics)
注: 只能使用注解的全路径 ,不能使用通配符
初此以外,还可使用参数匹配
// 匹配com.manerfan包下 全部被Statistics注解修饰的方法 @Before("within(com.manerfan..*) && @annotation(st)") public void animalBefore(JoinPoint jp, Statistics st /* 这里代入目标方法上的注解,能够获取注解中的值 */) { /* ... */ }
注: 注解只能应用于目标类上的方法上,不可应用于接口类方法,不可直接应用于目标类