我在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的常用方法,但也没有错。