基础概念
Spring提供了两种容器类型:BeanFactory和ApplicationContext。
- BeanFactory。基础类型IoC容器,提供完整的IoC服务支持。()Spring框架提倡使用POJO,那么把每个业务对象看作一个JavaBean对象,或许更容易理解为什么Spring的IoC基本容器会起这么一个名字。)
- ApplicationContext。ApplicationContext在BeanFactory的基础上构建,是相对比较高级的容器实现,除了拥有BeanFactory的所有支持,ApplicationContext还提供了其他高级特性。
Bean的作用域:
- Spring容器最初提供了两种bean的scope类型:singleton和prototype,但发布2.0之后,又引入了另外三种scope类型,即request、session和global session类型。不过这三种类型有所限制,只能在Web应用中使用。
- 配置中的bean定义可以看作是一个模板,容器会根据这个模板来构造对象。但是要根据这个模板构造多少对象实例,又该让这些构造完的对象实例存活多久,则由容器根据bean定义的scope语意来决定。
- singleton:在Spring的IoC容器中只存在一个实例,所有对该对象的引用将共享这个实例。该实例从容器启动,并因为第一次被请求而初始化之后,将一直存活到容器退出,也就是说,它与IoC容器“几乎”拥有相同的“寿命”。(标记为singleton的bean是由容器来保证这种类型的bean在同一个容器中只存在一个共享实例;而Singleton模式则是保证在同一个Classloader中只存在一个这种类型的实例)。通常情况下,如果你不指定bean的scope,singleton便是容器默认的scope
- prototype:容器在接到该类型对象的请求的时候,会每次都重新生成一个新的对象实例给请求方。虽然这种类型的对象的实例化以及属性设置等工作都是由容器负责的,但是只要准备完毕,并且对象实例返回给请求方之后,容器就不再拥有当前返回对象的引用,请求方需要自己负责当前返回对象的后继生命周期的管理工作,包括该对象的销毁。也就是说,容器每次返回给请求方一个新的对象实例之后,就任由这个对象实例“自生自灭”
- request、session和global session:不像之前的singleton和prototype那么“通用”,因为它们只适用于Web应用程序,通常是与XmlWebApplicationContext共同使用
验证作用域
- 编写context的工具类
@Component
public class ContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ContextUtil.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
public static <T> T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz);
}
public static <T> T getBean(String name, Class<T> clazz) {
return getApplicationContext().getBean(name, clazz);
}
}
- 编写如下示例
@RestController
//@Scope("prototype")
public class DemoController {
@RequestMapping("/show")
public String toShow(ModelMap model) {
System.out.println(++i);
System.out.println(SpringContextUtil.getBean(DemoController.class));
return "show";
}
@RequestMapping("/test")
public String test() {
System.out.println(++i);
System.out.println(SpringContextUtil.getBean(DemoController.class));
return "test";
}
}
多次交替调用上面两个请求可以看到(默认scope为singleton):
- 数字i跟随调用次数递增
- DemoController每次输出的一样的,仅生成了一个
- @Scope(“singleton”)注解,对这个bean的请求都会返回这个唯一的实例。
在单例中,每次拿到的都是DemoController的一个实例,但是所在的线程却不是一个,多次请求中可能会有多个线程,不要搞混淆了这里。
使用@Scope(“prototype”)后,再多次调用,可以看到:
- 数字i每次都是1
- DemoController跟随请求每次都有变化,
- @Scope(“prototype”) 注解,将该类变成多例模式。对这个bean的每次请求都会创建一个新的bean实例,类似于new。
Best Practice:
- 用哪个scope?这决定于你所注入的对象是否包含状态。从实际开发来说,比较适合用单例模式的就是dao/service,因为他们一般不包含变化的成员变量
- 不要在controller中定义成员变量,万一必须要定义一个非静态成员变量时候,则通过注解@Scope(“prototype”),将其设置为多例模式,防止出现惊喜 ?