文章目录
1. 源码分析aop的切面@Aspect解析
前文分析spring ioc源码时,了解到,spring 解析内部注解,通常通过后置处理器进行解析,上文提到 aop注入的后置处理器AnnotationAwareAspectJAutoProxyCreator。在进行切面解析时,其实存在一些父子后置处理器之间的调用关系。
通过源码了解到,@Aspect解析的起点在spring bean的实例化之前AbstractAutoProxyCreator#postProcessBeforeInstantiation方法中。
该方法主要作用是获取spring容器中的切面类,同时将切面和通知存在缓存中,调用链本人通过debug形式打印出来了。
核心代码AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors,在该方法中找到所有的通知并返回。
protected List<Advisor> findCandidateAdvisors() {
// Add all the Spring advisors found according to superclass rules.
//根据父类的规则,找到所有的spring 通知
List<Advisor> advisors = super.findCandidateAdvisors();
// Build Advisors for all AspectJ aspects in the bean factory.
//获取Bean工厂中所有AspectJ切面的通知
if (this.aspectJAdvisorsBuilder != null) {
//找到所有的通知,并放入到aspectJAdvisorsBuilder中缓存起来,并返回
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
return advisors;
}
aspectJAdvisorsBuilder这个spring内部使用的建造者模式中,会缓存所用容器中的切面,切点,通知等信息。以便后期需要代理类动态增强时进行织入。
2. 源码分析aop使用jdk代理还是cglib动态代理?
通过源码了解到,spring在实例化bean之后的后置处理器中AbstractAutoProxyCreator#postProcessAfterInitialization方法中,判断是否需要去创建代理对象,找到动态代理创建时需要的通知,并进行代理的创建。
我将本文的重点放在了动态代理的创建,至于如何根据切点找到代理类的通知在此不进行详细描述,这里是创建代理类的调用链。
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
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);
}
//cgilb动态代理
return new ObjenesisCglibAopProxy(config);
}
else {
//jdk动态代理
return new JdkDynamicAopProxy(config);
}
}
在本步骤中spring aop创建了动态代理的实例,并且作为bean的实例化对象。那么spring aop具体是如何创建的呢?
3. spring中jdk动态代理和aop动态代理
jdk动态代理:
原理: 利用反射机制生成一个实现代理接口的实现类,在调用具体方法前调用InvokeHandler来处理。
jdk动态代理关键点
1.目标类(被代理类)必须实现接口。
2.自定义InvocationHandler 类实现 InvocationHandler 接口的invoke(),在invoke方法中通过反射完成代理对象的方法调用。在该过程中可以增强我们的目标对象方法调用,即织入我们自定义的逻辑。
关键增强的逻辑一般都在 实现该接口的invoke()方法中。
来看看spring的jdk动态代理源码,分析粒度不大,先简单介绍
熟悉jdk动态代理的同学就知道,jdk动态代理核心就在handler的invoke方法中,即JdkDynamicAopProxy#invoke,该类实现了InvocationHandler 接口的invoke()方法。
通过代码可以看到动态织入通知的起点执行逻辑,
注意: 本文最后会有一张时许图详细描述ReflectiveMethodInvocation().proceed()方法中,通知拦截器链的织入过程。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Object target = null;
try {
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
return equals(args[0]);
}
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
return hashCode();
}
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return AopProxyUtils.ultimateTargetClass(this.advised);
}
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// Service invocations on ProxyConfig with the proxy config...
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal;
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// Get the interception chain for this method.
//获取此方法的拦截链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) {
// 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 = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// We need to create a method invocation...
//我们需要创建一个方法调用...
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
//通过拦截器链进入连接点,该处逻辑与cglib相似,会执行方法拦截intercep。
retVal = invocation.proceed();
}
// Massage return value if necessary.
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target &&
returnType != Object.class && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// Special case: it returned "this" and the return type of the method
// is type-compatible. Note that we can't help if the target sets
// a reference to itself in another returned object.
retVal = proxy;
}
else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
// Must have come from TargetSource.
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
cglib动态代理:
原理: 是利用asm,加载代理类的class文件,通过修改其字节码生成子类,覆盖父类的方法,来进行处理。(注意 :目标类或方法最好不要声明成final )。
cglib动态代理关键点
1.目标类(被代理类)不需要实现接口。
2.通过增强器类Enhancer创建代理对象,设置织入回调的拦截器链setCallbackFilter,(前置通知等等都是通过此功能实现)。
3.spring aop中有一个关键的内部类,定义了动态通知的拦截CglibAopProxy.DynamicAdvisedInterceptor。
4.spring 的方法拦截链中的每个拦截器都实现了MethodInterceptor接口。
Cglib中的拦截器有点多,可以看一下CglibAopProxy的内部类带有Interceptor的很多。
拦截器Interceptor很多,但是我们的关注点应该放在动态织入通知的拦截器中DynamicAdvisedInterceptor的invoke方法,看该方法中的方法拦截,其中和jdk动态代理织入的逻辑相似。都调用了new CglibMethodInvocation().proceed()方法。那么通知织入的核心其实在该该类的proceed方法中。
@Override
@Nullable
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Object target = null;
TargetSource targetSource = this.advised.getTargetSource();
try {
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
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...
//我们需要创建一个方法调用。该方法调用的proceed,就是一个通知动态织入到代理对象的过程
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
那就看一下ReflectiveMethodInvocation#proceed是如何执行的
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
//我们从索引-1开始并提前增加,直至将所有同通知拦截执行完毕。
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;
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
if (dm.methodMatcher.matches(this.method, 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);
}
}
将断点调制该处,我们可以看到通知拦截器链的执行顺序:
afteThrowingr -> afterReturning -> after ->afterAround -> MethodBefore.
至于为什么是这个执行顺序,肯定是有讲究的,但是本人并没有继续深入,有兴趣可以继续追踪一下源码。
ok,那么它是如何执行通知的链式调用的呢,代码看了,看一张时序图,思路会更加清晰。
4. Advice通知MethodIntercept方法拦截织入代理时序图
个人觉得,spring的aop实现整体流程涉及到的点很多,但是大体思路并不算很复杂。真正的基础和复杂点是jdk和cgilib如何在jvm运行时动态生成代理类。
上一篇:6、spring核心源码解析之aop概况流程1
[下一篇:8.未完待续]