Spring AOP 代理的实现——AOP 源码解析(四)
一、概述
ReflectiveMethodInvocation(ProxyMethodInvocation)+ MethodInterceptor实现了具体切面逻辑,接下来看看AOP的最基本原理——动态代理,更具体的说,是JDK/CGLIB这种动态代理的技术。
思考一个经典的问题,看下面这个类(接口代码省略):
public class HelloGreet implements Greet {
@Override
public String greet(@String name) {
another(name);
return "hello " + name + " !";
}
@Override
public void another(String arg) {
System.out.println(arg);
}
}
在greet方法中,调用了自己的another方法,如果我们分别使用JDK和CGLIB对这个类的这两个方法进行代理,问题来了:
调用代理对象的greet方法,greet又调用了another方法,那么another方法会被代理么?
JDK vs CGLIB原理的区别,相信你已经耳熟能详了,就是代理原理不同
- JDK - 基于接口,代理接口方法
- CGLIB - 基于继承,只能代理非final方法
那么,答案就比较明显了
- JDK不会,JDK基于接口实现,greet调用another就是内部调用,another是不会被代理的;
- CGLIB可以,CGLIB基于继承,greet调用another,相当于多态,自然会调用到子类的another方法;
那么,如果是使用Spring提供的代理功能呢?
比较反直觉的是,即使是设置了proxy-target-class=true,强制使用CGLIB代理,内部调用时,也不会被代理。
其实原因之前的文章已经揭晓了,代理执行时,调用的ReflectiveMethodInvocation的proceed的方法,在执行完拦截器方法后,执行被代理对象的方法时,使用的是真正的被代理对象调用的,也就无法使用多态的特性。拦截器链都执行完成后,执行的invokeJoinpoint方法:
protected Object invokeJoinpoint() throws Throwable {
return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
}
那该如何让Spring AOP的代理对象,内部调用被代理到呢?别着急,后面会有回答。
二、源码解析
回过头来,我们来看下spring是如何利用JDK和CGLIB实现动态代理的。不出意外,这两种模式生成的代理对象,抽象成了一个接口AopProxy。两种实现方式,分别是:
- JdkDynamicAopProxy - JDK方式代理实现
- CglibAopProxy - CGLIB方式实现
- ObjenesisCglibAopProxy - CglibAopProxy子类,复写了创建代理的方法,spring4之后的默认使用这个来创建代理
2.1 简单工厂
不出意外,使用哪种方式创建对象,使用了简单工厂模式,接口是AopProxyFactory,默认实现是DefaultAopProxyFactory,从他的createAopProxy方法可以得到Spring的默认规则,如果被代理对象实现了接口,使用JDK代理,否则使用CGLIB,如果开启了optimize或者设置proxyTargetClass=true,则强制使用CGLIB。当然还有其他细节,比如被代理的类仅仅是个接口,或者本身就是一个JDK代理生成的类,则只能使用JDK的方式代理。
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// 开启optimize || proxyTarget属性为true || 没有用户实现除SpringProxy以外的接口
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.");
}
// 目标就是一个接口,当然就只能使用JDK了
// 或者被代理的calss是一个由JDK代理生成的类
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
}
else {
return new JdkDynamicAopProxy(config);
}
}
/**
* Determine whether the supplied {@link AdvisedSupport} has only the
* {@link org.springframework.aop.SpringProxy} interface specified
* (or no proxy interfaces specified at all).
*/
private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
Class<?>[] ifcs = config.getProxiedInterfaces();
return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
}
源码出自:DefaultAopProxyFactory
2.2 JdkDynamicAopProxy - JDK动态代理
接下来,看下JDK方式的动态代理的实现。翻开JdkDynamicAopProxy的是实现,使用JDK代理,自然实现了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 {
// 处理equals/hashCode
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();
}
// 如果是DecoratingProxy,返回最终的targetClass,也不知道这是哪种场景需要的
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return AopProxyUtils.ultimateTargetClass(this.advised);
}
// 如果被代理对象是Advised的实现类,不代理,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;
// 如果exposeProxy为true,则设置被代理对象到ThreadLocal中
//这是因为一个被代理的对象的a方法直接调用b方法的话,b是无法被代理的,把代理对象暴露出来,a方法可以这样调用b方法
//((XXXService)AopContext.currentProxy()).b();
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// 尽量晚的获取target对象,减少持有时间,防止这时一个“池”中的对象
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// 获取拦截器链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// 没有拦截器,则直接调用
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 {
// 否则创建ReflectiveMethodInvocation,调用proceed方法
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
}
// 如果返回的this,则处理成代理对象,方便链式调用
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 {
// 归还target
if (target != null && !targetSource.isStatic()) {
// Must have come from TargetSource.
targetSource.releaseTarget(target);
}
// 处理多层代理的场景
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
源码出自:JdkDynamicAopProxy
其中有几个点,需要详细介绍一下。
2.2.1 expose-proxy
aop有个expose-proxy属性配置,用于暴露代理对象,解决之前提到的被代理类内部调用的问题。expose-proxy=true,spring会在代理方法被调用的时候,暴露当前代理对象到AopContext的ThreadLocal中,这样,如果同一个类中的方法内部调用,可以从AopContext中获取代理对象,比如我们内部调用another方法,可以这样:
((XXXService)AopContext.currentProxy()).another();
所以,在调用ReflectiveMethodInvocation.proceed之前,如果exposeProxy=true,则设置当前代理对象到ThreadLocal中:
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
执行完成之后,在finally块中,设置成原来的proxy,因为可能有多个代理对象之间在调用,需要还原回去。
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
2.2.2 返回值
Spring还考虑到了返回值是this的场景,通常我们为了链式调用,比如StringBuilder的append方法,我们可以sb.append("hello").append("world"),这样不停的append下去,就是因为append返回了this,这种情况下,spring会把this,替换成代理对象,以让代理方法能够在后续的链式调用中执行。
2.2.3 拦截器链的获取
所有的拦截器,其实在构造JdkDynamicAopProxy的时候,就已经存在了,就在this.advised中,也就是在AdvisedSupport中已经存在了。而getInterceptorsAndDynamicInterceptionAdvice方法只是过滤了一下,并对结果加了缓存。为什么要过滤呢,因为构造JdkDynamicAopProxy时的拦截器链,是针对整个被代理的对象的,而不是某个方法,到具体方法执行的时候,还要再次过滤一下,只保留针对该方法的拦截器。其实也就是根据注解中定义的Pointcut去过滤。
2.2.4 TargetSource
JdkDynamicAopProxy并没有直接持有目标(被代理)对象,而是持有的TargetSource对象(在AdvisedSupport类中)。我们看下TargetSource:
public interface TargetSource extends TargetClassAware {
Class<?> getTargetClass();
boolean isStatic();
Object getTarget() throws Exception;
void releaseTarget(Object target) throws Exception;
}
为什么不直接持有target对象呢,这主要是可以代理一个“动态”对象,比如说,这个对象是一个“池化对象”,比如数据库的连接池中的连接(这也是为什么要尽量短的持有对象,且最后要归还的原因),也可以是一个具有代码热部署能力的对象。通过这个TargetSource接口的抽象,在执行的时,才通过getTarget方法获取对象,执行完成后,在finally中归还对象,就可以实现代理“动态”对象的能力。
参考: Spring Aop之Target Source详解
2.3 ObjenesisCglibAopProxy & CglibAopProxy - CGLIB代理
2.3.1 ObjenesisCglibAopProxy
上文动态代理创建中可以看到,CGLIB代理,使用的是ObjenesisCglibAopProxy,它是CglibAopProxy的子类,只是复写了createProxyClassAndInstance方法
@Override
protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
Class<?> proxyClass = enhancer.createClass();
Object proxyInstance = null;
// 尝试使用objenesis方式创建
if (objenesis.isWorthTrying()) {
try {
proxyInstance = objenesis.newInstance(proxyClass, enhancer.getUseCache());
}
catch (Throwable ex) {
logger.debug("Unable to instantiate proxy using Objenesis, " +
"falling back to regular proxy construction", ex);
}
}
if (proxyInstance == null) {
//出现异常,采用普通方式创建,省略代码。。。
}
((Factory) proxyInstance).setCallbacks(callbacks);
return proxyInstance;
}
那么objenesis究竟是个啥呢,简单来说,就是一个用于创建对象的类库,可以绕过构造方法来创建对象。猜测Spring是基于性能考虑,使用objenesis方式,创建CGLIB增加后的类的对象。关于objenesis更多细节可以参考 Java编程之创建对象——Objenesis简单使用_objenesis cglib创建对象-CSDN博客
2.3.2 CglibAopProxy
其实GCLIB代理的大部分逻辑,还是靠CglibAopProxy来实现的。CGLIB,主要要看他设置的callback,也就是MethodInterceptor的实现。 CglibAopProxy设置了好多callback,其中就有类似equals/hashCode的callback,不过最重要的,还是负责实现aop的DynamicAdvisedInterceptor,它也实现了诸如处理expose-proxy的功能,处理了返回值,最重要的逻辑,还是创建了CglibMethodInvocation,然后调用了它的proceed方法。
CglibMethodInvocation又是个什么呢,他是ReflectiveMethodInvocation的子类。proceed方法,还是调用的父类的方法,只是复写了invokeJoinpoint方法,稍微优化了一下。
CglibAopProxy的具体实现细节就不看了,和JDK都是类似的。
系列所有文章
转载自:https://juejin.cn/post/7366507899407188009