美文网首页
[Java]04.通过方法注入,解决单例Bean中依赖原型Bea

[Java]04.通过方法注入,解决单例Bean中依赖原型Bea

作者: NiceBen | 来源:发表于2021-09-26 14:22 被阅读0次

方法注入

在大多数应用场景中,容器中的大部分 bean 都是 单例的。当单例 bean 需要与另一个单例 bean 协作或非单例 bean 需要与另一个非单例 bean 协作时,您通常通过将一个 bean 定义为另一个 bean 的属性来处理依赖关系。当 bean 生命周期不同时就会出现问题。假设单例 bean A 需要使用非单例(原型)bean B,可能在 A 上的每次方法调用上。容器只创建单例 bean A 一次,因此只有一次设置属性的机会。容器无法在每次需要时为 bean A 提供 bean B 的新实例。

一个解决方案是放弃一些控制反转。您可以通过实现接口来使 bean A 了解容器ApplicationContextAware,并通过getBean("B")每次 bean A 需要时调用容器来请求(通常是新的)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;
    }
}

前面是不可取的,因为业务代码知道并耦合到 Spring Framework。方法注入是 Spring IoC 容器的一个有点高级的特性,可以让你干净地处理这个用例。

查找方法注入

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

对于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();
}

在包含要注入的方法(CommandManager在本例中为 the)的客户端类中,要注入的方法需要以下形式的签名:

<public|protected> [abstract] <return-type> theMethodName(no-arguments);

如果方法是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>

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

或者,在基于注解的组件模型中,您可以通过@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();
}

或者,更惯用的是,您可以依靠目标 bean 根据查找方法的声明返回类型进行解析:

public abstract class CommandManager {

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

    @Lookup
    protected abstract Command createCommand();
}

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

以上信息摘抄于 Spring 官方文档,如果其他内容,可参照 Spring Framework 官方文档。

相关文章

网友评论

      本文标题:[Java]04.通过方法注入,解决单例Bean中依赖原型Bea

      本文链接:https://www.haomeiwen.com/subject/dppqnltx.html