我在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配置文件的脆弱选择来使我的每个测试都能完全按照我的需要运行?>
答案 0 :(得分:1)
功能Bean是Spring 5的新功能,它更适合于将功能注册为Bean提供程序。如果您需要的只是基于代码的配置,则无需执行此操作,而是可以使用标准的基于Spring注释的配置。
竞争示例,配置类:
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)
。
如果您不想使用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();
}
它通常不被使用,但是也没有错。
如果您确实也想避免使用配置类,则也可以这样:
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中并不是一种常见的方法,但这也没有错。
答案 1 :(得分:1)
如果我正确理解了它,则只需要条件豆进行测试,建议您在主类上声明“生产” @Bean
,然后为您的测试使用属性spring.main.allow-bean-definition-overriding=true
@TestConfiguration
覆盖了您需要的bean。
类似这样的东西:
@SpringBootTest(properties={"spring.main.allow-bean-definition-overriding=true"})
public class MyConditionalTest {
@Test
public void testMyStuff() {
// do your test here
}
@TestConfiguration
public OverrideSpringBean {
@Bean
public IDataLayer dataLayer() {
return new MockPersistenceLayer();
}
}
}
答案 2 :(得分:0)
据我了解,您正在寻找可以根据特定需求在特定实现上运行的内容。 请调查此类:
org.springframework.beans.factory.config.ServiceLocatorFactoryBean
您可以配置它并定义实现并根据要求获取bean。
<beans:bean id="dataStrategyFactory" class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
<beans:property name="serviceLocatorInterface" value="com.abc.DataStrategyFactory" />
</beans:bean>
<beans:alias name="FileImpl" alias="FILE" />
<beans:alias name="DBImpl" alias="DB" />
<beans:alias name="WSImpl" alias="WS" />
<beans:alias name="NativeImpl" alias="DEFAULT" />
提供接口实现(此处为DataStrategyFactory),并根据需要在运行时获取对象。