1、先简单的概述下
关于切面的概念可以参考下这篇文章,已经解释的很好了: https://mp.weixin.qq.com/s/BYxXMAEIfOSCCx1KanP9lg
但是上面流于概念的讲解,少了一些具体源码上的分析。
下面按照我的理解,说明一下吧。
1.1 实现一个切面功能的建议代码
定义一个功能类
@Component
public class MathCalculator {
public int divile(int a, int b) {
// step1 : copy refence
// step2 : patse whithout format : com.wy.aop.MathCalculator#divile
return a / b;
}
}
声明一个切面:
@Aspect
@Component
public class LogAspect {
@Pointcut("execution(public int com.wy.aop.MathCalculator.divile(..))")
public void pointCut() {
}
@Before("pointCut()")
public void logStart(JoinPoint joinPoint) {
System.out
.println(joinPoint.getSignature() + " Before 除法运行:方法名:" + "参数表:" + Arrays.asList(joinPoint.getArgs()));
}
@After("com.wy.aop.LogAspect.pointCut()")
public void logEnd(JoinPoint joinPoint) {
System.out.println(joinPoint.getSignature() + "After 除法结束:");
}
@AfterReturning(value = "pointCut()", returning = "result")
public void logReturn(JoinPoint joinPoint, Object result) {
System.out.println(joinPoint.getSignature() + "AfterReturning 除法返回:" + result);
}
@AfterThrowing(value = "pointCut()", throwing = "exception")
public void logExp(JoinPoint joinPoint, Exception exception) {
System.out.println(joinPoint.getSignature() + "AfterThrowing 除法异常:" + exception.getMessage());
}
}
配置配置类
@EnableAspectJAutoProxy
@ComponentScan(value = "com.wy.aop")
@Configuration
public class MainConfigOfAOP {
}
1.2 简单分析
具体可以分为三大步
1、通过 EnableAspectJAutoProxy 的注解:给IOC容器中注册一个组件:AnnotationAwareAspectJAutoProxyCreator
参考另外一篇文章:六. 容器的创建流程
AnnotationAwareAspectJAutoProxyCreator 是一个后置处理器。
具体实现类名 :org.springframework.aop.config.internalAutoProxyCreator
internalAutoProxyCreator=AnnotationAwareAspectJAutoProxyCreator
2、初始化 bean 的时候,对目标方法获取增强的代理类:CglibAopProxy
finishBeanFactoryInitialization() 初始化剩下的单实例bean
- 1)、创建业务逻辑组件和切面组件
- 2)、AnnotationAwareAspectJAutoProxyCreator拦截组件的创建过程
- 3)、组件创建完之后,判断组件是否需要增强
是:切面的通知方法,包装成增强器(Advisor);给业务逻辑组件创建一个代理对象(cglib);
把切面的通知方法,包装成增强器(Advisor)
给业务逻辑组件创建一个代理对象(CglibAopProxy)
3、使用 代理对象 CglibAopProxy 增强类,执行目标方法,触发拦截器链
通过 CglibAopProxy.intercept( ) 拦截。
执行顺序:
具体的代码流程:下面再论述。
2. @EnableAspectJAutoProxy
AOP 的核心理解:
- 看给容器中注册了什么组件?
- 这个组件什么时候工作?
- 这个组件的功能是什么?
2.1 @EnableAspectJAutoProxy 声明了什么?
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
}
通过这个注解,从 IOC 容器中定义一个组件 : org.springframework.aop.config.internalAutoProxyCreator
具体的 bean 是 : AnnotationAwareAspectJAutoProxyCreator
2.2 internalAutoProxyCreator 是什么?
先看看他的实现继承结构
主要关注: 它实现了 BeanPostProcessor (在bean初始化完成前后做事情)、自动装配BeanFactory (Aware)
3. AnnotationAwareAspectJAutpProxyCreator 做了什么?
此节请结合 六、容器的创建流程
3.1 postProcessBeforeInstantiation
关键源码如下 :
if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
这里主要关心 业务类(MathCalculator) 和 切面类(LogAspect)
大致步骤如下:
1、判断当前 bean 是否在 advisedBeans 中(保存了所有需要增强bean)
2、判断当前bean是否是基础类型的Advice、Pointcut、Advisor、AopInfrastructureBean,
或者是否是切面(@Aspect)
3、是否需要跳过
如果包含 则直接 ruturn
如果都不包含,则进入后续,做一些简单的处理
把所有的切面对应的增强信息 包装成了 Advisor
InstantiationModelAwarePointcutAdvisor: expression [pointCut()]; advice method [public void com.wy.aop.LogAspect.logStart(org.aspectj.lang.JoinPoint)]; perClauseKind=SINGLETON
增强器是 : InstantiationModelAwarePointcutAdvisor
所以 postProcessBeforeInstantiation 主要做一些前置工作。
3.2 postProcessAfterInitialization
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
主要步骤: wrapIfNecessary
总结:经过这一步骤。如果这个bean 有canApply 的Adivse 。就会得到一个增强类的代理对象 CglibAopProxy
return wrapIfNecessary(bean, beanName, cacheKey);
// Create proxy if we have advice.
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
1、获取当前 bean 的所有增强器
返回的 specificInterceptors 数组
- 1、找到候选的所有的增强器(找哪些通知方法是需要切入当前bean方法的)
- 2、获取到能在bean使用的增强器。
- 3、给增强器排序
具体的可以 debug 看下源码
2. 保存当前 bean 在 advisedBeans
this.advisedBeans.put(cacheKey, Boolean.TRUE);
标识当前 bean 已经被增强过
3. 创建当前 bean 的代理对象
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
给容器中返回当前组件使用cglib增强了的代理对象;
之后,容器中获得到的就是这个组件(bean:MathCalculator)的代理对象,执行目标方法的时候,代理对象就会执行通知方法的流程;也就是切面的执行流程
所以保护 Advice 的 bean 的生命周期是:
4. 使用增强器
在调用目标方法的时候 :
MathCalculator mathCalculator = ctx.getBean(MathCalculator.class);
// new MathCalculator() 不会起作用 因为没有使用容器 spring容器管理的
int divile = mathCalculator.divile(4, 2);
此时的目标方法 是 : CglibAopProxy 增强后的代理对象。 里面包括 五个增强器(Advice)
保存了通知方法的信息。
4.1 使用拦截器 intercept
通过 CglibAopProxy.intercept() 拦截目标方法的执行
首先获取 Interception 链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// Check whether we only have one InvokerInterceptor: that is,
// no real advice, but just reflective invocation of the target.
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// We can skip creating a MethodInvocation: just invoke the target directly.
// Note that the final invoker must be an InvokerInterceptor, so we know
// it does nothing but a reflective operation on the target, and no hot
// swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = methodProxy.invoke(target, argsToUse);
}
else {
// We need to create a method invocation...
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
拦截器链。 就是多个 intercept 组成的链式结构
核心 是1. 如何获取的拦截器连
2.拦截器链是如何工作的
4.2 如何获取拦截器链
源码类 : org/springframework/aop/framework/DefaultAdvisorChainFactory.java
this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(this, method, targetClass);
遍历所有的增强器转换为 MethodInterceptor
获得 list 就是拦截器链 chain
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
最终目的 : 获取CglibMethodInvocation,调用 proceed() 方法
就是触发拦截器链的触发过程。
4.3 拦截器链的触发过程
执行 实现类的方法
int divile = mathCalculator.divile(4, 2);
源码如下:
@Override
@Nullable
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
return proceed();
}
}
else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
拦截器链的触发过程;
- 1)、如果没有拦截器执行执行目标方法,或者执行到了最后一个拦截器 。 执行目标方法;
- 2)、链式获取每一个拦截器,拦截器执行invoke方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行;
拦截器链的机制,保证通知方法与目标方法的执行顺序;
为了减少企业,先看一下调用的流程图
这个设计的也太经典了
对应的调用链:
执行代码:
还没有执行方法前。 依次调用所有的Aspect 的拦截器
执行最后一个前置的时候
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
return mi.proceed();
}
然后执行目标方法 : invokeJoinpoint()
然后执行
try {
return mi.proceed();
}
finally {
// 返回上一次方法
// 每一个上级方法 Advise 都有自己单独做的事情
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
每一个拦截器等待下一个拦截器 执行完之后 返回在执行
依次是
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
Object retVal = mi.proceed();
this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
return retVal;
}
如果抛出异常:
try {
return mi.proceed();
}
catch (Throwable ex) {
if (shouldInvokeOnThrowing(ex)) {
invokeAdviceMethod(getJoinPointMatch(), null, ex);
}
throw ex;
}
拦截器链。 保证了通知方法和目标方法的顺序, before invoke return 的顺序
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。