1. 程式人生 > >1.4 依賴(part2)

1.4 依賴(part2)

官方英文版地址:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html

備註:翻譯如有不對,請多指正,謝謝。

 

1.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屬性或者建構函式引數用來引用其他被管理的協作beans,或者內建的基本型別值。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的轉換服務( conversion service )用於將這些值從字串轉換成屬性或引數的實際型別。

<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.

下面的例子使用p名稱空間( p-namespace)來完成更簡潔的XML配置。

<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更加簡潔,然後,除非你使用像IntelliJ IDEA或者Spring Tool Suite(STS)類似的支援在進行bean定義時自動屬性填充的IDE,否則將會在執行時而不是在設計時才會發現拼寫錯誤。強烈推薦使用這些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例項。這是一個很好的快捷方式,也是Spring團隊支援使用巢狀元素而不是<value>屬性少數幾個地方之一。

 

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.

idref元素是一種將容器中另外一個bean元素的id傳遞給<constructor-arg/>或者<property/>元素的方式。

<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真實存在。在第二個變體中,不會在client物件的targetName屬性上設定的值進行任何校驗。只有在client初始化時才會發現輸入錯誤(可能導致嚴重的後果)。如果client是原型bean,這種型別和由此產生的異常可能在容器部署之後很長時間才會發現。

 

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>元素帶來好處的一個常用地方就是在ProxyFactoryBean定義中的AOP攔截器配置(AOP interceptors )。使用<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、本地或者父屬性指定其他物件的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.

不管是否是在相同的xml檔案中,通過<ref/>標籤的bean屬性指定目標bean是最常用的形式,並且允許建立對相同容器或者父容器中任何bean的引用。<ref/>元素的bean屬性可能和目標bean的id屬性或者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相同,異或是和目標bean的name屬性值的任何一個值相同,而目標bean必須在當前容器的父容器中。你可以使用這個bean的引用的變體,當你知道這個容器的層次結構並且用一個和父類bean同名的代理時,你通常會使用這個bean的變體。

<!-- 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 the ref 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 existing ref local references to ref bean when upgrading to the 4.0 schema.

<ref/>元素的local屬性,由於不支援常規bean引用之外的值,所以在4.0 beans xsd中已經不再支援。當升級到4.0的時候,只需要簡單的將存在的ref local引用改為ref bean引用即可。

 

Inner beans

A <bean/> element inside the <property/> or <constructor-arg/> elements defines a so-called inner bean.

在<property/>或者<constructor-arg/>元素中的<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.

內部類定義並不需要宣告的Id或者name屬性,如果指定的話,容器也不會使用它們作為唯一識別符號。容器在建立的時候也會忽略scope標識:內部bean總是匿名的,並且和包含它的外部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的建立是和包含它的容器聯絡在一起的,但是銷燬回撥允許在請求作用域宣告週期內發生。這並不是常見的場景,內部類一般簡單的和包含它的容器擁有相同的作用域。

 

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集合型別中的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">[email protected]</prop>
            <prop key="support">[email protected]</prop>
            <prop key="development">[email protected]</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結構的值,可以是下面幾種型別中的任何一種。

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/>來整合或者重寫父容器中的元素值。這就是說,子集合中的值是將父集合和子集合中的元素合併後的結果,子集合中的元素可能覆寫了在父集合中指定的值。

The following example demonstrates collection merging:

接下來的例子展示了集合的合併:

<beans>
    <bean id="parent" abstract="true" class="example.ComplexObject">
        <property name="adminEmails">
            <props>
                <prop key="administrator">[email protected]</prop>
                <prop key="support">[email protected]</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">[email protected]</prop>
                <prop key="support">[email protected]</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.

注意,在子bean定義的adminEmails屬性上使用了merge=true屬性。當容器子bean被解析和初始化的時候,生成的例項有一個adminEmails屬性集合,這個集合中包含了父集合和子集合中的所有元素。

[email protected]
[email protected]
[email protected]

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/>這個特殊的例子中,維護中集合中元素的順序,和Java型別List的元素的順序和<list/>元素中定義的順序一致,父集合中的值在子集合中的值的前面。在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)進行合併,如果你嘗試去這麼做,將會丟擲一個異常。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.

伴隨著Java5之後泛型型別的引入,你可以使用強型別集合。這就是說,可以定義一個集合型別值包含String元素(舉個例子)成為可能。如果你使用Spring將強型別集合注入到bean中,你可以充分利用Spring的型別轉換支援,以便在將元素新增到集合前將這些元素轉換為合適的型別。

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將空引數視為空字串。下面基於XML的配置元資料將email屬性的值設定為空字串。

<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。

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支援使用名稱空間形式的擴充套件配置,在本章中討論的bean配置是以XML文件的方式定義的,然而,p名稱空間並不是在XSD檔案中定義,只在Spring的核心中存在。

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="[email protected]"/>
    </bean>

    <bean name="p-namespace" class="com.example.ExampleBean"
        p:email="[email protected]"/>
</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屬性。p名稱空間告訴Spring包含一個屬性宣告。如前面說明,p名稱空間沒有模式定義,在p後面可以設定屬性的名稱。

This next example includes two more bean definitions that both have a reference to another bean:

接下來的例子包含了兩個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名稱空間的property值,還包含了特殊形式宣告的property引用。第一個bean定義使用<property name="spouse" ref="jane">來建立一個對jane的引用,第二個bean定義使用p:spouse-ref="jane"來做相同的事情。在這個例子中,spouse是一個屬性名,-ref用來表示它不是一個簡單的值,而是一個對其他bean的引用。

 

XML shortcut with the c-namespace

Similar to the 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.

和p名稱空間相似,c名稱空間,是在Spring3.1之後才引入的,可以使用內聯屬性來配置構造引數,而不是使用<constructor-arg>元素。

Let’s review the examples from Constructor-based dependency injection with the c: namespace:

讓我們使用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="[email protected]"/>
    </bean>

    <!-- c-namespace declaration -->
    <bean id="foo" class="x.y.Foo" c:bar-ref="bar" c:baz-ref="baz" c:email="[email protected]"/>

</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名稱空間相同的轉換形式來設定構造引數,末尾使用-ref來標識bean引用。即使在XSD模式中沒有定義,單同樣需要宣告它。

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"/>

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屬性含有一個bob屬性,這個bob屬性又含有一個sammy屬性,這個sammy屬性的值為123。為了使這個配置生效,在bean被建立之後,fred屬性和bob屬性都不能為null,否則將丟擲一個空指標異常。