一、 Bean的配置
Spring 容器支持 XML 和 Properties 两种格式的配置文件,通常使用的是xml,xml配置文件的根元素是<beans>,<beans>中包含了多个<bean>子元素,每一个<bean>子元素定义了一个Bean,并描述了该Bean如何被装配到Spring容器中。下表则列出了bean中常用属性及子元素表:
属性或子元素名称 | 描述 |
---|---|
id | 是一个 Bean 的唯一标识符,Spring容器对Bean的配置、管理都通过该属性来完成 |
name | Spring 容器同样可以通过此属性对容器中的Bean进行配置和管理,name 属性中可以为Bean指定多个名称,每个名称之间用逗号或分号隔开 |
class | 该属性指定了Bean的具体实现类,它必须是一个完整的类名,使用类的全限定名 |
scope | 用来设定Bean实例的作用域,其属性值有:singleton(单例)、prototype(原型)、request、session、global Session、application 和 websocket。其默认值为 singleton |
constructor-arg | <bean>元素的子元素,可以使用此元素传入构造参数进行实例化。该元素的index属性指定构造参数的序号(从0开始),type属性指定构造参数的类型,参数值可以通过ref 属性或value 属性直接指定,也可以通过ref 和value 子元素指定 |
property | <bean>元素的子元素,用于调用Bean实例中的setter方法完成属性赋值,从而完成依赖注入。该元素的name属性指定Bean实例中的相应属性名,ref属性或value属性用于指定参数值 |
ref | <property>、<constructor-arg>等元素的属性或子元素,可以用于指定对Bean工厂中某个Bean实例的引用 |
value | <property>、<constructor-arg>等元素的属性或子元素,可以用于直接制定一个常量值 |
list | 用于封装List或数组类型的依赖注入 |
set | 用于封装Set类型属性的依赖注入 |
map | 用于封装Map类型属性的依赖注入 |
entry | <map>元素的子元素,用于设置一个键值对。其key 属性指定字符串类型的键值,ref或value的子元素指定其值,也可以通过value-ref 或 value属性指定其值 |
通常一个普通的bean只需要定义id(或name)和class两个属性即可,若Bean中未指定id和name,则Spring会将class值当作id使用
二、 Bean的实例化
1、构造器实例化
创建一个Bean1类
public class Bean1 {
}
applicationContext.xml配置文件中添加一个bean
<bean id="bean1" class="com.itheima.ioc.Bean1"/>
创建一个测试类
public class TestBean {
@Test
public void TestBeanFun(){
ApplicationContext appc = new ClassPathXmlApplicationContext("applicationContext.xml");
Bean1 bean1 = appc.getBean("bean1",Bean1.class);
System.out.println(bean1);
}
}
运行结果:
Spring 容器 ApplicationContext会加载配置文件。在加载时,Spring容器会通过id为‘bean1’的实现类Bean1中的默认的无参构造方法对Bean进行实例化。
2、 静态工厂方式实例化
使用静态工厂是实例化Bean的另一种方式。该方式要求开发者创建一个静态工厂的方法来创建Bean的实例,其Bean配置中的class属性所指定的不再是Bean实例的实现类,而是静态工厂类,同时还需要使用factory-method属性来指定所创建的静态工厂方法。
创建一个Bean2类
public class Bean2 {
}
创建一个MyBean2Factory类并创建一个静态方法返回Bean2的实例
public class MyBean2Factory {
public static Bean2 createBean() {
return new Bean2();
}
}
在src下创建一个bean2.xml的配置文件,如下编辑
<?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">
<bean id="bean2"
class="com.itheima.factory.MyBean2Factory"
factory-method="createBean"/>
</beans>
上述配置中,首先通过
创建一个测试类测试静态工厂方式实例化Bean
public class TestFactory {
@org.junit.Test
public void Test() {
ApplicationContext appc = new ClassPathXmlApplicationContext("bean2.xml");
System.out.println(appc.getBean("bean2"));
}
}
运行结果如图所示:
3、实例工厂方式实例化
此种工厂方式的工厂类中,不再使用静态方法创建Bean实例,而是采用直接创建Bean实例的方式。同时,在配置文件中,需要实例化的Bean也不是通过class属性直接指向的实例化类,而是通过factory-bean属性指向配置的实例工厂方式的使用。
创建一个Bean3类
public class Bean3 {
}
创建工厂类MyBean3Factory,在类中使用默认无参构造方法输出“bean3工厂实例化中”语句,并使用createBean()方法创建Bean3对象
public class MyBean3Factory {
public MyBean3Factory() {
System.out.println("bean3 工厂实例化中");
}
public Bean3 createBean() {
return new Bean3();
}
}
创建bean3.xml配置文件,如下所示:
<?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">
<bean id="myBean3Factory"
class="com.itheima.factory.MyBean3Factory"/>
<bean id="bean3" factory-bean="myBean3Factory"
factory-method="createBean"/>
</beans>
上述配置中,首先配置了一个工厂Bean,然后配置了需要实例化的Bean。在id为bean3的bean中,使用factory-bean属性指向配置的实例工厂,该属性值就是工厂Bean的id。使用factory-method属性来确定使用工厂中的createBean()方法
创建测试类
public class TestFactory3 {
@org.junit.Test
public void Test() {
ApplicationContext appc = new ClassPathXmlApplicationContext("bean3.xml");
System.out.println(appc.getBean("bean3"));
}
}
测试输出结果如下:
三、 Bean的作用域
1、作用域的种类
作用域名称 | 说明 |
---|---|
singleton(单例) | 使用singleton定义的Bean在Spring容器中将只有一个实例,也就是说,无论有多少个Bean引用它,始终将指向同一个对象。这也是Spring容器默认的作用域 |
prototype(原型) | 每次通过Spring容器获取的prototype定义的Bean时,容器都将创建一个新的Bean实例 |
request | 在一次http请求中,容器会返回该Bean的同一个实例。对不同的HTTP请求则会产生一个新的Bean,而且该Bean仅在当前HTTP Request内有效 |
session | 再一次HTTP Session 中,容器会返回该Bean的同一个实例。对不同的HTTP 请求则会产生一个新的Bean,而且该Bean仅在当前HTTP Session有效 |
globalSession | 在一个全局的HTTP Session中,容器会返回该Bean的同一个实例。仅在使用portlet上下文时有效 |
application | 为每个ServletContext对象创建一个实例。仅在Web相关的ApplicationContext中生效 |
websocket | 为每个websocket对象创建一个实例。仅在Web相关的ApplicationContext中生效 |
2、 singleton作用域
singleton 是 Spring 容器的默认作用域,当Bean的作用域为singleton时,Spring容器就只会存在一个共享的Bean实例,并且所有对Bean的请求,只要id与该Bean的id属性相匹配,就会返回同一个Bean实例。singleton 作用域对于无会话状态的Bean(如Dao组件,service组件)来说,时最理想的选择。
在Spring配置文件中,Bean的作用域是通过<bean>元素的scope
属性来指定的,该属性值可设置为 singleton、prototype、request、session、globalSession、application 和 websocket。
例如:
<bean id="scope" class="com.cn.scpoe" scope="singleton"/>
创建一个Scope类
public class Scope {
}
创建bean4.xml配置如下
<?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">
<bean id="scope" class="com.itheima.scope.Scope" scope="singleton"/>
</beans>
创建测试类测试singleton作用域
public class TestScope {
@org.junit.Test
public void Test() {
ApplicationContext appc = new ClassPathXmlApplicationContext("bean4.xml");
System.out.println(appc.getBean("scope"));
System.out.println(appc.getBean("scope"));
}
}
输出结果如下:
由结果可知两次输出结果相同,这说明Spring容器只创建了一个Scope类的实例。需要注意的是,如果不设置scope="singleton",其结果也是一个实例,因为Spring容器默认作用域就是singleton
3、 prototype作用域
对需要保持回话状态的Bean(如Struts2的Action类)应该使用prototype作用域。在使用prototype作用域时,spring容器会为每个对该Bean的请求都创建一个新的实例。
将标题2、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">
<bean id="scope" class="com.itheima.scope.Scope" scope="prototype"/>
</beans>
再次运行测试程序结果如图:
结果说明两次输出的Bean实例并不相同,这说明该在prototype作用域下,创建了两个不同的Scope实例
四、 Bean的生命周期
Spring容器可以管理singleton作用域的Bean的生命周期,在此作用域下,Spring能够精确地知道该Bean何时被创建,何时初始化完成以及何时被销毁。对于prototype作用域的Bean,Spring只负责创建,当容器创建了Bean实例后,Bean的实例就交给客户端代码来管理,Spring容器将不再跟踪其生命周期。每次客户端请求prototype作用域的Bean时,Spring容器都会创建一个新的实例并且不会管那些被配置成prototype作用域的Bean的生命周期。
在一般情况下常会在Bean的postinitiation(初始化后)和predestruction(销毁前)执行一些相关操作。
Spring生命周期如下图所示:
从图中可以分析,Bean的生命周期的整个执行过程描述如下:
- 根据配置情况调用Bean构造方法或工厂方法实例化Bean。
- 利用依赖注入完成Bean中所有属性值得配置注入。
- 如果Bean实现了BeanNameAware接口,则Spring调用Bean的setBeanName()方法传入当前Bean的id值。
- 如果Bean实现了BeanFactoryAware接口,则Spring调用setBeanFactory()方法传入当前工厂实例的引用。
- 如果Bean实现了ApplicationContextAware接口,则Spring调用setApplicationContext()方法传入当前ApplicationContext实例的引用。
- 如果BeanPostProcessor和Bean关联,则Spring将调用该接口的预初始化方法postProcessBeforeInitialzation()对Bean进行加工操作,这个非常重要,Spring的AOP就是用它实现的。
- 如果Bean实现了InitializingBean接口,则Spring将调用afterPropertiesSet()方法。
- 如果有配置文件中通过init-method 属性指定了初始化方法,则调用该初始化方法。
- 如果有BeanPostProcessor和Bean关联,则Spring将调用该接口的初始化方法postProcessAfterInitialization()。此时,Bean已经可以被应用系统使用了。
- 如果在
中指定了该Bean的作用范围为scope = "singleton",则将该Bean放入Spring IOC 的缓存池中,将触发Spring对该Bean的生命周期管理;如果在 中指定了该Bean的作用范围为scope = "prototype",则将该Bean交给调用者,调用者管理该Bean的生命周期,Spring不再管理该Bean。 - 如果Bean实现了DisposableBean接口,则Spring会调用destory()方法将Spring中的Bean销毁;如果在配置文件中通过destory-method属性指定了Bean的销毁方法,则Spring将调用该方法进行销毁。