一、AOP 的基本使用
AOP 的使用分为三步走:
- 将业务逻辑组件和切面类都加入到容器中:告诉 Spring 哪个是切面类;
@Aspect
- 在切入类上的每一个通知方法上标注通知注解:告诉 Spring 何时何地运行(切入点表达式)
@Pointcut
、@Before
~~~ - 在配置类上开启基于注解的
AOP
模式;@EnableAspectJAutoProxy
使用 aop
相关的注解必须先导入依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
下面以一个计算器的例子来介绍 AOP 的基本使用:
1、待增强类
这是一个简单的计算器类,为了能够演示异常,所以创建了一个有除法的方法。
代码语言:javascript复制public class MathCalculator {
/**
* 除法
*
* @param i 被除数
* @param j 除数
* @return 返回运算结果
*/
public int div(int i, int j) {
return i / j;
}
}
2、增强类
我们想通过 AOP 实现记录除法运行的日志信息,所以新建一个 Log 类。
代码语言:javascript复制@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() "}");
}
}
3、配置类
最后在配置类上开启注解版 AOP,同时注册组件到容器中。
代码语言:javascript复制@Configuration
// Spring 中有很多 EnableXXX 代表开启某一项功能: 取代了配置
@EnableAspectJAutoProxy
public class MainConfigOfAOP {
@Bean
public MathCalculator mathCalculator() {
return new MathCalculator();
}
@Bean
public LogAspect logAspect() {
return new LogAspect();
}
}
4、测试
测试及运行结果。
代码语言:javascript复制@Test
public void test12() {
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfigOfAOP.class);
MathCalculator calculator = context.getBean(MathCalculator.class);
System.out.println(calculator.div(4, 2));
}
二、注解 AOP 的实现原理
1、@EnableAspectJAutoProxy
整个 AOP 要想起作用,必须加上 @EnableAspectJAutoProxy
注解,这个注解的作用是什么呢?
点进去该注解:
代码语言:javascript复制@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
看到了要导入一个 AspectJAutoProxyRegistrar
类组件,它继承自一个接口 ImportBeanDefinitionRegistrar
,这个接口我们之前讲过,他是添加自定义组件的接口,在这里:https://blog.csdn.net/weixin_43941364/article/details/107243459。
这说明 @EnableAspectJAutoProxy
注解的作用就是给容器中添加组件, 追踪 AspectJAutoProxyRegistrar
类的方法,发现有这么一段代码:
这段代码的作用就是先看一下容器中有没有
代码语言:javascript复制public static final String AUTO_PROXY_CREATOR_BEAN_NAME =
"org.springframework.aop.config.internalAutoProxyCreator";
internalAutoProxyCreator
这个类,同时我们看到在调用上述方法的时候,传入了一个类型:
该类型是 AspectJAwareAdvisorAutoProxyCreator
实体类,看一下该类的继承结构。
2、AspectJAwareAdvisorAutoProxyCreator
可以看到该类实现了一个接口,就是 BeanPostProcessor
接口,他是一个 后置处理器 。这个接口是 Bean 生命周期相关的接口。
所以我们要重点分析一下该类的执行顺序,接下来 打断点调试 之前举的计算器的例子。
3、容器的创建流程
从容器启动开始分析:
代码语言:javascript复制@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
方法刷新容器;
3.1、注册后置处理器
使用 registerBeanPostProcessors(beanFactory)
注册 Bean 的后置处理器,来拦截 Bean 的创建
- 先获取 IOC 容器中已经定义了的需要创建对象的所有
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)`
代码语言:txt复制2. 然后给属性赋值 `populateBean(beanName, mbd, instanceWrapper)`
代码语言:txt复制3. 最后初始化 Bean `exposedObject = initializeBean(beanName, exposedObject, mbd)`
代码语言:txt复制 - `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
类型的后置处理器。
3.2、初始化剩下的单实例 Bean
finishBeanFactoryInitialization(beanFactory)
完成 BeanFactory 的初始化工作
- 遍历获取容器中所有的 Bean,依次创建对象:
getBean、doGetBean、getSingleton
; - 创建 Bean(业务逻辑组件和切面组件);
- 先从缓存中获取当前 Bean,如果能获取到,说明 Bean 是之前创建过的,直接使用,否则再创建; 先从缓存中检查有没有这个 Bean // Eagerly check singleton cache for manually registered singletons. Object sharedInstance = getSingleton(beanName); 如果 他等于 null,才会继续执行下面的方法 sharedInstance = getSingleton(beanName, () -> { 只要创建好的 Bean 都会被缓存起来,这也是 Spring 保证单实例 Bean 的实现原理。
- `createBean` 创建 Bean:`AnnotationAwareAspectJAutoProxyCreator` 会在任何 Bean 创建完成之前先尝试返回 Bean 的实例,其实就是拦截; BeanPostProcessor 是在对象创建Bean完成初始化前后调用的,而 InstantiationAwareBeanPostProcessor 是在创建Bean实例之前先尝试用后置处理器返回对象的。
代码语言:txt复制 - `Object bean = resolveBeforeInstantiation(beanName, mbdToUse)`,这句话的意思是希望后置处理器返回一个代理对象,如果能返回代理对象就使用,如果不能就继续;这个方法的实现就是拿到所有后置处理器,如果是 InstantiationAwareBeanPostProcessor,就执行 postProcessBeforeInstantiation 方法 bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName); if (bean != null) { bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); }
代码语言:txt复制 - `Object beanInstance = doCreateBean(beanName, mbdToUse, args)`,真正的去创建一个 Bean,和之前 **`3.6`** 的流程是一样的。
所以
AnnotationAwareAspectJAutoProxyCreator
会在任何 Bean 创建完成之前先尝试返回 Bean 的实例,因为他实现了InstantiationAwareBeanPostProcessor
接口,这个接口有两个方法,一个是postProcessBeforeInstantiation
,另一个是postProcessAfterInstantiation
,这两个方法是在 Bean 创建完成前后执行的,而BeanPostProcessor
接口的两个方法是在创建完成并且初始化前后调用的。
- 在每一个 Bean 创建之前调用
postProcessBeforeInstantiation
方法,在这一步找出需要增强的 Bean;
- 判断当前 Bean 是否在 `advisedBeans` 中(它保存了所有需要增强的 Bean )
代码语言:txt复制- 判断当前 Bean 是否是基础类型 `isInfrastructureClass`
或者是切面。
代码语言:txt复制- 判断是否该跳过 `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 ;
- 在 Bean 创建之后调用
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(); }
那么他是
代码语言:txt复制1. 保存当前 Bean 到 `advisedBeans` 中;
2. 如果当前 Bean 需要增强,创建当前 Bean 的代理对象;
- 获取所有的增强器(通知方法)
代码语言:txt复制 - 保存到 `proxyFactory` 中; proxyFactory.getProxy(getProxyClassLoader());
代码语言:txt复制 - 创建代理对象,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.以后容器中获取到的就是这个组件的 代理对象 ,执行目标方法的时候,代理对象就会执行通知方法的流程。
3.3、执行目标方法
我们在测试方法上面打断点,看看除法运行的时候都有啥:
观察到此时的对象已经是 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 个包括一个默认的
代码语言:txt复制- 遍历所有的增强器,将其转为 `Interceptor`。for (Advisor advisor : advisors) registry.getInterceptors(advisor)
代码语言:txt复制- 将增强器转为 `List<MethodInterceptor>`:
- 如果本来就是 `MethodInterceptor`,则直接加到集合中;
代码语言:txt复制 - 如果不是,则使用 `AdvisorAdapter` 适配器转为 `MethodInterceptor`。 怎么转的呢,其实这里就是强转然后包装了一下,源码如下。
@Override public MethodInterceptor getInterceptor(Advisor advisor) { AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice(); return new AfterReturningAdviceInterceptor(advice); }
可以看到这就是
代码语言:txt复制 - 转化完成返回 `MethodInterceptor` 数组。
代码语言:txt复制- 所以 **拦截器链** 就是每一个通知方法又被包装成为方法拦截器,利用 `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(); }
代码语言:txt复制- 如果有拦截器就链式的获取每一个拦截器,拦截器执行 `invoke` 方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行。这里的返回值是还是拦截器,传入的是这个拦截器本身,每次调用都会减少一个长度,并且改变当前的拦截器,所以执行顺序是栈式的结构。 return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
代码语言:txt复制 - 首先执行到 `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
但是现在已经开始回溯了,因为方法都已经入栈了,此时执行 前置通知 中的方法,控制台输出如下:
然后执行 后置通知 :
执行 返回通知 :
最后所有的通知执行完毕,由于没有异常产生,所以没有执行异常通知: