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

1.4 依賴(part3)

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

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

 

1.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/>元素來實現依賴。然而,有時候bean之間的依賴關係並不那麼直接,舉個例子,一個類中的靜態初始化器需要被觸發,如資料庫驅動註冊。depends-on屬性可以顯示強制一個或多個bean在一個使用該元素的bean初始化前提前初始化。接下來的例子使用depend-s-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的依賴關係,可以給depengs-on的屬性值提供一個bean名稱列表,各名稱之間使用分號、空格或者分號分隔:

<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 a depends-on relationship with a given bean are destroyed first, prior to the given bean itself being destroyed. Thus depends-on can also control shutdown order.

在bean定義中的depends-on屬性,可以指定初始化階段依賴和銷燬階段依賴項。定義與給定bean依賴關係的獨立bean首先會被銷燬,然後再銷燬給定bean本身。因此,depends-on可以控制關閉順序。

 

1.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的實現類在初始化過程中急切的建立和配置所有的單例bean。一般來說,這種預先初始化是期望的,因為配置和周圍環境的錯誤會被立刻發現,而不是幾小時後者幾天之後。當這種行為不是期望的時候,你可以標記這個bean定義為延遲初始化的。延遲載入的bean會告訴IoC容器只在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消費的時候,以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中。

You can also control lazy-initialization at the container level by using the default-lazy-init attribute on the <beans/> element; for example:

你可以使用<bean/>元素的default-lazy-init屬性來控制容器級別的延遲載入。

<beans default-lazy-init="true">
    <!-- no beans will be pre-instantiated... -->
</beans>

 

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

Spring容器會自動注入協作bean之間的依賴關係。你可以允許Spring檢查ApplicationContext的內容來自動解析協作bean。自動注入有如下的優點:

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

  • 自動注入可以顯著的減少指定屬性和構造引數的需要。
  • 自動注入可以隨著物件的發展而更新配置,舉個例子,如果你需要去給一個類新增一個依賴,在你不需要修改配置的情況下就可以自動滿足依賴。因此,當代碼庫變的更加穩定時,自動注入在開發的過程中非常重要,而不需要切換到顯示自動注入的選項。

