1 IoC 简介
IoC(Inversion Of Control)
即控制反转,也被称为依赖注入。控制反转就是应用本身不负责依赖对象的创建和维护,将对象的创建和维护交给外部容器处理,这样应用的控制权就交给了外部容器,控制权的转移就是控制反转,目的是为了获得更好的维护性以及降低耦合度。
org.springframework.beans
和 org.springframework.context
包是 IoC 容器的基础,BeanFactory
和 ApplicationContext
接口提供了管理各种对象类型 bean 的功能。
1.1 容器简介
IOC 容器
用来管理应用对象的配置和生命周期,配置每个对象的创建和销毁,也可以配置每个 bean 是否只有一个实例,以及它们是如何相互关联的。
核心配置文件(applicationContext.xml/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="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions go here -->
</beans>
1.1.1 实例化容器
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"});
services.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">
<!-- services -->
<bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
<property name="accountDao" ref="accountDao"/>
<property name="itemDao" ref="itemDao"/>
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions for services go here -->
</beans>
daos.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="accountDao"
class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao">
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao">
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions for data access objects go here -->
</beans>
对于大型项目,通常可以分模块定义 bean,然后再整合到一个统一的配置文件中,方便管理。
<beans>
<import resource="services.xml"/>
<import resource="resources/messageSource.xml"/>
<import resource="/resources/themeSource.xml"/>
<bean id="bean1" class="..."/>
<bean id="bean2" class="..."/>
</beans>
1.1.2 使用容器
// create and configure beans
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"});
// retrieve configured instance
PetStoreService service = context.getBean("petStore", PetStoreService.class);
// use configured instance
List<String> userList = service.getUsernameList();
1.2 Bean 简介
Spring IoC 容器管理一个或多个 bean。这些 bean 都是 Spring 根据配置文件创建的。
bean 主要有如下定义:
- class: bean 对应的类的全限定名
- id/name: bean 对应的配置文件中的唯一标识
- scope: bean 的作用域
- constructor arguments: bean 的构造参数
- properties: bean 的注入类型
- autowiring mode: bean 自动注入
- lazy-initialization mode: bean 懒加载
- initialization method: bean 的初始化
- destrcution method: bean 的销毁
具体会在之后做相关说明。
1.2.1 bean 命名
在配置文件中,可以使用 id 或者 name 属性指定 bean 的唯一标识。
在 3.1 的版本前,id 属性的值必须遵循 xml 中 id 属性的规范,对于一些特殊字符,xml 是无法解析,此时就可以使用 name 来指定;从 3.1 版本开始,id 属性值只要遵循 xml 中 string 的规范就行,即使是特殊字符,xml 也可以解析,但 id 的唯一性仍需要保证。因为 3.1 版本放宽了 id 属性的规范,此时,id 和 name 的区别已不大,但 name 属性还可以为一个 bean 指定多个名字(多个名字之间用 ,
、 ;
或空格隔开),这个是 id 所不具备的。
另外,bean 的命名不是必须提供的,如果省略,Spring 会自动生成一个唯一的名字,只是这样我们就无法通过 id/name 来实例化 bean,不过,它仍然有它的用途(如:内部 bean,自动注入等),具体会在之后的文章中说明。
除了直接通过 id/name 属性指定 bean 的名字,还可以通过 alias
来为 bean 再指定别名,不过,此时通过别名和原 bean 名实例化的 bean 对象是同一个,即只会实例化一次。
<alias name="fromName" alias="toName"/>
1.2.2 实例化 bean
在配置文件中定义的 bean,默认情况下,会在 IoC 容器实例化的时候全部实例化,通过各自的默认无参构造方法。
1.2.2.1 通过默认的构造方法
只要做如下配置,在 IoC 容器实例化时,对应的 bean 就会自动调用默认的无参构造器初始化。
<bean id="exampleBean" class="examples.ExampleBean"/>
<bean name="anotherExample" class="examples.ExampleBeanTwo"/>
1.2.2.2 通过工厂方法
- 静态工厂
此处定义的 bean 为目标 bean。由于目标 bean 是通过工厂的静态方法生成的,所以无需实例化工厂 bean。目标 bean 的定义中,class
对应静态工厂的类名,factory-method
指定的是生成目标 bean 的静态工厂方法。
<bean id="clientService"
class="examples.ClientService"
factory-method="createInstance"/>
public class ClientService {
private static ClientService clientService = new ClientService();
private ClientService() {}
public static ClientService createInstance() {
return clientService;
}
}
- 实例工厂
区别于静态工厂,此处需要先实例化工厂 bean,再通过工厂 bean 的方法实例化目标 bean。此时,目标 bean 的定义中,bean-factory
为工厂 bean 的 id,factory-method
为工厂 bean 中生成目标 bean 的工厂方法。
<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>
<!-- the bean to be created via the factory bean -->
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
private DefaultServiceLocator() {}
public ClientService createClientServiceInstance() {
return clientService;
}
}
- 一个工厂类也可以实现多个工厂方法,生成多个 bean。
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
<bean id="accountService"
factory-bean="serviceLocator"
factory-method="createAccountServiceInstance"/>
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
private static AccountService accountService = new AccountServiceImpl();
private DefaultServiceLocator() {}
public ClientService createClientServiceInstance() {
return clientService;
}
public AccountService createAccountServiceInstance() {
return accountService;
}
}
- 带参数的工厂
如果工厂方法有参数,必须通过 <constructor-arg>
标签来配置,而不是 <property>
。
<bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance">
<constructor-arg ref="anotherExampleBean"/>
<constructor-arg ref="yetAnotherBean"/>
<constructor-arg value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
public class ExampleBean {
private ExampleBean(...) {
...
}
public static ExampleBean createInstance (
AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
ExampleBean eb = new ExampleBean (...);
// some other operations...
return eb;
}
}