Spring中Bean的实例化过程
Spring中Bean的实例化过程
了解Bean的是实例化模式
默认情况下,Spring中的所有bean都是单例的。这意味着Spring维护一个bean的实例,所有依赖对象都使用同一个实例,对ApplicationContext.getBean()的所有调用都会返回同一个实例。可以使用标识比较(==)而不是equals()比较来检查bean是否相同。
术语
单例
(singleton)在Java中可互换使用,分别指不同的概念:应用程序中具有单个实例的对象以及Singleton设计模式。通常将第一个概念成为单例
,而将Singleton设计模式简称为Singleton。如果把使用单例实例与使用Singleton设计模式的需求搞混淆,就会出现问题。以下代码片段显示了Java中Singleton设计模式的典型实现:
public class Singleton{
private static Singleton instance;
static {
instance = new Singleton();
}
public static Singleton getInstance(){
return instance;
}
}
虽然上述设计模式实现了自己的目标,即允许在整个应用程序中维护和访问单个类的实例,但这样做的代价是增加了耦合。应用程序代码必须对Singleton类有明确的了解才能获得实例,从而完全清除了对接口进行编码的能力。
实际上,Singleton设计模式是两种模式。第一种模式和预期的一样,主要用来维护对象的单个实例。第二种模式是一种用于查找对象的模式,该模式不太常用,因为它完全消除了使用接口的可能性。使用Singleton设计模式也使得任意替换实现变得更困难,因为大多数需要Singleton实例的对象都直接访问Singleton对象。当试图对应用程序进行单元测试时,这可能会导致各种令人头疼的问题,因为无法用模拟替代Singleton以进行测试。
幸运的是,如果使用Spring,则可以利用单例实例化模型,而无须使用Singleton设计模式。Spring种的所有bean默认情况下都创建Singleton实例,Spring使用相同的实例来完成对bean的所有请求。当然,Spring不仅限于使用Singleton实例:仍然可以创建新的bean的实例,以满足每个依赖项以及对getBean()的调用。Spring在完成这些工作的同时不会对应用程序代码产生任何影响,因此,通常将Spring称为实例化模式不可知的。这是一个强大的概念。如果正在使用一个单例对象,但发现它并不合适多线程访问,那么可以将它改为非单例(原型),同时不会影响任何应用程序代码。
@Component
@Scope("prototype")
public class Singleton{
}
选择实例化模式
大多数情况下,很容易看出哪种实例化模式是合适的。通常单例是bean的默认模式。一般来说,单例应该在下列情况下使用:
- 没有状态的共享对象: 假设有一个不保持状态且有许多依赖对象的对象。因为如果没有状态就不需要同步,所以每次依赖对象需要使用该对象进行一些处理时,不需要创建bean的新实例。
- 具有制度状态的共享对象: 这与前一种情况类似,但有一些只读状态。在这种情况下,仍然不需要同步,所以每次创建一个实例来满足bean的每个请求只会增加开销。
- 具有共享状态的共享对象: 如果有一个必须共享状态的bean,那么单例是最理想的选择。在这种情况下,确保状态写入同步尽可能细化。
- 具有可写状态的高通量(high-throughput)对象: 如果有一个bean在应用程序种被大量使用,那么你会发现相对于不断创建数百个bean实例,使用一个单例并保持对该bean状态的所有写访问同步会获得更好的性能。当使用这种方法时,保持同步要尽可能精细,同时不要牺牲一致性。当应用程序长时间创建大量实例时,当共享对象只有少量可写状态时,以及当实例化新实例的代价非常高时,你会发现这种方法特别有用。
在以下情况下,应该考虑使用非单例:
- 具有可写状态的对象: 如果有一个拥有大量可写状态的bean,那么你会发现保持同步所需要的成本大于创建新实例以处理来自依赖的每个请求的成本。
- 具有私有状态的对象: 某些依赖对象需要具有私有状态的bean,以便它们可以于依赖于该bean的其他对象分开进行。在这种情况下,单例显然不合适,应该使用非单例。
从Spring的实例化管理中获得的主要好处是,只需要付出很少的努力,应用程序就可以与单例相关的较低内存使用率中受益。如果发现单例模式无法满足应用程序的需要,那么修改配置以使用非单例模式也是一项很容易完成的任务。
实现bean作用域
除单例和原型作用域外,当需要为更特定的目的而定义Spring bean时,还可以使用其他作用域。也可以实现自己的自定义作用域,并在Spring和ApplicationContext中注册它。Spring从版本4起支持以下bean作用域:
- 单例作用域: 默认为单例作用域。每个Spring IoC容器只会创建一个对象。
- 原型作用域: 当应用程序请求时,Spring将创建一个新的实例。
-
请求作用域: 用于Web应用程序。当为Web应用程序使用SpringMVC时,首先针对每个HTTP
请求
实例化带有请求作用域的bean,然后在请求完成时销毁。 -
会话作用域: 用于Web应用程序。当为Web应用程序使用SpringMVC时,首先针对每个HTTP
会话
实例化带有请求作用域的bean,然后在请求完成时销毁。 - 全局会话作用域: 用于基于Portlet的Web应用程序。带有全局会话作用域的bean可以在同一个SpringMVC驱动的门户应用程序的所有Portlet之间共享。
- 线程作用域: 当一个新线程请求bean实例的时,Spring将创建一个新的bean实例,而对于同一线程,返回相同的bean实例。请注意,线程作用域默认情况下未注册。
-
自定义作用域: 可以通过实现
org.springframework.beans.factory.config.Scope
接口创建自定义作用域,并在Spring配置中注册自定义作用域(对于XML,请使用org.springframework.beans.factory.config.CustomScopeConfigurer
类)。
Spring Bean的生命周期
- 引用
- 《Spring 5高级编程》(第5版)
上一篇: Spring中实例化bean的方法
下一篇: 深度优先和广度优先的Python实现