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.

因为参数的数量足以区分每一种可能的选择,这种快捷方式允许你只输入与参数类型匹配的最短字符串,从而节省大量的输入。

猜你喜欢

转载自blog.csdn.net/andamajing/article/details/81838787
1.4
今日推荐