<aop:aspect>: 定义切面, 包括通知和切点. 是一般的bean
//定义切面
public class SleepHelperAspect{
public void beforeSleep(){
System.out.println("睡觉前要脱衣服!");
}
public void afterSleep(){
System.out.println("起床后要穿衣服!");
}
}
<bean id="sleepHelperAspect" class="com.ghs.aop.SleepHelperAspect"></bean>
<aop:aspectj-autoproxy/>
<aop:config>
<aop:pointcut expression="execution(* *.sleep(..))" id="sleepPointcut"/>
<aop:aspect ref="sleepHelperAspect">
<!--前置通知-->
<aop:before method="beforeSleep" pointcut-ref="sleepPointcut"/>
<!--后置通知-->
<aop:after method="afterSleep" pointcut-ref="sleepPointcut"/>
</aop:aspect>
</aop:config>
<aop:advisor> 定义通知器, 跟切面一样,也包括通知(advice)和切点(PointCut) . 通知必须实现Advice接口.
//定义通知
public class SleepHelper implements MethodBeforeAdvice,AfterReturningAdvice{
@Override
public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
System.out.println("睡觉前要脱衣服!");
}
@Override
public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
System.out.println("起床后要穿衣服!");
}
}
<bean id="sleepHelper" class="com.noob.aop.SleepHelper"></bean>
<aop:aspectj-autoproxy/>
<aop:config>
<aop:pointcut expression="execution(* *.sleep(..))" id="sleepPointcut"/>
<aop:advisor advice-ref="sleepHelper" pointcut-ref="sleepPointcut"/>
</aop:config>
Advice
(通知、切面): 某个连接点所采用的处理逻辑,也就是向连接点注入的代码, AOP在特定的切入点上执行的增强处理。JointPoint
(连接点):程序运行中的某个阶段点,比如方法的调用、异常的抛出等。Pointcut
(切入点): JoinPoint的集合,是程序中需要注入Advice的位置的集合,指明Advice要在什么样的条件下才能被触发,在程序中主要体现为书写切入点表达式。Advisor
(增强): **PointCut 和 Advice的综合体,**完整描述了一个advice将会在pointcut所定义的位置被触发。@Aspect
(切面): 通常是一个类的注解,类中可以定义切入点和通知AOP Proxy
:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类。表示式(expression)和签名(signature)
//Pointcut表示式
@Pointcut("execution(* com.savage.aop.MessageSender.*(..))")
//Point签名
private void log(){}
由下列方式来定义或者通过 &&、 ||、 !、 的方式进行组合:
execution
:匹配 方法执行的连接点;within
: 目标对象target的类型是否和within中指定的类型匹配 。参数可指定包路径或具体的类全路径名。this(类型全限定名)
: 通过aop创建的代理对象的类型是否和this中指定的类型匹配;注意判断的目标是代理对象;this中使用的表达式必须是类型全限定名,不支持通配符。target(类型全限定名)
: 判断目标对象的类型是否和指定的类型匹配;注意判断的目标是实际对象的类型;表达式必须是类型全限定名,不支持通配符。args
:匹配 当前执行的方法传入的参数为指定类型的执行方法;@within
:自定义注解标注在类上,该类的所有方法(不包含子类方法)执行aop方法@target
:匹配 当前目标对象类型的执行方法,其中目标对象类持有指定的注解;@args
: 当前执行的方法传入的参数持有指定注解;@annotation
:匹配 执行方法持有指定注解;execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?) 其中后面跟着“?”的是可选项
括号中各个pattern分别表示:
**AspectJMethodBeforeAdvice**
标识一个前置增强方法.**AspectJAfterAdvice**
f***inal***增强,不管是抛出异常或者正常退出都会执行.AspectJAfterReturningAdvice
**后置增强,方法正常退出时执行.AspectJAfterThrowingAdvice
**异常抛出增强.AspectJAroundAdvice
**环绕增强.常用的方法:
使用**@Around
处理时,需要将第一个JoinPoint
参数定义为ProceedingJoinPoint
类型才可使用方法proceed
。
因为在org.springframework.aop.aspectj.AbstractAspectJAdvice
里默认supportsProceedingJoinPoint()
为false; 而AspectJAroundAdvice
重写为true;
Caused by: java.lang.IllegalArgumentException: ProceedingJoinPoint is only supported for around advice
at org.springframework.aop.aspectj.AbstractAspectJAdvice.maybeBindProceedingJoinPoint(AbstractAspectJAdvice.java:414) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
at org.springframework.aop.aspectj.AbstractAspectJAdvice.calculateArgumentBindings(AbstractAspectJAdvice.java:388) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
at org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory.getAdvice(ReflectiveAspectJAdvisorFactory.java:294) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
at org.springframework.aop.aspectj.annotation.InstantiationModelAwarePointcutAdvisorImpl.instantiateAdvice(InstantiationModelAwarePointcutAdvisorImpl.java:149) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
at org.springframework.aop.aspectj.annotation.InstantiationModelAwarePointcutAdvisorImpl.<init>(InstantiationModelAwarePointcutAdvisorImpl.java:113) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
at org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory.getAdvisor(ReflectiveAspectJAdvisorFactory.java:198) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
at org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory.getAdvisors(ReflectiveAspectJAdvisorFactory.java:126) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
at org.springframework.aop.aspectj.annotation.BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors(BeanFactoryAspectJAdvisorsBuilder.java:110) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
at org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors(AnnotationAwareAspectJAutoProxyCreator.java:95) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
at org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator.shouldSkip(AspectJAwareAdvisorAutoProxyCreator.java:101) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessBeforeInstantiation(AbstractAutoProxyCreator.java:251) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInstantiation(AbstractAutowireCapableBeanFactory.java:1124) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.resolveBeforeInstantiation(AbstractAutowireCapableBeanFactory.java:1097) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:504) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
... 15 common frames omitted
spring-core
模块中: 提供支持cglib
的org.springframework.cglib.proxy.MethodInterceptor
是给 org.springframework.cglib.proxy.Enhancer
使用的 org.springframework.cglib.proxy.Callback
public interface MethodInterceptor extends Callback {
Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4) throws Throwable;
}
spring-aop
模块中:org.aopalliance.intercept.MethodInterceptor
根本上是一个org.aopalliance.aop.Advice
public interface MethodInterceptor extends Interceptor {
Object invoke(MethodInvocation invocation) throws Throwable;
}
public interface Interceptor extends Advice {}
@AfterReturning(
pointcut="execution(* com.abc.service.*.access*(..)) && args(time, name)",
returning="returnValue")
public void invoke(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"两个同名属性。这两个形参的类型由access方法同名参数类型指定。一旦指定了, 则这两个形参类型将用于限制该切入点只匹配第一个参数类型为Date,第二个参数类型为String的方法(方法参数个数和类型若有不同均不匹配);
access方法只需要满足"time", "name"参数的顺序和pointcut中args(time, name)的顺序相同即可,"returnValue"位置顺序无所谓。
eg.
//将被access方法匹配
public String accessAdvice(Date d, String n) {
System.out.println("方法:accessAdvice");
return "aa";
}
package com.noob.controller.Interceptor;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class AdviceInterceptor {
@Pointcut(value = "execution(public String com.noob.controller.BService.testAdvice(..))")
public void pointcut() {}
@Before("pointcut()")
public void before(JoinPoint point) {
System.out.println("before");
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
System.out.println("around begin");
try {
Object proceed = point.proceed();
System.out.println("around after");
return proceed;
} catch (Throwable e) {
System.out.println("around after exception");
throw e;
}
}
@After("pointcut()")
public void after() throws Throwable {
System.out.println("after");
}
@AfterReturning("pointcut()")
public void afterReturning() throws Throwable {
System.out.println("afterReturning");
}
@AfterThrowing("pointcut()")
public void afterThrowing() {
System.out.println("afterThrowing");
}
}
---
@Component
public class BService {
public String testAdvice() {
System.out.println("目标方法testAdvice");
throw new RuntimeException("fail");
// return "testAdvice";
}
}
正常情况执行结果:
around begin
before
目标方法testAdvice
around after
after
afterReturning
有异常情况下:
around begin
before
目标方法testAdvice
around after exception
after
afterThrowing
测试过程中发现,debug模式下可知JoinPoint的实际类型是org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint
(before、around 都是该实际类型)
但若代码直接写定该类型,启动报错
Caused by: java.lang.IllegalArgumentException: error at ::0 formal unbound in pointcut
at org.aspectj.weaver.tools.PointcutParser.parsePointcutExpression(PointcutParser.java:319) ~[aspectjweaver-1.9.4.jar:na]
at org.springframework.aop.aspectj.AspectJExpressionPointcut.buildPointcutExpression(AspectJExpressionPointcut.java:227) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
at org.springframework.aop.aspectj.AspectJExpressionPointcut.obtainPointcutExpression(AspectJExpressionPointcut.java:198) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
at org.springframework.aop.aspectj.AspectJExpressionPointcut.getClassFilter(AspectJExpressionPointcut.java:177) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
at org.springframework.aop.support.AopUtils.canApply(AopUtils.java:225) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
at org.springframework.aop.support.AopUtils.canApply(AopUtils.java:288) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
at org.springframework.aop.support.AopUtils.findAdvisorsThatCanApply(AopUtils.java:320) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findAdvisorsThatCanApply(AbstractAdvisorAutoProxyCreator.java:126) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findEligibleAdvisors(AbstractAdvisorAutoProxyCreator.java:95) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean(AbstractAdvisorAutoProxyCreator.java:76) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(AbstractAutoProxyCreator.java:347) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessAfterInitialization(AbstractAutoProxyCreator.java:299) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:429) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1782) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:593) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
因为, 执行到ReflectiveAspectJAdvisorFactory.getAdvisors -> ReflectiveAspectJAdvisorFactory.getAdvice -> AbstractAspectJAdvice.calculateArgumentBindings()
时:对JoinPoint.class
与ProceedingJoinPoint.class
是不用额外处理参数绑定。
所以MethodInvocationProceedingJoinPoint
被当作额外的参数。
后续先执行进入org.aspectj.weaver.tools.PointcutParser.parsePointcutExpression -> PointcutParser.resolvePointcutExpression
:
PointcutParser.buildResolutionScope
创建参数类型绑定关系。
接着执行进入org.aspectj.weaver.patterns.Pointcut.resolve
:
首先 Pointcut.resolveBindings
来根据实际情况解析该绑定关系; 接着执行校验方法Bindings.checkAllBound
因真实绑定为空,判定失败后异常
优先级高的切面类里的增强处理 优先于 优先级低的切面类。
在“进入”连接点时,最高优先级的增强处理将先被织入(eg. 给定的两个不同切面类Before增强处理中,优先级高的那个会先执行);
在“退出”连接点时,最高优先级的增强处理会最后被织入(eg. 给定的两个不同切面类After增强处理中,优先级高的那个会后执行)。
eg. 优先级为1的切面类Bean1包含了@Before,优先级为2的切面类Bean2包含了@Around,虽然@Around优先级高于@Before,但由于Bean1的优先级高于Bean2的优先级,因此Bean1中的@Before先被织入。
Spring提供了如下两种解决方案指定不同切面类里的增强处理的优先级:
同一个切面类里的两个相同类型的增强处理在同一个连接点被织入时,Spring AOP将以随机的顺序来织入这两个增强处理,没有办法指定它们的织入顺序。即使给这两个 advice 添加了 @Order 这个注解,也不行!
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/qq_43842093/article/details/121779305
内容来源于网络,如有侵权,请联系作者删除!