1

<div style="font-size:34px">Spring4参考手册中文版</div>

<h1>作者简介</h1>
翻译 铁柱 wwwshiym@gmail.com
顾问 张丙天

铁柱 曾任中科软科技股份有限公司应用系统事业群部技术副总监、首席架构师,2008年加入中科软。擅长SOA、企业信息化架构,精通Java、Spring,在多线程、io、虚拟机调优、网络通信及支撑大型网站的领域有较多经验,对技术有浓厚的兴趣。现致力于无线、数据、业务平台、组件化方面取得突破。

张丙天

<h1>前言</h1>
我是前言。

<h1 id="spring-core">Part III. 核心技术</h1>
本部分参考手册完全覆盖了Srping 框架的全部技术

首先是Spring IoC控制反转。深入彻底的IoC讲解之后,紧随其后的是全面解说Spring AOP。Spring有自己的AOP框架,该框架概念简单易于理解,能解决Java企业应用中80%的需求

Spring也集成了AspectJ,AspectJ是现今java领域功能最丰富、最成熟的AOP实现。

最后面的部分是tdd测试驱动开发,也是Spring 开发团队最为推崇的开发方法,主要内容有单元测试和spirng对集成测试的支持。Spring 团队发现,正确的使用Ioc,会使单元测试和集成测试更加简单(因为类中使用Setter和构造函数,将使它们更容易的配合,而无需使用set up组装)。同时,为测试弄了专门的单独章节,希望你能领悟这一点

<h2 id="beans">IoC容器</h2>
<h3 id="beans-introduction">springIOC容器和beans简介</h3>

本章讲解spring的控制反转(IoC)的spring 框架实现 [1] 原理. IoC 又名 依赖注入 (DI). 它是一个由对象定义依赖的处理手法,也就是如何与其他对象协同工作, 可以通过以下途径定义依赖:构造参数注入、工厂方法的参数注入、属性注入(是指对象实例化后或者从工厂方法返回一个实例后设置其属性)。容器创建bean时候, 注入 依赖。 这个控制倒转了, 因此得名控制反转 (IoC)。反转了哪些控制,不再是由bean自己控制依赖类的实例化和定位, 而是使用了类似于 服务定位 模式的机制来控制。

org.springframework.beansorg.springframework.context 这两个包是spring IOC容器的基础包. BeanFactory 接口 提供了各种配置,用于管理任何对象. ApplicationContextBeanFactory的子接口. 它提供以下功能,使集成Spring’s AOP功能 更容易; 消息资源处理(用于国际化);事件发布;应用层指定上下文环境,像用于web应用的WebApplicationContext.

简而言之, BeanFactory 提供了配置框架和基础功能, ApplicationContext 增加了更多的企业应用用能. ApplicationContextBeanFactory的超集, 是本章的spring IOC示例中的指定容器. 用BeanFactory 替代 ApplicationContext, 更多的信息请参看 Section 4.17, “The BeanFactory”.

应用中的对象并且是由spring 容器 管理的,被称为beans.就是对象,由spring容器管理的诸如实例化、组装等等操作. bean可以由应用中的多个对象组成。Bean通过容器和配置元数据 ,使用反射技术,去组装依赖对象。

<h3 id="beans-basics">容器概述</h3>
接口org.springframework.context.ApplicationContext代表了srping IoC 容器,负责实例化、配置和组装前面提到的beans。容器依据配置配置元数据去实例化、配置、组装。配置元数据可以用XML、Java 注解、或者Java编码表示。在配置元数据中,可以定义组成应用的对象,以及对象之间的依赖关系。

Spring 提供了一些开箱即用的ApplicationContext接口的实现。在单独的应用中,通常使用ClassPathXmlApplicationContext或者FileSystemXmlApplicationContext。当使用XML定义配置元数据时,可通过一小段xml配置使容器支持其他格式的配置元数据,比如Java 注解、Java Code。

大多数的应用场景中,不需要硬编码来实例化一个Spring IoC 的容器。举个栗子,web应用中,在web.xml中大概8行左右的配置就可以实例化一个Spring Ioc容器(see Section 4.16.4, “Convenient ApplicationContext instantiation for web applications”)。若再有STS(Spring eclipse 套件),简单的钩钩点点即可完成此配置。

下面的示意图是spring工作原理。ApplicationContext将应用中的类与配置元数据相结合,实例化后,即可得到一个可配置、可执行的系统或应用。
The Spring IoC container

spring 工作原理示意图

<h4 id="beans-factory-metadata">配置元数据</h4>
如上图所示,Spring IoC容器使用某种格式的配置元数据;配置元数据,就是告诉Ioc容器如何将对象实例化、配置、组装。

配置元数据是默认使用简单直观的xml格式,也是本章样例中使用最多的,这些样例程序用以说明spring Ioc 容器的核心概念和功能

注意

基于XML的元数据并不是唯一的格式。Spring IoC容器已经和提及到的元数据格式完全解耦了。目前,很多码农都选择Java-based configuration

欲了解其他元数据格式的使用,请参看:

  • Annotation-based configuration: Spring 2.5 引进的支持java 注解配置元数据
  • Java-based configuration: Spring3.0时,将Spring JavaConfig project的很多功能集成到核心Spring框架中。Thus you can define beans external to your application classes by using Java rather than XML files.你可以使用java配置类定义bean,无需xml,该配置类与应用类无关。想要尝鲜的话,请参看@Configuration,@Bean,@Import,@DependsOn注解

Spring配置由Spring bean的定义组成,这些bean必须被容器管理,至少1个,通常会有多个。基于XML的配置元数据,大概这么配置,根节点<beans>中配置子节点<bean>。Java configuration使用是这样的,一个带有@Configuration类注解的类中,方法上使用@Bean方法注解。

bean的定义要与应用中实际的类相一致。可以定义service 层的对象、Dao对象、类似Struts的表现成的对象、像Hibernate SessionFactories这样的基础对象,JMS队列等等。通常不会去定义细粒度域对象,因为它们由DAO或者Service负责创建、加载。然而,通过集成AspectJ,可以配置非Srping容器创建的对象。参看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="...">
    <!-- bean的详细配置 -->
    </bean>
    
    <bean id="..." class="...">
    <!-- bean的详细配置 -->
    </bean>
    
    <!-- 其他bean -->

</beans>

id属性是个字串,是bean的唯一标示符。class属性定义了bean的类型,要使用类的全限定类名(含有包路径)。id属性的值,可以作为合作bean的引用标示符。上面未展示如何引用其他对象;详情参看Dependencies

<h4 id='beans-factory-instantiation'>容器实例化</h4>
Spring IoC的实例化易如反掌。ApplicationContext构造函数支持定位路径,定位路径也可以是多个,它是标识实际资源的字串,容器使用该标识加载配置元数据,支持多种资源,比如:本地文件系统、CLASSPATH等等。

    ApplicationContext context = 
        new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"});  

注意

在学习了Spring IoC容器之后,也许你想了解更多的Spring的资源,如前所述在第6章,资源使用URI语法定位输入流,Spring提供了方便的机制读取输入流。在第6.7章“Application contexts and Resource paths”,专门讲述5用 资源路径构造应用上下文,资源路径也是惯用手法。
接下来的样例展示了配置service层对象:

<?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"/>
    <!-- 有关属性配置 -->
    </bean>

    <!--更多的Service bean -->

</beans>

下面的样例展示了数据访问对象dao.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>

上述内容中service层由PetStoreServiceImpl类、2个dao对象JpaAccountDaoJpaItemDao(基于JPA ORM标准)。属性name元素引用了JavaBean的属性,ref元素引用了其他bean定义。这个引用表示实际对象之间的引用依赖。配置一个对象的依赖,详情请参看Dependencies

<h5 id='beans-factory-xml-import'>引入基于xml的元数据</h5>
多个配置文件共同定义bean非常有用。通常,每个XML配置文件在你的架构中代表一个逻辑层或者一个模块。

你可以使用应用上下文(applicationContext)的构造函数去加载所有xml中定义的bean。这个构造函数使用多个资源定位,就像前面中提到的。或者,也可以用一个或者多个资源引用,即使用<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:services.xml,messageSource.xml,themeSource.xml 。被引入的文件的路径对于引入配置文件来说都是相对路径,所以service.xml必须在引入配置文件的相同文件路径或者相同的类路径中。而messageSource.xmlthemeSource.xml必须在引入配置文件所在的文件夹下的resouce文件夹下。正如你所看到的 /开头会被忽略掉,因为这些路径是相对路径,推荐不要使用/开头的格式。导入(imported)文件内容,包含根节点<beans/>,配置中XML bean定义 必须经过Spring语法校验通过。

