java - 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
类与查找方法注入Example
相结合,从单例范围的 bean 中按需创建实例。
一、配置类:
@Configuration
public class DemoConfiguration {
@Autowired IFooBean fooBean;
@Autowired IBarBean barBean;
@Bean()
@Scope("prototype")
Example newExample(String name) {
return new Example(fooBean, barBean, name);
}
}
这里应该没有什么太令人惊讶的了,除了name
参数 to newExample
。您可以像我在上面所做的那样自动装配容器可以满足的依赖项 ( fooBean
and barBean
),但是由于配置类的实例是像 Spring 的任何其他 bean 一样创建的,您还可以使用任何其他机制:将ObjectFactory
orObjectProvider
注入到配置中,让它实现ApplicationContextAware
,甚至为它们使用查找方法注入。fooBean
如果您需要避免并barBean
像自动装配到配置 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 会正确地将参数从查找方法传递到工厂方法。
出于测试目的,我只getExample()
用不同的值调用了两次,name
以证明我们每次都获得了一个不同的实例,其中每次都注入了正确的东西。
使用以下小型 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)
那是:
- a
FooBean
被创建 - a
BarBean
被创建 - 使用
Example
上述两个 bean 创建一个name
- 这
Example
是返回UseExample
- 一个不同
Example
的被创建,具有相同的FooBean
andBarBean
,并name
设置为"bbb"
这个时间。
我假设您熟悉如何设置基于 java 的配置和组件扫描以及上述示例所依赖的所有其他管道。我使用 Spring Boot 以一种简单的方式获得了整个 shebang。
如果您Example
从其他原型范围的 bean 创建 s ,则可能有一种方法可以通过范围传递仅运行时依赖项的值,但我不知道从哪里开始回答如何做到这一点,尤其是在不知道bean 的实际范围以及它们之间的关系。无论哪种方式,上述解决方案似乎都更简单易懂。
推荐阅读
- api - 如何在 POST 请求中使用带有参数的 REST API 触发 Jenkins 管道的构建?
- azure - 如果具有 DeployIfNotExist 效果的 Azure Policy 的范围是资源组,它是否能够部署资源?
- web - 如何将 Alexa 与 Web 应用程序集成?
- node.js - 如何知道条带json数据中的订阅开始日期和月份结束日期?
- bash - 在 Bash 中使用 mv comman 重命名存储在变量中的多个文件
- git - 如何在git中删除整个分支文件夹
- c# - Xamarin.Forms - 使用 XAML 元素作为参数
- php - symfony - Prestashop 1.7 - 我无法访问后台(管理面板)
- python - Oracle MERGE 重写为 PySpark。如果为空 - 更新,否则 - 插入
- php - 将关联数组插入数据库