logo头像
Snippet 博客主题

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

本文于592天之前发表,文中内容可能已经过时

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

单例注册表

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/**
* @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()方法都是获得同一个对象.测试如下:

1
2
3
4
5
6
7
8
9
 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就是采用了这种单例注册表的特殊方式实现单例模式的.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
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()方法,下面深入分析该方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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对象。

支付宝打赏 微信打赏

请作者喝杯咖啡吧