【发布时间】:2018-09-26 17:20:24
【问题描述】:
绝大多数教程都处理退化的情况,即只有一个实现用于注入接口。但是,我不知所措,到目前为止,我还没有找到任何关于如何构建一个应用程序的线索,该应用程序的几个专门部分提供了几种不同的通用接口实现,以注入到通用部分(又名策略模式,又名控制反转)。
在我的现实生活中,我有一个 Tomcat 服务器,上面部署了一个应用程序,其中几个部分为外部世界提供了不同的接口。在这个应用程序中,在一个专用的@Configuration 中为一个通用的interface 定义一个@Bean 总是会导致其他专用部分接收相同的@Bean,即使它们(只是看似?)独立的@Configurations 定义了一个不同的@ 987654327@.
举个简单的例子,我尝试编写一个 Spring-boot 应用程序,它表现出相同的行为并具有相同的通用架构:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@FunctionalInterface
interface Service { boolean test(); }
class CommonProcess {
@Autowired
Service service;
public boolean test() { return this.service.test(); }
}
@Configuration
class BaseConfig {
@Bean
CommonProcess commonProcess() { return new CommonProcess(); }
}
@Configuration
class ConfigA {
@Bean
CommandLineRunner processA() {
return new CommandLineRunner() {
@Autowired
private CommonProcess process;
@Override
public void run(String... args) throws Exception {
System.out.println(this.process.test());
}
};
}
@Bean
Service service() { return () -> false; }
}
@Configuration
class ConfigB {
@Bean
CommandLineRunner processB() {
return new CommandLineRunner() {
@Autowired
private CommonProcess process;
@Override
public void run(String... args) throws Exception {
System.out.println(this.process.test());
}
};
}
@Bean
Service service() { return () -> true; }
}
@SpringBootConfiguration
@Import(value = { BaseConfig.class, ConfigA.class, ConfigB.class })
class App {
public static void main(String[] args) {
System.exit(SpringApplication.exit(SpringApplication.run(App.class, args)));
}
}
这段代码的意图如下:
-
ConfigA和ConfigB都导入 BaseConfig,因为它们的进程使用相同的CommonProcess。 -
ConfigA和ConfigB都定义了它们特定的、专门的Service实现,以提供来自不同来源的共同值
(例如,一份来自 XML,一份来自 JSON)。 -
App类是我将部署在 Tomcat 服务器上的 Servlet 的替身。显然,App 必须知道(提供)Server 应该提供的所有接口,所以 App 必须@ImportConfigA和ConfigB。
我的理解是,需要存在这样一个应用程序抽象层的“叶节点”的“收集点”,以便将它们全部公开
(在这个例子中,通过简单地运行它们,通过注册它们的 Spring 控制器在 Tomcat 服务器中)。
现在,可以观察到以下行为:
- 按原样启动应用程序将打印
false false或true true,但不会打印预期的false true或true false; - 从
App中删除@Import会导致应用程序无法运行任何东西。
而预期的行为是:
- 其中
CommonProcess是从ConfigA调用的,它使用service的ConfigA - 其中
CommonProcess是从ConfigB调用的,它使用service的ConfigB
问题:产生预期行为的规范方法是什么?
(首选基于注释的解决方案)
参考纯 Java 中的工作示例:
import java.util.Arrays;
import java.util.List;
@FunctionalInterface
interface Service { boolean test(); }
class CommonProcess {
public static final CommonProcess INSTANCE = new CommonProcess();
public boolean test(Service service) { return service.test(); }
}
class ProcessA implements Runnable {
// specific project knows generic project -> no need to inject
private static final CommonProcess commonProcess = CommonProcess.INSTANCE;
private static final Service service = () -> false;
public void run() {
// generic project does not know specific project -> specifics are injected
System.out.println(this.commonProcess.test(this.service));
}
}
class ProcessB implements Runnable {
// specific project knows generic project -> no need to inject
private static final CommonProcess commonProcess = CommonProcess.INSTANCE;
private static final Service service = () -> true;
public void run() {
// generic project does not know specific project -> specifics are injected
System.out.println(this.commonProcess.test(this.service));
}
}
class PlainApp {
private static final List<Runnable> processes = Arrays.asList(new ProcessA(), new ProcessB());
public static void main(String[] args) {
for (Runnable process : processes)
process.run();
}
}
这里的输出确实如预期false true。
【问题讨论】:
-
您错误地认为在 ConfigA 中声明的 bean 会更喜欢使用在 ConfigA 中声明的其他 bean。事实并非如此。 Spring 看到一个配置,它是所有配置的联合。 docs.spring.io/spring/docs/current/spring-framework-reference/…
标签: java spring-annotations spring-bean