Spring方法注入&方法查找注入实现单例对象依赖原型对象

495 阅读2分钟

1.方法注入

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

一个解决方案是放弃某些控制反转。可以通过实现ApplicationContextAware接口获取到ApplicationContext,并通过getBean("B")到容器调用请求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;
    }
}

以上实现方式的缺点就是,创建对象的逻辑与业务代码过于耦合。 参考官方文档

2.@Lookup(查找方法注入)

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

使用@Lookup注释声明一个查找方法,如以下示例所示:

public abstract class CommandManager {

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

    @Lookup
    protected abstract MyCommand createCommand();
}

查找方法注入的声明形式:

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

参考官方文档

另外还有一种基于Bean配置实现查找方法注入,参考

@Bean
@Scope("prototype")
public AsyncCommand asyncCommand() {
    AsyncCommand command = new AsyncCommand();
    // inject dependencies here as required
    return command;
}

@Bean
public CommandManager commandManager() {
    // return new anonymous implementation of CommandManager with createCommand()
    // overridden to return a new prototype Command object
    return new CommandManager() {
        protected Command createCommand() {
            return asyncCommand();
        }
    }
}

3.方法替换注入

不太常用,可以跳过这部分内容。replaced method注入是spring动态改变bean里方法的实现。需要改变的方法,使用spring内原有其他类(需要继承接口org.springframework.beans.factory.support.MethodReplacer)的逻辑,替换这个方法。通过改变方法执行逻辑来动态改变方法。内部实现为使用cglib方法,重新生成子类,重写配置的方法和返回对象,达到动态改变的效果。

参考官方文档