Java 在一个简单的例子中,SpringIOC实际上是如何工作的?
绝大多数教程都处理退化的情况,即只有一个实现用于注入接口。然而,我不知所措,到目前为止还没有找到任何关于如何构建一个应用程序的线索,在这个应用程序中,几个专门的部分提供了几个不同的公共接口实现,以注入到公共部分(又名策略模式,又名控制反转) 在我的现实生活中,我有一个Tomcat服务器,服务器上部署了一个应用程序,其中几个部分为外部世界提供了不同的接口。在此应用程序中,在一个专门的Java 在一个简单的例子中,SpringIOC实际上是如何工作的?,java,spring-annotations,spring-bean,Java,Spring Annotations,Spring Bean,绝大多数教程都处理退化的情况,即只有一个实现用于注入接口。然而,我不知所措,到目前为止还没有找到任何关于如何构建一个应用程序的线索,在这个应用程序中,几个专门的部分提供了几个不同的公共接口实现,以注入到公共部分(又名策略模式,又名控制反转) 在我的现实生活中,我有一个Tomcat服务器,服务器上部署了一个应用程序,其中几个部分为外部世界提供了不同的接口。在此应用程序中,在一个专门的@Configuration中为公共接口定义@Bean,总是会导致其他专门部分接收相同的@Bean,即使它们(仅表面
@Configuration
中为公共接口定义@Bean
,总是会导致其他专门部分接收相同的@Bean
,即使它们(仅表面上)独立的@Configuration
定义了不同的@Bean
举一个简单的例子,我尝试编写一个Spring引导应用程序,它表现出相同的行为,并且具有相同的通用架构:
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的替代。显然,应用程序必须知道(提供)服务器应该提供的所有接口,因此应用程序必须@Import
同时ConfigA
和ConfigB
我的理解是,应用程序抽象层的“叶节点”需要存在这样一个“收集点”,以便将它们全部公开给世界
(在本例中,只需在Tomcat服务器中通过注册Spring控制器来运行它们)
现在,可以观察到以下行为:
按原样启动应用程序将打印假假
或真
,但不会打印预期的假
或真假
李>
从App
中删除@Import
将导致应用程序无法运行任何东西
鉴于预期行为为:
当从ConfigA
调用CommonProcess
时,它使用ConfigA的服务
当从ConfigB
调用CommonProcess
时,它使用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();
}
}
导入java.util.array;
导入java.util.List;
@功能接口
接口服务{boolean test();}
类公共进程{
公共静态最终CommonProcess实例=新建CommonProcess();
公共布尔测试(服务服务){return Service.test();}
}
类ProcessA实现可运行{
//特定项目知道通用项目->无需注入
私有静态最终CommonProcess CommonProcess=CommonProcess.INSTANCE;
私有静态最终服务=()->false;
公开募捐{
//通用项目不知道特定项目->注入了具体内容
System.out.println(this.commonProcess.test(this.service));
}
}
类ProcessB实现可运行{
//特定项目知道通用项目->无需注入
私有静态最终CommonProcess CommonProcess=CommonProcess.INSTANCE;
私有静态最终服务=()->true;
公开募捐{
//通用项目不知道特定项目->注入了具体内容
System.out.println(this.commonProcess.test(this.service));
}
}
类PlainApp{
私有静态最终列表进程=Arrays.asList(新进程A(),新进程B());
公共静态void main(字符串[]args){
for(可运行进程:进程)
process.run();
}
}
这里的输出确实如预期的那样false-true
您过度考虑了Spring IoC,并将@配置
与ApplicationContext
(实际的IoC容器)混淆了
@Configuration
在已存在容器的范围内处理。而该委员会曾表示:
@Import
表示JavaConfig与XML配置的
元素的等价物。一个配置类可以导入任意数量的其他配置类,它们的bean定义将被处理为本地定义
也就是说,所有导入和发现的@Configurations都会加载到同一个容器中
之后,将创建所有单例bean。然后它们被连接在一起
在一个容器中,可以有多个相同类型但名称不同的bean。在JavaConfig中,bean名称派生自工厂方法名或类名。在服务
的情况下,只有一个名称,服务
,因此只有一个类型为服务
的bean。如果仔细观察,您将看到一条启动消息,内容大致如下“用不同的定义覆盖bean‘服务’的bean定义:将[factoryBeanName=ConfigA;factoryMethodName=service;在ConfigA中定义]替换为[factoryBeanName=ConfigB;factoryMethodName=service;在ConfigB中定义]
”
然后将唯一的服务
连接到所需的任何位置(在commonProcess
、configA
和configB
)
在您的特定情况下,您可以通过将Service
传递给CommonProcess.test()
来解决这个问题,就像在普通Java版本中一样,并给出一个唯一的n
@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);
}
}
@SpringBootConfiguration
@Import(value = { BaseConfig.class })
class App {
public static void main(String[] args) {
SpringApplicationBuilder app = new SpringApplicationBuilder(App.class);
app.child(ConfigA.class).run(args);
app.child(ConfigB.class).run(args);
}
}