Bean的作用域
以Spring4.3为例,其中为Bean的实例化定义了7种作用域分别为:
1.singleton(单例):使用singleton定义的Bean在Spring容器中将只有一个实例,也就是说,无论有多少个Bean引用它,始终将指向同一个对象。这也是Spring容器默认的作用域。
2.prototype(原型):每次通过Spring容器获取的prototype定义的Bean时,容器都将创建一个新的Bean实例。
3.request:在一次HTTP请求中,容器会返回该Bean的同一个实例。对不同的HTTP请求则会产生一个新的Bean。而且该Bean仅在当前HTTP Request内有效。
4.session:在一次HTTP Session中,容器会返回该Bean的同一个实例。对不同的HTTP请求则会产生一个新的Bean,而且该Bean仅在当前HTTP Session内有效。
5.globalSession:在一个全局的HTTP Session中,容器会返回该Bean的同一个实例。仅在使用portlet上下文时有效。
6.application:为每个ServletContext对象创建一个实例。仅在Web相关的ApplicationContext中生效。
7.websocket:为每个websocket对象创建一个实例。仅在Web相关的ApplicationContext中生效。
这7中作用域中最为常用的就是singleton(单例)和prototype(原型) 两种作用域,根据这两种最为常用的作用域,编写简单程序来具体了解一下他们的效果和区别。
singleton作用域
singleton是Spring容器默认的作用域,当Bean的作用域为singleton时,Spring容器就只会存在一个共享的Bean实例。singleton作用域的Bean对于无会话状态的Bean(如Dao组件,Service组件)来说,是最理想的选择。
首先在src目录下创建一个包,在包中创建一个类命名为Scope,使用构造器实例化方法来实例它,也就是调用它的默认无参构造方法来完成实例化。再创建一个配置文件,在其中加入bean子元素,将其id值设置为scope其class属性设置为Scope类的全限定名,并加入新属性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-4.3.xsd">
<bean id="scope" class="beginner.scope.Scope" scope="singleton" />
</beans>
创建测试类,在测试类中编写一个main方法,依旧是先定义配置文件的路径,然后加载配置文件,最后输出获得的实例对象,代码如下:
package beginner.scope;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SingletonText {
public static void main(String[] args) {
// 定义配置文件路径
String xmlPath = "beginner/scope/Scope.xml";
// 加载配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
// 输出获得的实例
System.out.println(applicationContext.getBean("scope"));
System.out.println(applicationContext.getBean("scope"));
}
}
小技巧,Ctrl+Alt+下方向键可以直接复制一行代码。
通过执行结果可以发现,获得的两个实例对象是完全相同的,也就是同一个实例对象。符合singleton作用域的描述。
prototype作用域
prototype作用域对于需要保持会话状态的Bean(如Struts2的Action类)应该使用prototype作用域。在使用prototype作用域时,Spring容器会为每个对该Bean的请求都创建一个新的实例。
对于prototype作用域的实验可以在singleton的代码基础上稍作修改就可以完成。首先,在配置文件中,使用小技巧复制一行bean子元素的代码,将原本的注释掉,将新的bean子元素的id属性设置为scope1(可以不改,此处只是为了稍作区分),scope属性设置为prototype即可。
<?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-4.3.xsd">
<!-- <bean id="scope" class="beginner.scope.Scope" scope="singleton" /> -->
<bean id="scope1" class="beginner.scope.Scope" scope="prototype" />
</beans>
将测试类代码中,输出语句中applicationContext.getBean(“scope”); 的scope相应修改为scope1即可。
package beginner.scope;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SingletonText {
public static void main(String[] args) {
// 定义配置文件路径
String xmlPath = "beginner/scope/Scope.xml";
// 加载配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
// 输出获得的实例
System.out.println(applicationContext.getBean("scope1"));
System.out.println(applicationContext.getBean("scope1"));
}
}
在执行结果可以看到,虽然输出的两个实例对象都为scope1对象,但是地址却不同,说明这是两个不同的实例对象,符合prototype作用域的描述,每次都返回一个新的实例。
Bean的生命周期
了解Spring中Bean的声明周期的意义在于,可以利用Bean在其存活期间的特定时刻完成一些相关操作。这种时刻可能有很多,但一般情况下,常会在Bean的postinitiation(初始化后,如传递参数) 和 predestruction(销毁前,如关闭资源) 执行一些相关操作。
Spring容器可以管理Bean的某一部分作用域的生命周期。
singleton | prototype |
---|---|
在singleton作用域中Spring可以全程的跟踪,在此作用域下,Spring能够精确的知道该Bean何时被创建,何时实例化完成以及何时被销毁。 | 如果作用域是prototype的情况下,Spring只负责创建,当容器创建完成Bean实例后,Bean的实例就交给客户端代码来管理,Spring容器将不再跟踪其生命周期 |
Spring容器中Bean的详细生命周期流程图:
1.实例化Bean。
2.设置属性值,利用依赖注入,完成Bean里属性的注入。
3.(当Bean对象实现BeanNameAware(接口) 时)调用BeanNameAware(接口) 的setBeanName() 方法。
4.(当Bean对象实现BeanFactoryAware(接口) 时)调用BeanFactoryAware(接口) 的setBeanFactory() 方法。
5.(当Bean对象实现ApplicationContextAware(接口) 时)调用 ApplicationContextAware(接口) 的 setApplicationContext() 方法。
6.调用BeanPostProcessor 的预初始化方法(在自定义初始化方法之前执行)。
7.调用InitializingBean 的afterPropertiesSet() 方法。
8.调用定制的初始化方法(自定义的初始化方法)。
演示singleton作用域中Bean的声明周期。
首先在src目录下新建一个包,在包下新建一个类命名为life,使用构造器实例化方法完成实例化,在默认的无参构造方法中加入一条输出语句,输出:实例化完成,再在其中定义一个初始化方法和销毁方法,分别输出语句初始化完成和销毁完成。具体代码如下:
package beginner.life;
public class Life {
private Life() {
// 构造方法
System.out.println("实例化完成");
}
public void initMethod() {
// 初始化方法
System.out.println("初始化完成");
}
public void destroyMethod() {
// 销毁方法
System.out.println("销魂完成");
}
}
接下来编写配置文件,定义其id和class属性,设置init-method 属性为类中定义的初始化方法名称,destroy-method 属性定义为类中定义的销毁方法名称。由于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-4.3.xsd">
<bean id="life" class="beginner.life.Life"
init-method="initMethod" destroy-method="destroyMethod" />
</beans>
编写测试类,依旧先定义配置文件路径,不过这次使用ClassPathXmlApplicationContext 因为ClassPathXmlApplicationContext 提供了关闭容器的方法,这样Bean的销毁方法才会被调用。最后关闭容器。具体代码如下:
package beginner.life;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class LifeTest {
public static void main(String[] args) {
// 定义配置文件路径
String xmlPath = "beginner/life/Life.xml";
// ClassPathXmlApplicationContext在加载配置文件时对Bean进行实例化
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(xmlPath);
// ClassPathXmlApplicationContext 提供了关闭容器的方法,这样Bean的销毁方法才会被调用
context.close();
}
}
可以由执行结果看到在singleton作用域下Bean的生命周期流程为:
1.实例化
2.初始化
3.销毁