15.4 Spring控制反转容器的使用
本节主要介绍**Spring
如何管理bean
和依赖关系
**。
15.4.1 通过构造器创建一个bean实例
前面已经介绍,通过调用ApplicationContext
的getBean
方法可以获取到一个bean
的实例。下面的配置文件中定义了一个名为product
的bean
。
一个简单的配置文件
1 2 3 4 5 6 7 8
| <?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 name="product" class="app15a.bean.Product"/> </beans>
|
该bean
的定义告诉Spring
通过默认无参的构造器来初始化Product
类。如果不存在该构造器(因为类作者重载了构造器,且没有显式定义默认构造器),则Spring
将抛出一个异常。
注意**,应采用id
或者name
属性标识一个bean
**。为了让Spring
创建一个Product
实例,应将bean
定义的name
值“product
”(具体实践中也可以是id
值)和Product
类型作为参数传递给ApplicationContext
的getBean
方法:
1 2 3 4 5 6
| ApplicationContext context = new ClassPathXmlApplicationContext( new String[] {"spring-config.xml"}); Product product1 = context.getBean("product", Product.class); product1.setName("Excellent snake oil"); System.out.println("product1: " + product1.getName());
|
15.4.2 通过工厂方法创建一个bean实例
除了通过类的构造器方式,**Spring
还同样支持通过调用一个工厂的方法来初始化类**。下面的bean
定义展示了通过工厂方法来实例化java.util.Calendar
:
1 2 3
| <bean id="calendar" class="java.util.Calendar" factory-method="getInstance"/>
|
本例中采用了id
属性,而非name
属性来标识bean
,并采用了getBean
方法来获取Calendar
实例:
1 2 3 4 5 6 7
| ApplicationContext context = new ClassPathXmlApplicationContext( new String[] {"spring-config.xml"});
Calendar calendar = context.getBean("calendar", Calendar.class);
|
15.4.3 DestroyMethod的使用
有时,我们希望一些类在被销毁前能执行一些方法。Spring
考虑到了这样的需求。可以在bean
定义中配置destroy-method
属性,来指定在销毁前要被执行的方法。
下面的例子中,我们配置Spring
通过java.util.concurrent.Executors
的静态方法newCachedThreadPool
来创建一个java.uitl.concurrent.ExecutorService
实例,并指定了destroy-method
属性值为shutdown
方法。这样,Spring
会在销毁ExecutorService
实例前调用其shutdown
方法:
1 2 3
| <bean id="executorService" class="java.util.concurrent.Executors" factory-method="newCachedThreadPool" destroy-method="shutdown"/>
|
15.4.4 向构造器传递参数
Spring
支持通过带参数的构造器来初始化类。
Product类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| package app15a.bean; import java.io.Serializable; public class Product implements Serializable { private static final long serialVersionUID = 748392348L; private String name; private String description; private float price; public Product() {} public Product(String name,String description,float price) { this.name = name; this.description = description; this.price = price; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } }
|
如下定义展示了如何通过参数名传递参数:
1 2 3 4 5 6 7 8 9 10 11
| <bean name="featuredProduct" class="app15a.bean.Product"> <constructor-arg name="name" value="终极橄榄油" /> <constructor-arg name="description" value="市场上最纯的橄榄油" /> <constructor-arg name="price" value="9.95" /> </bean>
|
这样,在创建Product
实例时,Spring
会调用如下构造器:
1 2 3 4 5
| public Product(String name, String description, float price) { this.name = name; this.description = description; this.price = price; }
|
除了通过名称传递参数外,Spring
还支持通过下标
方式传递参数,具体如下:
1 2 3 4 5 6 7
| <bean name="featuredProduct2" class="app15a.bean.Product"> <constructor-arg index="0" value="终极橄榄油" /> <constructor-arg index="1" value="市场上最纯的橄榄油" /> <constructor-arg index="2" value="9.95" /> </bean>
|
需要说明的是,采用这种下标
方式,对应构造器的所有参数必须传递,缺一不可。
15.4.5 setter方式依赖注入
下面以Employee
类和Address
类为例,介绍setter
方式依赖注入。
Employee类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| package app15a.bean; public class Employee { private String firstName; private String lastName; private Address homeAddress; public Employee() {} public Employee(String firstName,String lastName,Address homeAddress) { this.firstName = firstName; this.lastName = lastName; this.homeAddress = homeAddress; } @Override public String toString() { return firstName + lastName + "," + homeAddress; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public Address getHomeAddress() { return homeAddress; } public void setHomeAddress(Address homeAddress) { this.homeAddress = homeAddress; } }
|
Address类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| package app15a.bean; public class Address { private String country; private String city; private String state; private String stateId; private String zipCode; @Override public String toString() { return country + "," + city + "," + state + "," + stateId + "," + zipCode; } public Address(String country,String city,String state,String stateId, String zipCode) { this.country = country; this.city = city; this.state = state; this.stateId = stateId; this.zipCode = zipCode; } }
|
Employee
依赖于Address
类,可以通过如下配置来保证每个Employee
实例都能包含Address
实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <bean name="simpleAddress" class="app15a.bean.Address"> <constructor-arg name="country" value="中国" /> <constructor-arg name="city" value="北京" /> <constructor-arg name="state" value="雾霾大道" /> <constructor-arg name="stateId" value="996" /> <constructor-arg name="zipCode" value="1314" /> </bean>
<bean name="employee1" class="app15a.bean.Employee"> <property name="firstName" value="张" /> <property name="lastName" value="三" /> <property name="homeAddress" ref="simpleAddress" /> </bean>
|
simpleAddress
对象是Address
类的一个实例,其通过构造器方式实例化。employee1
对象则通过配置property
元素来调用setter
方法以设置值。需要注意的是,**homeAddress
属性配置的是simpleAddress
对象的引用**。
被引用对象A的配置定义不需要在引用该对象A的对象B之前定义。本例中,employee1
对象可以出现在simpleAddress
对象定义之前。
15.4.6 构造器方式依赖注入
上面的Employee
类提供了一个可以传递参数的构造器,如下所示:
1 2 3 4 5 6 7
| public Employee(String firstName, String lastName, Address homeAddress) { this.firstName = firstName; this.lastName = lastName; this.homeAddress = homeAddress; }
|
所以,我们还可以将Address
对象通过构造器注入,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <bean name="employee2" class="app15a.bean.Employee"> <constructor-arg name="firstName" value="李" /> <constructor-arg name="lastName" value="四" /> <constructor-arg name="homeAddress" ref="simpleAddress" /> </bean>
<bean name="simpleAddress" class="app15a.bean.Address"> <constructor-arg name="country" value="中国" /> <constructor-arg name="city" value="北京" /> <constructor-arg name="state" value="雾霾大道" /> <constructor-arg name="stateId" value="996" /> <constructor-arg name="zipCode" value="1314" /> </bean>
|
完整代码
项目结构
spring-config.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| <?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 name="product" class="app15a.bean.Product" /> <bean name="featuredProduct" class="app15a.bean.Product"> <constructor-arg name="name" value="终极橄榄油" /> <constructor-arg name="description" value="市场上最纯的橄榄油" /> <constructor-arg name="price" value="9.95" /> </bean> <bean name="featuredProduct2" class="app15a.bean.Product"> <constructor-arg index="0" value="终极橄榄油" /> <constructor-arg index="1" value="市场上最纯的橄榄油" /> <constructor-arg index="2" value="9.95" /> </bean> <bean id="calendar" class="java.util.Calendar" factory-method="getInstance" /> <bean name="employee1" class="app15a.bean.Employee"> <property name="firstName" value="张" /> <property name="lastName" value="三" /> <property name="homeAddress" ref="simpleAddress" /> </bean> <bean name="employee2" class="app15a.bean.Employee"> <constructor-arg name="firstName" value="李" /> <constructor-arg name="lastName" value="四" /> <constructor-arg name="homeAddress" ref="simpleAddress" /> </bean> <bean name="simpleAddress" class="app15a.bean.Address"> <constructor-arg name="country" value="中国" /> <constructor-arg name="city" value="北京" /> <constructor-arg name="state" value="雾霾大道" /> <constructor-arg name="stateId" value="996" /> <constructor-arg name="zipCode" value="1314" /> </bean> </beans>
|
Main.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| package app15a; import java.util.Calendar; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import app15a.bean.Employee; import app15a.bean.Product; public class Main { public static void main(String[] args) { @SuppressWarnings( "resource" ) ApplicationContext context = new ClassPathXmlApplicationContext( new String[] {"spring-config.xml"}); Product product1 = context.getBean("product", Product.class); product1.setName("真是个棒棒的产品"); System.out.println("product1: " + product1.getName()); Product product2 = context.getBean("product", Product.class); System.out.println("product2: " + product2.getName()); Product featuredProduct = context.getBean("featuredProduct", Product.class); System.out.println("featuredProduct: " + featuredProduct.getName() + ", " + featuredProduct.getDescription() + ", " + featuredProduct.getPrice()); Calendar calendar = context.getBean("calendar", java.util.Calendar.class); System.out.println("calendar: " + calendar.getTime()); Employee employee1 = context.getBean("employee1", Employee.class); System.out.print( "employee1: " + employee1.getFirstName() + employee1.getLastName()); System.out.println(",地址:" + employee1.getHomeAddress()); Employee employee2 = context.getBean("employee2", Employee.class); System.out.print( "employee2: " + employee2.getFirstName() + employee2.getLastName()); System.out.println(",地址:" + employee2.getHomeAddress()); } }
|