内容简介:单例模式 - 单例注册表与 Spring 实现单例剖析
在 Spring 中,单例模式是十分常用的,因为 Spring Bean 默认是单例模式,即只有一个共享的实例存在,所有对这个 Bean 的请求都会返回这个唯一的实例。
单例注册表
首先,我们先来写一个案例。这个案例中,我们通过 Map 缓存单例对象,实现单例注册表。值得注意的是,我采用了 ConcurrentHashMap 是出于线程安全的考量。
public class SingletonReg {
private final static Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);
static {
SingletonReg singletonReg = new SingletonReg();
singletonObjects.put(singletonReg.getClass().getName(), singletonReg);
}
private SingletonReg() {}
public static SingletonReg getInstance(String name) {
if (name == null) {
name = "com.lianggzone.designpattern.singleton.sample.SingletonReg";
}
if (singletonObjects.get(name) == null) {
try {
singletonObjects.put(name, Class.forName(name).newInstance());
} catch (Exception ex) {
ex.printStackTrace();
}
}
return (SingletonReg) singletonObjects.get(name);
}
}
我们来分析下,上面案例中的源码。
private final static Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);
此外,getInstance() 方法通过传入类名进行判断,如果参数为 null,我们默认分配一个 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;
// 从单例注册表中检查是否存在单例缓存
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// ...忽略代码
// 返回缓存实例
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
// ...忽略代码
try {
// ...忽略代码
// 单例模式,处理分支
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// ...忽略代码
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// 原型模式,处理分支
else if (mbd.isPrototype()) {
}
// 其他
else {
}
}
catch (BeansException ex) {
// ...忽略代码
}
}
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 对象。
(完)
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 微服务注册中心注册表与hashcode实现golang版
- 正则表达式注册表验证笔记整理
- 彻底删除SQL Server注册表的方法
- HOWTO: 使用 USMT 捕获指定的用户注册表数据
- 通告 | ENS 注册表迁移:修复漏洞,增加新功能
- 对比AWS、Azure和Google的云容器注册表
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Elements of Programming
Alexander A. Stepanov、Paul McJones / Addison-Wesley Professional / 2009-6-19 / USD 39.99
Elements of Programming provides a different understanding of programming than is presented elsewhere. Its major premise is that practical programming, like other areas of science and engineering, mus......一起来看看 《Elements of Programming》 这本书的介绍吧!