Spring component depends on the configuration which load props from application.properties but props are not loaded - spring

I have some difficulties and I can't realize how to let it work because of lack of knowledge in Spring framework.
What I'm trying to do is pretty simple. I want to use injected AppProperties in RestTemplateComponent constuctor or getRestTemplate method but all props are null.
I think I understand why.. it's because RestTemplateComponent by the order was loaded first and that's why AppProperties props are null.
Is it possible some how to tell to Spring to load AppProperties first in order to use it in RestTemplateComponent.
By the way problem occurs only on start there is no any problem when I inject it in controller and during request use these objects.
application.properties
integration.url=http://...
#Configuration
#ConstructorBinding
#ConfigurationProperties("integration")
public class AppProperties {
private String url;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
#Component
public class RestTemplateComponent {
private final AppProperties appProperties;
public RestTemplateComponent(AppProperties appProperties) {
this.appProperties = appProperties;
}
#Bean
public RestTemplate getRestTemplate() {
RestTemplate restTemplate = new RestTemplate();
//Custom authorization
return restTemplate;
}
}

Ok, finally I did it work by adding annotation #PropertySource({"classpath:application.properties"}) within my AppProperties class and then when I try to autowire AppProperties in SpringBootApplication I see in debug that props are initialized.

Related

required a bean of type 'org.springframework.cloud.netflix.ribbon.SpringClientFactory' that could not be found

I have this test project which I would like to migrate to more recent version:
#Configuration
public class LoadbalancerConfig extends RibbonLoadBalancerClient {
public LoadbalancerConfig(SpringClientFactory clientFactory) {
super(clientFactory);
}
}
Full code example: https://github.com/rcbandit111/Generic_SO_POC/blob/master/src/main/java/org/merchant/database/service/sql/LoadbalancerConfig.java
Do you know how I can migrate this code to latest load balancer version?
I think examining the RibbonAutoConfiguration class gives you a good hint of how you should configure things.
First remove #Configuration from LoadbalancerConfig, I also renamed LoadbalancerConfig to CustomLoadbalancer to prevent confusion.
public class CustomLoadbalancer extends RibbonLoadBalancerClient {
public CustomLoadbalancer(SpringClientFactory clientFactory) {
super(clientFactory);
}
}
add the following dependency to your gradle
com.netflix.ribbon:ribbon:2.7.18
then add a configuration class like:
#Configuration
#ConditionalOnClass({Ribbon.class})
#AutoConfigureAfter(name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
#ConditionalOnProperty(value = "spring.cloud.loadbalancer.ribbon.enabled",
havingValue = "true", matchIfMissing = true)
#AutoConfigureBefore(LoadBalancerAutoConfiguration.class)
public class LoadBalancerClientConfig {
#Autowired(required = false)
private List<RibbonClientSpecification> configurations = new ArrayList<>();
#Bean
public CustomLoadbalancer customLoadbalancer() {
return new CustomLoadbalancer(springClientFactory());
}
#Bean
public SpringClientFactory springClientFactory() {
SpringClientFactory factory = new SpringClientFactory();
factory.setConfigurations(this.configurations);
return factory;
}
}
If you want to use Spring cloud load balancer instead of above configuration add spring-cloud-starter-loadbalancer dependency to your gradle.build and for configuration you only need this bean:
#LoadBalanced
#Bean
RestTemplate getRestTemplate() {
return new RestTemplate();
}
This RestTemplate pretty works identical to standard RestTemplate class, except instead of using physical location of the service, you need to build the URL using Eureka service ID.
Here is an example of how could you possibly use it in your code
#Component
public class LoadBalancedTemplateClient {
#Autowired
RestTemplate restTemplate;
#Autowired
User user;
public ResponseEntity<Result> getResult() {
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder
.fromHttpUrl("http://application-id/v1/") // application id registered in eureka
.queryParam("id", user.getUserId());
return restTemplate.getForEntity(uriComponentsBuilder.toUriString(),
Result.class);
}
}
Also if you wish to use reactive client the process is the same first define the bean:
#LoadBalanced
#Bean
WebClient.Builder webClientBuilder() {
return WebClient.builder();
}
and then inject and use it when you need:
#Autowired
private WebClient.Builder webClient;
public Mono<String> doSomething() {
return webClient
.build()
.get()
.uri("http://application-id/v1/")
.retrieve()
.bodyToMono(String.class);
}
Also you can check documentation for additional information: documentation

