经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring Boot » 查看文章
Springboot源码分析之TargetSource
来源:cnblogs  作者:TheGir1  时间:2019/8/27 10:02:34  对本文有异议

摘要:

其实我第一次看见这个东西的时候也是不解,代理目标源不就是一个class嘛还需要封装干嘛。。。

其实proxy代理的不是target,而是TargetSource,这点非常重要,一定要分清楚!!!

通常情况下,一个代理对象只能代理一个target,每次方法调用的目标也是唯一固定的target。但是,如果让proxy代理TargetSource,可以使得每次方法调用的target实例都不同(当然也可以相同,这取决于TargetSource实现)。这种机制使得方法调用变得灵活,可以扩展出很多高级功能,如:单利,原型,本地线程,目标对象池、运行时目标对象热替换目标源等等。

file

Spring内置的TargetSource

SingletonTargetSource
  1. public class SingletonTargetSource implements TargetSource, Serializable {
  2. /** Target cached and invoked using reflection. */
  3. private final Object target;
  4. //省略无关代码......
  5. @Override
  6. public Object getTarget() {
  7. return this.target;
  8. }
  9. //省略无关代码......
  10. }

从这个目标源取得的目标对象是单例的,成员变量target缓存了目标对象,每次getTarget()都是返回这个对象。

PrototypeTargetSource
  1. public class PrototypeTargetSource extends AbstractPrototypeBasedTargetSource {
  2. /**
  3. * Obtain a new prototype instance for every call.
  4. * @see #newPrototypeInstance()
  5. */
  6. @Override
  7. public Object getTarget() throws BeansException {
  8. return newPrototypeInstance();
  9. }
  10. /**
  11. * Destroy the given independent instance.
  12. * @see #destroyPrototypeInstance
  13. */
  14. @Override
  15. public void releaseTarget(Object target) {
  16. destroyPrototypeInstance(target);
  17. }
  18. //省略无关代码......
  19. }

每次getTarget()将生成prototype类型的bean,即其生成的bean并不是单例的,因而使用这个类型的TargetSource时需要注意,封装的目标bean必须是prototype类型的。PrototypeTargetSource继承了AbstractBeanFactoryBasedTargetSource拥有了创建bean的能力。

  1. public abstract class AbstractPrototypeBasedTargetSource extends AbstractBeanFactoryBasedTargetSource {
  2. //省略无关代码......
  3. /**
  4. * Subclasses should call this method to create a new prototype instance.
  5. * @throws BeansException if bean creation failed
  6. */
  7. protected Object newPrototypeInstance() throws BeansException {
  8. if (logger.isDebugEnabled()) {
  9. logger.debug("Creating new instance of bean '" + getTargetBeanName() + "'");
  10. }
  11. return getBeanFactory().getBean(getTargetBeanName());
  12. }
  13. /**
  14. * Subclasses should call this method to destroy an obsolete prototype instance.
  15. * @param target the bean instance to destroy
  16. */
  17. protected void destroyPrototypeInstance(Object target) {
  18. if (logger.isDebugEnabled()) {
  19. logger.debug("Destroying instance of bean '" + getTargetBeanName() + "'");
  20. }
  21. if (getBeanFactory() instanceof ConfigurableBeanFactory) {
  22. ((ConfigurableBeanFactory) getBeanFactory()).destroyBean(getTargetBeanName(), target);
  23. }
  24. else if (target instanceof DisposableBean) {
  25. try {
  26. ((DisposableBean) target).destroy();
  27. }
  28. catch (Throwable ex) {
  29. logger.warn("Destroy method on bean with name '" + getTargetBeanName() + "' threw an exception", ex);
  30. }
  31. }
  32. }
  33. //省略无关代码......
  34. }

可以看到,PrototypeTargetSource的生成prototype类型bean的方式主要是委托给BeanFactory进行的,因为BeanFactory自有一套生成prototype类型的bean的逻辑,因而PrototypeTargetSource也就具有生成prototype类型bean的能力,这也就是我们要生成的目标bean必须声明为prototype类型的原因。

