如何在启动 Spring 引导时将 Java class 的实例提升为 Spring Bean?

How can I promote an instance of a Java class to a Spring Bean when launching Spring Boot?

启动 Spring 引导应用程序时,如何将 Java class 的实例动态传递给 Spring 配置?

我有一个 ThirdPartyService 在实例化时需要 SomeConfig。现在,我想使用自定义 ServiceWrapper 包装此服务,然后使用 Spring Boot 以编程方式启动它,而不需要 ServiceWrapper 的用户知道 Spring Boot 是在幕后使用的,ServiceWrapperTest 让您了解我打算如何使用它:

@Service
public class ThirdPartyService {
    private final SomeConfig someConfig;

    @Autowired
    public ThirdPartyService(final SomeConfig someConfig) {
        this.someConfig = someConfig;
    }

    void start() {
        // reads values from someConfig and starts the service
    }

    void stop() {
        // stops the service
    }
}
public class SomeConfig {
    // getters and setters omitted
}
@SpringBootApplication
public class ServiceWrapper {
    private ThirdPartyService thirdPartyService;

    public ServiceWrapper() {
    }

    public void start(SomeConfig someConfig) {
        // how can I pass someConfig to Spring Boot?
        thirdPartyService = SpringApplication.run(ThirdPartyService.class).getBean(ThirdPartyService.class);
    }

    public void stop() {
        if (thirdPartyService != null) {
            thirdPartyService.stop();
        }
    }
}
class ServiceWrapperTest {

    @Test
    void testStartAndStop() {
        SomeConfig someConfig = new SomeConfig();
        ServiceWrapper serviceWrapper = new ServiceWrapper(someConfig);

        serviceWrapper.start();
        serviceWrapper.stop();
    }
}

毫不奇怪,测试失败并出现以下错误:

***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of constructor in com.example.test.ThirdPartyService required a bean of type 'com.example.test.SomeConfig' that could not be found.


Action:

Consider defining a bean of type 'com.example.test.SomeConfig' in your configuration.

如何将 SomeConfig 实例传递给 Spring Beot 以便它符合自动装配的条件?是否可以使用 @Bean@Configuration 或类似的(我不这么认为)?


Spring 引导版本 2.6.1

你可以用 @Component 注释它,如果它只是第三方 class 并在 中执行标准构造函数或字段注入服务包装器。此外,自 Spring 5.2 以来,您不必使用 @Autowired.

显式注释依赖注入
@Service
 public class ThirdPartyService {
 
    private final SomeConfig someConfig;

    public ThirdPartyService(final SomeConfig someConfig) {
        this.someConfig = someConfig;
    }

    void start() {
        // reads values from someConfig and starts the service
    }

    void stop() {
        // stops the service
    }
}

@Component
public class SomeConfig {
    // getters and setters omitted
}

如果我理解正确的话。

你是这个意思吗:

@SpringBootApplication
public class ServiceWrapper {

    @Bean
    SomeConfig getSomeConfig() {
      return someConfig;
    }

    private static SomeConfig someConfig; // really not thread safe
    private ApplicationContext context;

    public void start(SomeConfig someConfig) {
        this.someConfig = someConfig;

        this.context = SpringApplication.run(ServiceWrapper.class);

        // do whatever you want with ThirdPartyService
        ThirdPartyService service = this.context.getBean(ThirdPartyService.class);
    }

    public void stop() {
        this.context.close();
    }
}

这样您就可以使 SomeConfig 成为 Spring 的 bean 并使用它来连接您的上下文,但您不需要更改 SomeConfig 中的任何代码。然后您填充了 ThirdPartyService,您可以使用 getter.

return 它

您可以使用 ApplicationContextInitializer 使 SomeConfig 的现有实例可用作 bean:

public void start(SomeConfig someConfig) {
    SpringApplication application = new SpringApplication(ServiceWrapper.class);
    application.addInitializers(new ApplicationContextInitializer<ConfigurableApplicationContext>() {

        @Override
        public void initialize(ConfigurableApplicationContext applicationContext) {
            applicationContext.getBeanFactory().registerSingleton("someConfig", someConfig);
        }
        
    });
    ThirdPartyService service = application.run().getBean(ThirdPartyService.class);
    // Use service as needed
}