How to get Redis Hash Configuration such as Time To Live from application.properties at Spring Boot?

I use
#Value("${cache.host}")
private String redisHost;
#Value("${cache.port}")
private int redisPort;
I want to get timeToLive in #RedishHash from application properties. How can get this config?
#RedisHash(value = "UserModel", timeToLive = 5)
I give manually above however I want to give from application.properties
i'm not sure if you can do that from application.properties, but u can do it by configuring a RedisCacheManager bean with java based configuration like below :
#Bean
public RedisCacheManager RedisCacheManager(RedisConnectionFactory redisConnectionFactory) {
Map<String, RedisCacheConfiguration> cacheConfig = new HashMap<String, RedisCacheConfiguration>();
cacheConfig.put("UserModel", RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(5)));
RedisCacheManager rdisCacheManager = new RedisCacheManager(
RedisCacheWriter.lockingRedisCacheWriter(redisConnectionFactory),
RedisCacheConfiguration.defaultCacheConfig(), cacheConfig);
return rdisCacheManager;
}
PS : this method should be in a class with #Configuration annotation
You can create a #Component where you are going to take the values from properties
#Component
public class RedisHashCustom {
private static String redisHashValue;
public static String getRedisHashVaue() {
return redisHashValue;
}
#Value("${application.redis.redishash.value}")
public void setRedisHashValue(String newRedisHashValue) {
redisHashValue= newRedisHashValue;
}
}
Then you need to reference as
#RedisHash(value = "#{T(com.redis.model.RedisHashCustom).getRedisHashValue() }")

Force SpingBoot to use Gson over Jackson

