我在Spring Boot中使用自动接线将接口的实现注入到标记为组件的类中。
有时我需要使用某些接口的特定实现来运行应用程序(和/或测试)。
我知道可以使用批注(@Qualifier@Primary等)的组合来完成此操作,但是这些都不适合我的需要。
我希望能够(可选)编写在ApplicationContext确定要创建我的接口的哪些实现之前运行的代码,并在该代码中重写这些决定中的一个或多个。

我试图使用这样的代码:

context.registerBean(MyService.class, () -> new MyService());


如此处所述:https://www.baeldung.com/spring-5-functional-beans

但是我找不到足够的位置在代码中尽早插入,以至于影响应用程序中的所有自动装配字段。
特别是在测试中,这是一个问题(标记为@SpringBootTest)。

我希望能够使用看起来类似于在C#中完成代码的代码:

在一个测试中,我可能会使用此代码,然后运行测试:

container.Register<IDataLayer, MockDataLayer>();
container.Register<IPersistenceLayer, FilePersistenceLayer>();


在另一个测试中,我可能会使用以下代码,然后运行测试:

container.Register<IDataLayer, SQLDataLayer>();
container.Register<IPersistenceLayer, MockPersistenceLayer>();


在生产中,我可能会运行

container.Register<IDataLayer, SQLDataLayer>();
container.Register<IPersistenceLayer, FilePersistenceLayer>();


或仅依靠文件配置。

是否可以对ApplicationContext所做的选择建立这种级别的控制,还是我必须依靠对标注和xml配置文件的脆弱选择来使我的每个测试都能完全按照我的要求运行?

最佳答案

功能Bean是Spring 5的新功能,它更适合于将功能注册为Bean提供程序。如果您需要的只是基于代码的配置,则不需要去那里,但可以使用标准的基于Spring注释的配置。

常规的标准Spring javaconfig

简单示例,配置类:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ApplicationConfiguration {

    @Bean
    public DemoManager helloWorld()
    {
        return new DemoManagerImpl();
    }
}


主班:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}


这将使用组件扫描来找到您的配置类,然后调用其方法来获取bean。您可以提供所需的配置类作为参数,而您提到的SpringBootTest也支持该类。

因此,在测试时,您可以使用自己的测试配置,然后自定义要加载的bean并提供其他bean。如果配置类是嵌套类,则甚至无需指定它:

@SpringBootTest
@RunWith(SpringRunner.class)
public class SpringBootDemoApplicationTests
{
    @Test
    public void testSomething() {
       // ...
    }

    @TestConfiguration
    static class MyTestConfiguration {

        //tests specific beans
        @Bean
        DataSource createDataSource(){
            //
        }
    }
}


使用@TestConfiguration将添加到您的配置中-如果您不想添加而是完全替换配置,请使用@SpringBootTest(classes = YourCustomConfiguration.class)

备选方案:使用spring javaconfig手动创建的应用程序上下文

如果您不想使用javaconfig或组件扫描,而又想“自己”注册配置类,则可以像在主类中使用这种main方法那样进行操作:

public static void main(String[] args) {
   ApplicationContext ctx = new AnnotationConfigApplicationContext(ApplicationConfiguration.class);

   HelloWorld helloWorld = ctx.getBean(HelloWorld.class);
   helloWorld.setMessage("Hello World!");
   helloWorld.getMessage();
}


它通常不被使用,但是也没有错。

备选方案2:手动应用程序上下文,手动Bean注册

如果您确实也想避免使用配置类,则也可以这样:

import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class SomeClass {
  public static void main(String args[]) {

    // first, we create empty context ourselves
    ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext();

    // then we get its bean factory to be able to register stuff
    ConfigurableListableBeanFactory beanFactory = ctx.getBeanFactory();

    // register our bean
    YourBean beanToRegister = new YourBean();
    beanFactory.registerSingleton(beanToRegister.getClass().getCanonicalName(), beanToRegister);

    ctx.refresh(); // context refresh actually updates the status

    // here we can test a bean was actually created and working
    YourBean helloWorld = ctx.getBean(YourBean.class);
    helloWorld.setAuthor("Hello World!");
    System.out.println(helloWorld.getAuthor());
  }
}


像其他选择一样,这不是Spring的常用方法,但也没有错。

09-09 21:35
查看更多