单例模式-Spring单例实现原理分析

Posted 彤哥博文

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单例模式-Spring单例实现原理分析相关的知识,希望对你有一定的参考价值。

在Spring中,被@Scope注解修饰Bean默认是单例模式的,即只有一个实例对象,多次获取Bean会拿到同一个对象.

单例注册表

Spring采用单例注册表的特殊方式实现单例模式.首先自己写个单例注册表.我们可以通过Map缓存单例对象,实现单例注册表.值得注意的是,采用ConcurrentHashMap是出于线程安全的考虑.

 
   
   
 
  1. /**

  2. * @author tong.li

  3. * @Description: 一个简单的单例实现,设计单例注册表

  4. * @packagename: com.yimi.yts.learningpath

  5. * @date 2018-03-27 10:04

  6. */

  7. public class SingletonReg {


  8. //构建采用ConcurrentHashMap,用于充当缓存注册表

  9. private final static Map<String, Object> singletonObjects = new ConcurrentHashMap<>(16);


  10. // 静态代码块只加载执行一次

  11. static {

  12. // 实例化Bean

  13. SingletonReg singletonReg = new SingletonReg();

  14. //并注册到注册表中,key为类的完全限定名,value为实例化对象

  15. singletonObjects.put(singletonReg.getClass().getName(),singletonReg);

  16. }


  17. /**

  18. * 私有化构造方法,避免外部创建本类实例

  19. */

  20. private SingletonReg() {}



  21. /**

  22. * 对外暴露获得该bean的方法,Spring框架一般会返回Object

  23. * @return

  24. */

  25. public static SingletonReg getInstance(String className) {

  26. if (StringUtils.isEmpty(className)) {

  27. return null;

  28. }

  29. //从注册表获取,如果没有直接创建

  30. if (singletonObjects.get(className) == null) {

  31. try {

  32. //如果为空,通过反射进行实例化

  33. singletonObjects.put(className, Class.forName(className).newInstance());

  34. } catch (Exception e) {

  35. e.printStackTrace();

  36. }

  37. }

  38. //从缓存表中回去,如果缓存命中直接返回

  39. return (SingletonReg)singletonObjects.get(className);

  40. }


  41. }

同过以上单例实现,getInstance()方法通过传入类名进行判断,如果参数为null,那就无法获取bean,如果参数不为空,先从缓存注册表命中,如果命中就return掉,没有命中通过反射机制实例化一个return. 这样多次获得调用getInstance()方法都是获得同一个对象.测试如下:

 
   
   
 
  1. public static void main(String[] args) {

  2. /*

  3. * 返回的都是同一个对象

  4. * com.yimi.yts.learningpath.SingletonReg@3d82c5f3

  5. * com.yimi.yts.learningpath.SingletonReg@3d82c5f3

  6. */

  7. System.out.println( SingletonReg.getInstance("com.yimi.yts.learningpath.SingletonReg"));

  8. System.out.println( SingletonReg.getInstance("com.yimi.yts.learningpath.SingletonReg"));

  9. }

Spring源码分析

通过上述实现,Spring就是采用了这种单例注册表的特殊方式实现单例模式的.

 
   
   
 
  1. public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {


  2. @SuppressWarnings("unchecked")

  3. protected <T> T doGetBean(

  4. final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)

  5. throws BeansException {

  6. //对Bean的name进行处理,防止非法字符

  7. final String beanName = transformedBeanName(name);

  8. Object bean;


  9. // Eagerly check singleton cache for manually registered singletons.

  10. //从单例注册表中检查是否存在单例缓存

  11. Object sharedInstance = getSingleton(beanName);

  12. if (sharedInstance != null && args == null) {

  13. //省略部分代码...

  14. // 返回缓存实例

  15. bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);

  16. }


  17. else {

  18. //省略代码...

  19. try {

  20. // ...忽略代码

  21. // 单例模式,实例化bean,处理分支

  22. // Create bean instance.

  23. if (mbd.isSingleton()) {

  24. sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {

  25. @Override

  26. public Object getObject() throws BeansException {

  27. try {

  28. return createBean(beanName, mbd, args);

  29. }

  30. catch (BeansException ex) {

  31. destroySingleton(beanName);

  32. throw ex;

  33. }

  34. }

  35. });

  36. bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);

  37. }

  38. //原型魔兽,处理分支

  39. else if (mbd.isPrototype()) {

  40. //省略代码

  41. }


  42. else {

  43. String scopeName = mbd.getScope();

  44. final Scope scope = this.scopes.get(scopeName);

  45. if (scope == null) {

  46. throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");

  47. }

  48. try {

  49. Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {

  50. @Override

  51. public Object getObject() throws BeansException {

  52. beforePrototypeCreation(beanName);

  53. try {

  54. return createBean(beanName, mbd, args);

  55. }

  56. finally {

  57. afterPrototypeCreation(beanName);

  58. }

  59. }

  60. });

  61. bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);

  62. }

  63. catch (IllegalStateException ex) {

  64. throw new BeanCreationException(beanName,

  65. "Scope '" + scopeName + "' is not active for the current thread; consider " +

  66. "defining a scoped proxy for this bean if you intend to refer to it from a singleton",

  67. ex);

  68. }

  69. }

  70. }

  71. catch (BeansException ex) {

  72. cleanupAfterBeanCreationFailure(beanName);

  73. throw ex;

  74. }

  75. }


  76. // Check if required type matches the type of the actual bean instance.

  77. if (requiredType != null && bean != null && !requiredType.isInstance(bean)) {

  78. try {

  79. return getTypeConverter().convertIfNecessary(bean, requiredType);

  80. }

  81. catch (TypeMismatchException ex) {

  82. if (logger.isDebugEnabled()) {

  83. logger.debug("Failed to convert bean '" + name + "' to required type '" +

  84. ClassUtils.getQualifiedName(requiredType) + "'", ex);

  85. }

  86. throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());

  87. }

  88. }

  89. return (T) bean;

  90. }

  91. }

其中中重要的代码是getSingleton()方法,下面深入分析该方法:

 
   
   
 
  1. public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {


  2. // 通过 Map 实现单例注册表

  3. private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);


  4. public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {

  5. Assert.notNull(beanName, "'beanName' must not be null");

  6. synchronized (this.singletonObjects) {

  7. // 检查缓存中是否存在实例

  8. Object singletonObject = this.singletonObjects.get(beanName);

  9. if (singletonObject == null) {

  10. // ...忽略代码

  11. try {

  12. singletonObject = singletonFactory.getObject();

  13. }

  14. catch (BeanCreationException ex) {

  15. // ...忽略代码

  16. }

  17. finally {

  18. // ...忽略代码

  19. }

  20. // 如果实例对象在不存在,我们注册到单例注册表中。

  21. addSingleton(beanName, singletonObject);

  22. }

  23. return (singletonObject != NULL_OBJECT ? singletonObject : null);

  24. }

  25. }


  26. protected void addSingleton(String beanName, Object singletonObject) {

  27. synchronized (this.singletonObjects) {

  28. this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));


  29. }

  30. }

  31. }

总结

综上述分析.我们可以得出:Spring对Bean实例的创建是采用单例注册表的方式进行实现的,而这个注册表的缓存是 ConcurrentHashMap对象。


以上是关于单例模式-Spring单例实现原理分析的主要内容,如果未能解决你的问题,请参考以下文章

单例模式详解

单例模式详解

spring注入如何实现单列模式?

单例模式-反射攻击的解决方案及原理分析

NopCommerce源码架构学习-二单例模式实现代码分析

C#如何实现进程单例运行