Spring中Bean的作用域与生命周期
作用域
singleton
Spring中的默认的作用域为singleton,即单例模式。
在xml配置文件中的bean标签属性可以省略scope="singleton"的配置。
对于同一个实现类无论调用多少次getBean()方法,只会生产一个bean对象。
例如下面代码:
// 创建一个接口
public interface UserDao {
public void say();
}
// 创建接口的实现类
public class UserDaoImpl implements UserDao {
@Override
public void say() {
System.out.println("userDaoImpl say ...");
}
}
编写xml配置文件,默认可以省略scope属性,因为默认为singleton实例。
也可以使用注解的方式规定作用域。@scope("singleton")
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 默认是scope="singleton",可以省略不写 -->
<bean class="top.lukeewin.scope.UserDaoImpl" id="user" scope="singleton"></bean>
</beans>
// 创建测试类
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SingletonTest {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean01.xml");
UserDao user = (UserDao) ctx.getBean("user");
UserDao user2 = (UserDao) ctx.getBean("user");
System.out.println(user == user2);
user.say();
}
}
运行结果:
从运行结果中可以得出结论:作用域定义为singleton时,只会产生一个实例,并且该实例由Spring容器产生,也由Spring管理。
prototype
原型模式。该模式下,只会由Spring创建,而管理交给客户代码。
xml配置文件中bean标签scope属性值为proptotype。在该模式下,每获取一个Bean对象都会重新创建。
代码如下:
我这里只写配置文件,其它的和上面的代码一样。
在原来的基础上增加一下代码
也可以使用注解的方式规定作用域:@scope("prototype")
<bean class="top.lukeewin.scope.UserDaoImpl" id="userDao" scope="prototype"></bean>
然后编写测试类
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SingletonTest {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean01.xml");
// singleton
UserDao user = (UserDao) ctx.getBean("user");
UserDao user2 = (UserDao) ctx.getBean("user");
// prototype
UserDao userDao = (UserDao) ctx.getBean("userDao");
UserDao userDao2 = (UserDao) ctx.getBean("userDao");
System.out.println(user == user2);
System.out.println(userDao == userDao2);
user.say();
}
}
运行结果:
从运行结果中可以得出结论:与singleton不同的是,作用域定义为prototype时,每次获取到的Bean实例都是不一样的。并且是由Spring产生,但是产生后就不属于Spring管理了,而是交给客户代码管理。
request
当一个bean的作用域为request,表示在一次HTTP请求中,一个bean定义对应一个实例;即每个HTTP请求都会有各自的bean实例,它们依据某个bean定义创建而成。该作用域仅在基于web的Spring ApplicationContext情形下有效。
session
当一个bean的作用域为session,表示在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
globalSession
当一个bean的作用域为globalSession,表示在一个全局的HTTP Session中,一个bean定义对应一个实例。典型情况下,仅在使用portlet context的时候有效。该作用域仅在基于web的Spring ApplicationContext情形下有效。
生命周期
Spring中Bean的实例化过程:
Bean的生命周期:
Bean实例化生命周期的执行过程如下:
- Spring对bean进行实例化,默认bean是单例;
- Spring对bean进行依赖注入;
- 如果bean实现了BeanNameAware接口,Spring将bean的名称传递给setBeanName()方法;
- 如果bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()方法,将BeanFactory实例传进来;
- 如果bean实现了ApplicationContextAware接口,它的setApplicationContext()方法将被调用,将应用上下文的引用传入到bean中;
- 如果bean实现了BeanPostProcessor接口,它的postProcessBeforeInitialization()方法将被调用;
- 如果bean中有方法添加了@PostConstruct注解,那么该方法将被调用;
- 如果bean实现了InitializingBean接口,spring将调用它的afterPropertiesSet()接口方法,类似的如果bean使用了init-method属性声明了初始化方法,该方法也会被调用;
- 如果在xml文件中通过
标签的init-method元素指定了初始化方法,那么该方法将被调用; - 如果bean实现了BeanPostProcessor接口,它的postProcessAfterInitialization()接口方法将被调用;
- 此时bean已经准备就绪,可以被应用程序使用了,他们将一直驻留在应用上下文中,直到该应用上下文被销毁;
- 如果bean中有方法添加了@PreDestroy注解,那么该方法将被调用;
- 若bean实现了DisposableBean接口,spring将调用它的distroy()接口方法。同样的,如果bean使用了destroy-method属性声明了销毁方法,则该方法被调用;
当scope="singleton"
时,默认情况下,会在Spring容器启动时实例化并完全初始化。如果想要延迟初始化,可以使用lazy-init="true"
来延迟初始化。
当scope="prototype"
时,默认情况下,只会实例化,并不会初始化,而是在请求bean时才初始化。在第一次请求每一个prototype的bean时,Spring容器都会调用其构造器创建这个对象,然后调用init-method属性值中所指定的方法。对象销毁的时候,Spring容器不会帮我们调用任何方法,因为是非单例,这个类型的对象有很多个,Spring容器一旦把这个对象交给你之后,就不再管理这个对象了。
Q.E.D.