注意

使用"../"表示父目录的相对路径是可以的,但是真心不推荐这样创建一个依赖应用外部文件的做法。尤其指出,使用"classpath:"资源类型的URLs(像这样:"classpath:../services.xml"),也是不推荐的,因为运行时处理过程会选择"最近的"根路径然后引入他的父目录配置文件。Classpath配置的改变,会导致应用选择一个不同的、错误的目录。
你可以使用全路径限定资源定位取代相对路径,比如:"file:C:/config/services.xml" 或者"classpath:/config/services.xml"。还有,你可以使用抽象路径来解耦应用和配置文件。使用一个逻辑定位更可取 ,比如:通过"${..}"占位符,使用JVM运行时计算出的路径。

<h4 id='beans-factory-client'>使用容器</h4>
ApplicationContext是一个高级工厂的接口,能维护各种bean以及他们之间依赖的注册。使用方法T getBean(String name, Class<T> requiredType),就能从定义的bean中获取实例。
ApplicationContext能让你读取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()从beans中获取实例。ApplicationContext接口有几种方法可以办到,但是理想的做法是不要使用他们。实际上,应用中根本就不该使用getBean()方法,这样就不依赖Sprig API了。比如,Spring集成了很多web框架,为各种web框架类提供了依赖注入,比如web框架的Controller和JSF-managed beans

<h3 id='beans-definition'>Bean概述</h3>
Spring IoC容器管理一个或多个bean。这些bean根据提供给容器的配置元数据创建的,比如使用XML格式<bean/>定义。
在容器内部,这些bean的定义用 BeanDefinition对象表示,BeanDefinition包含了下列元数据:

  • 全路径(含包路径)类名:代表bean的实际实现类。
  • Bean行为配置元素,它规定了bean在容器中行为(作用域,生命周期回调函数等等)
  • 引用其他bean,就是为了bean能正常工作而所需的其他bean的引用。这些引用类也称为合作类或者依赖类。
  • 其他配置,为实例设置的其他属性配置。比如说,管理连接池的bean的连接数,池大小的上限。

这些元数据将转换成bean定义(BeanDefinition类)的属性。

The bean definition

属性 详情
class Section 5.3.2, “Instantiating beans”
name Section 5.3.1, “Naming beans”
scope Section 5.5, “Bean scopes”
constructor arguments Section 5.4.1, “Dependency injection”
properties Section 5.4.1, “Dependency injection”
autowiring mode Section 5.4.5, “Autowiring collaborators”
lazy-initialization mode Section 5.4.4, “Lazy-initialized beans”
initialization method the section called “Initialization callbacks”
destruction method the section called “Destruction callbacks”

除了bean的信息以外,BeanDefinition也包含创建特殊bean的信息,ApplicationContext的实现也允许注册由用户创建而非IoC容器创建的对象。通过访问ApplicationContext’s BeanFactory的方法getBeanFactory(),该方法返回BeanFactory的实现DefaultListableBeanFactoryDefaultListableBeanFactory类支持这种注册,通过registerSingleton(..)registerBeanDefinition(..)方法实现。然而,典型的应用只用元数据定义的bean就可以单独运行。

<h4 id='beans-beanname'>beans命名</h4>
bean有一个或者多个标示符。这些标示符必须是所在容器范围内必唯一的。通常情况一下,一个bean仅有一个标示符,如果有需求需要多个,多出来的将被当做别名。

在XML格式配置元数据中,使用 id 或者 name 属性来作为bean的标示符。id属性只能有1个。命名规范是字符数字混编(myBean,fooService,等等),但也支持特殊字符,可以包含。若想给bean起个别名,则可使用name属性来指定,可以是多个,用英文的逗号(,)分隔、分号(;)也行、空格也行。注意,在Spring3.1以前,id属性定义成了xsd:ID类型,该类型强制为字符(译者心里说:估计字母+特殊字符,不支持数字的意思,有待验证,没工夫验证去了,翻译进度太慢了。再说了,现在都用4了,你再说3.0怎么着怎么着,那不跟孔乙己似的跟别人吹嘘茴香豆有四种写法)。3.1版开始,它被定义为xsd:string类型。注意,bean id的唯一性约束依然被容器强制使用,尽管xml解析器不再支持了。译者注:在spring3(含)以前,id是可以相同的,容器会替换相同id的bean,而在新版中,容器初始化过程中发现id相同抛出异常,停止实例化

