文章作者:Tyan
博客:noahsnail.com
3.4 Dependencies
A typical enterprise application does not consist of a single object (or bean in the Spring parlance). Even the simplest application has a few objects that work together to present what the end-user sees as a coherent application. This next section explains how you go from defining a number of bean definitions that stand alone to a fully realized application where objects collaborate to achieve a goal.
标准企业应用不会由一个对象(或Spring用语中的bean)组成。即使是最简单的应用也是由一些对象共同工作,呈现给终端用户用户看到的是一个连续的应用。接下来的一节阐述了如何从定义许多独立的bean定义到完全实现的应用,它是一个通过对象协作来实现目标的过程。
3.4.1 Dependency Injection
Dependency injection (DI) is a process whereby objects define their dependencies, that is, the other objects they work with, only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. The container then injects those dependencies when it creates the bean. This process is fundamentally the inverse, hence the name Inversion of Control (IoC), of the bean itself controlling the instantiation or location of its dependencies on its own by using direct construction of classes, or the Service Locator pattern.
依赖注入(DI)是一个处理过程,凭借对象之间依赖关系,也就是和它们一起工作的其它对象,只能通过构造函数参数,传递参数给工厂方法,在构造完成或工厂方法返回对象实例之后再设置对象实例的属性。当创建bean时容器再将这些依赖对象注入进去。这个过程从根本上颠倒了bean本身通过直接构建类或通过一种机制例如服务定位模式来控制依赖对象的实例化或定位,因此命名为控制反转(IoC)。
Code is cleaner with the DI principle and decoupling is more effective when objects are provided with their dependencies. The object does not look up its dependencies, and does not know the location or class of the dependencies. As such, your classes become easier to test, in particular when the dependencies are on interfaces or abstract base classes, which allow for stub or mock implementations to be used in unit tests.
使用依赖注入原则会使代码更简洁,当对象由依赖关系提供时解耦更有效。对象不会查找它的依赖,不知道依赖的位置和依赖关系的类别。同样的,你的类也变的更容易测试,尤其是依赖关系在接口或抽象基类之间的时候,这种情况下单元测试中会要求存桩或模拟实现。(注:Stub和Mock都是软件测试中使用的东西,如有疑问请自行google或百度)。
DI exists in two major variants, Constructor-based dependency injection and Setter-based dependency injection.
依赖有两个主要变种,基于构造函数的依赖注入和基于Setter的依赖注入。
Constructor-based dependency injection
Constructor-based DI is accomplished by the container invoking a constructor with a number of arguments, each representing a dependency. Calling a static
factory method with specific arguments to construct the bean is nearly equivalent, and this discussion treats arguments to a constructor and to a static
factory method similarly. The following example shows a class that can only be dependency-injected with constructor injection. Notice that there is nothing special about this class, it is a POJO that has no dependencies on container specific interfaces, base classes or annotations.
基于构造函数的依赖注入通过容器调用有参数的构造函数来实现,每个参数表示一个依赖。调用指定参数的静态工厂方法来构造bean是近似等价的,这里的讨论将给构造函数和静态工厂方法传参看成是类似的。接下来的例子展示了一个类仅能通过构建函数注入进行依赖注入。注意这个类没什么特别的,它是一个POJO,不依赖于容器特定的接口,基类或注解。
public class SimpleMovieLister {
// the SimpleMovieLister has a dependency on a MovieFinder
private MovieFinder movieFinder;
// a constructor so that the Spring container can inject a MovieFinder
public SimpleMovieLister(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// business logic that actually uses the injected MovieFinder is omitted...
}
Constructor argument resolution
Constructor argument resolution matching occurs using the argument’s type. If no potential ambiguity exists in the constructor arguments of a bean definition, then the order in which the constructor arguments are defined in a bean definition is the order in which those arguments are supplied to the appropriate constructor when the bean is being instantiated. Consider the following class:
构造函数参数解析使用参数类型进行匹配。如果bean定义的构造函数参数中不存在潜在的歧义,bean定义中定义构造函数参数的顺序为bean实例化时提供给恰当构造函数的参数顺序。细想下面的类:
package x.y;
public class Foo {
public Foo(Bar bar, Baz baz) {
// ...
}
}
No potential ambiguity exists, assuming that Bar
and Baz
classes are not related by inheritance. Thus the following configuration works fine, and you do not need to specify the constructor argument indexes and/or types explicitly in the <constructor-arg/>
element.
不存在潜在的歧义,假设Bar
类和Baz
类之间不存在继承关系。因此下面的配置会工作良好,你不必在<constructor-arg/>
元素中显式的指定构造函数参数索引的与/或类型。
<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>
When another bean is referenced, the type is known, and matching can occur (as was the case with the preceding example). When a simple type is used, such as <value>true</value>
, Spring cannot determine the type of the value, and so cannot match by type without help. Consider the following class:
当引用另一个bean时,类型已知,匹配正确(像上面的例子一样)。当使用简单类型时,例如<value>true</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;
}
}
In the preceding scenario, the container can use type matching with simple types if you explicitly specify the type of the constructor argument using the type attribute. For example:
在上面的场景中,如果你用type
属性显式的指定了构造参数的类型,对于简单类型容器可以使用类型匹配。例如:
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg type="int" value="7500000"/>
<constructor-arg type="java.lang.String" value="42"/>
</bean>
Use the index
attribute to specify explicitly the index of constructor arguments. For example:
使用index
属性来显式的指定构造函数参数的索引,例如:
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg index="0" value="7500000"/>
<constructor-arg index="1" value="42"/>
</bean>
In addition to resolving the ambiguity of multiple simple values, specifying an index resolves ambiguity where a constructor has two arguments of the same type. Note that the index is 0 based.
除了要解析多个简单值的歧义性之外,当构造函数有两个相同类型的的参数时,指定索引可以解决歧义问题。注意索引是从0开始的。
You can also use the constructor parameter name for value disambiguation:
你也可以使用构造函数参数名字解决值的歧义问题。
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg name="years" value="7500000"/>
<constructor-arg name="ultimateAnswer" value="42"/>
</bean>
Keep in mind that to make this work out of the box your code must be compiled with the debug flag enabled so that Spring can look up the parameter name from the constructor. If you can’t compile your code with debug flag (or don’t want to) you can use @ConstructorProperties
JDK annotation to explicitly name your constructor arguments. The sample class would then have to look as follows:
记住,要使这个起作用你的代码必须使用调试模式进行编译,这样Spring可以从构造函数中查找参数名称。如果你不能用调试模式进行编译(或不想),你可以使用JDK注解@ConstructorProperties
显式的命名你的构造函数参数。样板类如下所示:
package examples;
public class ExampleBean {
// Fields omitted
@ConstructorProperties({"years", "ultimateAnswer"})
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}
Setter-based dependency injection
Setter-based DI is accomplished by the container calling setter methods on your beans after invoking a no-argument constructor or no-argument static factory method to instantiate your bean.
基于Setter的依赖注入在容器调用无参构造函数或无参静态工厂方法之后,通过调用bean的setter方法来实现依赖注入。
The following example shows a class that can only be dependency-injected using pure setter injection. This class is conventional Java. It is a POJO that has no dependencies on container specific interfaces, base classes or annotations.
下面的例子显示了一个类只能通过纯粹的setter注入进行依赖注入。这个类是常见的Java类。它是一个不依赖于容器中特定接口、基类或注解的POJO。
public class SimpleMovieLister {
// the SimpleMovieLister has a dependency on the MovieFinder
private MovieFinder movieFinder;
// a setter method so that the Spring container can inject a MovieFinder
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// business logic that actually uses the injected MovieFinder is omitted...
}
The ApplicationContext
supports constructor-based and setter-based DI for the beans it manages. It also supports setter-based DI after some dependencies have already been injected through the constructor approach. You configure the dependencies in the form of a BeanDefinition
, which you use in conjunction with PropertyEditor
instances to convert properties from one format to another. However, most Spring users do not work with these classes directly (i.e., programmatically) but rather with XML bean definitions, annotated components (i.e., classes annotated with @Component
, @Controller
, etc.), or @Bean
methods in Java-based @Configuration
classes. These sources are then converted internally into instances of BeanDefinition
and used to load an entire Spring IoC container instance.
ApplicationContext
支持基于构造函数和基于setter对它管理的bean进行依赖注入。它也支持一些依赖通过构造函数方法注入之后,使用基于setter的依赖注入。使用BeanDefinition
形式配置依赖项,结合PropertyEditor
实例可以将属性从一种形式转成另一种形式。然而大多数Spring用户直接使用这些类(例如以编程形式),而使用XML定义bean,注解组件(例如类中使用 @Component
,,@Controller
注解等等),或在基于Java的@Configuration
类使用@Bean
方法。
Constructor-based or setter-based DI?
Since you can mix constructor-based and setter-based DI, it is a good rule of thumb to use constructors for mandatory dependencies and setter methods or configuration methods for optional dependencies. Note that use of the
@Required
annotation on a setter method can be used to make the property a required dependency.
The Spring team generally advocates constructor injection as it enables one to implement application components as immutable objects and to ensure that required dependencies are not
null
. Furthermore constructor-injected components are always returned to client (calling) code in a fully initialized state. As a side note, a large number of constructor arguments is a bad code smell, implying that the class likely has too many responsibilities and should be refactored to better address proper separation of concerns.
Setter injection should primarily only be used for optional dependencies that can be assigned reasonable default values within the class. Otherwise, not-null checks must be performed everywhere the code uses the dependency. One benefit of setter injection is that setter methods make objects of that class amenable to reconfiguration or re-injection later. Management through
JMX MBeans
is therefore a compelling use case for setter injection.
Use the DI style that makes the most sense for a particular class. Sometimes, when dealing with third-party classes for which you do not have the source, the choice is made for you. For example, if a third-party class does not expose any setter methods, then constructor injection may be the only available form of DI.
使用基于构造函数的依赖注入还是基于setter的依赖注入?
你可以混合使用基于构造函数的依赖注入和基于setter的依赖注入,强制依赖使用构造函数注入,可选依赖使用setter方法或配置方法注入是一个很好的经验法则。注意在setter方法上使用
@Required
注解会检查依赖是否注入。
当实现的应用组件是不可变对象时,Spring团队通常主张构造函数注入,这样可以确保所需的依赖非空。此外,基于构造函数注入的组件总是以完全初始化状态返回客户(调用)代码。作为附注,含有许多构造函数参数的代码给人的感觉很差,这意味着类可能有很多职责,应该进行重构以便更好的处理关注问题的分离。
setter注入应该主要用来可选依赖上,在类内可以给可选依赖指定合理的默认值。此外,在每处使用依赖的代码都要进行非空检查。setter注入的一个好处就是setter方法使类的对象在后面可以进行再配置或再注入。
JMX MBeans
的管理是setter注入一个非常好的案例。
使用依赖注入的类型对于特定的类是最有意义的。有时候,当处理没有源码的第三方类时,使用哪种方式取决于你。例如,如果第三方库没有提供任何setter方法,构造函数注入可能是依赖注入唯一可行的方式。
Dependency resolution process
The container performs bean dependency resolution as follows:
The
ApplicationContext
is created and initialized with configuration metadata that describes all the beans. Configuration metadata can be specified via XML, Java code, or annotations.For each bean, its dependencies are expressed in the form of properties, constructor arguments, or arguments to the static-factory method if you are using that instead of a normal constructor. These dependencies are provided to the bean, when the bean is actually created.
Each property or constructor argument is an actual definition of the value to set, or a reference to another bean in the container.
Each property or constructor argument which is a value is converted from its specified format to the actual type of that property or constructor argument. By default Spring can convert a value supplied in string format to all built-in types, such as int, long, String, boolean, etc.
容器按下面的过程处理bean依赖解析:
创建
ApplicationContext
并使用描述所有bean的配置元数据初始化ApplicationContext
,配置元数据可以通过XML,Java代码或注解指定。对于每一个bean,它的依赖通过属性、构造函数参数、或静态工厂方法参数的形式表示,静态工厂方法可以替代标准的构造函数。当bean在实际创建时,这些依赖会提供给bean。
每个属性或构造函数参数或者是根据实际定义设置的值,或者是容器中另一个bean的引用。
每个属性或构造函数参数是一个从指定形式转成实际类型的属性或构造函数参数的值。
The Spring container validates the configuration of each bean as the container is created. However, the bean properties themselves are not set until the bean is actually created. Beans that are singleton-scoped and set to be pre-instantiated (the default) are created when the container is created. Scopes are defined in Section 3.5, “Bean scopes”. Otherwise, the bean is created only when it is requested. Creation of a bean potentially causes a graph of beans to be created, as the bean’s dependencies and its dependencies' dependencies (and so on) are created and assigned. Note that resolution mismatches among those dependencies may show up late, i.e. on first creation of the affected bean.
当容器创建后Spring容器会验证每个bean的配置。然而,bean属性本身只有bean创建时才会进行设置。bean是单例的并且当容器创建时会进行提前实例化(默认情况)。作用范围是在3.5 小节"Bean scopes"中定义的。此外,只有需要时候才会创建bean。bean的创建可能会引起beans图的创建,当bean的依赖和它的依赖的依赖(等等)创建和指定的时候。注意这些依赖中解析不匹配可能会在后面出现,例如,受影响的bean第一次创建时。
Circular dependencies
If you use predominantly constructor injection, it is possible to create an unresolvable circular dependency scenario.
For example: Class A requires an instance of class B through constructor injection, and class B requires an instance of class A through constructor injection. If you configure beans for classes A and B to be injected into each other, the Spring IoC container detects this circular reference at runtime, and throws a
BeanCurrentlyInCreationException
.
One possible solution is to edit the source code of some classes to be configured by setters rather than constructors. Alternatively, avoid constructor injection and use setter injection only. In other words, although it is not recommended, you can configure circular dependencies with setter injection.
Unlike the typical case (with no circular dependencies), a circular dependency between bean A and bean B forces one of the beans to be injected into the other prior to being fully initialized itself (a classic chicken/egg scenario).
循环依赖
如果你主要使用构造函数注入,有可能会出现一个不能解决的循环依赖状况。
例如,类A需要通过构造函数注入得到一个类B的实例,而类B需要通过构造函数获得一个类A的实例。如果你为类A和类B配置了互相注入的bean,Spring IoC容器在运行时检测到循环引用,会抛出
BeanCurrentlyInCreationException
。
一个可能的解决方案是编译某个类的源代码使其通过setter注入而不是构造函数注入。或者,避免构造函数注入仅用setter注入。换句话说,尽管是不被推荐的,但你可以通过setter注入配置循环依赖。
不像通常的情况(没有循环依赖),bean A和bean B之间的循环依赖可以强制其中的一个bean优先注入另一个bean中,可以使其完全初始化(古老的鸡/蛋场景)。
You can generally trust Spring to do the right thing. It detects configuration problems, such as references to non-existent beans and circular dependencies, at container load-time. Spring sets properties and resolves dependencies as late as possible, when the bean is actually created. This means that a Spring container which has loaded correctly can later generate an exception when you request an object if there is a problem creating that object or one of its dependencies. For example, the bean throws an exception as a result of a missing or invalid property. This potentially delayed visibility of some configuration issues is why ApplicationContext implementations by default pre-instantiate singleton beans. At the cost of some upfront time and memory to create these beans before they are actually needed, you discover configuration issues when the ApplicationContext
is created, not later. You can still override this default behavior so that singleton beans will lazy-initialize, rather than be pre-instantiated.
通常情况下你可以信任Spring去做正确的事情。在容器加载时它检测配置问题,例如引用不存在的beans和循环依赖。当bean实际创建时,Spring设置属性和解析依赖尽可能的晚。这意味着Spring容器正确加载但后面可能会产生异常,当你请求一个对象时,创建对象或它的某个依赖时出现问题,这时容器就会抛出异常。例如,由于缺失或存在无效属性,bean会抛出异常。在真正需要这些beans之前创建它们,会花费一些前期时间和内存,但当ApplicationContext
创建时你会发现配置问题,而不是在创建之后。为了单例bean延迟初始化而不是预先实例化,你仍需要重写这个默认行为。
If no circular dependencies exist, when one or more collaborating beans are being injected into a dependent bean, each collaborating bean is totally configured prior to being injected into the dependent bean. This means that if bean A has a dependency on bean B, the Spring IoC container completely configures bean B prior to invoking the setter method on bean A. In other words, the bean is instantiated (if not a pre-instantiated singleton), its dependencies are set, and the relevant lifecycle methods (such as a configured init method or the InitializingBean callback method) are invoked.
如果没有循环依赖存在,当一个或更多协作beans注入到一个独立的bean中,在注入独立bean之前,每个协作bean都是完全配置的。这意味着如果bean A有个依赖为bean B,Spring IoC容器在调用bean A的setter方法之前会完整的配置bean B。换句话说,bean被实例化(不是预先实例化的单例),设置依赖和相关的生命周期方法(例如配置初始化方法或初始化bean回调方法)被调用。
Examples of dependency injection
The following example uses XML-based configuration metadata for setter-based DI. A small part of a Spring XML configuration file specifies some bean definitions:
下面的例子使用基于XML的配置元数据进行setter注入。Spring XML配置文件中的一小部分指定了一些bean的定义:
<bean id="exampleBean" class="examples.ExampleBean">
<!-- setter injection using the nested ref element -->
<property name="beanOne">
<ref bean="anotherExampleBean"/>
</property>
<!-- setter injection using the neater ref attribute -->
<property name="beanTwo" ref="yetAnotherBean"/>
<property name="integerProperty" value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
public class ExampleBean {
private AnotherBean beanOne;
private YetAnotherBean beanTwo;
private int i;
public void setBeanOne(AnotherBean beanOne) {
this.beanOne = beanOne;
}
public void setBeanTwo(YetAnotherBean beanTwo) {
this.beanTwo = beanTwo;
}
public void setIntegerProperty(int i) {
this.i = i;
}
}
In the preceding example, setters are declared to match against the properties specified in the XML file. The following example uses constructor-based DI:
在上面的例子中,setter声明匹配XML文件中指定的属性。下面的例子使用了基于构造函数的依赖注入:
<bean id="exampleBean" class="examples.ExampleBean">
<!-- constructor injection using the nested ref element -->
<constructor-arg>
<ref bean="anotherExampleBean"/>
</constructor-arg>
<!-- constructor injection using the neater ref attribute -->
<constructor-arg ref="yetAnotherBean"/>
<constructor-arg type="int" value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
public class ExampleBean {
private AnotherBean beanOne;
private YetAnotherBean beanTwo;
private int i;
public ExampleBean(
AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
this.beanOne = anotherBean;
this.beanTwo = yetAnotherBean;
this.i = i;
}
}
The constructor arguments specified in the bean definition will be used as arguments to the constructor of the ExampleBean
.
bean定义中指定的构造函数参数将作为ExampleBean
的构造函数参数使用。
Now consider a variant of this example, where instead of using a constructor, Spring is told to call a static factory method to return an instance of the object:
现在考虑这个例子的一个变种,不使用构造函数,而是Spring调用静态工厂方法返回对象的一个实例:
<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 {
// a private constructor
private ExampleBean(...) {
...
}
// a static factory method; the arguments to this method can be
// considered the dependencies of the bean that is returned,
// regardless of how those arguments are actually used.
public static ExampleBean createInstance (
AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
ExampleBean eb = new ExampleBean (...);
// some other operations...
return eb;
}
}
Arguments to the static
factory method are supplied via <constructor-arg/>
elements, exactly the same as if a constructor had actually been used. The type of the class being returned by the factory method does not have to be of the same type as the class that contains the static
factory method, although in this example it is. An instance (non-static) factory method would be used in an essentially identical fashion (aside from the use of the factory-bean
attribute instead of the class
attribute), so details will not be discussed here.
静态工厂方法的参数通过<constructor-arg/>
元素提供,与构造函数使用的完全一样。虽然这个例子中工厂方法返回值的类型与包含静态工厂方法的类的类型一样,但它们可以不一样。工厂方法的实例(非静态)的使用本质上样式完全一样(除了使用factory-bean
属性代替class
属性之外),因此这儿不讨论这些细节。
3.4.2 Dependencies and configuration in detail
As mentioned in the previous section, you can define bean properties and constructor arguments as references to other managed beans (collaborators), or as values defined inline. Spring’s XML-based configuration metadata supports sub-element types within its <property/>
and <constructor-arg/>
elements for this purpose.
正如上一节提到的那样,你可以通过引用其它被管理bean(协作者)来定义bean的属性和构造函数参数,或者在行内定义值。为了实现这个功能,Spring的基于XML的配置元数据在它的<property/>
和<constructor-arg/>
中支持子元素类型。
Straight values (primitives, Strings, and so on)
The value
attribute of the <property/>
element specifies a property or constructor argument as a human-readable string representation. Spring’s conversion service is used to convert these values from a String
to the actual type of the property or argument.
<property/>
元素的value
属性指定了一个属性或构造函数参数作为可读的字符串表示。使用Spring的转换服务将这些值从String
转成属性或参数的真实类型。
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<!-- results in a setDriverClassName(String) call -->
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
<property name="username" value="root"/>
<property name="password" value="masterkaoli"/>
</bean>
The following example uses the p-namespace for even more succinct XML configuration.
下面的例子为了更简洁的XML配置使用了p命名空间.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"
p:driverClassName="com.mysql.jdbc.Driver"
p:url="jdbc:mysql://localhost:3306/mydb"
p:username="root"
p:password="masterkaoli"/>
</beans>
The preceding XML is more succinct; however, typos are discovered at runtime rather than design time, unless you use an IDE such as IntelliJ IDEA
or the Spring Tool Suite (STS)
that support automatic property completion when you create bean definitions. Such IDE assistance is highly recommended.
上面的XML是更简洁的;然而,错别字是在运行时发现而不是在设计时,除非你使用IDE例如IntelliJ IDEA
或Spring Tool Suite (STS)
,当你创建bean定义时它们支持自动的属性补全。IDE辅助是强烈推荐的。
You can also configure a java.util.Properties
instance as:
你也可以配置java.util.Properties
实例:
<bean id="mappings"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<!-- typed as a java.util.Properties -->
<property name="properties">
<value>
jdbc.driver.className=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mydb
</value>
</property>
</bean>
The Spring container converts the text inside the <value/>
element into a java.util.Properties
instance by using the JavaBeans PropertyEditor
mechanism. This is a nice shortcut, and is one of a few places where the Spring team do favor the use of the nested <value/>
element over the value
attribute style.
Spring容器通过JavaBeans的PropertyEditor
机制将<value/>
元素内部的文本转成java.util.Properties
实例。这是一个很好的捷径,使用嵌入的<value/>
元素而不是使用value
属性的方式,是Spring团队支持的几个地方之一。
The idref element
The idref
element is simply an error-proof way to pass the id (string value - not a reference) of another bean in the container to a <constructor-arg/>
or <property/>
element.
在容器中传递另一个bean的id(字符串值,不是引用)到<constructor-arg/>
或<property/>
元素时,idref
元素是一种简单的的误差检验方式。
<bean id="theTargetBean" class="..."/>
<bean id="theClientBean" class="...">
<property name="targetName">
<idref bean="theTargetBean" />
</property>
</bean>
The above bean definition snippet is exactly equivalent (at runtime) to the following snippet:
上面的bean定义片段与下面的代码片段是等价的(运行时):
<bean id="theTargetBean" class="..." />
<bean id="client" class="...">
<property name="targetName" value="theTargetBean" />
</bean>
The first form is preferable to the second, because using the idref
tag allows the container to validate at deployment time that the referenced, named bean actually exists. In the second variation, no validation is performed on the value that is passed to the targetName
property of the client
bean. Typos are only discovered (with most likely fatal results) when the client
bean is actually instantiated. If the client
bean is a prototype bean, this typo and the resulting exception may only be discovered long after the container is deployed.
第一种形式优于第二种形式,因为idref
标签允许容器在部署时验证引用的bean,即命名的bean实际存在。在第二种形式中,当值传给client
的targetName
时没有进行验证。拼写错误只有在client
bean实际创建时才会发现(最可能有严重后果)。如果client
bean是原型bean,拼写错误和产生的异常可能只有在容器部署很长时间之后才会发现。
The
local
attribute on theidref
element is no longer supported in the 4.0 beans xsd since it does not provide value over a regular bean reference anymore. Simply change your existingidref local
references toidref bean
when upgrading to the 4.0 schema.
idref
元素的local
属性在4.0 beans xsd中不再支持,因为它不再为合格的bean引用提供值。简单将你现有的idref local
引用改成idref bean
当更新到4.0 schema时。
A common place (at least in versions earlier than Spring 2.0) where the <idref/>
element brings value is in the configuration of AOP interceptors in a ProxyFactoryBean
bean definition. Using <idref/>
elements when you specify the interceptor names prevents you from misspelling an interceptor id.
<idref/>
元素带来值的通常位置(至少在Spring 2.0之前)是在ProxyFactoryBean
bean定义中的AOP拦截器配置中。当你指定拦截器名字时使用<idref/>
元素来防止误拼拦截器id。
References to other beans (collaborators)
The ref
element is the final element inside a <constructor-arg/>
or <property/>
definition element. Here you set the value of the specified property of a bean to be a reference to another bean (a collaborator) managed by the container. The referenced bean is a dependency of the bean whose property will be set, and it is initialized on demand as needed before the property is set. (If the collaborator is a singleton bean, it may be initialized already by the container.) All references are ultimately a reference to another object. Scoping and validation depend on whether you specify the id/name of the other object through the bean
, local
, or parent
attributes.
ref
元素是<constructor-arg/>
或<property/>
定义元素的最终的元素。在这个元素中设置bean的指定属性的值,值为容器管理的另一个bean(协作bean)的引用。引用的bean是设置属性bean的依赖,在属性设置之前引用bean需要进行初始化。(如果协作bean是一个单例模式的bean,它可能已经被容器初始化了。)所有引用bean根本上都是另一个对象的引用。作用域和验证是根据你是否通过bean
,local
,或parent
属性指定了另一个对象的id/name来决定的。
Specifying the target bean through the bean
attribute of the <ref/>
tag is the most general form, and allows creation of a reference to any bean in the same container or parent container, regardless of whether it is in the same XML file. The value of the bean
attribute may be the same as the id
attribute of the target bean, or as one of the values in the name
attribute of the target bean.
通过<ref/>
标签的bean
属性指定目标bean是最常用的形式,允许创建同容器或父容器中任何bean的引用,不管它是否是在同一个XML文件中。bean
属性的值可能与目标bean的id
属性值相同,或与目标bean的name
属性值相同。
<ref bean="someBean"/>
Specifying the target bean through the parent
attribute creates a reference to a bean that is in a parent container of the current container. The value of the parent
attribute may be the same as either the id
attribute of the target bean, or one of the values in the name
attribute of the target bean, and the target bean must be in a parent container of the current one. You use this bean reference variant mainly when you have a hierarchy of containers and you want to wrap an existing bean in a parent container with a proxy that will have the same name as the parent bean.
通过parent
属性指定目标bean会引用当前容器的父容器中的bean。parent
属性的值可能与目标bean的id
值或name
值相同,目标bean必须在当前容器的父容器中。当你有一个容器分层的时候你可以使用parent
,你想将现有bean包裹在有代理的父容器中且现有bean与父容器中的bean同名,你可以使用parent
属性。
<!-- in the parent context -->
<bean id="accountService" class="com.foo.SimpleAccountService">
<!-- insert dependencies as required as here -->
</bean>
<!-- in the child (descendant) context -->
<bean id="accountService" <!-- bean name is the same as the parent bean -->
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target">
<ref parent="accountService"/> <!-- notice how we refer to the parent bean -->
</property>
<!-- insert other configuration and dependencies as required here -->
</bean>
The
local
attribute on theref
element is no longer supported in the 4.0 beans xsd since it does not provide value over a regularbean
reference anymore. Simply change your existingref local
references toref bean
when upgrading to the 4.0 schema.
idref
元素的local
属性在4.0 beans xsd中不再支持,因为它不再为合格的bean引用提供值。简单将你现有的idref local
引用改成idref bean
当更新到4.0 schema时。
Inner beans
A <bean/>
element inside the <property/>
or <constructor-arg/>
elements defines a so-called inner bean.
<property/>
或<constructor-arg/>
元素内的<bean/>
元素中定义bean称为内部bean。
<bean id="outer" class="...">
<!-- instead of using a reference to a target bean, simply define the target bean inline -->
<property name="target">
<bean class="com.example.Person"> <!-- this is the inner bean -->
<property name="name" value="Fiona Apple"/>
<property name="age" value="25"/>
</bean>
</property>
</bean>
An inner bean definition does not require a defined id or name; if specified, the container does not use such a value as an identifier. The container also ignores the scope
flag on creation: Inner beans are always anonymous and they are always created with the outer bean. It is not possible to inject inner beans into collaborating beans other than into the enclosing bean or to access them independently.
内部bean定义不要求定义id或name;如果指定了,容器不用用这个值作为标识符。容器创建时也忽略scope
标记:内部bean总是匿名的且它们总是由外部bean创建。除了注入到封闭bean中或独立的访问它们,不可能将内部bean注入到协作bean中。
As a corner case, it is possible to receive destruction callbacks from a custom scope, e.g. for a request-scoped inner bean contained within a singleton bean: The creation of the inner bean instance will be tied to its containing bean, but destruction callbacks allow it to participate in the request scope’s lifecycle. This is not a common scenario; inner beans typically simply share their containing bean’s scope.
作为一种很少出现的情况,从特定的域中有可能会收到销毁回调函数,例如,对于请求域内的内部bean包含单例bean:内部bean实例的创建会绑定到它的包含bean,但销毁回调函数允许它进入到请求域的生命周期中。这不是一个常见的场景;内部bean通常简单的共享它们的包含bean的作用域。
Collections
In the <list/>
, <set/>
, <map/>
, and <props/>
elements, you set the properties and arguments of the Java Collection
types List
, Set
, Map
, and Properties
, respectively.
在<list/>
,<set/>
,<map/>
和<props/>
元素中,你要分别设置Java Collection
类型list
,set
,map
和Properties
的属性和参数。
<bean id="moreComplexObject" class="example.ComplexObject">
<!-- results in a setAdminEmails(java.util.Properties) call -->
<property name="adminEmails">
<props>
<prop key="administrator">administrator@example.org</prop>
<prop key="support">support@example.org</prop>
<prop key="development">development@example.org</prop>
</props>
</property>
<!-- results in a setSomeList(java.util.List) call -->
<property name="someList">
<list>
<value>a list element followed by a reference</value>
<ref bean="myDataSource" />
</list>
</property>
<!-- results in a setSomeMap(java.util.Map) call -->
<property name="someMap">
<map>
<entry key="an entry" value="just some string"/>
<entry key ="a ref" value-ref="myDataSource"/>
</map>
</property>
<!-- results in a setSomeSet(java.util.Set) call -->
<property name="someSet">
<set>
<value>just some string</value>
<ref bean="myDataSource" />
</set>
</property>
</bean>
The value of a map key or value, or a set value, can also again be any of the following elements:
map的key或value,或者是set value的值也可以是下面元素中的任何一个:
bean | ref | idref | list | set | map | props | value | null
Collection merging
The Spring container also supports the merging of collections. An application developer can define a parent-style <list/>
, <map/>
, <set/>
or <props/>
element, and have child-style <list/>
, <map/>
, <set/>
or <props/>
elements inherit and override values from the parent collection. That is, the child collection’s values are the result of merging the elements of the parent and child collections, with the child’s collection elements overriding values specified in the parent collection.
Spring也支持集合的合并。应用开发者可以定义父类型<list/>
,<map/>
,<set/>
或<props/>
元素,可以有继承和覆盖父集合的子类型元素<list/>
,<map/>
,<set/>
或<props/>
。也就是说,子集合的值是父集合和子集合中元素合并的结果,子集合元素覆盖了父集合元素的值。
This section on merging discusses the parent-child bean mechanism. Readers unfamiliar with parent and child bean definitions may wish to read the relevant section before continuing.
关于合并的这节讨论了父子bean机制。对父子bean定义不熟悉的读者可以去读相关的章节。
The following example demonstrates collection merging:
下面的例子示范了集合合并:
<beans>
<bean id="parent" abstract="true" class="example.ComplexObject">
<property name="adminEmails">
<props>
<prop key="administrator">administrator@example.com</prop>
<prop key="support">support@example.com</prop>
</props>
</property>
</bean>
<bean id="child" parent="parent">
<property name="adminEmails">
<!-- the merge is specified on the child collection definition -->
<props merge="true">
<prop key="sales">sales@example.com</prop>
<prop key="support">support@example.co.uk</prop>
</props>
</property>
</bean>
<beans>
Notice the use of the merge=true
attribute on the <props/>
element of the adminEmails
property of the child
bean definition. When the child
bean is resolved and instantiated by the container, the resulting instance has an adminEmails
Properties
collection that contains the result of the merging of the child’s adminEmails
collection with the parent’s adminEmails
collection.
注意child
bean定义中的adminEmails
属性下的<props/>
元素使用了merge=true
属性。当容器解析并实例化child
bean时,最终的实例含有adminEmails
Properties
集合,集合中的值是子adminEmails
集合和父adminEmails
集合合并的结果。
administrator=administrator@example.com
sales=sales@example.com
support=support@example.co.uk
The child Properties
collection’s value set inherits all property elements from the parent <props/>
, and the child’s value for the support
value overrides the value in the parent collection.
子Properties
集合的值继承了父<props/>
中的所有属性元素,子集合中的support
值覆盖了父集合中的值。
This merging behavior applies similarly to the <list/>
, <map/>
, and <set/>
collection types. In the specific case of the <list/>
element, the semantics associated with the List
collection type, that is, the notion of an ordered
collection of values, is maintained; the parent’s values precede all of the child list’s values. In the case of the Map
, Set
, and Properties
collection types, no ordering exists. Hence no ordering semantics are in effect for the collection types that underlie the associated Map
, Set
, and Properties
implementation types that the container uses internally.
<list/>
,<map/>
和<set/>
集合类型中的合并与上面类似。在特定的<list/>
元素情况下,关于List
集合类型的语义,也就是说,有序集合值的概念仍然是保留的;父list中的值领先于所有子list中的值。在Map
,Set
和Properties
集合类型,不存在顺序。因此,无序语义在容器内部使用的集合类型Map
,Set
和Properties
的实现基础上是有效的。
Limitations of collection merging
You cannot merge different collection types (such as a Map
and a List
), and if you do attempt to do so an appropriate Exception
is thrown. The merge
attribute must be specified on the lower, inherited, child definition; specifying the merge
attribute on a parent collection definition is redundant and will not result in the desired merging.
你不能合并不同的集合类型(例如Map
和List
),如果你试图合并不同的集合类型会有适当的抛出Exception
。merge
属性必须在更低的、继承的子定义中;在父集合定义中指定merge
属性是多余的并且不会进行合并。
Strongly-typed collection
With the introduction of generic types in Java 5, you can use strongly typed collections. That is, it is possible to declare a Collection
type such that it can only contain String
elements (for example). If you are using Spring to dependency-inject a strongly-typed Collection
into a bean, you can take advantage of Spring’s type-conversion support such that the elements of your strongly-typed Collection
instances are converted to the appropriate type prior to being added to the Collection
.
随着Java 5中泛型的引入,你可以使用强类型集合。也就是说,你可以声明一个Collection
类型但它只能包含String
元素(例子)。如果你使用Spring将一个强类型的Collection
注入到bean中,你可以利用Spring的类型转换支持,例如在将元素添加到Collection
之前,将你的强类型Collection
实例中的元素转成恰当的类型。
public class Foo {
private Map<String, Float> accounts;
public void setAccounts(Map<String, Float> accounts) {
this.accounts = accounts;
}
}
<beans>
<bean id="foo" class="x.y.Foo">
<property name="accounts">
<map>
<entry key="one" value="9.99"/>
<entry key="two" value="2.75"/>
<entry key="six" value="3.99"/>
</map>
</property>
</bean>
</beans>
When the accounts
property of the foo
bean is prepared for injection, the generics information about the element type of the strongly-typed Map<String, Float>
is available by reflection. Thus Spring’s type conversion infrastructure recognizes the various value elements as being of type Float
, and the string values 9.99, 2.75
, and 3.99
are converted into an actual Float
type.
当注入foo
bean的accounts
属性时,强类型Map<String, Float>
中元素类型的泛型信息可以通过反射得到。因此Spring的类型转换结构能识别各种值元素的类型为Float
,字符串9.99, 2.75
和3.99
会被转换成实际的Float
类型。
Null and empty string values
Spring treats empty arguments for properties and the like as empty Strings
. The following XML-based configuration metadata snippet sets the email property to the empty String
value ("").
Spring把属性的空参数都处理为空Strings
。下面基于XML的配置元数据片段将email属性设为空String
值("")。
<bean class="ExampleBean">
<property name="email" value=""/>
</bean>
The preceding example is equivalent to the following Java code:
上面的例子与下面的Java代码是等价的:
exampleBean.setEmail("")
The <null/>
element handles null
values. For example:
<null/>
元素处理null
值。例如:
<bean class="ExampleBean">
<property name="email">
<null/>
</property>
</bean>
The above configuration is equivalent to the following Java code:
上面的配置与下面的Java代码等价。
exampleBean.setEmail(null)
XML shortcut with the p-namespace
The p-namespace enables you to use the bean
element’s attributes, instead of nested <property/>
elements, to describe your property values and/or collaborating beans.
p命名空间可以让你不需要嵌入<property/>
元素便能使用bean
元素的属性来描述你的属性值以及/或协作beans。
Spring supports extensible configuration formats with namespaces, which are based on an XML Schema definition. The beans configuration format discussed in this chapter is defined in an XML Schema document. However, the p-namespace is not defined in an XSD file and exists only in the core of Spring.
Spring支持含有命名空间的扩展配置形式,命名控件是基于XML Schema定义的。本章讨论的beans配置形式是在XML Schema文档中定义的。但是p命名空间不能在XSD文件中定义并且只在Spring core中存在。
The following example shows two XML snippets that resolve to the same result: The first uses standard XML format and the second uses the p-namespace.
下面的例子显示了两个XML片段,解析结果是相同的:第一个是标准的XML形式,第二个使用了p命名空间。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="classic" class="com.example.ExampleBean">
<property name="email" value="foo@bar.com"/>
</bean>
<bean name="p-namespace" class="com.example.ExampleBean"
p:email="foo@bar.com"/>
</beans>
The example shows an attribute in the p-namespace called email in the bean definition. This tells Spring to include a property declaration. As previously mentioned, the p-namespace does not have a schema definition, so you can set the name of the attribute to the property name.
这个例子显示了bean定义中p命名空间中有个一个叫email的属性。这会通知Spring包含属性声明。如前面所述,p命名空间没有schema定义,因此你可以将特性值(attribute)设到属性值(property)上。
This next example includes two more bean definitions that both have a reference to another bean:
下面的例子包括两个bean定义,且它们都引用了另一个bean:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="john-classic" class="com.example.Person">
<property name="name" value="John Doe"/>
<property name="spouse" ref="jane"/>
</bean>
<bean name="john-modern"
class="com.example.Person"
p:name="John Doe"
p:spouse-ref="jane"/>
<bean name="jane" class="com.example.Person">
<property name="name" value="Jane Doe"/>
</bean>
</beans>
As you can see, this example includes not only a property value using the p-namespace, but also uses a special format to declare property references. Whereas the first bean definition uses <property name="spouse" ref="jane"/>
to create a reference from bean john
to bean jane
, the second bean definition uses p:spouse-ref="jane"
as an attribute to do the exact same thing. In this case spouse
is the property name, whereas the -ref
part indicates that this is not a straight value but rather a reference to another bean.
正如你所看到的,这个例子不仅包括使用了p命名空间的属性值,而且使用了一种特定的形式来声明属性引用。然而第一个bean定义使用<property name="spouse" ref="jane"/>
创建了一个从bean john
到bean jane
的引用,第二个bean定义使用p:spouse-ref="jane"
作为一个特性同样定义了从bean john
到bean jane
的引用。在spouse
是属性名的情况下,-ref
部分表示这不是一个直接的值而是另一个bean的引用。
The p-namespace is not as flexible as the standard XML format. For example, the format for declaring property references clashes with properties that end in
Ref
, whereas the standard XML format does not. We recommend that you choose your approach carefully and communicate this to your team members, to avoid producing XML documents that use all three approaches at the same time.
p命名空间不是标准的XML格式,例如,声明的属性引用会与以
Ref
结尾的属性相冲突,而标准XML格式则不会。我们建议你仔细的选择你的方法并与你的团队成员交流,避免生成的XML文档同时使用了三种方式。
XML shortcut with the c-namespace
Similar to the the section called “XML shortcut with the p-namespace”, the c-namespace, newly introduced in Spring 3.1, allows usage of inlined attributes for configuring the constructor arguments rather then nested constructor-arg
elements.
与“XML shortcut with the p-namespace”小节类似,在Spring 3.1新引入的c命名空间允许使用行内属性配置构造函数参数而不用嵌入constructor-arg
元素。
Let’s review the examples from the section called “Constructor-based dependency injection” with the c:
namespace:
让我们重新回顾一下“Constructor-based dependency injection”小节中的例子并使用c:
命名空间:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bar" class="x.y.Bar"/>
<bean id="baz" class="x.y.Baz"/>
<!-- traditional declaration -->
<bean id="foo" class="x.y.Foo">
<constructor-arg ref="bar"/>
<constructor-arg ref="baz"/>
<constructor-arg value="foo@bar.com"/>
</bean>
<!-- c-namespace declaration -->
<bean id="foo" class="x.y.Foo" c:bar-ref="bar" c:baz-ref="baz" c:email="foo@bar.com"/>
</beans>
The c:
namespace uses the same conventions as the p:
one (trailing -ref
for bean references) for setting the constructor arguments by their names. And just as well, it needs to be declared even though it is not defined in an XSD schema (but it exists inside the Spring core).
c:
命名空间遵循与p:
命名空间相同的约定在通过名字设置构造函数参数时。同样的,它也需要进行声明,虽然它不能在XSD schema中使用(但在Spring core中存在)。
For the rare cases where the constructor argument names are not available (usually if the bytecode was compiled without debugging information), one can use fallback to the argument indexes:
对于很少出现的不能找到构造函数参数名字的情况(通常如果编译字节码且没有调试信息),可以使用参数索引:
<!-- c-namespace index declaration -->
<bean id="foo" class="x.y.Foo" c:_0-ref="bar" c:_1-ref="baz"/>
Due to the XML grammar, the index notation requires the presence of the leading
_
as XML attribute names cannot start with a number (even though some IDE allow it).
由于XML语法,索引符号需要前面加上
_
,因为XML属性名字不能以数字开头(即使一些IDE允许)。
In practice, the constructor resolution mechanism is quite efficient in matching arguments so unless one really needs to, we recommend using the name notation through-out your configuration.
在实践中,构造函数解析机制能有效匹配参数,因此除非真的需要,否则我们推荐在配置中使用名字符号。
Compound property names
You can use compound or nested property names when you set bean properties, as long as all components of the path except the final property name are not null
. Consider the following bean definition.
当你设置bean属性时,你可以使用混合的或嵌入的属性名字,只要路径中除了最后的属性名之外所有组件都是非null
。考虑下面的bean定义。
<bean id="foo" class="foo.Bar">
<property name="fred.bob.sammy" value="123" />
</bean>
The foo
bean has a fred
property, which has a bob
property, which has a sammy
property, and that final sammy
property is being set to the value 123
. In order for this to work, the fred
property of foo
, and the bob
property of fred
must not be null
after the bean is constructed, or a NullPointerException
is thrown.
foo
bean有一个fred
属性,fred
有一个sammy
属性,bob
有一个sammy
属性,最后的sammy
属性设置值为123
。为了这样设置,foo
的fred
属性,fred
的bob
属性在bean创建后必须是非null
或抛出NullPointerException
。
3.4.3 Using depends-on
If a bean is a dependency of another that usually means that one bean is set as a property of another. Typically you accomplish this with the <ref/>
element in XML-based configuration metadata. However, sometimes dependencies between beans are less direct; for example, a static initializer in a class needs to be triggered, such as database driver registration. The depends-on
attribute can explicitly force one or more beans to be initialized before the bean using this element is initialized. The following example uses the depends-on
attribute to express a dependency on a single bean:
如果一个bean是另一个bean的一个依赖,这通常意味着一个bean作为另一个bean的一个属性去设置。在基于XML的配置元数据中通常使用<ref/>
元素实现。然而有时beans之间的依赖关系是间接的;例如,类中的静态初始化程序需要触发,例如数据驱动注册。depends-on
特性能显示的强制一个bean或多个beans在使用这个元素的bean初始化之前进行初始化。下面的例子使用depends-on
特性表示一个单一bean的一个依赖:
<bean id="beanOne" class="ExampleBean" depends-on="manager"/>
<bean id="manager" class="ManagerBean" />
To express a dependency on multiple beans, supply a list of bean names as the value of the depends-on
attribute, with commas, whitespace and semicolons, used as valid delimiters:
为了表示多个bean上的依赖关系,提供一个bean名字列表作为depends-on
特性的值,用逗号,空格或分号作为有效分隔符:
<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
<property name="manager" ref="manager" />
</bean>
<bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />
The
depends-on
attribute in the bean definition can specify both an initialization time dependency and, in the case of singleton beans only, a corresponding destroy time dependency. Dependent beans that define adepends-on
relationship with a given bean are destroyed first, prior to the given bean itself being destroyed. Thusdepends-on
can also control shutdown order.
depends-on
特性在bean定义中可以指定初始化时的依赖和对应的销毁时依赖(仅在单例情况下)。依赖beans与给定bean之间定义了一个depends-on
关系,依赖beans在给定bean本身被销毁之前首先被销毁。因此depends-on
也可以控制销毁顺序。
3.4.4 Lazy-initialized beans
By default, ApplicationContext
implementations eagerly create and configure all singleton beans as part of the initialization process. Generally, this pre-instantiation is desirable, because errors in the configuration or surrounding environment are discovered immediately, as opposed to hours or even days later. When this behavior is not desirable, you can prevent pre-instantiation of a singleton bean by marking the bean definition as lazy-initialized. A lazy-initialized bean tells the IoC container to create a bean instance when it is first requested, rather than at startup.
默认情况下,作为初始化过程的一部分,ApplicationContext
实现时渴望创建并配置所有的单例beans。通常情况下,预实例化是必要的,因为配置中或周围环境中的错误可以立即发现,与几小时或几天后发现截然相反。当预实例化是不必要的时候,你可通过标记bean定义为延迟初始化来阻止单例bean的预实例化。延迟初始化的bean会通知IoC容器当第一次请求bean时创建一个bean实例,而不是在启动时创建。
In XML, this behavior is controlled by the lazy-init
attribute on the <bean/>
element; for example:
在XML中,延迟初始化通过<bean/>
元素中的lazy-init
特性来控制;例如:
<bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"/>
<bean name="not.lazy" class="com.foo.AnotherBean"/>
When the preceding configuration is consumed by an ApplicationContext
, the bean named lazy
is not eagerly pre-instantiated when the ApplicationContext
is starting up, whereas the not.lazy
bean is eagerly pre-instantiated.
当ApplicationContext
读取到上面的配置,ApplicationContext
启动时名字为lazy
的bean不会进行预实例化,而名字为not.lazy
的bean会进行预实例化。
However, when a lazy-initialized bean is a dependency of a singleton bean that is not lazy-initialized, the ApplicationContext
creates the lazy-initialized bean at startup, because it must satisfy the singleton’s dependencies. The lazy-initialized bean is injected into a singleton bean elsewhere that is not lazy-initialized.
然而,当延迟初始化的bean是一个非延迟初始化的单例bean的依赖时,ApplicationContext
会在启动时创建延迟初始化的bean,因为它必须提供单例bean的依赖。延迟初始化的bean会注入到单例bean中,而在其它地方它是非延迟初始化的。
You can also control lazy-initialization at the container level by using the default-lazy-init
attribute on the <beans/>
element; for example:
你也可以在容器中通过<beans/>
中的default-lazy-init
特性控制延迟初始化;例如:
<beans default-lazy-init="true">
<!-- no beans will be pre-instantiated... -->
</beans>
3.4.5 Autowiring collaborators
The Spring container can autowire relationships between collaborating beans. You can allow Spring to resolve collaborators (other beans) automatically for your bean by inspecting the contents of the ApplicationContext
. Autowiring has the following advantages:
Autowiring can significantly reduce the need to specify properties or constructor arguments. (Other mechanisms such as a bean template discussed elsewhere in this chapter are also valuable in this regard.)
Autowiring can update a configuration as your objects evolve. For example, if you need to add a dependency to a class, that dependency can be satisfied automatically without you needing to modify the configuration. Thus autowiring can be especially useful during development, without negating the option of switching to explicit wiring when the code base becomes more stable.
Spring容器能自动装配协作beans之间的关联关系。你可以允许Spring通过检查ApplicationContext
中的内容自动的为你的bean解析协作者(其它bean)。自动装配有以下优势:
自动装配能明显减少指定属性或构造函数参数的需要。(其它的机制例如在本章其它地方讨论的bean模板在这一点上也是非常重要的。)
当对象变化时自动装配能更新配置。例如,如果你需要增加一个类的依赖项,依赖项可以是满足自动装配的而不需要你去修改配置。因此自动装配在开发时尤其有用,当代码基础变的更稳定时可以改为显式装配。
When using XML-based configuration metadata, you specify autowire mode for a bean definition with the autowire
attribute of the <bean/>
element. The autowiring functionality has four modes. You specify autowiring per bean and thus can choose which ones to autowire.
当使用基于XML的配置元数据时,通过使用<bean/>
元素的autowire
特性你可以指定一个bean定义的自动装配模式。自动注入功能有四种模式。你可以指定每个bean的自动装配模式,因此你可以选择使用哪一种模式。
Table 3.2. Autowiring modes
Mode | Explanation |
---|---|
no | (Default) No autowiring. Bean references must be defined via a ref element. Changing the default setting is not recommended for larger deployments, because specifying collaborators explicitly gives greater control and clarity. To some extent, it documents the structure of a system. |
byName | Autowiring by property name. Spring looks for a bean with the same name as the property that needs to be autowired. For example, if a bean definition is set to autowire by name, and it contains a master property (that is, it has a setMaster(..) method), Spring looks for a bean definition named master , and uses it to set the property. |
byType | Allows a property to be autowired if exactly one bean of the property type exists in the container. If more than one exists, a fatal exception is thrown, which indicates that you may not use byType autowiring for that bean. If there are no matching beans, nothing happens; the property is not set. |
constructor | Analogous to byType , but applies to constructor arguments. If there is not exactly one bean of the constructor argument type in the container, a fatal error is raised. |
表 3.2 自动装配模式
模式 | 解析 |
---|---|
no | (默认)无自动装配。引用bean必须通过ref 元素定义。对于更大的部署,不推荐更改默认设置,因为显式指定协作者更清晰并且更易控制。在某种程度上来说,它记录了系统的结构。 |
byName | 通过属性名称自动装配。Spring寻找与需要自动装配的属性同名的bean。例如,如果一个bean定义设置为通过名称自动装配,它有一个master 属性(也就是说,它有一个setMaster(..) 方法),Spring寻找名字为master 的bean定义,使用它设置属性值。 |
byType | 如果容器中含有属性类型已知的一个bean,那么可以允许按类型自动装配属性。如果此类型的bean不止一个,则会抛出致命的异常,这意味着你可能不能使用byType 来注入那个bean。如果没有匹配的bean,则什么也不做;属性没有被设置。 |
constructor | 与byType 类似,但是应用到构造函数参数上的。如果容器中没有一个构造函数参数bean的确定类型,将会抛出一个致命的异常。 |
With byType or constructor autowiring mode, you can wire arrays and typed-collections. In such cases all autowire candidates within the container that match the expected type are provided to satisfy the dependency. You can autowire strongly-typed Maps if the expected key type is String
. An autowired Maps values will consist of all bean instances that match the expected type, and the Maps keys will contain the corresponding bean names.
通过byType
或构造函数自动装配模式,你可以配置数组和集合类型。在这种情况下容器内所有能匹配期望类型的自动装配候选对象将被提供合适的依赖项。如果期望的key
类型是String
类型,你可以自动装配强类型的Maps
。自动装配的Maps
的值将有所有匹配期望类型的bean组成,Maps
的键将包含对应的bean名称。
You can combine autowire behavior with dependency checking, which is performed after autowiring completes.
你可以将依赖检查与自动装配相结合,它将在自动装配完成之后执行。
Limitations and disadvantages of autowiring
Autowiring works best when it is used consistently across a project. If autowiring is not used in general, it might be confusing to developers to use it to wire only one or two bean definitions.
当自动装配在整个工程中一致的使用时其效果最好。如果通常情况下不使用自动装配,仅在一两个bean定义中使用自动装配开发人员可能感到非常困惑。
Consider the limitations and disadvantages of autowiring:
Explicit dependencies in
property
andconstructor-arg
settings always override autowiring. You cannot autowire so-called simple properties such as primitives,Strings
, andClasses
(and arrays of such simple properties). This limitation is by-design.Autowiring is less exact than explicit wiring. Although, as noted in the above table, Spring is careful to avoid guessing in case of ambiguity that might have unexpected results, the relationships between your Spring-managed objects are no longer documented explicitly.
Wiring information may not be available to tools that may generate documentation from a Spring container.
Multiple bean definitions within the container may match the type specified by the setter method or constructor argument to be autowired. For arrays, collections, or Maps, this is not necessarily a problem. However for dependencies that expect a single value, this ambiguity is not arbitrarily resolved. If no unique bean definition is available, an exception is thrown.
考虑一下自动装配的限制与缺点:
property
和constructor-arg
中显式依赖的设置总是会覆盖自动装配。你不能自动装配所谓的简单属性例如基本类型,Strings
和Classes
(和简单类型的数组)。这是设计上的限制。与显式配置相比,自动装配是更不确定的。尽管Spring小心的避免猜测以防歧义性引起无法预料的后果,但Spring管理的对象之间的关系不再被显式的记录。
Spring容器中能产生文档的工具可能得不到配置信息。
setter方法或构造函数参数指定的类型进行自动装配时可能匹配到容器中多个bean的定义。对于数组,集合或
Maps
而言,这是一个不必要的问题。然而对于只期望一个值的依赖而言,这个歧义性不能任意解决。如果不能获得唯一的bean定义,会抛出异常。
In the latter scenario, you have several options:
Abandon autowiring in favor of explicit wiring.
Avoid autowiring for a bean definition by setting its
autowire-candidate
attributes tofalse
as described in the next section.Designate a single bean definition as the primary candidate by setting the
primary
attribute of its<bean/>
element totrue
.Implement the more fine-grained control available with annotation-based configuration, as described in Section 3.9, “Annotation-based container configuration”.
后面的方案中,你有一些选择:
放弃自动装配支持显式配置。
通过设置bean的
autowire-candidate
特性为false
来避免自动装配。通过设置
<bean/>
元素的primary
特性为true
来指定一个单例bean定义作为主要的候选bean。通过基于注解的配置实现更多细颗粒的控制,如3.9小节 "基于注解的容器配置"。
Excluding a bean from autowiring
On a per-bean basis, you can exclude a bean from autowiring. In Spring’s XML format, set the autowire-candidate
attribute of the <bean/>
element to false
; the container makes that specific bean definition unavailable to the autowiring infrastructure (including annotation style configurations such as @Autowired
).
在单个bean的基础上,你可以排除bean在自动装配之外。在Spring的XML形式中,设置<bean/>
元素的autowire-candidate
特性为false
;容器会使自动装配基础框架不能得到指定bean定义(包括注解类型的配置,例如@Autowired
)。
You can also limit autowire candidates based on pattern-matching against bean names. The top-level <beans/>
element accepts one or more patterns within its default-autowire-candidates
attribute. For example, to limit autowire candidate status to any bean whose name ends with Repository, provide a value of *Repository
. To provide multiple patterns, define them in a comma-separated list. An explicit value of true
or false
for a bean definitions autowire-candidate
attribute always takes precedence, and for such beans, the pattern matching rules do not apply.
你也可以根据bean名称的匹配模式限制自动装配的候选目标。顶层的<beans/>
元素可以接收default-autowire-candidates
特性中的一个或多个模式。例如,为了限制自动装配候选目标匹配任何名字以Repository
结尾的bean,可以提供一个*Repository
值。为了提供多种模式,可以定义一个以逗号为分隔符的列表。bean定义中autowire-candidate
特性显示的值true
或false
最是优先起作用的,对于这些bean而言,模式匹配规则不起作用。
These techniques are useful for beans that you never want to be injected into other beans by autowiring. It does not mean that an excluded bean cannot itself be configured using autowiring. Rather, the bean itself is not a candidate for autowiring other beans.
这些技术对于那些你从不想通过自动装配方式注入到其它bean中的beans而言是很有用的。这不意味着一个排除的bean它本身不能通过自动装配进行配置。更确切的说,bean本身不是一个进行其它bean进行自动装配的候选者。
3.4.6 Method injection
In most application scenarios, most beans in the container are singletons. When a singleton bean needs to collaborate with another singleton bean, or a non-singleton bean needs to collaborate with another non-singleton bean, you typically handle the dependency by defining one bean as a property of the other. A problem arises when the bean lifecycles are different. Suppose singleton bean A needs to use non-singleton (prototype) bean B, perhaps on each method invocation on A. The container only creates the singleton bean A once, and thus only gets one opportunity to set the properties. The container cannot provide bean A with a new instance of bean B every time one is needed.
在大多数应用场景中,容器中的大多数bean是单例的。当一个单例bean需要与另一个单例bean协作时,或一个非单例bean需要与另一个非单例bean协作时,你通常通过定义一个bean作为另一个bean的一个属性来处理这个依赖关系。当bean的生命周期不同时问题就出现了。假设一个单例bean A需要使用非单例(标准)bean B时,也许A中的每一个方法调用都要使用bean B。容器仅创建单例bean A一次,因此仅有一次设置属性的机会。容器不能在每次需要bean B时提供一个bean B的新的实例。
A solution is to forego some inversion of control. You can make bean A aware of the container by implementing the ApplicationContextAware
interface, and by making a getBean("B") call to the container ask for (a typically new) bean B instance every time bean A needs it. The following is an example of this approach:
一个解决方案是放弃一些控制反转。你可以使bean A通过实现ApplicationContextAware
接口感知到容器,每个bean A需要的时候就通过getBean("B")
调用向容器请求(通常是新的)一个bean B的实例。下面是这种方法的一个例子:
// a class that uses a stateful Command-style class to perform some processing
package fiona.apple;
// Spring-API imports
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class CommandManager implements ApplicationContextAware {
private ApplicationContext applicationContext;
public Object process(Map commandState) {
// grab a new instance of the appropriate Command
Command command = createCommand();
// set the state on the (hopefully brand new) Command instance
command.setState(commandState);
return command.execute();
}
protected Command createCommand() {
// notice the Spring API dependency!
return this.applicationContext.getBean("command", Command.class);
}
public void setApplicationContext(
ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
The preceding is not desirable, because the business code is aware of and coupled to the Spring Framework. Method Injection, a somewhat advanced feature of the Spring IoC container, allows this use case to be handled in a clean fashion.
前面所讲的不是让人满意的,因为业务代码能感知并耦合了Spring框架。方法注入,Spring IoC容器的一个有点高级的特性,允许使用一种干净的方式来处理这个案例。
You can read more about the motivation for Method Injection in this blog entry.
你可以在blog entry中了解更多关于方法注入的动机。
Lookup method injection
Lookup method injection is the ability of the container to override methods on container managed beans, to return the lookup result for another named bean in the container. The lookup typically involves a prototype bean as in the scenario described in the preceding section. The Spring Framework implements this method injection by using bytecode generation from the CGLIB library to generate dynamically a subclass that overrides the method.
查找方法注入是容器的一种覆盖其管理的beans中的方法的能力,可以返回容器中另一个命名bean查找结果。查找通常会涉及到一个标准bean,如前一小节中讲的那样。Spring框架实现了查找方法注入,它是通过使用CGLIB库生成的字节码来动态的产生一个覆盖这个方法的子类。
- For this dynamic subclassing to work, the class that the Spring bean container will subclass cannot be final, and the method to be overridden cannot be final either.
- Unit-testing a class that has an abstract method requires you to subclass the class yourself and to supply a stub implementation of the abstract method.
- Concrete methods are also necessary for component scanning which requires concrete classes to pick up.
- A further key limitation is that lookup methods won’t work with factory methods and in particular not with
@Bean
methods in configuration classes, since the container is not in charge of creating the instance in that case and therefore cannot create a runtime-generated subclass on the fly.
- Finally, objects that have been the target of method injection cannot be serialized.
- 为了使动态子类化起作用,Spring bean容器要进行子类化的类不能是最终的类,要进行重写的方法也不是最终的方法。
- 单元测试一个含有抽象方法的类需要你自己对这个类进行子类化,并且提供这个抽象方法的
stub
实现。
- 实体方法对于要求获得实体类的组件扫描也是必需的。
- 一个更关键的限制是查找方法不能与工厂方法一起工作,尤其是在配置类中不能与
@Bean
方法同时起作用,由于那种情况下容器不能控制实例的创建,因此不能在飞速写入中创建一个运行时产生的子类。
- 最后,方法注入的目标对象不能被序列化。
Looking at the CommandManager
class in the previous code snippet, you see that the Spring container will dynamically override the implementation of the createCommand()
method. Your CommandManager
class will not have any Spring dependencies, as can be seen in the reworked example:
看一下前面代码片中的CommandManager
类,你可以看到Spring容器将会动态的覆盖createCommand()
方法的实现。CommandManager
类不会有任何Spring依赖,重写的例子如下:
package fiona.apple;
// no more Spring imports!
public abstract class CommandManager {
public Object process(Object commandState) {
// grab a new instance of the appropriate Command interface
Command command = createCommand();
// set the state on the (hopefully brand new) Command instance
command.setState(commandState);
return command.execute();
}
// okay... but where is the implementation of this method?
protected abstract Command createCommand();
}
In the client class containing the method to be injected (the CommandManager
in this case), the method to be injected requires a signature of the following form:
客户类中包含要注入的方法(在这个例子中是CommandManager
),要注入的方法需要下面形式的一个签名:
<public|protected> [abstract] <return-type> theMethodName(no-arguments);
If the method is abstract, the dynamically-generated subclass implements the method. Otherwise, the dynamically-generated subclass overrides the concrete method defined in the original class. For example:
如果这个方法是抽象的,动态产生的子类会实现这个方法。另外,动态产生的子类会覆盖原来的类中定义的实体方法。例如:
<!-- a stateful bean deployed as a prototype (non-singleton) -->
<bean id="command" class="fiona.apple.AsyncCommand" scope="prototype">
<!-- inject dependencies here as required -->
</bean>
<!-- commandProcessor uses statefulCommandHelper -->
<bean id="commandManager" class="fiona.apple.CommandManager">
<lookup-method name="createCommand" bean="command"/>
</bean>
The bean identified as commandManager calls its own method createCommand()
whenever it needs a new instance of the command
bean. You must be careful to deploy the command
bean as a prototype, if that is actually what is needed. If it is deployed as a singleton, the same instance of the command
bean is returned each time.
无论什么时候识别为commandManager
的bean需要一个command
bean的新实例,它都会调用它的createCommand()
方法。如果真的需要的话,你必须小心的部署command
bean为一个原型。如果它被部署为一个单例,每次都会返回同一个command
实例。
The interested reader may also find the
ServiceLocatorFactoryBean
(in theorg.springframework.beans.factory.config
package) to be of use. The approach used inServiceLocatorFactoryBean
is similar to that of another utility class,ObjectFactoryCreatingFactoryBean
, but it allows you to specify your own lookup interface as opposed to a Spring-specific lookup interface. Consult the javadocs of these classes for additional information.
感兴趣的读者可能也会发现
ServiceLocatorFactoryBean
(在org.springframework.beans.factory.config
包中)使用这种方法。ServiceLocatorFactoryBean
中使用的方法与另一个工具类ObjectFactoryCreatingFactoryBean
中的方法类似,但它允许你指定你自己的查找接口,与Spring特定的查找接口相反。这些类的额外信息请查询Java文档。
Arbitrary method replacement
A less useful form of method injection than lookup method injection is the ability to replace arbitrary methods in a managed bean with another method implementation. Users may safely skip the rest of this section until the functionality is actually needed.
一种比查找方法注入更少使用的形式是用另一种方法实现替换管理的bean中任意方法的能力。用户可以安全跳过本节剩下的部分,直到这个方法真正需要的时候再看。
With XML-based configuration metadata, you can use the replaced-method
element to replace an existing method implementation with another, for a deployed bean. Consider the following class, with a method computeValue
, which we want to override:
在基于XML的配置元数据中,对于一个部署的bean,你可以通过replaced-method
元素用另一个方法实现替换现有的方法实现。考虑下面的类,有一个我们想覆盖的computeValue
方法:
public class MyValueCalculator {
public String computeValue(String input) {
// some real code...
}
// some other methods...
}
A class implementing the org.springframework.beans.factory.support.MethodReplacer
interface provides the new method definition.
实现了org.springframework.beans.factory.support.MethodReplacer
接口的类提供了一种新的方法定义。
/**
* meant to be used to override the existing computeValue(String)
* implementation in MyValueCalculator
*/
public class ReplacementComputeValue implements MethodReplacer {
public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
// get the input value, work with it, and return a computed result
String input = (String) args[0];
...
return ...;
}
}
The bean definition to deploy the original class and specify the method override would look like this:
部署最初的类的bean定义和指定的重写方法如下:
<bean id="myValueCalculator" class="x.y.z.MyValueCalculator">
<!-- arbitrary method replacement -->
<replaced-method name="computeValue" replacer="replacementComputeValue">
<arg-type>String</arg-type>
</replaced-method>
</bean>
<bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>
You can use one or more contained <arg-type/>
elements within the <replaced-method/>
element to indicate the method signature of the method being overridden. The signature for the arguments is necessary only if the method is overloaded and multiple variants exist within the class. For convenience, the type string for an argument may be a substring of the fully qualified type name. For example, the following all match java.lang.String
:
你可以在<replaced-method/>
元素中使用一个或多个包含<arg-type/>
元素来指出要覆盖的方法的方法签名。只有类中进行了方法重载且有多个重载变种的时候,参数的签名才是必需的。为了简便,字符串类型的参数可能是全拼类型名称的一个子串。例如,下面的所有写法都能匹配java.lang.String
:
java.lang.String
String
Str
Because the number of arguments is often enough to distinguish between each possible choice, this shortcut can save a lot of typing, by allowing you to type only the shortest string that will match an argument type.
因为参数数目经常是足够区分每个可能的选择的,通过允许定义匹配参数类型的最短字符串类型,这个缩写可以保存许多类型。