When using XML-based configuration metadata [2], 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的配置元資料時,你可以給含有autowire屬性的<bean/>元素bean定義指定自動注入模式。自動注入從功能上看有四種模式,你可以為每個bean指定自動注入模式,從而可以選擇自動注入到哪個bean。

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.

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或者construtor自動注入模式時,你可以自動注入陣列或者型別集合。在這種情況下,

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 and constructor-arg settings always override autowiring. You cannot autowire so-called simple properties such as primitives, Strings, and Classes (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/>設定中的依賴總是自動注入。你不可以自動注入像基礎資料型別、String、類(以及這些屬性的陣列)這樣的所謂簡單屬性。這個限制是設計造成的。
  • 自動注入不如顯示注入精確。如上面的圖表所示,Spring小心的避免可能導致意外結果的模糊性進行猜測,這些模糊性是由你的Spring容器管理的物件不是顯示宣告導致的。
  • 從Spring容器生成文件時可能無法正確使用自動注入資訊。
  • 容器中的bean定義可以匹配setter方法或者建構函式引數型別,並進行自動注入。對於陣列、集合、或者Map,這不是一個問題。然而,對於期望的是簡單值的依賴關係,這種模糊性不是能任意解決的。如果沒有唯一的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 to false as described in the next section.

  • Designate a single bean definition as the primary candidate by setting the primary attribute of its <bean/> element to true.

  • Implement the more fine-grained control available with annotation-based configuration, as described in Annotation-based container configuration.

  • 放棄自動注入,選擇顯示注入;
  • 通過設定bean的autowire-candidate屬性為false(將會在下一章節介紹)來避免自動注入;
  • 通過將<bean/>元素的primary屬性設定為true,用來將單個bean的定義宣告為主要候選物件;
  • 通過基於註解的配置實現更細粒度的控制,如在章節Annotation-based container configuration所述。

 

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/>在其defalut-autowire-candidate屬性中支援一個或多個模式。舉個例子,為了限制自動注入的候選項物件為那些名稱以Repository結尾的物件,設定自動注入的值為*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被自動注入到其他bean的時候非常有用。這並不意味著排除在外的bean本身並不能通過autowireing配置。而是這個bean本身不是其他bean注入候選項。

 

1.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 A一次,因此只有一次機會來設定屬性。容器無法保證bean A在每次需要bean B的時候都建立一個新的例項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:

一個解決方案是放棄一些控制反轉。你可以通過實現ApplicationContextAware介面來讓bean A知道容器,或者在bean A需要bean B的時候通過向容器中進行一次getBean("B")呼叫來生成一下新的例項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.

你可以在 this 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.

Lookup方法注入是容器提供的用於在容器管理bean物件上重寫方法的一種能力,並且為容器中的其他命名bean返回一個結果。如前一節所述,lookup涉及原型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.

為了讓這個動態子類能夠正常工作,這個由Spring容器生成的類不能是final型別的,並且重寫的方法也不能是final的。

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.

單元測試具有抽象方法的類,需要你對該類進行子類化,並且提供抽象方法的具體子類實現。

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.

另一個關鍵限制是,lookup方法不能與工廠方法一起工作,特別第,不能與配置類中的@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:

如果方法是abstract的,那麼動態生成的子類需要實現這個方法。否則,動態生成的子類將會抽象在原類中定義的具體方法,舉個例子:

<!-- a stateful bean deployed as a prototype (non-singleton) -->
<bean id="myCommand" 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="myCommand"/>
</bean>

The bean identified as commandManager calls its own method createCommand() whenever it needs a new instance of the myCommand bean. You must be careful to deploy the myCommand bean as a prototype, if that is actually what is needed. If it is as a singleton, the same instance of the myCommand bean is returned each time.

標識為commandManager的bean,在每次需要一個新的myCommand物件時都會觸發呼叫createCommand()方法。你必須小心的把myCommand物件定義為原型域,如果這是實際需要的。如果它是單例,myCommand物件每次返回的都是同一個例項。

Alternatively, within the annotation-based component model, you may declare a lookup method through the @Lookup annotation:

或者,在基於註解的元件模型中,你可以通過@Lookup註解來宣告一個lookup方法:

public abstract class CommandManager {

    public Object process(Object commandState) {
        Command command = createCommand();
        command.setState(commandState);
        return command.execute();
    }

    @Lookup("myCommand")
    protected abstract Command createCommand();
}

Or, more idiomatically, you may rely on the target bean getting resolved against the declared return type of the lookup method:

或者,更常見的是,你可能依賴於根據lookup方法宣告的返回型別解析出來的目標bean。

public abstract class CommandManager {

    public Object process(Object commandState) {
        MyCommand command = createCommand();
        command.setState(commandState);
        return command.execute();
    }

    @Lookup
    protected abstract MyCommand createCommand();
}

Note that you will typically declare such annotated lookup methods with a concrete stub implementation, in order for them to be compatible with Spring’s component scanning rules where abstract classes get ignored by default. This limitation does not apply in case of explicitly registered or explicitly imported bean classes.

需要註解的是,為了使它們與Spring的元件掃描規則相容,你可能使用@Loopup註解來宣告一個具有具體子類實現的方法。預設情況下,抽象類會被忽略掉。這個限制對於顯示註冊或者顯示注入的bean類並不適用。

 

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.

不如lookup方法注入的另外一種形式方法注入是用另外一個方法實現來替換被管理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的配置元資料中,你可以使用replaced-method元素來使用另一個方法來替換一個已有的方法。

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/>元素,用來表示需要重寫的方法簽名。當方法被重寫覆蓋並且在類中有多個變體時,方法的簽名是必需的。為了方便起見,作為引數的String型別可能是全限定型別名的一部分,舉個例子,下面所有都匹配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.

因為引數的數量足以區分每一種可能的選擇,這種快捷方式允許你只輸入與引數型別匹配的最短字串,從而節省大量的輸入。