idname属性不是bean所必须的。若未明确指定id或者name属性,容器会给它生成一个唯一name属性。当然了,如果你想通过bean的name属性引用,使用ref元素方式,或者是类似于Service Locator模式方式检索bean(译者想:应该是指调用ApplicationContext.getBean()方法获取bean,类似这种方式。Service Locator是一种设计模式,其实换个名字是不是更合适,DL(Dependency Lookup依赖查找)。虽然现在我也不明白,但是下面有专门的章节讲解,翻到时候再详细了解),就必须给bean指定 name了。之所以支持无name bean特性,是为了使内部类自动装配。

Bean命名规范

bean命名规范使用java命名规范中实例属性名(也称域,Field)规范。小写字母开头的驼峰式。像这样
(不包括单引号)`accountManager`,`accountService`,`userDao`,
`loginController`,等等

规范的命名使配置易读易理解。若使用Spring AOP,通过名字增强(译注:大多数Spring AOP教材中
的 通知)一坨bean时,规范的命名将带来极大的方便。

<h5 id='beans-beanname-alias'>bean定义之外设置别名</h5>
定义的bean内,可以给bean多个标识符,组合id属性值和任意数量的name属性值。这些标识符均可作为该bean的别名,对于有些场景中,别名机制非常有用,比如应用中组件对自身的引用。(译注:一个类持有一个本类的实例作为属性,看起来应该是这样的,以下代码为推测,可以执行)
Bean类

public class SomeBean {
    //注意看这个属性,就是本类
    private SomeBean someBean;
    
    public SomeBean(){}
    
    public void setSomeBean(SomeBean someBean) {
        this.someBean = someBean;
    }
}

配置元数据

<?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的别名,使用,/;/空格 分隔都是可以是-->
    <bean id="someBeanId" name="someBean,someBeanA;someBeanB someBeanC" class="com.example.spring.bean.SomeBean">
        <!--将别名为someBeanA 的bean 注入给 id为someBeanId 的bean的属性 'someBean'-->
        <property name="someBean" ref="someBeanA"></property>
    </bean>
</beans>

测试代码

@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class SomeBeanTests {
    
    @Autowired
    @Qualifier("someBeanId")
    private SomeBean someBean;
    
    @Test
    public void testSimpleProperties() throws Exception {
    }
    
}

在bean的定义处指定所有别名有时候并不合适,然而,在其他配置文件中给bean设置别名却更为恰当。此法通常应用在大型系统的场景中,配置文件分散在各个子系统中,每个子系统都有本系统的bean定义。XML格式配置元数据,提供<alias/>元素,可以搞定此用法。

<alias name="fromName" alias="toName"/>

这种情况下,在同容器中有个叫fromName的bean,或者叫其他的阿猫阿狗之类的,再使用此别名定义之后,即可被当做toName来引用。

举个栗子,子系统A中的配置元数据也许引用了一个被命名为subsystemA-dataSource的bean。子系统B也许引用了一个subsystemB-dataSource。将这两个子系统整合到主应用中,而主应用使用了一个myApp-dataSource,为了使3个bean引用同一个对象,得在MyApp配置元数据中使用别名定义:

<alias name="subsystemA-dataSource" alias="subsystemB-dataSource"/>
<alias name="subsystemA-dataSource" alias="myApp-dataSource" />

现在,每个组件和主应用都能通过bean 名引用dataSource,而bean名都是唯一的保证不与其他定义冲突(实际上创建了一个命名空间),但他们引用的都是同一个bean。

Java-configuration

如果你使用了Java-configuration@Bean注解也提供了别名,详见Section 5.12.3, “Using the @Bean annotation”

<h4 id='beans-factory-class'>bean实例化</h4>
bean的定义,本质是如何创建一个或多个对象的配方。容器被请求时,会解析配置元数据中的bean定义并封装,使用封装配置创建(或者获取)对象实例。

