Spring基础知识(5)- Spring Bean (二)
Spring Bean作用域、Spring Bean生命周期
1. Spring Bean作用域
Spring Bean 的 5 种作用域:
作用域 | 描述 |
singleton | 单例模式,表示在 Spring 容器中只有一个 Bean 实例 |
prototype | 原型模式,表示每次通过 Spring 容器获取 Bean 时,容器都会创建一个新的 Bean 实例。 |
request | 每次 HTTP 请求,容器都会创建一个 Bean 实例。该作用域只在当前 HTTP Request 内有效。 |
session | 同一个 HTTP Session 共享一个 Bean 实例,不同的 Session 使用不同的 Bean 实例。该作用域仅在当前 HTTP Session 内有效。 |
application |
同一个 Web 应用共享一个 Bean 实例,该作用域在当前 ServletContext 内有效。 与 singleton 类似,但 singleton 表示每个 IoC 容器中仅有一个 Bean 实例,而一个 Web 应用中可能会存在多个 IoC 容器,但一个 Web 应用只会有一个 ServletContext,也可以说 application 才是 Web 应用中货真价实的单例模式。 |
注:singleton 和 prototype 可以直接在常规的 Spring IoC 容器(例如 ClassPathXmlApplicationContext)中使用,其它的基于 Web 的 ApplicationContext 实现(例如 XmlWebApplicationContext)中使用。
1)singleton
当 Bean 的作用域为 singleton 时,Spring IoC 容器中只会存在一个共享的 Bean 实例,这个 Bean 被称为 singleton bean。
在没有指定作用域时,Bean 的作用域被默认为 singleton。
Spring 配置文件(XML格式),配置格式如下:
注解配置格式如下:
@Component
@Scope("singleton")
class TestBean {
}
示例:
1 package com.example; 2 3 import org.springframework.context.support.ClassPathXmlApplicationContext; 4 5 public class BeanApp { 6 7 public static void main( String[] args ) { 8 9 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml"); 10 TestBean testBean1 = (TestBean) context.getBean("testBean"); 11 TestBean testBean2 = (TestBean) context.getBean("testBean"); 12 13 System.out.println(testBean1); 14 System.out.println(testBean2); 15 } 16 } 17 18 class TestBean { 19 private String name; 20 21 public void setName(String name) { 22 this.name = name; 23 } 24 }
输出:
com.example.TestBean@3901d134
com.example.TestBean@3901d134
从控制台的输出可以看出,两次获得的 Bean 实例的地址完全一样,这说明 IoC 容器只创建了一个 singletonBean 实例。singleton 是默认作用域,因此即使省略 scope 属性,控制台的输出结果也一样的。
2)prototype
当 Bean 的作用域为 prototype 时,Spring 容器会在每次请求该 Bean 时,都创建一个新的 Bean 实例,这个 Bean 被称为 prototype bean。
Spring 配置文件(XML格式),配置格式如下:
注解配置格式如下:
@Component
@Scope("prototype")
class TestBean {
}
使用 singleton 的示例代码,输出:
com.example.TestBean@3901d134
com.example.TestBean@14d3bc22
从控制台的输出可以看出,两次获得的 Bean 实例的地址不一样,这说明在 prototype 作用域下,Spring 容器创建了两个不同的 prototypeBean 实例。
3)request
request 只适用于 Web 程序,每一次 HTTP 请求都会产生一个新的 bean,同时该 bean 仅在当前 HTTP request 内有效,当请求结束后,该对象的生命周期即告结束。
Spring 配置文件(XML格式),配置格式如下:
注解配置格式如下:
@Component
@Scope("request")
class TestBean {
}
4) session
session 只适用于Web程序,session 作用域表示该针对每一次 HTTP 请求都会产生一个新的 bean,同时该 bean 仅在当前 HTTP session 内有效。
Spring 配置文件(XML格式),配置格式如下:
注解配置格式如下:
@Component
@Scope("session")
class TestBean {
}
5)globalSession
global session 作用域类似于标准的 HTTP session 作用域,不过仅仅在基于 portlet 的 web 应用中才有意义。Portlet 规范定义了全局 Session 的概念,它被所有构成某个 portlet web 应用的各种不同的 portlet 所共享。在global session 作用域中定义的 bean 被限定于全局portlet Session的生命周期范围内。
Spring 配置文件(XML格式),配置格式如下:
注解配置格式如下:
@Component
@Scope("globalSession")
class TestBean {
}
2. Spring Bean生命周期
在传统的 Java 应用中,Bean 的生命周期很简单,使用 Java 关键字 new 进行 Bean 的实例化后,这个 Bean 就可以使用了。一旦这个 Bean 长期不被使用,Java 自动进行垃圾回收。
相比之下,Spring 中 Bean 的生命周期较复杂,大致可以分为以下 5 个阶段:
(1) Bean 的实例化
(2) Bean 属性赋值
(3) Bean 的初始化
(4) Bean 的使用
(5) Bean 的销毁
1)Spring 生命周期流程
Spring Bean 的完整生命周期从创建 Spring IoC 容器开始,直到最终 Spring IoC 容器销毁 Bean 为止,Bean 生命周期的整个执行过程描述如下:
(1) Spring 启动,查找并加载需要被 Spring 管理的 Bean,对 Bean 进行实例化。
(2) 对 Bean 进行属性注入。
(3) 如果 Bean 实现了 BeanNameAware 接口,则 Spring 调用 Bean 的 setBeanName() 方法传入当前 Bean 的 id 值。
(4) 如果 Bean 实现了 BeanFactoryAware 接口,则 Spring 调用 setBeanFactory() 方法传入当前工厂实例的引用。
(5) 如果 Bean 实现了 ApplicationContextAware 接口,则 Spring 调用 setApplicationContext() 方法传入当前 ApplicationContext 实例的引用。
(6) 如果 Bean 实现了 BeanPostProcessor 接口,则 Spring 调用该接口的 postProcessBeforeInitialzation() 方法对 Bean 进行加工操作,此处非常重要,Spring 的 AOP 就是利用它实现的。
(7) 如果 Bean 实现了 InitializingBean 接口,则 Spring 将调用 afterPropertiesSet() 方法。
(8) 如果在配置文件中通过 init-method 属性指定了初始化方法,则调用该初始化方法。
(9) 如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的 postProcessAfterInitialization()方法 。此时,Bean 已经可以被应用系统使用了。
(10) 如果在
(11) 如果 Bean 实现了 DisposableBean 接口,则 Spring 会调用 destory() 方法销毁 Bean;如果在配置文件中通过 destory-method 属性指定了 Bean 的销毁方法,则 Spring 将调用该方法对 Bean 进行销毁。
2) 生命周期回调方法
Bean 的生命周期回调方法主要有两种:
(1) 初始化回调方法:在 Spring Bean 被初始化后调用,执行一些自定义的回调操作。
(2) 销毁回调方法:在 Spring Bean 被销毁前调用,执行一些自定义的回调操作。
通过以下 3 种方式自定义:
(1) 通过接口实现
(2) 通过 XML 配置实现
(3) 使用注解实现
Bean 中有多种生命周期回调方法时,优先级顺序为:注解 > 接口 > XML 配置。
3) 使用接口实现回调方法
可以在 Spring Bean 的 Java 类中,通过实现 InitializingBean 和 DisposableBean 接口,指定 Bean 的生命周期回调方法。
方法 | 描述 |
afterPropertiesSet() | InitializingBean 接口的初始化回调,指定初始化回调方法,这个方法会在 Spring Bean 被初始化后被调用,执行一些自定义的回调操作。 |
destroy() | DisposableBean 接口的销毁回调,指定销毁回调方法,这个方法会在 Spring Bean 被销毁前被调用,执行一些自定义的回调操作。 |
*注:通常情况下,不建议通过这种方式指定生命周期回调方法,这是由于这种方式会导致代码的耦合性过高。
Spring 配置文件(XML格式),配置格式如下:
示例:
1 package com.example; 2 3 import org.springframework.context.support.ClassPathXmlApplicationContext; 4 import org.springframework.beans.factory.DisposableBean; 5 import org.springframework.beans.factory.InitializingBean; 6 7 public class App { 8 9 public static void main( String[] args ) { 10 11 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml"); 12 13 DemoBean demoBean = (DemoBean) context.getBean("demoBean"); 14 demoBean.display(); 15 context.close(); 16 } 17 } 18 19 class DemoBean implements InitializingBean, DisposableBean { 20 21 private String name; 22 23 public void setName(String name) { 24 this.name = name; 25 } 26 27 public void display() { 28 System.out.println("DemoBean -> name = " + name); 29 } 30 31 @Override 32 public void afterPropertiesSet() throws Exception { 33 System.out.println("DemoBean -> afterPropertiesSet()"); 34 } 35 36 @Override 37 public void destroy() throws Exception { 38 System.out.println("DemoBean -> destroy()"); 39 } 40 41 }
输出:
DemoBean -> afterPropertiesSet()
DemoBean -> name = Life Cycle
DemoBean -> destroy()
4)使用 XML 配置实现回调方法
可以在 Spring 的 XML 配置中,通过
属性 | 描述 |
init-method | 指定初始化回调方法,这个方法会在 Spring Bean 被初始化后被调用,执行一些自定义的回调操作。 |
destory-method | 指定销毁回调方法,这个方法会在 Spring Bean 被销毁前被调用,执行一些自定义的回调操作。 |
Spring 配置文件(XML格式),配置格式如下:
...
如果
...
示例:
1 package com.example; 2 3 import org.springframework.context.support.ClassPathXmlApplicationContext; 4 5 6 public class App { 7 8 public static void main( String[] args ) { 9 10 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml"); 11 12 XMLDemoBean xmlDemoBean = (XMLDemoBean) context.getBean("xmlDemoBean"); 13 xmlDemoBean.display(); 14 context.close(); 15 } 16 } 17 18 19 class XMLDemoBean { 20 private String name; 21 22 public void setName(String name) { 23 this.name = name; 24 } 25 26 public void display() { 27 System.out.println("XMLDemoBean -> name = " + name); 28 } 29 30 public void init() { 31 System.out.println("XMLDemoBean -> init()"); 32 } 33 34 public void destroy() { 35 System.out.println("XMLDemoBean -> destroy()"); 36 } 37 }
输出:
XMLDemoBean -> init()
XMLDemoBean -> name = XML Life Cycle
XMLDemoBean -> destroy()
5)使用注解实现回调方法
可以通过 JSR-250 的 @PostConstruct 和 @PreDestroy 注解,指定 Bean 的生命周期回调方法。
注解 | 描述 |
@PostConstruct | 指定初始化回调方法,这个方法会在 Spring Bean 被初始化后被调用,执行一些自定义的回调操作。 |
@PreDestroy | 指定销毁回调方法,这个方法会在 Spring Bean 被销毁前被调用,执行一些自定义的回调操作。 |
示例:
1 package com.example; 2 3 import org.springframework.context.support.ClassPathXmlApplicationContext; 4 import javax.annotation.PostConstruct; 5 import javax.annotation.PreDestroy; 6 import org.springframework.stereotype.Component; 7 import org.springframework.beans.factory.annotation.Value; 8 9 public class App { 10 11 public static void main( String[] args ) { 12 13 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml"); 14 15 AnnotationDemoBean annotationDemoBean = (AnnotationDemoBean) context.getBean("annotationDemoBean"); 16 annotationDemoBean.display(); 17 context.close(); 18 } 19 } 20 21 @Component("annotationDemoBean") 22 class AnnotationDemoBean { 23 @Value("Annotation Life Cycle") 24 private String name; 25 26 public void setName(String name) { 27 this.name = name; 28 } 29 30 public void display() { 31 System.out.println("AnnotationDemoBean -> name = " + name); 32 } 33 34 @PostConstruct 35 public void init() { 36 System.out.println("AnnotationDemoBean -> init()"); 37 } 38 39 @PreDestroy 40 public void destroy() { 41 System.out.println("AnnotationDemoBean -> destroy()"); 42 } 43 }
输出:
AnnotationDemoBean -> init()
AnnotationDemoBean -> name = Annotation Life Cycle
AnnotationDemoBean -> destroy()