Can feign client in spring boot be injected via constructor injection?
#AllArgsConstructor
class ApiPortImpl implements ApiPort {
private final ApiClient feignClient;
#Override
public String getAuthToken() {
return feignClient.getToken();
}
}
interface ApiPort {
String getAuthToken();
}
#FeignClient(name = "api-client", url = "${some_url}", path = "/", configuration = RestConfiguration.class)
interface ApiClient {
#PostMapping(value = "/identity/token", consumes = "application/x-www-form-urlencoded")
String getToken();
}
#Configuration
class RestConfiguration {
#Bean
ApiPort apiPort(){
return new ApiPortImpl(<how to do constructor injection of the feign client ??>);
}
}
I do not want to use #Component annotation.
In the above mentioned code block, how can I instantiate the ApiPortImpl bean?
You can inject ApiClient as a parameter to method annotated with #Bean:
#Configuration
class RestConfiguration {
#Bean
ApiPort apiPort(#Autowired ApiClient apiClient){
return new ApiPortImpl(apiClient);
}
}
This should work even without #Autowired annotation.
Docs Reference: Declaring a Bean
Related
I have a Rest Controller, a Service and a Feign Client being used inside the service.
Now, I need to conditionally create the controller bean based on an environment variable. I have been able to set up the configuration and it looks like it should work. So far so good. But I am having a hard time testing the conditional bean creation.
The REST Controller:
#RestController
#RequestMapping("/api/path")
public class AbcController {
private final AbcService abcService;
#Autowired
public AbcController(AbcService abcService) {
this.abcService = abcService;
}
...
}
The Service:
#Service
public class AbcService {
private final AbcClient abcClient;
#Autowired
public AbcService(AbcClient abcClient) {
this.abcClient = abcClient;
}
Feign Client:
#Component
#FeignClient(name = "abc", url = "${abc.url}", decode404 = true)
public interface AbcClient {
#RequestMapping(
value = "/match",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE
)
MatchResponse getSsoIds(RequestDto requestDto);
}
Configuration for conditionally loading the Controller class:
#Configuration
public class AbcConfiguration {
#Bean
#ConditionalOnProperty(
value="feature.enabled",
havingValue = "true",
matchIfMissing = true
)
public AbcController abcController(AbcService abcService) {
return new AbcController(abcService);
}
}
The Test class for testing the conditional bean creation:
public class AbcControllerTest {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(AbcConfiguration.class));
#Test
public void loadsControllerBeanWhenPropertyIsEnabled() {
this.contextRunner
.withPropertyValues("feature.enabled=true")
.run(context -> context.assertThat().hasSingleBean(AbcController.class));
}
#Test
public void loadsControllerBeanWithoutDefinedProperty() {
this.contextRunner
.run(context -> context.assertThat().hasSingleBean(AbcController.class));
}
#Test
public void doesNotLoadControllerBeanWhenPropertyIsDisabled() {
this.contextRunner
.withPropertyValues("feature.enabled=false")
.run(context -> context.assertThat().doesNotHaveBean(AbcController.class));
}
}
I have tried to run the test but the first 2 tests always ends up complaining about missing or non-configurable beans. The last test runs as expected.
With the above test setup it complains about missing AbcService bean, which we can solve by adding
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = AbcService.class)
to the top of the test class, but then it complains No qualifying bean of type 'de.xxx.xxx.AbcClient' and we cannot create a bean of the feignClient AbcClient because it is an interface.
Help needed! How to make these tests pass?
I use Spring 5.1.4.RELEASE and have a trouble injecting two fields of same interface via constructor when using #Profile annotation on bean configuration methods. I have a simple Publisher component like follows:
#Component
public class Publisher {
private final MyClient prodClient;
private final MyClient testClient;
#java.beans.ConstructorProperties({"prodClient", "testClient"})
public Publisher(MyClient prodClient, MyClient testClient) {
this.prodClient = prodClient;
this.testClient = testClient;
}
}
When I mark whole configuration with #Profile annotation, then it works as expected:
#Profile(Profiles.MY_CLIENT)
#Configuration
public class ClientConfig {
#Bean
public MyClient prodClient() {
return new HttpClient("prod.client.channel");
}
#Bean
public MyClient testClient() {
return new HttpClient("test.client.channel");
}
}
The above configuration is OK, but the problem occurs when I want to have the #Profile annotation only on some methods inside a configuration class:
#Configuration
public class ClientConfig {
#Profile(Profiles.MY_CLIENT)
#Bean
public MyClient prodClient() {
return new HttpClient();
}
#Profile(Profiles.MY_CLIENT)
#Bean
public MyClient testClient() {
return new HttpClient();
}
// some other beans...
}
Then I get an error during startup:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in com.test.Publisher required a bean of type 'com.test.MyClient' that could not be found.
UPDATE:
It's solved. It was my mistake. I had two more bean methods annotated with different #Profile for integration tests, but they had the same name for production code (annotated with Profiles.MY_CLIENT profile):
#Configuration
public class ClientConfig {
#Profile(Profiles.MY_CLIENT)
#Bean
public MyClient prodClient() {
return new HttpClient();
}
#Profile(Profiles.MY_CLIENT)
#Bean
public MyClient testClient() {
return new HttpClient();
}
// ... other beans
#Profile(Profiles.MOCK_MY_CLIENT)
#Bean
public MyClient prodClient() {
return new MockClient();
}
#Profile(Profiles.MOCK_MY_CLIENT)
#Bean
public MyClient testClient() {
return new MockClient();
}
}
Mmm, If you try to inject a list of this components?
Something like
public Publisher(List<MyClient> clients) {
}
and in the client implementations you set a flag that could be useful to know when you should use it.
In this code here:
#java.beans.ConstructorProperties({"prodClient", "testClient"})
public Publisher(MyClient prodClient, MyClient testClient) {
this.prodClient = prodClient;
this.testClient = testClient;
}
Try using the #Autowired annotation on the parameters instead:
public Publisher(#Autowired MyClient prodClient, #Autowired MyClient testClient) {
this.prodClient = prodClient;
this.testClient = testClient;
}
I have creating soap service using Apache CXF, I have created a #WebService. In that service, I need to inject the #Service. When I #Autowire that service that instance remains null.
Endpoint initialized
#Bean
public Endpoint endpointToken() {
EndpointImpl endpoint = new EndpointImpl(bus, new GenerateLoginToken());
endpoint.publish("/Token");
return endpoint;
}
Serivce Class
#WebService(serviceName = "GenerateToken", portName = "TokenPort",
targetNamespace = "http://service.ws.samp",
endpointInterface = "com.web.sigel.ws.soap.webServices.GenerateToken")
#Service("AuthService")
public class GenerateLoginToken implements GenerateToken {
#Autowired
private AuthService authService; //this remains Null whenever i make a call.
#Override
#RequestWrapper(localName = "loginRequest", targetNamespace = "http://service.ws.samp", className = "com.web.sigel.ws.soap.security.LoginRequest")
public LoginResponse generateToken(LoginRequest loginRequest) {
LoginResponse loginResponse = new LoginResponse();
String token = authService.createAuthToken(loginRequest);
loginResponse.setToken(token);
return loginResponse;
}
}
Is there anyway, I can inject my service.
This is happening because you are creating a new instance of GeneratingLoginToken in your Endpoint bean:
EndpointImpl endpoint = new EndpointImpl(bus, new GenerateLoginToken());
This means Spring does not know about your new instance since it is not a Spring bean itself. Instead you should autowire GenerateLoginToken and use the Spring bean instance of this class which should have all the beans wired up to it correctly and hence AuthService should not be null:
#Autowire
GenerateLoginToken generateLoginToken;
#Bean
public Endpoint endpointToken() {
EndpointImpl endpoint = new EndpointImpl(bus, generateLoginToken);
endpoint.publish("/Token");
return endpoint;
}
I would like to inject a singleton object dependency to a Spring bean. The catch is that I can't access and modify the class whose object I want to be injected. Let me describe on the example.
So I have my interface, and the implementation of this interface, like the following.
public interface MyServiceProxy {
String BEAN_NAME = "MyServiceProxy";
Data getData(String dataId);
}
public class MyServiceProxyImpl implements MyServiceProxy {
private final MyServiceClient client;
public MyServiceProxyImpl(MyServiceClient client) {
this.client = client;
}
#Override
public Data getData(String dataId) {//...}
Then in my Configuration class, I am creating a bean, but I need to pass it the MyServiceClient object in the constructor, and the catch is that I can't make MyServiceClient a bean because it's from external package and I can't modify it.
#Configuration
public class MyServiceProxyConfiguration {
#Bean(name = MyServiceProxy.BEAN_NAME)
public MyServiceProxy getMyServiceProxy(MyServiceClient client) { // could not autowire client
return new MyServiceProxyImpl(client);
}
}
So what I would like to do, is being able to pass/autowire an argument to getMyServiceProxy bean. Currently IntelliJ is giving me an error Could not autowire client. How can this be achieved?
UPDATE
Would something like the following work? Because IntelliJ is still reporting an "could not autowire" error. So if I created a bean method that returns the client I want injected, and then add #Inject annotation to the method where I want it injected.
public class MyServiceClientBuilder {
private final ClientBuilder builder;
public MyServiceClientBuilder(ClientBuilder builder) {
this.builder = builder;
}
#Bean
public MyServiceClient build() {
return builder.newClient();
}
#Configuration
public class MyServiceProxyConfiguration {
#Inject
#Bean(name = MyServiceProxy.BEAN_NAME)
public MyServiceProxy getMyServiceProxy(MyServiceClient client) { // could not autowire client
return new MyServiceProxyImpl(client);
}
}
You can define MyServiceClient as a separate bean in your configuration file like this:
#Configuration
public class MyServiceProxyConfiguration {
#Bean
public MyServiceClient getMyServiceClient () {
return MyServiceClient.getInstance(); //initiate MyServiceClient
}
#Bean(name = MyServiceProxy.BEAN_NAME)
public MyServiceProxy getMyServiceProxy(MyServiceClient client) {
return new MyServiceProxyImpl(client);
}
}
I have not tested this code, but it should work.
Say I have some application-wide configuration:
#Configuration
public class RestTemplateConfiguration {
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public HttpClient anHttpClient() {
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager();
// set some properties
// ...
return HttpClientBuilder.create().setConnectionManager(poolingHttpClientConnectionManager).build();
}
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public ClientHttpRequestInterceptor aRequestInterceptor() {
....
}
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public ClientHttpRequestInterceptor anotherRequestInterceptor() {
....
}
}
Then I have some particular service that looks like:
#Service
public class MyService {
private final RestTemplate myParticularRestTemplate;
#Autowired
public MyService(RestTemplate myParticularRestTemplate) {
this.myParticularRestTemplate = myParticularRestTemplate;
}
/***
* Some incredible application logic
***/
#Configuration
public static class Config {
private int SOME_READ_TIMEOUT;
private int SOME_CONNECT_TIMEOUT;
#Bean
public RestTemplate myParticularRestTemplate(HttpClient anHttpClient, List<ClientHttpRequestInterceptor> interceptors) {
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(anHttpClient);
clientHttpRequestFactory.setReadTimeout(SOME_READ_TIMEOUT);
clientHttpRequestFactory.setConnectTimeout(SOME_CONNECT_TIMEOUT);
RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory);
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
}
}
I want the interceptors to be injected into RestTemplates system-wide, but, as it is, because I've got to access nested objects of the RestTemplate to set particular configuration, I need to instantiate the RestTemplate myself -- at least as I understand it.
Is there a nicer way to address this that doesn't require configuration for specific RestTemplates to know about the List<ClientHttpRequestInterceptor>?