若使用XML格式配置元数据,得为将要实例化的对象指定类型(或者说是类),使用<bean/>元素的class属性实现。class属性 映射到BeanDefinition类实例的Class属性(域),这个class属性是<bean/>元素必须的。(例外情况,参看“Instantiation using an instance factory method”Section 5.7, “Bean definition inheritance”。使用Class域属性,通过以下两种方式:

  • 通常,通过指定bean的class 属性,容器使用反射调用其构造函数直接创建bean,有点像Java 编码中使用new操作符。
  • 指定class实际类含有用于创建对象的静态工厂方法,这是不常使用的场景,容器会调用类的静态工厂方法创建bean。调用静态工厂方法返回的对象类型也许是相同类型,也许完全是其他类。

<div class="sidebar">
<b>内部类命名</b> 若要定义静态内部类,得将类名劈开。


举例来说,现在在<span class="scode">com.example</span>包有个类<span class="scode">Foo</span>,该类有静态内部类<span class="scode">Bar</span>,定义<span class="scode">Bar</span>的Spring bean的<span class="scode">class</span>属性差不多是这样


<code class="scode">com.example.Foo$Bar</code>



注意<span class="scode">$</span>字符,用它来分隔内部类名和外围类名
</div>

<h4 id='beans-factory-class-ctor'>用构造函数实例化</h4>
若是使用构造函数方式创建bean,所有的常规类都可以使用Spring来创建、管理。也就是说,开发的类无需实现任何特殊接口或者使用某种特殊编码风格。仅需指定bean的class即可。对于特殊的bean管理,取决于你使用的IoC类型,也许需要一个默认的空构造。

Spring IoC容器几乎能管理任何你需要管理的类,不局限于真正的JavaBeans。大多数Spring的用户心中,真正的JavaBean是这样的:仅有1个默认的无参构造函数、属性、setter、getter。嗯,比如,现在需要使用一个废弃连接池,它肯定不符合JavaBean规范,Spring照样能管理。

使用XML格式配置元数据 定义bean的class,如下所示:

<bean id="exampleBean" class="examples.ExampleBean"/>

<bean name="anotherExample" class="examples.ExampleBeanTwo"/>  

如何为构造函数指定参数?如何在对象实力话之后设置其属性?请参看Injecting Dependencies

<h5 id='beans-factory-class-static-factory-method'>使用静态工厂方法实例化</h5>
定义使用使用静态工厂方法创建的bean时,得指定工厂方法类的作为class属性值,并且还得指定工厂方法类中用于创建bean的方法名称,作为factory-method属性值。工厂方法可以有参数,调用该方法即可返回对象实例,就像通过构造函数创建对象实例一样。此种bean定义是为了兼容遗留系统中的静态工厂

下面的bean定义,是使用工厂方法创建bean的方式。定义中,无需指定返回对象的类型(class),而是指定工厂方法类的class。下例中,createInstance()方法必须是一个static静态方法。

<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;
    }
}   

<h5 id='beans-factory-class-instance-factory-method'>使用实例工厂方法实例化</h5>
静态工厂方法类似的还有实例工厂方法,使用实例工厂方法的方式实例化,是调用容器中已存在的bean的一个非静态方法来创建一个bean。用法是,1、class属性置空设置。 2、设置factory-bean属性,其值为当前容器(或者父容器)中bean的名字,该bean包含可供调用的创建对象的实例方法。3、设置factory-method属性,其值为工厂方法名。

<!-- 工厂类, 包含一个方法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;
    }

}

上例中展示了工厂类本身也可以通过 DI 管理和配置。参看DI详情

注意

Srping 资料中,factory bean是指一个Spring配置的bean,该bean能通过实例或者静态工厂方法创建对象。对比之下,FactoryBean(注意大写)是指Spring术语FactoryBean。这段没太理解,解释factory bean和FactoryBean

<h3 id='beans-dependencies'>依赖</h3>
企业应用绝不会只有1个简单对象(或者说Spring bean)。哪怕是最简单的应用,也会包含许多对象协同工作。下一章节讲述,如何为真正的应用定义大量的、独立的bean,并让这些对象一起合作。

