单例模式源码分析(jdk+spring+mybatis)

我们首先看一个简单的,Runtime这个类在java.lang下的,我们看一下getRuntime()

public static Runtime getRuntime() {
	return currentRuntime;
}

可以看到这里直接返回一个currentRuntime

private static Runtime currentRuntime = new Runtime();

我们可以看到这个对象是static,并且在类加载的时候就已经创建出来了,属于饿汉式,非常简单,我们再看一个Desktop,

awt的,只不过现在用JAVAD写服务端比较多一点,但是这个也分领域,某些也用JAVA写桌面程序非常多,打个比方,Eclipse就是

用JAVA写的,还是非常强大的,那我们看一下,我们看一下getDesktop这个方法

public static synchronized Desktop getDesktop(){
	if (GraphicsEnvironment.isHeadless()) throw new HeadlessException();
	if (!Desktop.isDesktopSupported()) {
		throw new UnsupportedOperationException("Desktop API is not " +
												"supported on the current platform");
	}

	sun.awt.AppContext context = sun.awt.AppContext.getAppContext();
	Desktop desktop = (Desktop)context.get(Desktop.class);

	if (desktop == null) {
		desktop = new Desktop();
		context.put(Desktop.class, desktop);
	}

	return desktop;
}

这个方法关注的,synchronized同步方法,获取桌面对象,同步方法,最后返回,那我们看一下这里面AppContext,这个是一个App的上下文,

然后下边做了各种判断,判断这个对象是否为空,如果是空的话就new一个,放进去,那我们来看一下这个对象context,这里面的put做了

什么呢

首先这里有this.table,点开table看一下,它是一个HashMap类型

这些判断先忽略,看重点,然后给这个map上了个锁,下面就开始put了,向HashMap里面放值,那我们再退回来,

public static synchronized Desktop getDesktop(){
	if (GraphicsEnvironment.isHeadless()) throw new HeadlessException();
	if (!Desktop.isDesktopSupported()) {
		throw new UnsupportedOperationException("Desktop API is not " +
												"supported on the current platform");
	}

	sun.awt.AppContext context = sun.awt.AppContext.getAppContext();
	Desktop desktop = (Desktop)context.get(Desktop.class);

	if (desktop == null) {
		desktop = new Desktop();
		context.put(Desktop.class, desktop);
	}

	return desktop;
}

这里面同步方法,sun.awt.AppContext context = sun.awt.AppContext.getAppContext();然后看一下这行,

这个从上下文里面,去获取这个类型的对象,然后下面又有空判断,如果为空就往里面put,最后就返回,那这里讲的

就非常像我们之前讲的容器单例,可以找到容器单例的一个影子,只不过这里加了各种同步的控制,包括put的时候,

还有这个方法调用的时候,来保证这一块是线程安全的,那讲完这些我们再想一下,那Spring中的单例,和我们写的这个

单例还不太一样,那Spring的单例呢,是基于容器的,打个比方,如果我写一个主函数,或者一个应用,启动了多个容器,

那在每个容器中都能够拿到这个单例对象,也就是说呢,Spring里面的单例模式和我们讲的单例模式,不是一个,

那Spring中的单例呢,是bean作用域中的一个,这个作用域在每个应用上下文中,仅创建一个,我们设置单例属性的这个实例,

那和我们的设计模式最大区别是什么呢,Spring将实例的数量,作用域在整个应用程序的上下文,而我们这个单例模式呢,

而我们写的这种单例模式,在JAVA应用程序中,是将这些实例的数量,限制在类加载器管理的整个空间里,所以刚刚也说了,

Spring启动容器的时候,每个容器即使是单例的,我都可以拿出来这个对象,那其实在Spring中,能找到一些单例的影子,

你看这个类,AbstractFactoryBean,我们找一个方法,getObject方法

我们可以看一下这里面做了一些判断,也就是说判断这个对象如果是单例的,就return,如果不是单例的就直接创建instance,

createInstance(),从这个名字就能够看出来,然后看一下这里面的语法,一个三元,是否被初始化呢,如果已经被初始化了,

直接返回,如果没有被初始化,调用getEarlySingletonInstance(),获取早期的单例对象,我们来看一下

如果单例对象是空的话,通过代理去获取这个对象,然后进行强转,把这个对象赋值上,然后对他进行返回,现在我们再看一个

关于Mybatis当中,使用单例模式的一些解决方案,我们打开他,public class ErrorContext,错误的上下文,那首先看到的

就是这个private static final ThreadLocal<ErrorContext> LOCAL = new ThreadLocal<ErrorContext>();

首先我们看一下他的构造器

private ErrorContext() {
}

他的构造器是私有的,然后看一下instance

public static ErrorContext instance() {
ErrorContext context = LOCAL.get();
if (context == null) {
  context = new ErrorContext();
  LOCAL.set(context);
}
return context;
}

这里就基于ThreadLocal来使用了,而这个ThreadLocal呢,又是一个私有的静态的final的

private static final ThreadLocal<ErrorContext> LOCAL = new ThreadLocal<ErrorContext>();

然后我们先不说这个,if (context == null) 我们先看一下这里,如果这个上下文是空的话,就创建一个新的,创建完成之后,

把它放到ThreadLocal里边,如果这个线程下次来的话,肯定能够get出来,然后就直接返回了,那Mybatis这里面的错误上下文,

也就是说他保证了每个线程,各自的数据,不同的线程,每个线程自己的错误上下文,自己保存好,就像前面所讲的,在线程隔离方面,

是非常有益处的,基于ThreadLocal的这种单例模式,我们也找到了,只不过他不是并不是整个应用全局唯一,而是在线程里是唯一的,

那单例模式的源码呢,就分析到这里,希望在单例模式这里有所收获,然后强调一点,如果问设计模式的话,喜欢问单例模式来挖掘深度,

希望能把我们所讲的这些单例模式,实现方案,优缺点,应用场景,及其在源码中的一些使用,还有比如序列化的破坏,反射攻击防御等等,

这些都能理解透

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值