You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
publicclassSingletonSpringTest {
@Testpublicvoidtest() {
// 创建两个应用上下文实例ApplicationContextfirstContext = newFileSystemXmlApplicationContext("applicationContext-test.xml");
ApplicationContextsecondContext = newFileSystemXmlApplicationContext("applicationContext-test.xml");
// 两个应用上下文来自同一个类加载器assertTrue("Class loaders for both contexts should be the same",
firstContext.getClassLoader() == secondContext.getClassLoader());
// 1.ShoppingCartfirstShoppingCart = (ShoppingCart) firstContext.getBean("shoppingCart");
ShoppingCartsecondShoppingCart = (ShoppingCart) secondContext.getBean("shoppingCart");
assertFalse("ShoppingCart instances got from different application context shouldn't be the same",
firstShoppingCart == secondShoppingCart);
// 2.ShoppingCartfirstShoppingCartBis = (ShoppingCart) firstContext.getBean("shoppingCart");
assertTrue("ShoppingCart instances got from the same application context should be the same",
firstShoppingCart == firstShoppingCartBis);
}
}
在 Spring 源码中,org.springframework.beans.factory.support.BeanDefinitionBuilder 类用于以编程的方式定义 bean,为 AbstractBeanDefinition 抽象类的实现设置值(作用域、工厂方法、属性等)。
publicclassBeanDefinitionBuilder {
// .../** * Set the init method for this definition. */publicBeanDefinitionBuildersetInitMethodName(StringmethodName) {
this.beanDefinition.setInitMethodName(methodName);
returnthis;
}
// ...
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"applicationContext-test.xml"})
publicclassSpringPrototypeTest {
@AutowiredprivateBeanFactorybeanFactory;
@Testpublicvoidtest() {
ShoppingCartcart1 = (ShoppingCart) beanFactory.getBean("shoppingCart");
assertTrue("Id of cart1 should be 9 but was "+cart1.getId(),cart1.getId() == 9);
cart1.setId(100);
ShoppingCartcart2 = (ShoppingCart) beanFactory.getBean("shoppingCart");
assertTrue("Id of cart2 should be 9 but was "+cart2.getId(), cart2.getId() == 9);
assertTrue("Id of second cart ("+cart2.getId()+") shouldn't be the same as the first one: "+cart1.getId(),
cart1.getId() != cart2.getId());
cart2.setId(cart1.getId());
assertTrue("Now (after cart2.setId(cart1.getId())), the id of second cart ("+cart2.getId()+") should be the same as the first one: "+cart1.getId(), cart1.getId() == cart2.getId());
assertTrue("Both instance shouldn't be the same", cart1 != cart2);
}
}
ShoppingCart 实例是直接从 bean 定义创建的。最初 cart1 和 cart2 对象的 id 值为9,它在测试结束时被修改,以证明两个引用都属于两个不同的对象(注意每次调用 getBean 创建的对象,生命周期都交由开发者管理,而不再是 Spring IoC 容器)。
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
单例模式(Singleton)
确保一个类只有一个实例,并提供该实例的全局访问点。
使用私有构造函数(确保不能通过构造函数来创建对象实例)、私有静态变量以及公有静态函数(返回唯一的私有静态变量)实现。
实现
懒汉式 - 线程不安全
私有静态变量 uniqueInstance 被延迟到第一次获取实例时实例化,如果没有用到该类则节约了实例化的开销。
线程不安全:如果多个线程能够同时进入
if (uniqueInstance == null)
,且此时 uniqueInstance 为null
,会有多个线程执行uniqueInstance = new Singleton();
,导致实例化多次。饿汉式 - 线程安全
在类加载阶段实例化,每次获取同一个实例,避免线程安全问题,但也丢失了延迟实例化带来的节约资源的好处。
懒汉式 - 线程安全
在静态方法加上
synchronized
同步,但会让线程阻塞时间过长、有性能问题,不推荐使用。双重校验锁 - 线程安全
uniqueInstance
只需被实例化一次。加锁操作只针对实例化,当uniqueInstance
没有被实例化时,才需要加锁。如果只使用一个 if 语句,在
uniqueInstance == null
下有两个线程执行 if 语句,则都会进入 if 块内,当两个线程都执行uniqueInstance = new Singleton();
就会进行两次实例化。因此必须双重校验:使用
volatile
可以禁止 JVM 的指令重排:uniqueInstance
用volatile
修饰。uniqueInstance = new Singleton();
执行流程:JVM 指令重排在多线程环境下可能导致一个线程获得还没有初始化的实例,即执行顺序有可能变成 1>3>2。
例如线程 T1 执行了 1 和 3,此时 T2 调用
getUniqueInstance()
后发现uniqueInstance
不为空,因此返回uniqueInstance
,但此时uniqueInstance
还未被初始化。静态内部类实现 - 线程安全
利用静态内部类延迟加载的特性:当
Singleton
类加载时,静态内部类SingletonHolder
未被加载进内存。只有当调用getUniqueInstance()
从而触发SingletonHolder.INSTANCE
时SingletonHolder
才会被加载,此时初始化INSTANCE
实例,并由 JVM 确保INSTANCE
只被实例化一次。枚举实现 - 线程安全
在多次序列化、反序列化后,不会得到多个实例。同时可以防止反射攻击,由 JVM 保证只会实例化一次。
应用
JDK 中的应用
Spring 中的应用
Spring 中 bean 作用域中的一种,在每个应用上下文中仅创建一个 bean 实例。
比如单例模式可以使用同一份配置文件起两个容器,即多实例,并检索两个单例作用域的 bean。
在 Spring 源码中,
org.springframework.beans.factory.config.AbstractFactoryBean
就是典型的单例应用例子,其getObject()
方法判断需求对象为单例时,对象只会被初始化一次。将测试 bean 定义为:测试用例:
Spring 单例与单例设计模式的区别:
使用相同的类加载器来加载两个应用程序上下文,但是 ShoppingCart 实例不一样(firstContext 和 secondContext 是源于同一个类加载器的、不同的两个应用上下文对象,说明由这不同的上下文对象创建的 bean 不是单例);
当比较两次创建并属于相同上下文的实例时,则它们是相等的(由于 bean 是单例作用域的,无论多少次从 firstContext 获取,都是同一个实例)。
简单工厂模式(Simple Factory)
在创建一个对象时不向客户暴露内部细节,并提供一个创建对象的通用接口。
实例化的操作单独放到工厂类中,让它来决定应该用哪个具体子类来实例化。
能解耦客户类和具体子类,客户类不需要知道有哪些子类、应当实例化哪个子类:客户类往往有多个,如果不使用简单工厂,所有客户类都要知道所有子类的细节。而且一旦子类发生改变,所有的客户类都要进行修改。
实现
Bad Case
以下的 SimpleFactory 是简单工厂实现,它被所有需要进行实例化的客户类调用。
工厂方法模式(Factory Method)
定义了创建对象的接口,但由子类决定实例化的类,工厂方法把实例化操作推迟到子类。
简单工厂创建对象的是另一个类,而工厂方法是由子类来创建对象。
图中
Factory
有一个doSomething()
方法,需要用到一个产品对象,产品对象由factoryMethod()
方法创建。该方法是抽象的,需要由子类去实现(每种工厂对应一种产品)。实现
应用
JDK 中的应用
Spring 中的应用
通过公共静态方法(如
getInstance
、valueOf
等)进行对象实例化,在 Spring 源码中,可以通过指定工厂方法来创建 bean,在 xml 文件中加入 bean 的配置:其中指定工厂方法为
createWelcomer
,这个 bean 对应的类需要提供名为createWelcomer
的静态方法:Spring 容器创建这个 bean 时,就会通过静态工厂方法
createWelcomer
实现,而不是传统的构造方法;这个方法接受一些参数标签,通常保留给传统构造方法。抽象工厂模式(Abstract Factory)
提供接口用于创建相关的对象家族:即很多相关的对象必须一起创建出来(工厂方法模式只创建一个对象)。
其使用工厂方法模式创建单一对象,
AbstractFactory
中的createProductA()
和createProductB()
方法都让子类实现,这两个方法单独来看就是在创建一个对象,符合工厂方法模式的定义。对象家族:
Client
通过AbstractFactory
同时调用两个方法来创建出相关的两个对象。从高层次来看抽象工厂使用组合,即
Cilent
组合了AbstractFactory
,而工厂方法模式使用了继承。实现
应用
JDK 中的应用
Spring 中的应用
类似工厂方法,区别在于抽象工厂有对应抽象产品,抽象工厂定义了构建产品对象方法,在 Spring 源码中抽象工厂的例子是 Bean 的创建。
org.springframework.beans.factory.BeanFactory
接口对应实现的抽象类是AbstractBeanFactory
,通过它的实现可以从 Spring 容器访问 bean,每个实现工厂对应不同的加载源(xml、classpath 等),通过 getBean 方法返回已创建的对象(共享实例,单例作用域)或初始化新的对象(原型作用域):第一个输出为
DefaultListableBeanFactory
,即默认的实现工厂,从该工厂获取产品、并强制转换为 ShoppingCart 类型。建造者模式(Builder)
用于简化复杂对象的构造:封装对象构造过程,并允许按步骤构造。
将参数传递给类的内部静态类的 setter 方法分解对象构造过程(支持链式调用),最终调用其 build 方法返回类的实例。
实现
以下是一个简易的 StringBuilder 实现,参考了 JDK 1.8 源码。
应用
JDK 中的应用
Spring 中的应用
在 Spring 源码中,
org.springframework.beans.factory.support.BeanDefinitionBuilder
类用于以编程的方式定义 bean,为AbstractBeanDefinition
抽象类的实现设置值(作用域、工厂方法、属性等)。原型模式(Prototype)
使用原型实例指定要创建对象的类型,通过复制这个原型来创建新对象。
实现
应用
JDK 中的应用
Spring 中的应用
原型模式与原型作用域(prototype)类似,通过复制已存在的对象来创建一个新的对象实例,创建出的副本所有属性与复制对象的属性相同。
在 Spring 源码中,
org.springframework.beans.factory.support.AbstractBeanFactory
可以初始化 bean 原型作用域,基于 xml 配置文件中 bean 的定义:ShoppingCart 实例是直接从 bean 定义创建的。最初 cart1 和 cart2 对象的 id 值为9,它在测试结束时被修改,以证明两个引用都属于两个不同的对象(注意每次调用
getBean
创建的对象,生命周期都交由开发者管理,而不再是 Spring IoC 容器)。Beta Was this translation helpful? Give feedback.
All reactions