<h4 id="beans-factory-collaborators">依赖注入</h4>
依赖注入(DI),是一个有对象定义依赖的手法,也就是,如何与其他对象合作,通过构造参数、工厂方法参数、或是在对象实例化之后设置对象属性,实例化既可以构造也可以是使用工厂方法。容器在它创建bean之后注入依赖。这个过程从根本上发生了反转,因此又名控制反转(Ioc),因为Spring bean自己控制依赖类的实例化或者定位 ,Spring bean中就有依赖类的定义,容器使用依赖类构造器创建依赖类实例,使用Service Locator模式定位依赖类。

DI机制使代码简洁,对象提供它们的依赖,解耦更高效。对象无需自己查找依赖。同样的,类更容易测试,尤其当依赖接口或者抽象类时,测试允许在单元测试中使用stub或者mock(模拟技术)实现。

DI有2种主要方式,构造注入setter注入
构造注入,容器调用构造函数并传参数,每个参数都是依赖。调用静态工厂方法并传参数方式构造bean和构造注入差不多,这里是指构造注入处理参数和静态工厂方法处理参数像类似。下例中展示了一个只能使用构造注入的类。注意,此类无任何特别之处,并未依赖容器指定的接口、基类、注解,就是一个POJO

public class SimpleMovieLister {

    // the SimpleMovieLister 依赖 a MovieFinder
    private MovieFinder movieFinder;

    //Spring容器能注入MovieFinder的构造函数
    public SimpleMovieLister(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // 实际如何使用MovieFinder的业务逻辑省略了

}

<h5 id='beans-factory-ctor-arguments-resolution'>构造函数参数解决方案</h5>
构造参数解决方案,会匹配所使用的参数类型。如果在bean的定义中,构造参数不存在歧义,那么,在bean定义中定义的构造参数的次序,在bean实例化时,就是提供给适合的构造参数的次序。看这个类:

package x.y;

public class Foo {

    public Foo(Bar bar, Baz baz) {
        // ...
    }

}

不存在歧义,假设BarBaz类没有集成关系,那么下面的配置是合法的,而且,不需要在<constructor-arg/>元素里指定构造参数的明确的indexes索引或者类型。

<beans>
    <bean id="foo" class="x.y.Foo">
        <constructor-arg ref="bar"/>
        <constructor-arg ref="baz"/>
    </bean>

    <bean id="bar" class="x.y.Bar"/>

    <bean id="baz" class="x.y.Baz"/>
</beans>

若需要引用另一个bean,类型已知,构造函数就可以匹配参数类型(像上面的示例)。使用简单类型时, 想<value>true</true>,Srping不能决定value类型情况,Spring就不能自己匹配类型。例如:

package examples;

public class ExampleBean {

    // Number of years to calculate the Ultimate Answer
    private int years;

    // The Answer to Life, the Universe, and Everything
    private String ultimateAnswer;

    public ExampleBean(int years, String ultimateAnswer) {
        this.years = years;
        this.ultimateAnswer = ultimateAnswer;
    }

}

上面的场景中,如果使用type属性明确指定构造参数的类型,容器就可以使用类型匹配。比如:

    <bean id="exampleBean" class="examples.ExampleBean">
        <constructor-arg type="int" value="7500000"/>
        <constructor-arg type="java.lang.String" value="42"/>
    </bean>

使用index属性明确指定构造参数的次序。比如

    <bean id="exampleBean" class="examples.ExampleBean">
        <constructor-arg index="0" value="7500000"/>
        <constructor-arg index="1" value="42"/>
    </bean>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容

  • 参考W3C Spring教程 Spring致力于J2EE应用的各种解决方案,而不仅仅专注于某一层解决方案。可以说S...
    王侦阅读 1,154评论 0 6
  • 开始使用Spring Framework 5.0和设计模式 介绍Spring框架 Spring简化了应用程序开发,...
    一颗懒能阅读 2,862评论 3 21
  • 2.1 我们的理念是:让别人为你服务 IoC是随着近年来轻量级容器(Lightweight Container)的...
    好好学习Sun阅读 2,702评论 0 11
  • 今天又单独把周重林的书拿出来看了一遍,看了三分之一。我发现我确实是书要看2遍才有印象,每次看的时候收获都不一样,然...
    茶女子张怡阅读 151评论 0 0
  • 等许桑再见到苏轩时已经是一个月以后了。篮球在篮球框边悬摇转动,苏轩腾空的身子落地。“啊啊啊……”随着女生疯狂的尖叫...
    忆夕笑雪阅读 297评论 0 0