读spring源码之理解TargetSource

今天在看spring plugin的源码时候遇到了这个TargetSource。具体代码:

public OrderAwarePluginRegistry<T, S> getObject() {
   return OrderAwarePluginRegistry.create(getBeans());
}

在getBeans()里代码如下:

protected List<T> getBeans() {
   ProxyFactory factory = new ProxyFactory(List.class, targetSource);
   return (List<T>) factory.getProxy();
}

getBeans()里面的方法,很明显,是想创建代理对象,在这里的场景目标对象是业务端实现了spring plugin插件的一系列具体实现。开始很难理解的的是这一句? new ProxyFactory(List.class, targetSource);为什么是targetSource,要对目标产生代理,拿到所有的目标对象不就好了吗,也就是一系列的插件,而且还有一个地方不能理解的是:我们知道动态代理分为jdk代理和cglib代理,这里明显是jdk代理,jdk代理需要目标类实现一个多个接口,问题来了,我们的plugin并没有实现List接口,在这里为什么能被代理呢?
先看看tagetSource是什么?
TargetSource是一个接口,其定义了四个方法

  1. Class<?> getTargetClass();
  2. boolean isStatic();
  3. Object getTarget() throws Exception;
  4. void releaseTarget(Object target) throws Exception;

具体看下源码到底是怎么获取代理的?

ProxyFactory继承了ProxyCreatorSupport,ProxyCreatorSupport继承了AdvisedSupport

//new ProxyFactory(List.class, targetSource)最终会调用AdvisedSupport的setTargetSource 【代码1】
ProxyFactory factory = new ProxyFactory(List.class, targetSource);
public void setTargetSource(@Nullable TargetSource targetSource) {
   this.targetSource = (targetSource != null ? targetSource : EMPTY_TARGET_SOURCE);
}

【代码1】
没有我们想要的TargetSource相关代码,接下来看 factory.getProxy();方法【代码2】,getProxy()方法分为两步,我们瞎看createAopProxy()第一步。

public Object getProxy() {
   return createAopProxy().getProxy();
}

【代码2】

protected final synchronized AopProxy createAopProxy() {
   if (!this.active) {
      activate();
   }
   return getAopProxyFactory().createAopProxy(this);
}
// getAopProxyFactory() 获取AopProxyFactory,这里是默认的DefaultAopProxyFactory,核心逻辑在createAopProxy里【代码3】

createAopProxy方法决定了使用jdk代理还是cglib代理,在我的场景下,走的jdk代理,看一下new JdkDynamicAopProxy(config);方法做了什么

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);
      }
      return new ObjenesisCglibAopProxy(config);
   }
   else {
      return new JdkDynamicAopProxy(config);
   }
}

【代码3】

JdkDynamicAopProxy就做了一件事,把config set给advised(AdvisedSupport的实例)

public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
   Assert.notNull(config, "AdvisedSupport must not be null");
   if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
      throw new AopConfigException("No advisors and no TargetSource specified");
   }
   this.advised = config;
}

new 完JdkDynamicAopProxy,【代码2】中的createAopProxy()第一步才真正返回,整个流程有点长,接着调用getProxy方法,然后我们看看JdkDynamicAopProxy的getProxy是怎么实现的?【代码4】

public Object getProxy(@Nullable ClassLoader classLoader) {
   if (logger.isTraceEnabled()) {
      logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
   }
   Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
   findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
   return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

【代码4】
jdk创建代理我们知道是需要类加载器的,接口,classLoader就是类加载器,proxiedInterfaces就是接口,由于JdkDynamicAopProxy实现了jdk的InvocationHandler接口,所以Proxy.newProxyInstance这里可以直接传递this指针,不同与我们之前写过的jdk创建代理对象的demo(不了解这个的可以baidu一下,估计百度的代码都是千篇一律的),这里我们好像并没有用到目标对象啊,照例说我们代理的是目标对象(在此场景是Plugin的实现),其实目标对象被包装到了TargetSource里面,而TargetSource又被包装到了advised,这样是可以的,虽然advised没有实现List接口但依旧是可以被代理的,之前我们经常在网上或者那里看到的一句话是“**JDK代理必须基于接口,目标对象必须要实现一个以上的接口”,放在这里看,这句话就不太合适了,其实jdk代理的本质,在代理对象身上调用的方法,都会先调用到InvocationHandler的invoke方法,因此只要在invoke方法里能够拿到目标对象就是可以调用目标方法,并且在调用目标方法之前执行我们织入的逻辑。**说了这么多,看下spring是怎么做的,因此需要看JdkDynamicAopProxy的incoke方法【代码5】

为了缩短篇幅,我省略了部分代码,可以看到先从advised获取到被包装的targetSource,然后再调用targetSource.getTarget获取到整整的目标对象,也就是我们想要被代理的对象,最后再利用反射调用目标对象的目标方法。在我分析的这个场景中,targetSource是org.springframework.plugin.core.support.AbstractTypeAwareSupport.BeansOfTypeTargetSource这一个类,大家也可以查看TargetSource的其它类实现的,例如spring单例模式目标对象是怎么被代理的,查看你这个类SingletonTargetSource,类似的可以看下多例模式下又是怎么被代理的。

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;

      Object retVal;
      .........
      target = targetSource.getTarget();
      Class<?> targetClass = (target != null ? target.getClass() : null);

      if (chain.isEmpty()) {
         Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
         retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
      }
      else {
         MethodInvocation invocation =
               new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
         retVal = invocation.proceed();
      }
      return retVal;
   }
}
public Object getProxy(@Nullable ClassLoader classLoader) {
   if (logger.isTraceEnabled()) {
      logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
   }
   Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
   findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
   return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

如果你看懂了我说的,那么你应该也能看懂下面的代码,或许下面的代码能帮你更好的理解上面的源码分析的。

public class LocalTest {

   @Data
   // 目标对象,并未实现List接口
   static class MyTarget {
      private static List<String> words = Lists.newArrayList("hello", "young man");
   }


   static class MyInvocation implements InvocationHandler {


      @Override
      public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable {
         return method.invoke(MyTarget.words, arguments);
      }
   }


   public static void main(String[] args) throws InterruptedException {


      Object proxyInstance = Proxy
            .newProxyInstance(MyTarget.class.getClassLoader(), new Class[] { List.class }, new MyInvocation());


      List list = (List) proxyInstance;
      System.out.println(list); // [hello, young man]
   }
}

总结:spring创建代理对象并不是直接对目标对象进项代理,而是先把目标对象包装成TargetSource,targetSource会被转换成Advised,这个是真正的被代理的类,只不过在invoke方法执行的时候,会从targetSource中取出真正的目标对象,然后调用目标对象身上的方法。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值