ThreadLocalTargetSource
  1. public class ThreadLocalTargetSource extends AbstractPrototypeBasedTargetSource
  2. implements ThreadLocalTargetSourceStats, DisposableBean {
  3. /**
  4. * ThreadLocal holding the target associated with the current
  5. * thread. Unlike most ThreadLocals, which are static, this variable
  6. * is meant to be per thread per instance of the ThreadLocalTargetSource class.
  7. */
  8. private final ThreadLocal<Object> targetInThread =
  9. new NamedThreadLocal<>("Thread-local instance of bean '" + getTargetBeanName() + "'");
  10. /**
  11. * Set of managed targets, enabling us to keep track of the targets we've created.
  12. */
  13. private final Set<Object> targetSet = new HashSet<>();
  14. //省略无关代码......
  15. /**
  16. * Implementation of abstract getTarget() method.
  17. * We look for a target held in a ThreadLocal. If we don't find one,
  18. * we create one and bind it to the thread. No synchronization is required.
  19. */
  20. @Override
  21. public Object getTarget() throws BeansException {
  22. ++this.invocationCount;
  23. Object target = this.targetInThread.get();
  24. if (target == null) {
  25. if (logger.isDebugEnabled()) {
  26. logger.debug("No target for prototype '" + getTargetBeanName() + "' bound to thread: " +
  27. "creating one and binding it to thread '" + Thread.currentThread().getName() + "'");
  28. }
  29. // Associate target with ThreadLocal.
  30. target = newPrototypeInstance();
  31. this.targetInThread.set(target);
  32. synchronized (this.targetSet) {
  33. this.targetSet.add(target);
  34. }
  35. }
  36. else {
  37. ++this.hitCount;
  38. }
  39. return target;
  40. }
  41. /**
  42. * Dispose of targets if necessary; clear ThreadLocal.
  43. * @see #destroyPrototypeInstance
  44. */
  45. @Override
  46. public void destroy() {
  47. logger.debug("Destroying ThreadLocalTargetSource bindings");
  48. synchronized (this.targetSet) {
  49. for (Object target : this.targetSet) {
  50. destroyPrototypeInstance(target);
  51. }
  52. this.targetSet.clear();
  53. }
  54. // Clear ThreadLocal, just in case.
  55. this.targetInThread.remove();
  56. }
  57. //省略无关代码......
  58. }

ThreadLocalTargetSource也就是和线程绑定的TargetSource,可以理解,其底层实现必然使用的是ThreadLocal。既然使用了ThreadLocal,也就是说我们需要注意两个问题:

  • 目标对象必须声明为prototype类型,因为每个线程都会持有一个不一样的对象;
  • 目标对象必须是无状态的,因为目标对象是和当前线程绑定的,而Spring是使用的线程池处理的请求,因而每个线程可能处理不同的请求,因而为了避免造成问题,目标对象必须是无状态的。
实现自定义的TargetSource
  1. package com.github.dqqzj.springboot.target;
  2. import org.springframework.aop.TargetSource;
  3. import org.springframework.util.Assert;
  4. import java.lang.reflect.Array;
  5. import java.util.concurrent.ThreadLocalRandom;
  6. import java.util.concurrent.atomic.AtomicInteger;
  7. /**
  8. * @author qinzhongjian
  9. * @date created in 2019-08-25 12:43
  10. * @description: TODO
  11. * @since JDK 1.8.0_212-b10z
  12. */
  13. public class DqqzjTargetSource implements TargetSource {
  14. private final AtomicInteger idx = new AtomicInteger();
  15. private final Object[] target;;
  16. public DqqzjTargetSource(Object[] target) {
  17. Assert.notNull(target, "Target object must not be null");
  18. this.target = target;
  19. }
  20. @Override
  21. public Class<?> getTargetClass() {
  22. return target.getClass();
  23. }
  24. @Override
  25. public boolean isStatic() {
  26. return false;
  27. }
  28. @Override
  29. public Object getTarget() throws Exception {
  30. return this.target[this.idx.getAndIncrement() & this.target.length - 1];
  31. }
  32. @Override
  33. public void releaseTarget(Object target) throws Exception {
  34. }
  35. }

实现自定义TargetSource主要有两个点要注意,一个是getTarget()方法,该方法中需要实现获取目标对象的逻辑,另一个是isStatic()方法,这个方法告知Spring是否需要缓存目标对象,在非单例的情况下一般是返回false

小结
  1. 本文主要首先讲解了Spring是如果在源码层面支持TargetSource的,然后讲解了TargetSource的使用原理,接着对Spring提供的常见`TargetSource`进行了讲解,最后使用一个自定义的TargetSource讲解了其使用方式。

原文链接:http://www.cnblogs.com/qinzj/p/11415057.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号