通过CLI

时间:2018-08-24 00:53:55

标签: spring spring-boot

我是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的情况下,注释是“硬编码的”,因此不可能以编程方式进行该解析(至少不是我能找到的)。那么,如何允许容器在运行时解析实现?有可能做到这一点吗?

2 个答案:

答案 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“拉动”的要求。