Spring源码学习(十九)---SpringAop_JDK和CGLIB动态代理调用过程
作者:互联网
这里主要是使用org.springframework:spring-beans:5.2.0.RELEASE进行分析
文章目录
1. 查看源码相关的快捷键
快捷键 | 作用 |
---|---|
Ctrl + Shift+i | 出现类似于预览的小窗口 |
Ctrl + Enter | (接上步)完全打开源码 |
Ctrl + 鼠标左键 | 一步到位打开源码 = Ctrl + Shift+i –>Ctrl + Enter |
Alt+7 | 查看类的有什么方法 |
ctrl+f12 | 查看继承方法 |
ctrl+h | 查看接口的实现类 |
alt+7 | 查看类的有什么方法 |
2下shift | 全局搜索整个项目 |
一. Spring AOP JDK动态代理调用过程分析
1. JDK动态代理:
具体实现原理:
1、通过实现InvocationHandlet接口创建自己的调用处理器
2、通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理
3、通过反射机制获取动态代理类的构造函数,其唯一参数类型就是调用处理器接口类型
4、通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数参入
JDK动态代理是面向接口的代理模式,如果被代理目标没有接口那么Spring也无能为力,
Spring通过java的反射机制生产被代理接口的新的匿名实现类,重写了其中AOP的增强方法。
@Override
@Nullable
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 {
// 1、处理equals方法,如果接口中没有定义equals而在实现类中覆盖了equals方法,那么该equals方法不会被增强
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
return equals(args[0]);
}
// 2、处理hashCode方法,如果接口中没有定义hashCode而在实现类中覆盖了hashCode方法,那么该hashCode方法不会被增强
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
return hashCode();
}
//3、如果目标对象是DecoratingProxy类型,则返回目标对象的最终对象类型DecoratingProxy接口只有一个getDecoratedClass方法,用于返回目标对象的最终对象类型
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return AopProxyUtils.ultimateTargetClass(this.advised);
}
// 4、如果目标对象是Advice类型,则直接使用反射进行调用
// opaque-->标记是否需要阻止通过该配置创建的代理对象转换为Advised类型,默认值为false,表示代理对象可以被转换为Advised类型
// method.getDeclaringClass().isInterface()-->目标对象是接口
// method.getDeclaringClass().isAssignableFrom(Advised.class)-->
// 是用来判断一个类Class1和另一个类Class2是否相同或者Class1类是不是Class2的父类
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;
// 5、解决目标对象内部自我调用无法实施切面增强,在这里暴露代理
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);
// 6、获取当前方法的拦截器链,并执行调用
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// 检测是否拦截器链是否为空,如果拦截器链为空,那么直接通过反射调用目标对象的方法,避免创建MethodInvocation
if (chain.isEmpty()) {
// 通过反射直接调用目标对象的方法
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// 创建MethodInvocation对象并调用proceed方法,拦截器链被封装到了invocation中
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// 调用拦截器链
retVal = invocation.proceed();
}
// 7、返回结果
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);
}
}
}
retVal = invocation.proceed()方法,即拦截器链的调用过程:
@Override
@Nullable
public Object proceed() throws Throwable {
//currentInterceptorIndex维护了一个计数器,该计数器从-1开始,当计数器值等于拦截方法长度减一时,
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
// 动态匹配增强
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
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 {
return proceed();
}
}
// 静态增强
else {
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
二. Spring AOP CGLIB动态代理调用过程分析
2、CGLib动态代理
CGLib是一个强大、高性能的Code生产类库,可以实现运行期动态扩展java类,Spring在运行期间通过 CGlib继承要被动态代理的类,重写父类的方法,实现AOP面向切面编程呢。
DynamicAdvisedInterceptor类的intercept()方法分析
:
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;
}
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// 获取当前方法的拦截器链,并执行调用
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// 检测是否拦截器链是否为空,如果拦截器链为空,那么直接通过反射调用目标对象的方法,避免创建MethodInvocation
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// 通过反射直接调用目标对象的方法
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = methodProxy.invoke(target, argsToUse);
}
else {
//拦截器链的调用方法proceed与JDK动态代理的proceed是一样的。
// 创建CglibMethodInvocation对象并调用proceed方法,拦截器链被封装到了retVal中
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);
}
}
}
三. JDK动态代理和CGLib动态代理两者对比:
-
JDK动态代理是面向接口,在创建代理实现类时比CGLib要快,创建代理速度快。
-
CGLib动态代理是通过字节码底层继承要代理类来实现(如果被代理类被final关键字所修饰,那么抱歉会失败),在创建代理这一块没有JDK动态代理快,但是运行速度比JDK动态代理要快。
使用注意:
-
如果要被代理的对象是个实现类,那么Spring会使用JDK动态代理来完成操作(Spirng默认采用JDK动态代理实现机制)
-
如果要被代理的对象不是个实现类那么,Spring会强制使用CGLib来实现动态代理。
标签:target,JDK,Object,代理,---,源码,动态,retVal,method 来源: https://blog.csdn.net/weixin_45480785/article/details/113819851