在Spring中,被@Scope注解修饰Bean默认是单例模式的,即只有一个实例对象,多次获取Bean会拿到同一个对象.
单例注册表
Spring采用单例注册表的特殊方式实现单例模式.首先自己写个单例注册表.我们可以通过Map缓存单例对象,实现单例注册表.值得注意的是,采用ConcurrentHashMap是出于线程安全的考虑.
/** * @author tong.li * @Description: 一个简单的单例实现,设计单例注册表 * @packagename: com.yimi.yts.learningpath * @date 2018-03-27 10:04 */ public class SingletonReg { //构建采用ConcurrentHashMap,用于充当缓存注册表 private final static Map<String, Object> singletonObjects = new ConcurrentHashMap<>(16); // 静态代码块只加载执行一次 static { // 实例化Bean SingletonReg singletonReg = new SingletonReg(); //并注册到注册表中,key为类的完全限定名,value为实例化对象 singletonObjects.put(singletonReg.getClass().getName(),singletonReg); } /** * 私有化构造方法,避免外部创建本类实例 */ private SingletonReg() {} /** * 对外暴露获得该bean的方法,Spring框架一般会返回Object * @return */ public static SingletonReg getInstance(String className) { if (StringUtils.isEmpty(className)) { return null; } //从注册表获取,如果没有直接创建 if (singletonObjects.get(className) == null) { try { //如果为空,通过反射进行实例化 singletonObjects.put(className, Class.forName(className).newInstance()); } catch (Exception e) { e.printStackTrace(); } } //从缓存表中回去,如果缓存命中直接返回 return (SingletonReg)singletonObjects.get(className); } }
同过以上单例实现,getInstance()方法通过传入类名进行判断,如果参数为null,那就无法获取bean,如果参数不为空,先从缓存注册表命中,如果命中就return掉,没有命中通过反射机制实例化一个return.
这样多次获得调用getInstance()方法都是获得同一个对象.测试如下:
public static void main(String[] args) { /* * 返回的都是同一个对象 * com.yimi.yts.learningpath.SingletonReg@3d82c5f3 * com.yimi.yts.learningpath.SingletonReg@3d82c5f3 */ System.out.println( SingletonReg.getInstance("com.yimi.yts.learningpath.SingletonReg")); System.out.println( SingletonReg.getInstance("com.yimi.yts.learningpath.SingletonReg")); }
Spring源码分析
通过上述实现,Spring就是采用了这种单例注册表的特殊方式实现单例模式的.
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory { @SuppressWarnings("unchecked") protected <T> T doGetBean( final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException { //对Bean的name进行处理,防止非法字符 final String beanName = transformedBeanName(name); Object bean; // Eagerly check singleton cache for manually registered singletons. //从单例注册表中检查是否存在单例缓存 Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { //省略部分代码... // 返回缓存实例 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { //省略代码... try { // ...忽略代码 // 单例模式,实例化bean,处理分支 // Create bean instance. if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() { @Override public Object getObject() throws BeansException { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { destroySingleton(beanName); throw ex; } } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } //原型魔兽,处理分支 else if (mbd.isPrototype()) { //省略代码 } else { String scopeName = mbd.getScope(); final Scope scope = this.scopes.get(scopeName); if (scope == null) { throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'"); } try { Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() { @Override public Object getObject() throws BeansException { beforePrototypeCreation(beanName); try { return createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } } }); bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } catch (IllegalStateException ex) { throw new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active for the current thread; consider " + "defining a scoped proxy for this bean if you intend to refer to it from a singleton", ex); } } } catch (BeansException ex) { cleanupAfterBeanCreationFailure(beanName); throw ex; } } // Check if required type matches the type of the actual bean instance. if (requiredType != null && bean != null && !requiredType.isInstance(bean)) { try { return getTypeConverter().convertIfNecessary(bean, requiredType); } catch (TypeMismatchException ex) { if (logger.isDebugEnabled()) { logger.debug("Failed to convert bean '" + name + "' to required type '" + ClassUtils.getQualifiedName(requiredType) + "'", ex); } throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } } return (T) bean; } }
其中中重要的代码是getSingleton()方法,下面深入分析该方法:
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry { // 通过 Map 实现单例注册表 private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64); public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(beanName, "'beanName' must not be null"); synchronized (this.singletonObjects) { // 检查缓存中是否存在实例 Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { // ...忽略代码 try { singletonObject = singletonFactory.getObject(); } catch (BeanCreationException ex) { // ...忽略代码 } finally { // ...忽略代码 } // 如果实例对象在不存在,我们注册到单例注册表中。 addSingleton(beanName, singletonObject); } return (singletonObject != NULL_OBJECT ? singletonObject : null); } } protected void addSingleton(String beanName, Object singletonObject) { synchronized (this.singletonObjects) { this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT)); } } }
总结:Spring对Bean实例的创建是采用单例注册表的方式进行实现的,而这个注册表的缓存是 ConcurrentHashMap对象。