这章包括了Spring框架对于IoC规则的实现。Ioc也同DI(依赖注入)。而对象是经过构造函数,工厂方法,或者一些Set方法来定义对象之间的依赖的。容器在建立这些Bean对象的时候同时就会注入这些依赖。这个过程是根本上的反转了,再也不由Bean自己来控制实例化和定位依赖,而是经过服务定位来控制这个过程,也是IoC(控制反转)的由来。javascript
org.springframework.beans
和org.springframework.context
包是Spring框架IoC容器的基础。BeanFactory
接口提供了一种先进的配置机制可以管理任何类型的对象。ApplicationContext
是BeanFactory
的子接口。它增长了一些跟Spring AOP特性更为简单的集成,包括信息资源处理(国际化使用),事件发表,以及应用层特别上下文的使用。html
简而言之,BeanFactory
提供了框架配置和基本的功能,而ApplicationContext
提供了更多企业级特性。ApplicationContext
是BeanFactory
的超集,并且在这章Spring IoC容器中惟一使用的。想要更多的了解BeanFactory
的话,请参考6.16。java
在Spring中,那些在你应用中,由Spring IoC容器管理的骨干对象,都叫作Bean。Bean就是一个由Spring IoC容器实例化,装载,以及管理的对象。Bean也是你应用中的对象。Bean以及Bean的那些依赖对象,都是经过容器使用的元数据反射成的。web
接口org.springframework.context.ApplicationContext
表示Spring IoC容器同时负责实例化,配置,以及装载前面说起的Bean对象。容器经过读取配置元数据来知道那些对象须要实例化,配置以及装载。配置元数据能够写到XML中,Java注解中,或者Java代码中。spring
几种不一样的由Spring针对ApplicationContext接口的实现都是能够直接使用的。在单机环境中,使用ClassPathXmlApplicationContext
或者FileSystemXmlApplicationContext
也是很是常见的。尽管XML是传统的定义元数据的格式,你也能够经过Java注解或者代码来提供额外的元数据。markdown
在大多数应用场景中,用户代码不须要实例化Spring IoC容器。好比,在Web应用场景下,只须要在web.xml中添加少数几行代码就能够由Web容器来建立Spring IoC容器。架构
下面的图是一个high-level的Spring工做图。你的应用的类以及配置当中的元数据只有在ApplicationContext建立了,初始化好,你才有一个彻底配置好的,可执行的系统或应用。并发
图6.1 Spring IoC 容器app
如前面的图所表现的,Spring IoC容器会使用配置元数据。这个数据也表示了你但愿Spring容器在应用中如何来实例化,配置,以及装载对象。框架
配置元数据传统的提供方式是使用简单直观的XML格式,固然也是本章所用来表达Spring IoC容器的一些关键的概念和特性的格式。
基于XML格式的元数据配置不是惟一的配置元数据的方式。Spring IoC容器自己和使用哪种元数据来写入配置是彻底解耦的。目前不少开发者也选择使用基于Java的方式来配置Spring应用。
关于使用其余不一样形式的元数据,能够参考
Spring 配置包括至少一种Bean的定义方式。基于XML配置元数据都是经过配置< bean/>这样的标签,在最高级别的< beans/>标签之下。也能够经过Java 配置使用 @Bean注解的方法到使用@Configuration注解的类上面。
这些Bean定义所关联的实际的对象构成了你的应用。一般,你能够定义服务层对象,数据接入层对象(Dao),表现层对象好比Struts里面的Action
实例,基础构成对象好比Hibernate的SessionFactories
,JMSQueues
等等。一般不配置细粒度的域对象的容器,由于它一般是DAOs的责任和业务逻辑建立和加载域对象。然而,你可使用Spring和AspectJ集成来配置在IoC容器控制以外的对象。能够参考文章 Using AspectJ to dependency-inject domain objects with Spring.
。
下面的例子展现了基于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>
id
属性是一个字符串,是用来区分独立的Bean定义的。class
属性定义了Bean使用的类型,用的是全名。id的值用来让Bean对象之间相互引用。
实例化Spring IoC容器很直接。ApplicationContext
的构造函数能够经过一些资源地址的字符串来让容器从中加载配置元数据。这些文件能够来自本地文件系统,或者Java的CLASSPATH等。
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>
在以前的例子中,服务层对象包括了类PetStoreServiceImpl
,而且两个数据接入对象类型分别是JpaAccountDao
以及JpaItemDao
(基于JPA O/R mapping 标准)。property name
元素指的是Bean属性的名字,ref
指的是另外一个定义的bean。这种id和ref元素的关联,表示了不一样对象之间的依赖关系。
有时将bean定义到多个XML文件更清晰一些。一般来讲,在开发者的架构中一个单独的XML配置文件表明一个单独的逻辑层,或者单独的模块。
开发者可使用应用上下文的构造函数来加载这些包含bean的XML。这个构造函数可使用多个资源路径,好比以前一节中展现的那样。或者可使用一个或者多个< import/>标签来加载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>
在上述例子中,外部的Bean定义经过3个文件:services.xml,messageSource.xml以及themeSource.xml来加载。全部路径都是相对于当前文件的所在的路径。因此services.xml必须和当前文件在同一个路径或classpath路径。而messageSource.xml和themeSource.xml必须定义在resources路径下。如上所述,第一个斜线是被忽视掉的,考虑到这些路径都是相对的,最好不要使用第一个下线。这些文件的内容是被引用的,包括最高级的< beans/>元素,因此这些文件针对Spring Bean的XML定义必须有效。
极可能,经过使用相对路径”../”来得到引用的文件,可是并不推荐这样作。这样作会建立一个针对当前应用的外部依赖。尤为是这个路径中包含“classpath”,注入这样的URL(好比,“classpath:../services.xml”),这样会引用一个运行时的classpath根目录,而后在查找其父目录。Classpath配置的改变可能会变成一个彻底不同的目录。
固然,你也可使用一些完整的路径而不使用相对路径,好比像“file:C:/config/service.xml”或者“classpath:/config/services.xml”。然而,必定要注意这样作你是在耦合你的应用到你本地的绝对路径上。一般,更好的方式是使用引用来针对这些绝对路径,好比“${…}”这类占位符,JVM系统是能够在运行时解析的。
ApplicationContext
是一个负责注册不一样Bean的工厂接口。能够经过T getBean(String name, Class<T> requiredType)
方法获取Bean的实例。
获取的代码以下:
// 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();
开发者可使用getBean()
来得到Bean对象。ApplicationContext
接口有几个方法来获取Bean实例,可是开发者的代码中可能用不到这些方法。事实上,开发者的应用不应调用getBean()
方法,且不依赖于Spring的API。好比,Spring与Web框架的集成,为多种框架的控制层等提供了依赖注入。
Spring IoC容器管理了不少的Bean对象。这些Bean对象都是根据容器的配置元数据所建立的,好比基于XML的<bean/>
定义。
在容器里面,Bean的定义都被表现为BeanDefinition
对象,包含如下元数据:
除了使用实现定义的元数据来建立Bean,ApplicationContext
的实现也容许开发者将已存在的容器外的对象注册为Bean对象。能够经过进去ApplicationContext的BeanFactory中的getBeanFactory()
方法来得到在DefaultListableBeanFactory
中实现的BeanFactory。DefaultListableBeanFactory
经过registerSingleton(..)
以及registerBeanDefinition(..)
支持前面的操做。然而,一般状况下,应用都只是使用元数据中定义的Bean对象。
Bean的元数据以及手工支持的单例的最好尽早注册到Spring容器中,防止容器在装载这些Bean的过程当中发生错误。然而,覆盖掉已存在的元数据和存在的单例Bean也是支持的,可是在运行时注册Bean有在官方上并不支持,并且由于Bean状态的不一致致使并发异常。
每个Bean都有不止一个区分符。这些区分符必须在这个容器中惟一。一般,一个Bean只有一个区分符,可是若是多余一个,那么额外的区分符也做为这个Bean的别名。
在基于XML配置的元数据中,你可使用id
或者name
属性来做为Bean的区分符。id
属性容许你特指惟一的一个id。方便起见,这个名字都是有字符跟数字的(‘myBean’, ‘fooService’等),也能够包含特殊的字符。若是你也经过其余别名来使用Bean,开发者也能够给Bean使用name
属性,以,
,;
或者空格来区分。因为历史的缘由,在Spring 3.1以前,id
属性是被定义成一种xsd:ID
类型的。在3.1中,id的类型还被定义成xsd:string
类型。
开发者也能够不给Bean定义id或者name。若是Bean没有名字或者id的话,容器会帮助Bean生成一个独特的名字。可是若是你想使用ref
这样的元素来定位到Bean的话,你仍是须要添加一个名字的。不使用名字主要是为了使用内在的Bean以及联合装载。
Bean的命名习惯
通常习惯就是根据Java的field变量的方式来命名。以小写开始的驼峰命名。好比accountManager
,userDao
,loginController
等。
一致的命名方式可让开发者配置更加简单易懂,并且若是你使用Spring AOP的话,这样作也颇有益处。
在Bean定义以外增长别名
在Bean定义自己,开发者经过使用id属性以及name属性能够为Bean定义多个名字。这些名字也一样能指向相同的Bean对象,在一些场景下是很实用的。好比容许组件引用多个依赖的话,经过名字会更有效。
然而,在Bean定义的时候来特指别名有的时候是不够的。有的时候引用别名来定义在其余的地方可以更清晰。这也是大系统的一些常见场景,根据不一样的子系统来区分配置信息,每一个子系统都有本身的定义。在基于XML的配置元数据中,可使用<alias/>
元素来作到。
<alias name="fromName" alias="toName"/>
这种状况下,在仙童的容器中,全部name是fromName
的Bean,也能经过alias定义来经过toName
来引用。
举例来讲,子系统A的元数据可能会经过一个subsystemA-dataSource
来引用其数据源。而子系统B的配置元数据可能经过subsystemB-dataSource
来引用数据源。当组合成一个应用时,会同时使用这两个子系统经过myApp-dataSource
来引用数据源。若是但愿经过3个名字来指向一个对象,你能够经过应用的配置元数据配置以下定义。
<alias name="subsystemA-dataSource" alias="subsystemB-dataSource"/>
<alias name="subsystemA-dataSource" alias="myApp-dataSource" />
如今每一个组件和主应用程序能够经过名称引用数据源是独一无二的,保证不与其余任何冲突定义(有效地建立一个名称空间),然而他们引用同一个bean。
Bean定义的自己其实也是建立Bean对象的菜谱,容器经过这个定义来的元数据来将建立实际的Bean对象。
若是开发者使用的是基于XML的配置元数据,开发者能够经过特指Bean的class
字段来肯定Bean被实例化成指定的对象。class
属性一般来讲,在Bean定义中是必须的。开发者能够经过以下方式使用Class
属性:
new
操做符。内部类。若是开发者想经过配置一个Bean为静态内部类,开发者须要指定二进制的嵌套类的类名。
举例来讲,好比有一个类名为Foo
在包com.example
包之中,并且这个Foo
类其中有一个静态的嵌套类叫作Bar
,那么若是想使用Bar
来做为Bean的话,它的class
属性须要为
com.example.Foo$Bar
须要注意的是,$
符号就是用来区外部类和内部类的。
经过构造函数实例化
当开发者经过构造函数来建立Bean对象的时候,全部的普通的类都可以和Spring协同工做。也就是说,通常的做为Bean的类是不须要实现一些特殊的接口的。仅仅指定Bean的类就足够了。然而,根据你使用IoC容器的不一样,开发者可能须要配置默认(无参)构造函数。
Spring IoC容器能够帮你管理任何你想要管理的类。并不只限于Bean对象。大多数的Spring开发者更多在容器中使用Bean对象配合getter,setter方法以及无参的构造函数。固然,开发者也能够在容器中管理一些非Bean样式的对象。好比说,一个不被引用的链接池,Spring仍然能够管理它。
使用基于XML的元数据配置方式,Bean的配置能够以下:
<bean id="exampleBean" class="examples.ExampleBean"/>
<bean name="anotherExample" class="examples.ExampleBeanTwo"/>
经过静态工厂方法实例化
当经过静态工厂方法来定义Bean对象的时候,开发者可使用class
属性来指定包含工厂方法的类,经过factory-method
来指定生成Bean的方法。开发者能够调用这个方法,并返回一个对象。
下面的Bean定义,就是经过调用工厂方法所建立的。定义不会指定方法返回的对象的类型,而是包含了工厂方法的类。在以下的例子中createInstance()
方法必须为静态方法。
<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的class
属性置空,而是用factory-bean
属性,特指容器中包含的那个包含建立该Bean实例方法的那个Bean。同时将这个Bean的factory-method
属性为实际的调用方法。
<!-- 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 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;
}
}
这个方法显示,工厂Bean自己也是能够经过依赖注入配置的。
在Spring文档中,工厂Bean指的是在Spring容器中配置的专门经过实例方法或者静态方法来建立Bean的一个Bean。相对而言,
FactoryBean
指的是Spring一种特指的FactoryBean
.