我是Spring Boot和注解的新手。我有一个接口(ApiClientInterface),我想在属性上使用@Autowired
批注来解析。为了这篇文章的缘故,假设我有两个实现...
实施一:
package com.java.app.apiclient;
@Component
class HttpRestAdapter implements ApiClient {
...
}
实施二:
package com.java.app.apiclient;
@Component
class FakeAdapter implements ApiClient {
...
}
我做了一些谷歌搜索,发现了@Qualifier
注释,但是我不确定这是正确的路径,因为这是一个CLI应用程序。理想情况下,这样的事情是可能的:
$ java -jar myjar --apiclient=fake
$ # - OR -
$ java -jar myjar --apiclient=resthttp
根据该--apiclient
参数的值,理想情况下,IoC容器将知道要解析哪个实现。换句话说,我希望此测试通过:
public class ApiServiceFactoryTest {
@Autowired
private ApiClient client;
@Test
public void testClientResolution() {
assertEquals(client.getClass(), isA(FakeClient.class));
}
}
我来自PHP世界,其中IoC容器绑定通常在ServiceProviders中显式注册,在那里可以使用诸如工厂之类的东西来确定应注册哪个实现。我想坚持使用注释和注入接口,而不是注入工厂,然后使用该工厂来获得正确的服务。
在Spring的情况下,注释是“硬编码的”,因此不可能以编程方式进行该解析(至少不是我能找到的)。那么,如何允许容器在运行时解析实现?有可能做到这一点吗?
答案 0 :(得分:1)
您可以使用基于BeanFactory
的注入。
@Component
public class Application {
private final ApplicationClient appClient;
public Application((@Autowired BeanFactory beanFactory) {
appClient = beanFactory.getBean(beanName, ApplicationClient.class)
}
}
对于测试,我强烈建议使用Spring Testing Framework
答案 1 :(得分:0)
经过一场相当尴尬的斗争,我终于想出了如何使用FactoryBean
完成此任务的方法。来自Baeldung的article对到达那里有很大帮助。这就是我最后得到的。
首先,我创建了一个FactoryBean
,或更准确地说,是扩展了AbstractFactoryBean
。
package com.java.app.apiclient;
@Component
public class ApiClientFactory extends AbstractFactoryBean<ApiClient> {
@Autowired
private FakeAdapter fakeAdapter;
@Autowired
private HttpRestAdapter httpRestAdapter;
@Override
public Class<?> getObjectType() {
return ApiClient.class;
}
@Override
public ApiClient createInstance() {
// Conditionally return the appropriate implementation.
// For now, force the noop flavor for demonstration purposes.
return fakeAdapter;
}
}
现在我有了一个工厂,当解析ApiClient
的实例时,我需要一种方法告诉Spring使用该工厂。这可以通过xml文件来实现,但更重要的是(至少对我来说)使用@Configuration
批注的Java类。
package package com.java.app.config;
@Configuration
public class ApplicationConfig {
// The name argument to the `@Bean` annotation here makes it so we
// can access the value with a `@Qualifier` annotation
@Bean(name = "apiClient")
public ApiClient ApiClient () {
return ApiClientFactory().createInstance();
}
@Bean
public ApiClientFactory apiClientFactory() {
return new ApiClientFactory();
}
@Bean
public FakeAdapter fakeAdapter() {
return new FakeAdapter();
}
@Bean
public HttpRestAdapter httpRestAdapter () {
return new HttpRestAdapter();
}
}
最后,是时候从容器中获取实例了,它很简单:
public class SomeClass {
@Autowired
@Qualifier("apiClient")
private ApiClient api;
}
这似乎是把戏!很有可能存在一种“更好”或更惯用的方式,但这满足了我从IoC容器“推送”的要求,而不是通过BeanFactory
“拉动”的要求。