spring-Method Injection方法注入

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 creates the singleton bean A only 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需要使用非单例(prototype)bean B,可能在对A的每次方法调用上都是如此,容器只创建一次单例bean A,因此只获得一次设置属性的机会。容器不能在每次需要bean B的时候都向bean A提供一个新的实例。

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 example shows this approach:

一个解决办法是放弃一些控制反转。您可以通过实现ApplicationContextAware接口,以及在每次bean A需要bean B实例时,通过对容器进行getBean(“B”)调用来让bean A知道容器。下面的例子展示了这种方法:

// 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, lets you handle this use case cleanly.

前面的方法并不可取,因为业务代码知道Spring框架并与之耦合。方法注入是Spring IoC容器的一种高级特性,它允许您干净地处理这个用例。

You can read more about the motivation for Method Injection in this blog entry.

您可以在这篇博客文章中阅读更多关于方法注入动机的内容。

Lookup Method Injection 查找方法注入

Lookup method injection is the ability of the container to override methods on container-managed beans and 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 dynamically generate a subclass that overrides the method.

查找方法注入是指容器覆盖容器管理bean上的方法并返回容器中另一个已命名bean的查找结果的能力。查找通常涉及prototype bean,如上一节所述的场景。Spring框架通过使用来自CGLIB库的字节码生成动态生成覆盖该方法的子类来实现这种方法注入。

For this dynamic subclassing to work, the class that the Spring bean container subclasses cannot be final, and the method to be overridden cannot be final, either.
要使这个动态子类工作,Spring bean容器子类的类不能是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.
单元测试具有抽象方法的类需要您自己创建类的子类,并提供抽象方法的存根实现。

Concrete methods are also necessary for component scanning, which requires concrete classes to pick up.
具体的方法对于组件扫描也是必要的,这需要具体的类来拾取。

A further key limitation is that lookup methods do not work with factory methods and in particular not with @Bean methods in configuration classes, since, in that case, the container is not in charge of creating the instance and therefore cannot create a runtime-generated subclass on the fly.
另一个关键的限制是,查找方法不能与工厂方法一起工作,特别是不与配置类中的@Bean方法一起工作,因为在这种情况下,容器不负责创建实例,因此不能动态地创建运行时生成的子类。

In the case of the CommandManager class in the previous code snippet, the Spring container dynamically overrides the implementation of the createCommand() method. The CommandManager class does not have any Spring dependencies, as the reworked example shows:

对于前面代码片段中的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 that contains 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. Consider the following example:

如果方法是抽象的,则动态生成的子类实现该方法。否则,动态生成的子类将覆盖在原始类中定义的具体方法。考虑下面的例子:

<!-- 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 createCommand() method 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 a singleton, the same instance of the myCommand bean is returned each time.

标识为commandManager的bean在需要myCommand bean的新实例时调用它自己的createCommand()方法。如果真的需要将myCommand bean部署为prototype ,则必须小心。如果是单例,则每次都返回相同的myCommand bean实例。

Alternatively, within the annotation-based component model, you can declare a lookup method through the @Lookup annotation, as the following example shows:

或者,在基于注释的组件模型中,您可以通过@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 can rely on the target bean getting resolved against the declared return type of the lookup method:

或者,更习惯的做法是,您可以依赖于根据查找方法声明的返回类型解析目标bean:

public abstract class CommandManager {

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

    @Lookup(注意这里在spring扫描规则时会失效,最好声明具体实现)
    protected abstract MyCommand createCommand();
}

Note that you should 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 to explicitly registered or explicitly imported bean classes.

请注意,您通常应该使用具体的存根实现来声明这种带注释的查找方法,以便它们与Spring的组件扫描规则兼容,其中抽象类在默认情况下会被忽略。此限制不适用于显式注册或显式导入的bean类。

Another way of accessing differently scoped target beans is an ObjectFactory/ Provider injection point. See Scoped Beans as Dependencies.You may also find the ServiceLocatorFactoryBean (in the org.springframework.beans.factory.config package) to be useful.
访问作用域不同的目标bean的另一种方法是ObjectFactory/ Provider注入点。请将作用域bean视为依赖项。您还可以在org.springframework.beans.factory package中找到ServiceLocatorFactoryBean)是有用的
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. You can safely skip the rest of this section until you actually need this functionality.

方法注入的一种不如查找方法注入有用的形式是用另一个方法实现替换托管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, which has a method called computeValue that we want to override:

对于基于xml的配置元数据,您可以使用replaced-method元素将一个已部署bean的现有方法实现替换为另一个方法实现。考虑下面的类,它有一个名为computeValue的方法,我们想要覆盖它:

public class MyValueCalculator {

    public String computeValue(String input) {
        // some real code...
    }

    // some other methods...
}

A class that implements the org.springframework.beans.factory.support.MethodReplacer interface provides the new method definition, as the following example shows:

实现org.springframework.bean .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 resemble the following example:

部署原始类并指定方法覆盖的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 <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:

可以使用< replacement -method/>元素中的一个或多个< argt-ype />元素来指示被重写的方法的方法签名。只有当方法被重载并且类中存在多个变体时,参数的签名才有必要。为了方便,参数的类型字符串可以是完全限定类型名的子字符串。例如,以下所有匹配

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 letting you type only the shortest string that matches an argument type.

由于参数的数量通常足以区分每种可能的选择,因此通过只输入与参数类型匹配的最短字符串,此快捷方式可以节省大量输入。

下一节:Bean Scopes Bean作用域

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

推荐阅读更多精彩内容