Spring Aop 标签详解
<aop:config>
<aop:aspect id="beforeAdviceBindingTests" ref="testAspect">
<aop:before method="oneIntArg"
pointcut="execution(* setAge(int)) and args(age)"/>
<aop:before method="oneObjectArg"
pointcut="execution(* getAge()) and this(bean)"/>
<aop:before method="oneIntAndOneObject"
pointcut="execution(* setAge(..)) and args(age) and target(bean)"
arg-names="age,bean"/>
<aop:before method="needsJoinPoint" pointcut="execution(* getAge())"/>
<aop:before method="needsJoinPointStaticPart" pointcut="execution(* getAge())"/>
</aop:aspect>
<!-- variation with external pointcut reference -->
<aop:aspect ref="authenticationLogger">
<aop:pointcut id="authenticationMethodWithString"
expression="execution(boolean *..SecurityManager.authenticate(..))
and args(username,java.lang.String)"/>
<aop:before pointcut-ref="authenticationMethodWithString"
method="logAuthenticationAttempt(java.lang.String)"/>
</aop:aspect>
</aop:config>
<aop:config>
是配置aop的切入点
<aop:aspect>
是切面信息,id
是切面标识,ref
是切面的定义类
<aop:pointcut>
是切点信息,id
是切点标识,expression
切点表达式,常见的切点表达式:
-
args()
:用于匹配当前执行的方法传入的 参数 为 指定类型 的执行方法args
是和execution
用在一起,用来过滤要被代理的方法的- 独立使用:
args(param1, param2, ..)
表示目标方法只需匹配前面param1和param2的类型即可 - 和
arg-names
一起使用:arg-names(param1, param2, ..)
表示参数1和参数2的类型由arg-names
所代表方法的参数确定
举例说明:
/** * 切面定义 */ @Aspect public class AccessArgAdviceTest { @AfterReturning( pointcut="execution(* com.abc.service.*.access*(..)) && args(time, name)", returning="returnValue") public void access(Date time, Object returnValue, String name) { System.out.println("目标方法中的参数String = " + name); System.out.println("目标方法中的参数Date = " + time); System.out.println("目标方法的返回结果returnValue = " + returnValue); } }
表达式中增加了
args(time, name)
部分,意味着可以在增强处理方法(access方法)中定义time和name两个属性——这两个形参的类型可以随意指定,但一旦指定了这两个参数的类型,则这两个形参类型将用于限制该切入点只匹配第一个参数类型为Date,第二个参数类型为name的方法(方法参数个数和类型若有不同均不匹配)。注意,在定义returning的时候,这个值(即上面的
returning="returnValue"
中的returnValue
)作为增强处理方法的形参时,位置可以随意。//将被AccessArgAdviceTest的access方法匹配 public String accessAdvice(Date d, String n) { System.out.println("方法:accessAdvice"); return "aa"; }
- 独立使用:
-
@args()
:用于匹配当前执行的方法传入的 参数 持有 指定注解 的执行方法 -
execution()
:用于匹配 方法 执行的连接点execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)
modifiers-pattern
方法修饰符:如public,可以不需要,?代表可以缺失ret-type-pattern
方法返回值类型,不可缺失declaring-type-pattern
类限定名匹配,可以缺失name-pattern
方法名,不能缺失param-pattern
方法参数,不能缺失throws-pattern
异常类型,可以缺失 -
this()
:用于匹配当前 AOP 代理对象类型的执行方法,注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配<aop:aspect ref="thisAsInterfaceAndTargetAsClassCounter"> <aop:before method="increment" pointcut="this(org.springframework.aop.aspectj.TestInterface)" /> </aop:aspect>
表示当前AOP对象实现了
TestInterface
接口的任何方法 -
target()
:用于匹配当前目标对象类型的执行方法,注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配<aop:aspect ref="thisAsInterfaceAndTargetAsClassCounter"> <aop:before method="increment" pointcut="this(org.springframework.aop.aspectj.TestInterface) and target(org.springframework.aop.aspectj.TestImpl)" /> </aop:aspect>
表示当前AOP对象实现了
TestInterface
接口的任何方法 并且 当前目标对象(非AOP对象)实现了TestImpl
类的任何方法 -
@target()
:用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解<aop:config > <aop:advisor id="logUserAdvisor" pointcut="@within(org.springframework.scripting.groovy.Log)" advice-ref="logUserAdvice"/> </aop:config>
表示任何目标对象持有Log注解的类方法,必须是在目标对象上声明这个注解,在接口上声明的对它不起作用
-
within()
:用于匹配指定类型内的方法执行<aop:config> <aop:pointcut id="getNameCalls" expression="execution(* getName(..)) and within(*..ITestBean+)"/> <aop:advisor id="getAgeAdvisor" pointcut="execution(* *..ITestBean.getAge(..))" advice-ref="getAgeCounter"/> <aop:advisor id="getNameAdvisor" pointcut-ref="getNameCalls" advice-ref="getNameCounter"/> </aop:config>
表示所有包下
ITestBean
类型及子类型的任何方法 -
@within()
:用于匹配所有持有指定注解类型内的方法<aop:config > <aop:advisor id="logUserAdvisor" pointcut="@within(org.springframework.scripting.groovy.Log)" advice-ref="logUserAdvice"/> </aop:config>
表示任何目标对象对应的类型持有Log注解的类方法,必须是在目标对象上声明这个注解,在接口上声明的对它不起作用。
-
@annotation
:用于匹配当前执行方法持有指定注解的方法@within
和@target
针对类的注解,@annotation
是针对方法的注解
<aop:before>
在方法调用之前调用通知
<aop:config>
<aop:aspect id="countAgeCalls" ref="countingAdvice">
<aop:pointcut id="pc" expression="execution(* getAge())"/>
<aop:before pointcut-ref="pc" method="myBeforeAdvice" />
<aop:after pointcut-ref="pc" method="myAfterAdvice" />
<aop:after-returning pointcut-ref="pc"
method="myAfterReturningAdvice" returning="age"/>
<aop:after-throwing pointcut-ref="pc"
method="myAfterThrowingAdvice" throwing="ex"/>
<aop:around pointcut-ref="pc" method="myAroundAdvice"/>
</aop:aspect>
</aop:config>
<aop:after>
在方法完成之后调用通知,无论方法执行成功与否
<aop:after-returning>
在方法执行成功之后调用通知
<aop:after-throwing>
在方法抛出异常后进行通知
<aop:around>
通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为
<aop:advisor>
定义通知器(通知器跟切面一样,也包括通知和切点)
<aop:config >
<aop:advisor id="logUserAdvisor"
pointcut="@within(org.springframework.scripting.groovy.Log)"
advice-ref="logUserAdvice"/>
</aop:config>
<bean id="logUserAdvice" class="org.springframework.scripting.groovy.LogUserAdvice" />
引入和织入的概念区别:
-
引入概念:允许我们向现有的类中添加方法或属性
-
织入概念:将切面应用到目标对象来创建的代理对象过程
切面在指定的连接点被织入到目标对象中,在目标对象的生命周期中有多个点可以织入
- 编译期——切面在目标类编译时期被织入,这种方式需要特殊编译器。AspectJ的织入编译器就是以这种方式织入切面。
- 类加载期——切面在类加载到
- JVM ,这种方式需要特殊的类加载器,他可以在目标类被引入应用之前增强该目标类的字节码。AspectJ5 的 LTW 就支持这种织入方式
- 运行期——切面在应用运行期间的某个时刻被织入。一般情况下,在织入切面时候,AOP 容器会为目标对象动态的创建代理对象。Spring AOP 就是以这种方式织入切面。