I am trying to force SpringBoot to use Gson instead of Jackson. I've read most of the articles I've found online and I am still seeing Jackson being used. Here's what I've done
Added
spring:
http: { converters: { preferred-json-mapper: gson } }
mvc: { converters: {preferred-json-mapper: gson } }
in application.yaml
Updated POM
Added gson dependency
Added jackson-databind to exclusion list in spring-boot-starter-web depedency.
Added #EnableAutoConfiguration(exclude = JacksonAutoConfiguration.class) to main class.
Written below #Configuration class:
#Configuration
#Slf4j
public class MyConfig implements WebMvcConfigurer {
#Override
public void extendMessageConverters (List<HttpMessageConverters<?>> converters) {
log.debug("Setting gson converter");
converters.add(new GsonHttpMessageConverter(myCustomGsonInstance()));
}
public Gson myCustomGsonInstance() {
return new Gson();
}
}
When running tests in debug, I can see that Jackson is still listed in the HttpMessageConverters list and Gson is not.
Update:
This behavior is seen while running live and in the below test class.
#AutoConfigureMockMvc
#SpringBootTest(webEnvironment = MOCK)
#ExtendWith(MockitoExtension.class)
public class MyTestClass {
#Autowired
private MyController controller;
private MockMvc mockMvc;
#BeforeEach
public void setUp(){
mockMvc = MockMvcBuilders.standaloneSetup(controller)
// .setMessageConverters(new GsonHttpMessageConverter(myCustomGsonInstance())) // if I add this, the test passes.
.build();
}
#Test
public void happyFlow(){
// given
URI uri = "/test/uri";
HttpHeaders headers = new HttpHeaders();
headers.set(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
// when
String responseBody = mockMvc.perform(get(uri).headers(headers)).andReturn().getResponse().getContentAsString();
// then
assertThat(responseBody, wasSerializedByGson());
}
}
It looks like you're using the wrong property for configuring the preferred JSON mapper. You are using spring.http.converters.preferred-json-mapper but the correct property is spring.mvc.converters.preferred-json-mapper. In application.yaml, that would be the following:
spring:
mvc:
converters:
preferred-json-mapper: gson
Spring Boot comes with Gson Auto Configuration support: Source Code
So you have to Autowire the Gson singleton instance to be used by your WebMvcConfigurer in addition to enabling the yaml property:
#Configuration
#Slf4j
public class MyConfig implements WebMvcConfigurer {
#Autowired
private Gson gson;
#Override
public void extendMessageConverters (List<HttpMessageConverters<?>> converters) {
log.debug("Setting gson converter");
converters.add(new GsonHttpMessageConverter(gson));
}
}
And the yaml properties borrowed from Andy Wilkinson:
spring:
mvc:
converters:
preferred-json-mapper: gson
With this setup Spring MVC is using the same Gson instance as the one Autowired in your configuration.
And in your test, it should look like this:
#WebMvcTest(MyController.class)
public class MyTestClass {
#Autowired
private MockMvc mockMvc;
#Autowired
private MyController controller;
#Test
public void happyFlow(){
// given
URI uri = "/test/uri";
HttpHeaders headers = new HttpHeaders();
headers.set(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
// when
String responseBody = mockMvc.perform(get(uri).headers(headers)).andReturn().getResponse().getContentAsString();
// then
assertThat(responseBody, wasSerializedByGson());
}
}

Spring, inject properties in a Bean instance based one of that Bean's field value, is it possible?

I have a Pojo I use to configure webservices' clients:
public class ServiceConfig {
private String url;
private String endpoint;
private String serviceName;
public ServiceConfig(String serviceName) {
super();
this.serviceName = serviceName;
}
}
Now, this is what my application.properties file looks like:
service1.url=http://localhost:8087/
service1.endpoint=SOME_ENDPOIT1
service2.url=http://localhost:8085/
service2.endpoint=SOME_ENDPOIT2
service3.url=http://localhost:8086/
service3.endpoint=SOME_ENDPOIT3
service4.url=http://localhost:8088/
service4.endpoint=SOME_ENDPOIT4
What I'm trying to achieve is for Spring to inject the correct properties when I instantiate ServiceConfig like this:
ServiceConfig sc = new ServiceConfig("service1");
Is it possible?
Are you using just spring or also spring-boot?
What about injecting org.springframework.core.env.Environment to your pojo and configuring it with it.
so something like this could work:
public class ServiceConfig {
private String url;
private String endpoint;
private String serviceName;
public ServiceConfig(String serviceName, Environment env) {
// TODO assert on serviceName not empty
this.serviceName = serviceName;
this.url = env.getProperty(serviceName.concat(".url");
this.endpoint = env.getProperty(serviceName.concat(".endpoint");
}
}
I guess there could be a simpler/more elegant solution, but I don't know your case.
spring-boot version
well with spring boot just define your pojo (field names must match property names)
public class ServiceConfig {
private String url;
private String endpoint;
// getters setters
}
and then in some configuration you can do this (note: value in ConfigurationProperties is prefix of your configuration in application.properties):
#Configuration
public class ServicesConfiguration {
#Bean
#ConfigurationProperties("service1")
ServiceConfig service1(){
return new ServiceConfig();
}
#Bean
#ConfigurationProperties("service2")
ServiceConfig service2(){
return new ServiceConfig();
}
}

Spring - should I use #Bean or #Component?

Here is the current code at my work.
Method 1
#Configuration
public class AppConfig {
#Bean
#Autowired(required = false)
public HttpClient createHttpClient() {
// do some connections configuration
return new HttpClient();
}
#Bean
#Autowired
public NameClient nameClient(HttpClient httpClient,
#Value("${ServiceUrl:NotConfigured}")
String serviceUrl) {
return new NameClient(httpClient, serviceUrl);
}
}
And the NameClient is a simple POJO looks like following
public class NameClient {
private HttpClient client;
private String url;
public NameClient(HttpClient client, String url) {
this.client = client;
this.url = url;
}
// other methods
}
Instead of using #Bean to configure, I wanted to follow this pattern:
Method 2
#Configuration
public class AppConfig {
#Bean
#Autowired(required = false)
public HttpClient createHttpClient() {
// do some connections configuration
return new HttpClient();
}
}
And use auto-scanning feature to get the bean
#Service //#Component will work too
public class NameClient {
#Autowired
private HttpClient client;
#Value("${ServiceUrl:NotConfigured}")
private String url;
public NameClient() {}
// other methods
}
Why the first method above is used/preferred? What is the advantage of one over the other? I read about the difference between using #Component and #Bean annotations.
They're equivalent.
You would typically use the second one when you own the NameClient class and can thus add Spring annotations in its source code.
You would use the first one when you don't own the NameClient class, and thus can't annotate it with the appropriate Spring annotations.

Resources