繁体   English   中英

春季动态依赖注入

[英]Dynamic dependency injection in spring

我正在寻找一种方法来创建一个可以“部分”自动装配的非单件弹簧豆。

@Component
class Example {
    private SpringBean1 bean1;
    private SpringBean2 bean2;
    private String dynamicDependancy;

    @Autowired
    public Example(SpringBean1 bean1, SpringBean2 bean2, String dynamicDepedency) {
        this.bean1 = bean1;
        this.bean2 = bean2;
        this.dynamicDepedency = dynamicDepedency;
    }
}

我想要这样的事情,因为有时只有在运行时才知道依赖。 我有一种方法可以创建一种创建静态成员类的工厂,这样我就可以测试静态成员类:

@Component
class ExampleFactory {
    private SpringBean1 bean1;
    private SpringBean2 bean2;

    @Autowired
    public ExampleFactory(SpringBean1 bean1, SpringBean2 bean2) {
        this.bean1 = bean1;
        this.bean2 = bean2;
    }

    public Example from(String dynamicDependency) {
        return new Example(bean1, bean2, dynamicDependency);
    }

    static class Example {
        private SpringBean1 bean1;
        private SpringBean2 bean2;
        private String dynamicDependancy;

        public Example(SpringBean1 bean1, SpringBean2 bean2, String 
            dynamicDependancy) {
            this.bean1 = bean1;
            this.bean2 = bean2;
            this.dynamicDependancy = dynamicDependancy;
        }
    }
}

我正在查看Prototype范围,并使用getBean(java.lang.String,java.lang.Object)使得更难使用依赖注入。 我想知道是否有任何“春天的方式”来做这些事情。

谢谢。

更新:我找到了另一个解决方案,并在另一篇文章中发布了答案: https//stackoverflow.com/a/52021965/2580829

使用由Spring注入的工厂然后公开创建Example实例的方法的基本方法是我如何做到这一点,所以它基本上是正确的。 如果您希望Spring使用其现代功能透明地完成此操作,您可以将@Configuration查找方法注入结合使用,以从单例范围的bean创建Example on-demand的实例。


一,配置类:

@Configuration
public class DemoConfiguration {
    @Autowired IFooBean fooBean;
    @Autowired IBarBean barBean;

    @Bean()
    @Scope("prototype")
    Example newExample(String name) {
        return new Example(fooBean, barBean, name);
    }
}

这里应该没有什么太令人惊讶的了,除了newExamplename参数。 您可以像我上面那样自动装配容器可以满足的依赖项( fooBeanbarBean ),但是由于配置类的实例与Spring的任何其他bean一样,您还可以使用任何其他机制:将ObjectFactoryObjectProvider注入配置让它实现ApplicationContextAware ,甚至为它们使用查找方法注入。 如果您需要避免fooBeanbarBean初始化,如果它们被自动装配到配置bean中,那么这将非常有用。

不要忘记将工厂方法的范围设置为"prototype" ,否则Spring将返回您使用的第一个bean,即使您为name传递了不同的值。


Example本身的实现类似于你问题中的实现:

public class Example {
    IFooBean fooBean;
    IBarBean barBean;
    String name;

    public Example(IFooBean fooBean, IBarBean barBean, String name) {
        System.out.printf("%s(fooBean=%s, barBean=%s, name=%s)\n", this, fooBean, barBean, name);
        this.fooBean = fooBean;
        this.barBean = barBean;
        this.name = name;
    }
}

然后,在您实际需要Example实例的位置,使用@Lookup注入工厂方法:

public interface IUsesExample {
    void doThing();
}

@Component
public class UsesExample implements IUsesExample {
    @Lookup
    protected Example getExample(String name) {return null;};

    public void doThing() {
        System.out.printf("%s.doThing(getExample() = %s)\n", this, getExample("aaa"));
        System.out.printf("%s.doThing(getExample() = %s)\n", this, getExample("bbb"));
    }
}

要使用@Component和扫描,这必须是一个具体的类,这意味着我们需要一个getExample()的虚拟实现; Spring将使用CGLIB替换它,调用上面DemoConfiguration定义的工厂方法。 Spring将正确地将参数从lookup方法传递给factory方法。

出于测试目的,我只使用不同的name值调用getExample()两次,以证明我们每次都注入了正确的东西,从而得到一个不同的实例。


使用以下小型Spring Boot应用程序对此进行测试:

@SpringBootApplication
public class DemoApplication {
    @Autowired IUsesExample usesExample;

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @PostConstruct
    void run() {
        usesExample.doThing();
    }
}

给出以下输出:

com.example.demo.FooBean@fd46303
com.example.demo.BarBean@6a62689d
com.example.demo.Example@66629f63(fooBean=com.example.demo.FooBean@fd46303, barBean=com.example.demo.BarBean@6a62689d, name=aaa)
com.example.demo.UsesExample$$EnhancerBySpringCGLIB$$68b994e8@6c345c5f.doThing(getExample() = com.example.demo.Example@66629f63)
com.example.demo.Example@6b5966e1(fooBean=com.example.demo.FooBean@fd46303, barBean=com.example.demo.BarBean@6a62689d, name=bbb)
com.example.demo.UsesExample$$EnhancerBySpringCGLIB$$68b994e8@6c345c5f.doThing(getExample() = com.example.demo.Example@6b5966e1)

那是:

  • 一个FooBean被创建
  • 一个BarBean被创建
  • 使用上面的两个bean和name创建一个Example
  • Example返回到UseExample
  • 创建一个不同的Example ,使用相同的FooBeanBarBean ,并且这次将name设置为"bbb"

我假设您熟悉如何设置基于Java的配置和组件扫描以及上述示例所依赖的所有其他管道。 我使用Spring Boot以简单的方式获得整个shebang。

如果您正在从其他原型范围的bean创建Example s,那么可能有一种方法可以通过范围传递仅运行时依赖项的值,但我不知道在哪里开始回答如何执行此操作,尤其是在不知道的情况下bean的实际范围以及它们之间的关系。 无论哪种方式,上述解决方案似乎更简单易懂。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM