AOP 的使用分为三步走:
@Aspect
@Pointcut
、@Before
~~~AOP
模式;@EnableAspectJAutoProxy
使用 aop
相关的注解必须先导入依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
下面以一个计算器的例子来介绍 AOP 的基本使用:
这是一个简单的计算器类,为了能够演示异常,所以创建了一个有除法的方法。
public class MathCalculator {
/**
* 除法
*
* @param i 被除数
* @param j 除数
* @return 返回运算结果
*/
public int div(int i, int j) {
return i / j;
}
}
我们想通过 AOP 实现记录除法运行的日志信息,所以新建一个 Log 类。
@Aspect
public class LogAspect {
/**
* 抽取出来的切入点表达式
*/
@Pointcut("execution(* top.wsuo.aop.MathCalculator.*(..))")
public void pointCut() {
}
/**
* 前置通知
*
* @param joinPoint 连接点
*/
@Before("pointCut()")
public void logStart(JoinPoint joinPoint) {
System.out.println(joinPoint.getSignature().getName() + "运行...参数列表是:{" + Arrays.toString(joinPoint.getArgs()) + "}");
}
/**
* 后置通知
*
* @param joinPoint 连接点
*/
@After("pointCut()")
public void logEnd(JoinPoint joinPoint) {
System.out.println(joinPoint.getSignature().getName() + "结束...");
}
/**
* 返回通知
*
* @param joinPoint 连接点
* @param result 执行结果
*/
@AfterReturning(value = "pointCut()", returning = "result")
public void logReturn(JoinPoint joinPoint, Object result) {
System.out.println(joinPoint.getSignature().getName() + "正常返回...运行结果:{" + result + "}");
}
/**
* 异常通知
*
* @param joinPoint 连接点
* @param exception 异常信息
*/
@AfterThrowing(value = "pointCut()", throwing = "exception")
public void logException(JoinPoint joinPoint, Exception exception) {
System.out.println(joinPoint.getSignature().getName() + "出现异常...异常信息:{" + exception.getMessage() + "}");
}
}
最后在配置类上开启注解版 AOP,同时注册组件到容器中。
@Configuration
// Spring 中有很多 EnableXXX 代表开启某一项功能: 取代了配置
@EnableAspectJAutoProxy
public class MainConfigOfAOP {
@Bean
public MathCalculator mathCalculator() {
return new MathCalculator();
}
@Bean
public LogAspect logAspect() {
return new LogAspect();
}
}
测试及运行结果。
@Test
public void test12() {
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfigOfAOP.class);
MathCalculator calculator = context.getBean(MathCalculator.class);
System.out.println(calculator.div(4, 2));
}
整个 AOP 要想起作用,必须加上 @EnableAspectJAutoProxy
注解,这个注解的作用是什么呢?
点进去该注解:
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
看到了要导入一个 AspectJAutoProxyRegistrar
类组件,它继承自一个接口 ImportBeanDefinitionRegistrar
,这个接口我们之前讲过,他是添加自定义组件的接口,在这里:https://blog.csdn.net/weixin_43941364/article/details/107243459。
这说明 @EnableAspectJAutoProxy
注解的作用就是给容器中添加组件, 追踪 AspectJAutoProxyRegistrar
类的方法,发现有这么一段代码:
这段代码的作用就是先看一下容器中有没有
public static final String AUTO_PROXY_CREATOR_BEAN_NAME =
"org.springframework.aop.config.internalAutoProxyCreator";
internalAutoProxyCreator
这个类,同时我们看到在调用上述方法的时候,传入了一个类型:
该类型是 AspectJAwareAdvisorAutoProxyCreator
实体类,看一下该类的继承结构。
可以看到该类实现了一个接口,就是 BeanPostProcessor
接口,他是一个 后置处理器 。这个接口是 Bean 生命周期相关的接口。
所以我们要重点分析一下该类的执行顺序,接下来 打断点调试 之前举的计算器的例子。
从容器启动开始分析:
@Test
public void test12() {
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfigOfAOP.class);
MathCalculator calculator = context.getBean(MathCalculator.class);
System.out.println(calculator.div(4, 2));
}
首先传入配置类,创建 IOC 容器;然后注册配置类,调用 refresh
方法刷新容器;
使用 registerBeanPostProcessors(beanFactory)
注册 Bean 的后置处理器,来拦截 Bean 的创建
BeanPostProcessor
BeanPostProcessor
PriorityOrdered
接口的 BeanPostProcessor
Ordered
接口的 BeanPostProcessor
Ordered
接口的 BeanPostProcessor
registerBeanPostProcessors
执行,注册 BeanPostProcessor
,实际上就是创建 BeanPostProcessor
对象,保存在容器中。
创建 org.springframework.aop.config.internalAutoProxyCreator
的 BeanPostProcessor
,它的类型是 AnnotationAwareAspectJAutoProxyCreator
。1. 首先创建 Bean 的实例 `instanceWrapper = createBeanInstance(beanName, mbd, args)`
2. 然后给属性赋值 `populateBean(beanName, mbd, instanceWrapper)`
3. 最后初始化 Bean `exposedObject = initializeBean(beanName, exposedObject, mbd)`
- `invokeAwareMethods(beanName, bean)` 初始化 Aware 接口的方法回调;
- `applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName)` 执行后置处理器的 **`postProcessBeforeInitialization`** 方法;
- `invokeInitMethods(beanName, wrappedBean, mbd)` 执行自定义初始化方法;
- `applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName)` 执行后置处理器的 **`postProcessAfterInitialization`** 方法;
4. 到此为止 `AnnotationAwareAspectJAutoProxyCreator` 类型的 BeanPostProcessor 创建成功;
BeanPostProcessor
对象之后,注册到 beanFactory
中
registerBeanPostProcessors(beanFactory, internalPostProcessors) 注册方法的实现: for (BeanPostProcessor postProcessor : postProcessors) { beanFactory.addBeanPostProcessor(postProcessor); }到此为止 AnnotationAwareAspectJAutoProxyCreator
就算是创建成功了,而它作为一个后置处理器,肯定有作用,下面分析一下他作为后置处理器做了什么事情。
注意 AnnotationAwareAspectJAutoProxyCreator
是 InstantiationAwareBeanPostProcessor
类型的后置处理器。
finishBeanFactoryInitialization(beanFactory)
完成 BeanFactory 的初始化工作
getBean、doGetBean、getSingleton
;- `createBean` 创建 Bean:`AnnotationAwareAspectJAutoProxyCreator` 会在任何 Bean 创建完成之前先尝试返回 Bean 的实例,其实就是拦截; BeanPostProcessor 是在对象创建Bean完成初始化前后调用的,而 InstantiationAwareBeanPostProcessor 是在创建Bean实例之前先尝试用后置处理器返回对象的。
- `Object bean = resolveBeforeInstantiation(beanName, mbdToUse)`,这句话的意思是希望后置处理器返回一个代理对象,如果能返回代理对象就使用,如果不能就继续;这个方法的实现就是拿到所有后置处理器,如果是 InstantiationAwareBeanPostProcessor,就执行 postProcessBeforeInstantiation 方法 bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName); if (bean != null) { bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); }
- `Object beanInstance = doCreateBean(beanName, mbdToUse, args)`,真正的去创建一个 Bean,和之前 **`3.6`** 的流程是一样的。
所以
AnnotationAwareAspectJAutoProxyCreator
会在任何 Bean 创建完成之前先尝试返回 Bean 的实例,因为他实现了InstantiationAwareBeanPostProcessor
接口,这个接口有两个方法,一个是postProcessBeforeInstantiation
,另一个是postProcessAfterInstantiation
,这两个方法是在 Bean 创建完成前后执行的,而BeanPostProcessor
接口的两个方法是在创建完成并且初始化前后调用的。
postProcessBeforeInstantiation
方法,在这一步找出需要增强的 Bean;- 判断当前 Bean 是否在 `advisedBeans` 中(它保存了所有需要增强的 Bean )
- 判断当前 Bean 是否是基础类型 `isInfrastructureClass`
或者是切面。
- 判断是否该跳过 `shouldSkip`:源码如下 @Override protected boolean shouldSkip(Class<?> beanClass, String beanName) { // TODO: Consider optimization by caching the list of the aspect names List<Advisor> candidateAdvisors = findCandidateAdvisors(); for (Advisor advisor : candidateAdvisors) { if (advisor instanceof AspectJPointcutAdvisor && ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) { return true; } } return super.shouldSkip(beanClass, beanName); }
首先获取所有候选的增强器,增强器就是切面里面的通知方法;
0 = {InstantiationModelAwarePointcutAdvisorImpl@2180} "InstantiationModelAwarePointcutAdvisor: expression pointCut(); advice method public void top.wsuo.aop.LogAspect.logStart(org.aspectj.lang.JoinPoint); perClauseKind=SINGLETON" 1 = {InstantiationModelAwarePointcutAdvisorImpl@2181} "InstantiationModelAwarePointcutAdvisor: expression pointCut(); advice method public void top.wsuo.aop.LogAspect.logEnd(org.aspectj.lang.JoinPoint); perClauseKind=SINGLETON" 2 = {InstantiationModelAwarePointcutAdvisorImpl@2182} "InstantiationModelAwarePointcutAdvisor: expression pointCut(); advice method public void top.wsuo.aop.LogAspect.logReturn(org.aspectj.lang.JoinPoint,java.lang.Object); perClauseKind=SINGLETON" 3 = {InstantiationModelAwarePointcutAdvisorImpl@2183} "InstantiationModelAwarePointcutAdvisor: expression pointCut(); advice method public void top.wsuo.aop.LogAspect.logException(org.aspectj.lang.JoinPoint,java.lang.Exception); perClauseKind=SINGLETON"
可以看到这就是我们的那几个通知方法。
只不过他把这些通知方法包装成为了一个 List<Advisor> candidateAdvisors
集合,每一个封装的通知方法的增强器是 InstantiationModelAwarePointcutAdvisor
。
这段代码的逻辑就是判断每一个增强器是否是 AspectJPointcutAdvisor
类型的,如果是返回 true ,如果不是就返回 false ;
postProcessAfterInitialization
方法,在这一步增强需要增强的 Bean:
@Override public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (!this.earlyProxyReferences.contains(cacheKey)) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }- 在 **`wrapIfNecessary`** 方法中,获取当前 Bean 的所有增强器(**通知方法**),判断是否需要包装(增强)。 // Create proxy if we have advice. Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
@Nullable protected Object[] getAdvicesAndAdvisorsForBean( Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) { List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName); if (advisors.isEmpty()) { return DO_NOT_PROXY; } return advisors.toArray(); }
那么他是
1. 保存当前 Bean 到 `advisedBeans` 中;
2. 如果当前 Bean 需要增强,创建当前 Bean 的代理对象;
- 获取所有的增强器(通知方法)
- 保存到 `proxyFactory` 中; proxyFactory.getProxy(getProxyClassLoader());
- 创建代理对象,Spring 自动决定使用哪一种动态代理 @Override public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces( Class<?> targetClass = config.getTargetClass(); if (targetClass == null) { throw new AopConfigException("TargetSource cannot determine target class: " + "Either an interface or a target is required for proxy creation."); } if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { return new JdkDynamicAopProxy(config); } return new ObjenesisCglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); } }
可以看到有两种自动代理,分别是
4.所以最后 wrapIfNecessary(bean, beanName, cacheKey)
方法就是返回了当前组件使用的 cglib
增强了的代理对象。
5.以后容器中获取到的就是这个组件的 代理对象 ,执行目标方法的时候,代理对象就会执行通知方法的流程。
我们在测试方法上面打断点,看看除法运行的时候都有啥:
观察到此时的对象已经是 cglib 代理之后的对象了,这个对象中保存了详细信息,比如所有的增强器和目标对象。
org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor
的 intercept
方法中。
本来是想执行目标的,但是代理之后就要先被拦截一下。ProxyFactory
对象获取将要执行的目标拦截器链;
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass)
拦截器链是如何获取的?
主要是在 getInterceptorsAndDynamicInterceptionAdvice
方法中。- 首先创建一个集合保存所有的拦截器,默认有 5 个List<Object> interceptorList = new ArrayList<>(advisors.length); 这 5 个包括一个默认的
- 遍历所有的增强器,将其转为 `Interceptor`。for (Advisor advisor : advisors) registry.getInterceptors(advisor)
- 将增强器转为 `List<MethodInterceptor>`:
- 如果本来就是 `MethodInterceptor`,则直接加到集合中;
- 如果不是,则使用 `AdvisorAdapter` 适配器转为 `MethodInterceptor`。 怎么转的呢,其实这里就是强转然后包装了一下,源码如下。
@Override public MethodInterceptor getInterceptor(Advisor advisor) { AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice(); return new AfterReturningAdviceInterceptor(advice); }
可以看到这就是
- 转化完成返回 `MethodInterceptor` 数组。
- 所以 **拦截器链** 就是每一个通知方法又被包装成为方法拦截器,利用 `MethodInterceptor` 的机制控制执行顺序。如果没有拦截器链,直接执行目标方法
retVal = methodProxy.invoke(target, argsToUse);
CglibMethodInvocation
对象,并调用它的 proceed
方法。
// We need to create a method invocation... retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();proceed
,所以只需要分析一下这个方法即可。- 如果没有拦截器或者是最后一个拦截器就执行目标方法if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { return invokeJoinpoint(); }
- 如果有拦截器就链式的获取每一个拦截器,拦截器执行 `invoke` 方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行。这里的返回值是还是拦截器,传入的是这个拦截器本身,每次调用都会减少一个长度,并且改变当前的拦截器,所以执行顺序是栈式的结构。 return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
- 首先执行到 `interceptorOrInterceptionAdvice` 的实现类 **`ExposeInvocationInterceptor`**,就是方法本身;
跟进去执行的是 org.springframework.aop.interceptor.ExposeInvocationInterceptor
的 invoke
方法,该方法的实现如下:
@Override public Object invoke(MethodInvocation mi) throws Throwable { MethodInvocation oldInvocation = invocation.get(); invocation.set(mi); try { return mi.proceed(); } finally { invocation.set(oldInvocation); } }
这一块代码的核心业务是放在 finally
中的,所以肯定会执行,下面接着跟进去 proceed
方法:
- 这个时候再次来到 proceed 方法,此时的下标变为 0,执行到 **`AspectJAfterThrowingAdvice`**,即异常通知;
跟进去执行 org.springframework.aop.aspectj.AspectJAfterThrowingAdvice
的 invoke
方法,该方法的实现如下:
@Override public Object invoke(MethodInvocation mi) throws Throwable { try { return mi.proceed(); } catch (Throwable ex) { if (shouldInvokeOnThrowing(ex)) { invokeAdviceMethod(getJoinPointMatch(), null, ex); } throw ex; } }
注意到这里有异常的捕捉,所以异常发生时是在这里处理的,没有异常则不会执行,继续跟进 proceed
方法。
- 这个时候再次来到 proceed 方法,此时的下标变为 1,执行到 **`AfterReturningAdviceInterceptor`**,即返回(最终)通知;
跟进去执行 org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor
的 invoke
方法,该方法的实现如下:
@Override public Object invoke(MethodInvocation mi) throws Throwable { Object retVal = mi.proceed(); this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis()); return retVal; }
就是先执行其他的,然后执行返回通知的内容,继续跟进 proceed
方法。
- 这个时候再次来到 proceed 方法,此时的下标变为 2,执行到 **`AspectJAfterAdvice`**,即后置通知;
跟进去执行 org.springframework.aop.aspectj.AspectJAfterAdvice
的 invoke
方法,该方法的实现如下:
@Override public Object invoke(MethodInvocation mi) throws Throwable { try { return mi.proceed(); } finally { invokeAdviceMethod(getJoinPointMatch(), null, null); } }
就是先执行后面的前置通知,然后执行后置通知的内容,继续跟进 proceed
方法。
- 这个时候再次来到 proceed 方法,此时的下标变为 3,执行到 **`MethodBeforeAdviceInterceptor`**,即前置通知;
跟进去执行 org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor
的 invoke
方法,该方法的实现如下:
@Override public Object invoke(MethodInvocation mi) throws Throwable { this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis()); return mi.proceed(); }
这块代码是先执行自己的业务,再往下传递,我们继续跟进 proceed
方法:
这个时候再次来到 proceed 方法,此时的下标变为 4,还是执行 MethodBeforeAdviceInterceptor
但是现在已经开始回溯了,因为方法都已经入栈了,此时执行 前置通知 中的方法,控制台输出如下:
然后执行 后置通知 :
执行 返回通知 :
最后所有的通知执行完毕,由于没有异常产生,所以没有执行异常通知: