Injecting properties on constructor - spring

I have to consume a REST api which follows a common syntax across all retrievable objects:
baseUrl + domainObjectName + qualifier
E.g.
"http://myweb.com/api/" + "cities" + "/{id}"
I created a BaseDao for my data layer and I would like to set up in DAO instantiation the base url for each domain object (baseUrl + domainObjectName). The problem is I have my api Base url defined in the properties file (and would like to keep it that way), and it is not available in the DAO constructor.
This is what I have:
public abstract class BaseDao {
protected static final String ID_QUALIFIER = "/{id}";
protected String domainObjectName = "";
protected String doBaseUrl = "";
#Value("#{config['baseUrlRest']}")
public String apiBaseUrl;
public GenericDaoRestImpl(String domainObjectName) {
this.domainObjectName = domainObjectName;
this.doBaseUrl = apiBaseUrl + domainObjectName;
}
}
When my dao is instantiated, apiBaseUrl is still null, although after creation it is indeed injecting the baseUrl property.
Is there any way around this, like injecting the property as a static constant?

This happens because Java doesn't allow to set fields of a class before the constructor is called. So Spring can't inject the value. There are two solutions:
Pass the value to the constructor instead (example 1)
Use #PostConstruct (example 2)
Example 1:
public GenericDaoRestImpl(
#Value("#{config['baseUrlRest']}") String apiBaseUrl
String domainObjectName
) {
...
}
Example 2:
#Value("#{config['baseUrlRest']}")
public String apiBaseUrl;
public GenericDaoRestImpl(String domainObjectName) {
this.domainObjectName = domainObjectName;
}
#PostConstruct
public void init() {
this.domainObjectName = domainObjectName;
this.doBaseUrl = apiBaseUrl + domainObjectName;
}
I prefer the #PostConstruct because constructor injection eventually leads to constructors with many parameters which makes them unwieldy.
If you don't like it, your third option is using the builder pattern with a fluent interface.

Related

Custom serialization of single #RestController endpoint

Is there a way (preferably some type of annotation) to register a custom serializer for a single endpoint in a #RestController? Extending the bean and putting a #JsonSerialize on it would be an option, but that demands an otherwise pretty useless new bean class. I tried the following:
#JsonSerialize(using = CustomSerializer.class)
#RequestMapping(value = "/some_endpoint/", method = RequestMethod.GET)
public SomeType someEndpoint() {
return someObject;
}
But the #JsonSerialize annotation doesn't appear to have any meaning to Spring in that context. Is there an alternative or is the extra bean class my only option?
You can use #JsonView(View.Summary::class) in the attributes you want to add or ignore and in the method you want to apply that view, for example:
public class View {
public interface Summary
}
public class A{
#JsonView(View.Summary.class)
private String serialized = "",
private String notSerialized = ""}
and then in the controller:
#JsonView(View.Summary.class)
#GetMapping("/")
#ResponseBody
public A getA(){
return A()
}
If you want to reverse the JsonView (serialize the atributtes who doesnt have the view). you can add the following propertie: spring.jackson.mapper.default-view-inclusion=true

Spring boot caching in #Service class does not work

I have problems with save some values in #Service method.
My code:
#Service(value = "SettingsService")
public class SettingsService {
...
public String getGlobalSettingsValue(Settings setting) {
getTotalEhCacheSize();
if(!setting.getGlobal()){
throw new IllegalStateException(setting.name() + " is not global setting");
}
GlobalSettings globalSettings = globalSettingsRepository.findBySetting(setting);
if(globalSettings != null)
return globalSettings.getValue();
else
return getGlobalEnumValue(setting)
}
#Cacheable(value = "noTimeCache", key = "#setting.name()")
public String getGlobalEnumValue(Settings setting) {
return Settings.valueOf(setting.name()).getDefaultValue();
}
My repository class:
#Repository
public interface GlobalSettingsRepository extends CrudRepository<GlobalSettings, Settings> {
#Cacheable(value = "noTimeCache", key = "#setting.name()", unless="#result == null")
GlobalSettings findBySetting(Settings setting);
It should work like this:
get value form DB if data exist,
if not save value from enum.
but it didn't save any data from DB or enum.
My cache config:
#Configuration
#EnableCaching
public class CacheConfig {
#Bean
public EhCacheCacheManager cacheManager(CacheManager cm) {
return new EhCacheCacheManager(cm);
}
#Bean
public EhCacheManagerFactoryBean ehcache() {
EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean();
ehCacheManagerFactoryBean.setConfigLocation(new ClassPathResource("ehcache.xml"));
return ehCacheManagerFactoryBean;
}
}
I have some example to make sure that cache is working in my project in rest method:
#RequestMapping(value = "/system/status", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> systemStatus() {
Object[] list = userPuzzleRepository.getAverageResponseByDateBetween(startDate, endDate);
...
}
public interface UserPuzzleRepository extends CrudRepository<UserPuzzle, Long> {
#Cacheable(value = "averageTimeAnswer", key = "#startDate")
#Query("select AVG(case when up.status='SUCCESS' OR up.status='FAILURE' OR up.status='TO_CHECK' then up.solvedTime else null end) from UserPuzzle up where up.solvedDate BETWEEN ?1 AND ?2")
Object[] getAverageResponseByDateBetween(Timestamp startDate, Timestamp endDate);
and it's work well.
What am I doing wwrong?
You have two methods in your SettingsService, one that is cached (getGlobalEnumValue(...)) and another one that isn't cached, but calls the other method (getGlobalSettingsValue(...)).
The way the Spring cache abstraction works however is by proxying your class (using Spring AOP). However, calls to methods within the same class will not call the proxied logic, but the direct business logic beneath. This means caching does not work if you're calling methods in the same bean.
So, if you're calling getGlobalSettingsValue(), it will not populate, nor use the cache when that method calls getGlobalEnumValue(...).
The possible solutions are:
Not calling another method in the same class when using proxies
Caching the other method as well
Using AspectJ rather than Spring AOP, which weaves the code directly into the byte code at compile time, rather than proxying the class. You can switch the mode by setting the #EnableCaching(mode = AdviceMode.ASPECTJ). However, you'll have to set up load time weaving as well.
Autowire the service into your service, and use that service rather than calling the method directly. By autowiring the service, you inject the proxy into your service.
The problem is in the place you call your cacheable method from. When you call your #Cacheable method from same class, you just call it from this reference, which means it doesn't wrapped by Spring's proxy, so Spring can't catch your invocation to handle it.
One on ways to solve this problem is to #Autowired service to itself and just call methods you expected spring have to handle by this reference:
#Service(value = "SettingsService")
public class SettingsService {
//...
#Autowired
private SettingsService settingsService;
//...
public String getGlobalSettingsValue(Settings setting) {
// ...
return settingsSerive.getGlobalEnumValue(setting)
//-----------------------^Look Here
}
#Cacheable(value = "noTimeCache", key = "#setting.name()")
public String getGlobalEnumValue(Settings setting) {
return Settings.valueOf(setting.name()).getDefaultValue();
}
}
But if you have such problems it means your classes are take on too much and aren't comply with the principle of "single class - single responsibility". The better solution would be to move method with #Cacheable to dedicated class.

Long Polling with Spring's DeferredResult

The client periodically calls an async method (long polling), passing it a value of a stock symbol, which the server uses to query the database and return the object back to the client.
I am using Spring's DeferredResult class, however I'm not familiar with how it works. Notice how I am using the symbol property (sent from client) to query the database for new data (see below).
Perhaps there is a better approach for long polling with Spring?
How do I pass the symbol property from the method deferredResult() to processQueues()?
private final Queue<DeferredResult<String>> responseBodyQueue = new ConcurrentLinkedQueue<>();
#RequestMapping("/poll/{symbol}")
public #ResponseBody DeferredResult<String> deferredResult(#PathVariable("symbol") String symbol) {
DeferredResult<String> result = new DeferredResult<String>();
this.responseBodyQueue.add(result);
return result;
}
#Scheduled(fixedRate=2000)
public void processQueues() {
for (DeferredResult<String> result : this.responseBodyQueue) {
Quote quote = jpaStockQuoteRepository.findStock(symbol);
result.setResult(quote);
this.responseBodyQueue.remove(result);
}
}
DeferredResult in Spring 4.1.7:
Subclasses can extend this class to easily associate additional data or behavior with the DeferredResult. For example, one might want to associate the user used to create the DeferredResult by extending the class and adding an additional property for the user. In this way, the user could easily be accessed later without the need to use a data structure to do the mapping.
You can extend DeferredResult and save the symbol parameter as a class field.
static class DeferredQuote extends DeferredResult<Quote> {
private final String symbol;
public DeferredQuote(String symbol) {
this.symbol = symbol;
}
}
#RequestMapping("/poll/{symbol}")
public #ResponseBody DeferredQuote deferredResult(#PathVariable("symbol") String symbol) {
DeferredQuote result = new DeferredQuote(symbol);
responseBodyQueue.add(result);
return result;
}
#Scheduled(fixedRate = 2000)
public void processQueues() {
for (DeferredQuote result : responseBodyQueue) {
Quote quote = jpaStockQuoteRepository.findStock(result.symbol);
result.setResult(quote);
responseBodyQueue.remove(result);
}
}

Spring: How to inject a value to static field?

With this class
#Component
public class Sample {
#Value("${my.name}")
public static String name;
}
If I try Sample.name, it is always 'null'. So I tried this.
public class Sample {
public static String name;
#PostConstruct
public void init(){
name = privateName;
}
#Value("${my.name}")
private String privateName;
public String getPrivateName() {
return privateName;
}
public void setPrivateName(String privateName) {
this.privateName = privateName;
}
}
This code works. Sample.name is set properly. Is this good way or not? If not, is there something more good way? And how to do it?
First of all, public static non-final fields are evil. Spring does not allow injecting to such fields for a reason.
Your workaround is valid, you don't even need getter/setter, private field is enough. On the other hand try this:
#Value("${my.name}")
public void setPrivateName(String privateName) {
Sample.name = privateName;
}
(works with #Autowired/#Resource). But to give you some constructive advice: Create a second class with private field and getter instead of public static field.
Soruce of this info is this: https://www.baeldung.com/spring-inject-static-field
Spring uses dependency injection to populate the specific value when it finds the #Value annotation. However, instead of handing the value to the instance variable, it's handed to the implicit setter instead. This setter then handles the population of our NAME_STATIC value.
#RestController
//or if you want to declare some specific use of the properties file then use
//#Configuration
//#PropertySource({"classpath:application-${youeEnvironment}.properties"})
public class PropertyController {
#Value("${name}")//not necessary
private String name;//not necessary
private static String NAME_STATIC;
#Value("${name}")
public void setNameStatic(String name){
PropertyController.NAME_STATIC = name;
}
}
This is my sample code for load static variable
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
#Component
public class OnelinkConfig {
public static int MODULE_CODE;
public static int DEFAULT_PAGE;
public static int DEFAULT_SIZE;
#Autowired
public void loadOnelinkConfig(#Value("${onelink.config.exception.module.code}") int code,
#Value("${onelink.config.default.page}") int page, #Value("${onelink.config.default.size}") int size) {
MODULE_CODE = code;
DEFAULT_PAGE = page;
DEFAULT_SIZE = size;
}
}
For those who want to use ApplicationContext in the main class of a Spring Boot application, you can just use the return value of SpringApplication.run.
Although workarounds may need to be implemented, one should try to avoid them in most scenarios if possible. Spring is great at handling dependency injection and treats most objects as Singletons. This means that Spring can handle the creation of objects for you, and the injection of these objects at runtime. When combining this with the fact that your Spring managed bean is likely a Singleton, the use of static methods and variables is largely unnecessary. You can simply autowire in an instance of the object you are looking for at the constructor level or variable level and reference the non-static version of the method or variable. This is ideal and behaves similarly to a static reference. Non static variables are basically static because you are only ever using one instance of the object in every part of the code and because of dependency injection you are never handling the instantiation of the object, just like with a static reference! Great! Now I'm sure there are instances where you need the work around (i.e. you aren't using dependency injection or class is not a singleton), but try to not use workarounds if possible. Also this is just my 2 cents. Someone may be able to offer 3. (:
public class InjectableClass{
#Value("${my.value}")
private String myString;
public String nonStaticMethod(){
return myString;
}
}
public class LogicClass{
private InjectableClass injectableClass;
#Autowire
public LogicClass(InjectableClass injectableClass){
this.injectableClass = injectableClass;
}
public void logicClassMethod(){
System.out.println("Hey! Here is the value I set on myString: " +
injectableClass.nonStaticMethod() + ". That was
basically like using a static method!");
}
}

Util class for accesing a Service in Spring 3

In Spring 3 it is not possible to set #Autowired in either static fields or methods, so since I want to declare an utility class such as:
public class SchoolYearServiceUtil {
private static SchoolYearService schoolYearService;
public static SchoolYear getSchoolYear(Long id) {
return schoolYearService.get(id);
}
}
to avoid having to inject the schoolYearService everywhere (jsp, command class...) in which I need it. In this case, I don't need an interface to be implemented by SchoolYearServiceUtil.
I don't want to have to initialize the object through code but getting the same instance as the Spring's one.
Which would be the best option to implement the getSchoolYear as a static method?
Thanks.
Would this be conceptually wrong?:
#Component
public class SchoolYearServiceUtil {
private static SchoolYearService schoolYearService;
#Autowired(required = true)
private SchoolYearServiceUtil(#Qualifier("schoolYearServiceImpl") SchoolYearService schoolYearService) {
SchoolYearServiceUtil.schoolYearService = schoolYearService;
}
public static SchoolYearService getSchoolYearService() {
return schoolYearService;
}
public static SchoolYear getSchoolYear(Long id) {
return getSchoolYearService().get(id);
}
}
I would have to make sure that only Spring calls once the constructor and the constructor is called nowhere else, that's why I declared the constructor as private.
I fully support skaffman's comment. You don't need static fields with DI. You just define a bean of scope singleton (default).
There is a way to obtain a bean statically, but you should be aware that it is not to be used in regular situations. (there are some valid applications). It is to use the WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext)
You notice that you need to pass a ServletContext argument.

Resources