AOP 的使用分为三步走:
@Aspect
@Pointcut
、@Before
~~~AOP
模式;@EnableAspectJAutoProxy
使用 aop
相关的注解必须先导入依赖:
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aspectsartifactId>
<version>5.1.2.RELEASEversion>
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
优先注册实现了 PriorityOrdered
接口的 BeanPostProcessor
再注册实现了 Ordered
接口的 BeanPostProcessor
之后注册没实现 Ordered
接口的 BeanPostProcessor
最后 registerBeanPostProcessors
执行,注册 BeanPostProcessor
,实际上就是创建 BeanPostProcessor
对象,保存在容器中。
创建 org.springframework.aop.config.internalAutoProxyCreator
的 BeanPostProcessor
,它的类型是 AnnotationAwareAspectJAutoProxyCreator
。
首先创建 Bean 的实例 instanceWrapper = createBeanInstance(beanName, mbd, args)
然后给属性赋值 populateBean(beanName, mbd, instanceWrapper)
最后初始化 Bean exposedObject = initializeBean(beanName, exposedObject, mbd)
invokeAwareMethods(beanName, bean)
初始化 Aware 接口的方法回调;applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName)
执行后置处理器的 postProcessBeforeInitialization
方法;invokeInitMethods(beanName, wrappedBean, mbd)
执行自定义初始化方法;applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName)
执行后置处理器的 postProcessAfterInitialization
方法;到此为止 AnnotationAwareAspectJAutoProxyCreator
类型的 BeanPostProcessor 创建成功;
创建完 BeanPostProcessor
对象之后,注册到 beanFactory
中
registerBeanPostProcessors(beanFactory, internalPostProcessors)
注册方法的实现:
for (BeanPostProcessor postProcessor : postProcessors) {
beanFactory.addBeanPostProcessor(postProcessor);
}
到此为止 AnnotationAwareAspectJAutoProxyCreator
就算是创建成功了,而它作为一个后置处理器,肯定有作用,下面分析一下他作为后置处理器做了什么事情。
注意 AnnotationAwareAspectJAutoProxyCreator
是 InstantiationAwareBeanPostProcessor
类型的后置处理器。
finishBeanFactoryInitialization(beanFactory)
完成 BeanFactory 的初始化工作
getBean、doGetBean、getSingleton
;先从缓存中获取当前 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实例之前先尝试用后置处理器返回对象的。
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
接口的两个方法是在创建完成并且初始化前后调用的。
在每一个 Bean 创建之前调用 postProcessBeforeInstantiation
方法,在这一步找出需要增强的 Bean;
判断当前 Bean 是否在 advisedBeans
中(它保存了所有需要增强的 Bean )
判断是否该跳过 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
集合,每一个封装的通知方法的增强器是 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();
}
那么他是 怎么找的增强器 呢 ?我们继续查看方法调用。
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
List<Advisor> candidateAdvisors = findCandidateAdvisors();
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
这一步是找到候选的所有增强器,即哪些通知方法是需要切入当前 Bean 方法的。
然后下面的方法 findAdvisorsThatCanApply
是获取到能在当前 Bean 使用的增强器,它使用了 canApply
方法判断。
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
if (advisor instanceof IntroductionAdvisor) {
return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
}
else if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pca = (PointcutAdvisor) advisor;
return canApply(pca.getPointcut(), targetClass, hasIntroductions);
}
else {
// It doesn't have a pointcut so we assume it applies.
return true;
}
}
最后是给这些增强器排序。
advisedBeans
中;获取所有的增强器(通知方法)
保存到 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);
}
}
可以看到有两种自动代理,分别是 JdkDynamicAopProxy
和 ObjenesisCglibAopProxy
。
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
方法中。
List<Object> interceptorList = new ArrayList<>(advisors.length);
这 5 个包括一个默认的 org.springframework.aop.interceptor.ExposeInvocationInterceptor.ADVISOR
和 4 个增强器。Interceptor
。for (Advisor advisor : advisors)
registry.getInterceptors(advisor)
List
:
如果本来就是 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);
}
}
这个时候再次来到 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 方法,此时的下标变为 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 方法,此时的下标变为 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 方法,此时的下标变为 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
但是现在已经开始回溯了,因为方法都已经入栈了,此时执行 前置通知 中的方法,控制台输出如下:
然后执行 后置通知 :
执行 返回通知 :
最后所有的通知执行完毕,由于没有异常产生,所以没有执行异常通知: