一、Spring的三种实例化Bean的方式
Spring提供了三种实例化Bean的方式。
- 使用类构造器实例化
<bean id="personService" class="cn.itcast.service.impl.PersonServiceBean"></bean>
不难看出,我们以前使用的就是该方式。上面的配置默认使用的是PersonServiceBean类的默认构造函数来实例化PersonServiceBean对象的。
- 使用静态工厂方法实例化
我们在编码剖析Spring管理Bean的原理案例的基础上使用这种方式来实例化bean。
首先我们要在cn.itcast.service.impl包中创建一个工厂类——PersonServiceBeanFactory.java,其代码如下:
public class PersonServiceBeanFactory {
public static PersonServiceBean createPersonServiceBean() {
return new PersonServiceBean();
}
}
然后修改Spring的配置文件为:
<?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="personService" class="cn.itcast.service.impl.PersonServiceBean"></bean>
<bean id="personService2" class=" cn.itcast.service.impl.PersonServiceBeanFactory"
factory-method="createPersonServiceBean" />
</beans>
最后,将SpringTest类的改为:
public class SpringTest {
@Test
public void test() {
// ApplicationContext是接口
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); // 实例化Spring容器
PersonService personService = (PersonService) ctx.getBean("personService2"); // 从Spring容器取得bean
personService.save();
}
}
测试test()方法,Eclipse控制台打印如下:
- 使用实例工厂方法实例化
我们同样在编码剖析Spring管理Bean的原理案例的基础上使用这种方式来实例化bean。
首先我们要修改工厂类——PersonServiceBeanFactory.java的代码为:
public class PersonServiceBeanFactory {
public static PersonServiceBean createPersonServiceBean() {
return new PersonServiceBean();
}
public PersonServiceBean createPersonServiceBean2() {
return new PersonServiceBean();
}
}
紧接着修改Spring的配置文件为:
<?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="personService" class="cn.itcast.service.impl.PersonServiceBean"></bean>
<bean id="personService2" class=" cn.itcast.service.impl.PersonServiceBeanFactory"
factory-method="createPersonServiceBean" />
<bean id="personServiceBeanFactory" class="cn.itcast.service.impl.PersonServiceBeanFactory"></bean>
<bean id="personService3" factory-bean="personServiceBeanFactory" factory-method="createPersonServiceBean2"></bean>
</beans>
最后,将SpringTest类的改为:
public class SpringTest {
@Test
public void test() {
// ApplicationContext是接口
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); // 实例化Spring容器
PersonService personService = (PersonService) ctx.getBean("personService3"); // 从Spring容器取得bean
personService.save();
}
}
测试test()方法,Eclipse控制台打印如下:
Spring提供了三种实例化Bean的方式,那么到底该使用哪种方式较稳妥呢?应根据实际情况决定,但可这样说,90%的可能都是采用第一种方式,即使用类构造器实例化bean。
二、配置Spring管理的bean的作用域
Spring管理的bean的作用域有:
- singleton
在每个Spring IoC容器中,一个bean定义只有一个对象实例。
以Spring的三种实例化Bean的方式的案例为基础,我们举例说明。首先我们将Spring的配置文件——beans.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="personService" class="cn.itcast.service.impl.PersonServiceBean"></bean>
</beans>
然后再将SpringTest类的代码改为:
public class SpringTest {
@Test
public void test() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); // 实例化Spring容器
PersonService personService1 = (PersonService) ctx.getBean("personService"); // 从Spring容器取得bean
PersonService personService2 = (PersonService) ctx.getBean("personService");
System.out.println(personService1 == personService2); // 输出true,两个变量所引用的对象是同一个,证实了默认情况下这个bean交给Spring容器管理之后,这个bean是一个单实例。
}
}
最后,测试test()方法,Eclipse的控制台输出true,说明了默认情况下bean交给Spring容器管理之后,这个bean就是一个单实例(单例模式)的,即每次调用getBean()方法,获取到的都是同一个bean实例。
- prototype
现在我们有了一个新的需求,那就是在客户端每次调用getBean()方法,获取到的都是一个新的bean实例,这个时候就需要用到prototype作用域了。每次从容器获取bean都是新的对象。
为了证明这一点,我们只须将Spring的配置文件——beans.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="personService" class="cn.itcast.service.impl.PersonServiceBean" scope="prototype"></bean>
</beans>
SpringTest类的代码不用改动,此时测试test()方法,Eclipse的控制台会输出false。这就已经证实了若bean的作用域置为prototype,那么每次从Spring容器获取bean都将是新的对象。
- request
- session
- global session
三、Spring管理的Bean的生命周期
bean的初始化时机
前面讲解了Spring容器管理的bean的作用域。接着我们就要思考一个问题:bean到底是在什么时候才进行实例化的呢?我们以这个问题为引子来展开本文的说明。
bean对象无外乎是在以下两个时刻进行实例化的:
- 调用getBean()方法时。
- Spring容器启动时。
那么bean对象到底是在哪个时刻进行实例化的,这与Bean的作用域有着某种联系。我们以配置Spring管理的bean的作用域的案例为基础进行深入探讨。为了能够清楚地看到bean对象的实例化,我们需要修改PersonServiceBean类的代码为:
public class PersonServiceBean implements PersonService {
public PersonServiceBean() {
System.out.println("我被实例化了");
}
@Override
public void save() {
System.out.println("我是save()方法");
}
}
- 当Spring的配置文件——beans.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="personService" class="cn.itcast.service.impl.PersonServiceBean"></bean>
</beans>
即bean的作用域为singleton时,我们修改SpringTest类的代码为:
public class SpringTest {
@Test
public void test() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); // 实例化Spring容器
}
}
此时,测试test()方法,Eclipse控制台输出:
我被实例化了
这说明了当bean的作用域为singleton时,bean对象是在Spring容器启动时就进行创建了。即默认情况下会在容器启动时初始化bean,但我们也可以指定bean节点的lazy-init=“true”来延迟初始化bean,这时候,只有第一次获取bean会才初始化bean。如:
我们将Spring的配置文件——beans.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="personService" class="cn.itcast.service.impl.PersonServiceBean" lazy-init="true"></bean>
</beans>
此时,测试test()方法,Eclipse控制台根本就不会输出这句话:
我被实例化了
lazy-init=”true”指定了不要在Spring容器启动时对这个bean进行实例化。
这时,只有将SpringTest类的代码修改为:
public class SpringTest {
@Test
public void test() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); // 实例化Spring容器
PersonService personService = (PersonService) ctx.getBean("personService"); // 从Spring容器取得bean
}
}
再次测试test()方法,Eclipse控制台才会输出这句话:
我被实例化了
如果想对所有bean都应用延迟初始化,可以在根节点beans设置default-lazy-init=“true”,如下:
<?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" default-lazy-init="true">
......
</beans>
- 当Spring的配置文件——beans.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="personService" class="cn.itcast.service.impl.PersonServiceBean" scope="prototype"></bean>
</beans>
即bean的作用域为prototype时,若SpringTest类的代码为:
public class SpringTest {
@Test
public void test() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); // 实例化Spring容器
}
}
测试test()方法,可以发现Eclipse控制台没输出这句话:
我被实例化了
这就说明了当bean的作用域为prototype时,bean对象并不会在Spring容器启动时就进行创建。
但是若将SpringTest类的代码改为:
public class SpringTest {
@Test
public void test() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); // 实例化Spring容器
PersonService personService = (PersonService) ctx.getBean("personService"); // 从Spring容器取得bean
}
}
此时,再测试test()方法,可以发现Eclipse控制台输出了这句话:
我被实例化了
证实了当bean的作用域为prototype时,bean对象将会在调用getBean()方法时进行创建。
指定bean的初始化方法和销毁方法
我们希望在bean被初始化的时候,就初始化某些资源。为了达到这样的目的,我们可修改PersonServiceBean类的代码为:
public class PersonServiceBean implements PersonService {
public void init() {
System.out.println("初始化某些资源");
}
public PersonServiceBean() {
System.out.println("我被实例化了");
}
@Override
public void save() {
System.out.println("我是save()方法");
}
}
这样,我们的目的就具体地成为:当Spring容器初始化PersonServiceBean对象之后,就要执行该对象的init()方法。为了达成这样的目的,只须修改Spring的配置文件——beans.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="personService" class="cn.itcast.service.impl.PersonServiceBean" lazy-init="false"
init-method="init" />
</beans>
若SpringTest类的代码为:
public class SpringTest {
@Test
public void test() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); // 实例化Spring容器
}
}
测试test()方法,Eclipse控制台将打印:
现在我们又希望在bean被销毁的时候,就释放或关闭某些资源。为了达到这样的目的,我们可修改PersonServiceBean类的代码为:
public class PersonServiceBean implements PersonService {
public void init() {
System.out.println("初始化某些资源");
}
public PersonServiceBean() {
System.out.println("我被实例化了");
}
@Override
public void save() {
System.out.println("我是save()方法");
}
/**
* bean到底是什么时候销毁的呢?如果没有人为地删除它,默认该bean一直在Spring容器中,
* 也就是说随着Spring容器的关闭,该bean才会被销毁。
*/
public void destroy() {
System.out.println("释放初始化的资源");
}
}
试着思考这样一个问题:bean对象到底是什么时候销毁的呢?答案是:如果没有人为地删除它,默认该bean一直在Spring容器中,也就是说随着Spring容器的关闭,该bean才会被销毁。
紧接着,我们要修改Spring的配置文件——beans.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="personService" class="cn.itcast.service.impl.PersonServiceBean" lazy-init="false"
init-method="init" destroy-method="destroy" />
</beans>
最后,我们要修改测试类——SpringTest.java的代码为:
public class SpringTest {
@Test
public void test() {
// ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); // 实例化Spring容器
AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
ctx.close(); // 正常关闭Spring容器
}
}
此时,测试test()方法,Eclipse控制台将打印:
这就是Spring管理的Bean的生命周期。