绝大多数教程处理的是退化的情况,即仅存在一个要实现的接口实现。但是,我茫然不知所措,到目前为止,如何构建一个应用程序并没有找到任何线索,该应用程序的几个专用部分提供了将注入到公共部分中的公共接口的几种不同实现(又称“策略模式”,又称“控制反转”)。 / p>
在我的现实生活中,我有一台Tomcat服务器,上面部署了一个应用程序,其中几个部分提供了与外界的不同接口。在此应用程序中,在一个专门的@Bean
中为公用interface
定义一个@Configuration
总是导致其他专门的部分接收相同的@Bean
,即使它们(似乎只是)独立@Configuration
定义了不同的@Bean
。
作为一个最小的例子,我试图编写一个表现出相同行为并具有相同通用体系结构的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
的特定,专用实现,以提供来自不同来源的共同价值App
是我将在Tomcat服务器上部署的Servlet的替代品。显然,App必须知道(提供)服务器应提供的所有接口,因此App必须同时@Import
和ConfigA
ConfigB
。现在,可以观察到以下行为:
false false
或true true
,但不会打印预期的false true
或true false
; @Import
中删除App
会导致该应用程序无法运行。预期的行为将是:
CommonProcess
调用ConfigA
的地方使用service
的{{1}} ConfigA
调用CommonProcess
的地方使用ConfigB
的{{1}} 问题:产生预期行为的规范方法是什么?
(首选基于注释的解决方案)
作为参考,使用纯Java的工作示例:
service
这里的输出确实符合预期ConfigB
。
答案 0 :(得分:1)
您对Spring IoC的想法过高,并将@Configuration
与ApplicationContext
(实际的IoC容器)相混淆。
@Configuration
在现有容器的范围内处理。 docs曾经说过:
@Import
表示JavaConfig等效于XML配置的<import/>
元素。一个配置类可以导入任意数量的其他配置类,它们的bean定义将像本地定义一样进行处理。
也就是说,所有导入和发现的@Configurations都被加载到同一容器中。
之后,将创建所有单例bean。然后将它们连接在一起。
在一个容器中,您可以有多个相同类型的豆,但没有相同的 name 。在JavaConfig中,bean名称是从工厂方法名称或类名称派生的。对于Service
,只有一个名称service
,因此只有一个类型为Service
的bean。如果仔细观察,您会看到“ Overriding bean definition for bean 'service' with a different definition: replacing [factoryBeanName=ConfigA; factoryMethodName=service; defined in ConfigA] with [factoryBeanName=ConfigB; factoryMethodName=service; defined in ConfigB]
”行中的启动消息
然后将一个唯一的service
连接到所需的任何地方(在commonProcess
,configA
和configB
中)。
在特定情况下,您可以通过将Service
传递到CommonProcess.test()
来解决此问题,就像在普通Java版本中一样,并为每个Service
实例(例如{{1} }和serviceA
):
serviceB
我还建议您研究bean scopes,尤其是工厂范围。
最后,Spring Boot支持hierarchy of ApplicationContext's,它实际上使您可以在一个可执行文件中创建子应用程序。这样@FunctionalInterface
interface Service {
boolean test();
}
class CommonProcess {
public boolean test(Service service) {
return service.test();
}
}
@Configuration
class BaseConfig {
@Bean
CommonProcess commonProcess() {
return new CommonProcess();
}
}
@Configuration
class ConfigA {
@Bean
CommandLineRunner processA(@Named("serviceA") Service service) {
return new CommandLineRunner() {
@Autowired
private CommonProcess process;
@Override
public void run(String... args) throws Exception {
System.out.println(this.process.test(service));
}
};
}
@Bean
Service serviceA() {
return () -> false;
}
}
@Configuration
class ConfigB {
@Bean
CommandLineRunner processB(@Named("serviceB") Service service) {
return new CommandLineRunner() {
@Autowired
private CommonProcess process;
@Override
public void run(String... args) throws Exception {
System.out.println(this.process.test(service));
}
@Bean
Service serviceB() {
return () -> true;
}
};
}
@Autowired
ApplicationContext applicationContext;
@PostConstruct
public void printBeans() {
System.out.println(Arrays.asList(applicationContext.getBeanDefinitionNames()));
}
@Bean
Service serviceB() {
return () -> true;
}
}
@SpringBootConfiguration
@Import(value = { BaseConfig.class, ConfigA.class, ConfigB.class })
class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
和ConfigA
可以各自拥有自己的名为ConfigB
的{{1}}实例。很少使用此功能。
Service