Annotation #Value doesn't work properly in Spring Boot? - java

CONTEXT:
I process reports with #Scheduled annotation and when invoke Component from Service property not getting initialized with #Value annotation even it physically exists in .properties and printed out in #PostConstruct.
DESCRIPTION:
ReportProcessor interface and InventoryReportProcessor implementation:
#FunctionalInterface
interface ReportProcessor {
public void process(OutputStream outputStream);
}
#Component
public class InventoryReportProcessor implement ReportProcessor {
#Value("${reportGenerator.path}")
private String destinationFileToSave;
/*
#PostConstruct
public void init() {
System.out.println(destinationFileToSave);
}
*/
#Override
public Map<String, Long> process(ByteArrayOutputStream outputStream) throws IOException {
System.out.println(destinationFileToSave);
// Some data processing in here
return null;
}
}
I use it from
#Service
public class ReportService {
#Value("${mws.appVersion}")
private String appVersion;
/* Other initialization and public API methods*/
#Scheduled(cron = "*/10 * * * * *")
public void processReport() {
InventoryReportProcessor reportProcessor = new InventoryReportProcessor();
Map<String, Long> skus = reportProcessor.process(new ByteArrayOutputStream());
}
}
My confusion comes from the fact that #Value in Service works fine but in #Component it returns null unless call in #PostConstruct. Also, if call #PostConstruct the value is still remains null in the rest of the class code.
I found similar Q&A and I did research in Srping docs but so far no single idea why it works this way and what can be a solution?

You need to Autowire the component to make your spring application aware of the component.
#Service
public class ReportService {
#Value("${mws.appVersion}")
private String appVersion;
/* Other initialization and public API methods*/
#Autowired
private ReportProcessor reportProcessor;
#Scheduled(cron = "*/10 * * * * *")
public void processReport() {
//InventoryReportProcessor reportProcessor = new InventoryReportProcessor();
Map<String, Long> skus = reportProcessor.process(new ByteArrayOutputStream());
}
}

Field injection is done after objects are constructed since obviously the container cannot set a property of something which doesn't exist.
at the time System.out.println(destinationFileToSave); triggers values are not being injected;
if you want to see it working try something like this
#Autowired
InventoryReportProcessor pross;
pross.process(ByteArrayOutputStream outputStream);
#PostConstruct works as it is being called after the object creation.

Spring will only parse #Value annotations on beans it knows. The code you use creates an instance of the class outside the scope of Spring and as such Spring will do nothing with it.
One thing you can do is to create the instance explictly or use Autowire:
#Autowired
private ReportProcessor reportProcessor;
tl:dr If you have configured your application context correctly then a #Value cannot be null as that will stop the correct startup of your application.
Change your Code from
#Value("${reportGenerator.path}")
private String destinationFileToSave;
to
#Value("${reportGenerator.path}")
public void setDestinationFileToSave(String destinationFileToSave) {
SendMessageController.destinationFileToSave = destinationFileToSave;
}

Related

My annotation #Value return null even it being used and called into component annotated classes

I'm using Spring and need some help:
I want to set one API key using application.properties instead of hardcoding it, but it always returns null. IntelliJ evaluates it correctly to the value I've set in the file.
I've already read other questions here and almost all solutions are saying that Spring can only "inject" those value anotations in managed classes, like Components, Beans, etc. That's what (think) I did and still got null!
Everything else is working as I intended. Any direction is appreciated!
My application.properties
api.someapiservice.key=08e...f
Class that uses the properties value:
#Component
public class ApiClient implements ApiClientInterface {
#Value("${api.someapiservice.key}")
private String API_KEY;
public ApiClient () {
System.out.println(API_KEY); //Returns null after spring log info: Initialized JPA EntityManagerFactory for persistence unit 'default'
...
}
Class that uses ApiClient:
#Component
public class SomeService {
private final SomeRepository someRepository;
private final ApiClient apiClient;
public PlaylistService(SomeRepository someRepository , ApiClient apiClient ) {
this.SomeRepository = SomeRepository;
this.apiClient = ApiClient;
}
Field injection can't possibly happen until after the instance is already constructed, so your #Value (or #Autowired) fields will always be null in the constructor. Move the #Value to a constructor parameter instead.
If you want to know what is the value of your #Value field on start up. You can use #PostConstruct annotation, or you can move #Value annotation on your class constructor.
private String API_KEY;
public ApiClient(#Value("${api.test.value}") String key) {
this.API_KEY= key;
System.out.println(this.API_KEY);
}
or using #PostConstruct Annotation
#Value("${api.someapiservice.key}")
private String API_KEY;
#PostConstruct
public void init() {
System.out.println(this.API_KEY);
}

Spring boot: How to parameterize #Scheduled

I am new to Spring and have only scratched the surface of what can be done with it.
I have a situation where I need to set up a recurring task using the #Scheduled annotation. The rate is specified as a member field in an object that is passed to the class encapsulating the method representing the task.
I've used the mechanism that allows for accessing the configuration or environment, e.g. #Scheduled(fixedRateString = "${some.property:default}"); this works great.
What I don't know how to do is insert the value from an object into the #Scheduled.
For example:
class MyClass {
private MyObject myObj;
public MyClass(MyObject myObj) {
this.myObj = myObj;
}
#Scheduled(fixedRateString = "${myObj.rate:5000}")
private void someTask() {
...
}
}
The code above, of course, does not work, I'm just giving an example of what I'm trying to do.
Any suggestions would be appreciated.
Yes you can use the #Scheduled annotation to do that with a SpEL expression (available on the #Scheduled annotation since Spring 4.3.x). Here's an example:
#Slf4j
#Configuration
#EnableScheduling
public class ExampleClass {
static class ScheduleCalculator {
public String calc() {
return "5000";
}
}
#Bean("scheduleCalculator")
public ScheduleCalculator createScheduleCalculator() {
return new ScheduleCalculator();
}
#Scheduled(fixedRateString = "#{scheduleCalculator.calc()}")
public void someTask() {
log.info("Hello world");
}
}
However, just because you can do it like this doesn't mean you necessarily should.
Your code may be easier to follow to folks that have to maintain it in the future if you use the spring task scheduler plus you get control of the thread pool used for scheduling instead of relying on the shared executor that all #Scheduled tasks get lumped into.
Unfortunately the spring bean creation process will not read local variables like that.
You can use the Spring TaskScheduler class.
Essentially you just have to define a thread pool that you will use to run the scheduled tasks (as a bean) and run taskScheduler.schedule(runnable, new CronTrigger("* * * * *")). There is a detailed example here:
https://www.baeldung.com/spring-task-scheduler
You can do like follow:
#Component
#ConfigurationProperties(prefix = "my.obj")
public class MyObject {
private String cronExecExpr = "*/5 * * * * *";
// getter and setter
}
class MyClass {
private MyObject myObj;
public MyClass(MyObject myObj) {
this.myObj = myObj;
}
#Scheduled(cron = "${my.obj.cron-exec-expr:*/5 * * * * *}")
private void someTask() {
...
}
}
As you can see her : https://www.baeldung.com/spring-scheduled-tasks
You can do that like follow :
A fixedDelay task:
#Scheduled(fixedDelayString = "${fixedDelay.in.milliseconds}")
A fixedRate task:
#Scheduled(fixedRateString = "${fixedRate.in.milliseconds}")
A cron expression based task:
#Scheduled(cron = "${cron.expression}")

When and how to instantiate a Spring Bean in my Rest Api

First of all, I'm a relative noob to Spring Boot, so keep that in mind.
I've got a REST api in which I'm trying to minimize database calls for the same object and I've determined that using a Spring Bean scoped to the Request is what I want. Assuming that is correct, here is what I'm trying to do:
1) Controller takes in a validated PhotoImportCommandDto command
PhotoCommandController
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<?> importPhoto(#Valid #RequestBody PhotoImportCommandDto command){
...
}
2) PhotoImportCommandDto is validated. Note the custom #UserExistsConstraint which validates that the user exists in the database by calling a service method.
PhotoImportCommandDto
#Component
public class PhotoImportCommandDto extends BaseCommand {
#NotNull(message = "userId must not be null!")
#UserExistsConstraint
private Long userId;
...
}
What I would like to do is somehow set a Spring Bean of the user that is validated in the #UserExistsConstraint and reference it in various methods that might be called throughout a Http request, but I'm not really sure how to do that. Since I've never really created my own Spring Beans, I don't know how to proceed. I've read various guides like this, but am still lost in how to implement it in my code.
Any help/examples would be much appreciated.
You can use the #Bean annotation.
#Configuration
public class MyConfiguration {
#Bean({"validUser"})
public User validUser() {
User user;
//instantiate user either from DB or anywhere else
return user;
}
then you can obtain the validUser.
#Component
public class PhotoImportCommandDto extends BaseCommand {
#Autowired
#Qualifier("validUser")
private User validUser;
...
}
I don't really know how to make annotations in Java. Anyway, in Spring, checking where the User exists in the DataBase or not is one line of code:
userRepository.findOne(user) == null
That is accomplished by the Spring Data JPA project:
Create a JPA Entity User.
Set the spring.datasource.url and login/password in the
resources/application.properties.
Create this interface:
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface UserRepository extends CrudRepository<User, Long> {
}
Note, Spring implements it behind the scences.
Inject this interface into your RestController (or any other Spring bean):
private UserRepository userRepository ;
**constructor**(UserRepository ur){
userRepository = ur;
}
Note, a Spring Bean is any class annotated #Component (this includes stereotype annotations like Controller, Repository - just look up the contents of an annotation, it may use #Component internally) or returned from a method which is annotated #Bean (can only be on the Component or Configuration class). A Component is injected by searching the classpath, Bean is injected more naturally.
Also note, injecting is specifying #Autowired annotation on field or constructor, on a factory method, or on a setter. The documentation recommends that you inject required dependencies into constructor and non-required into the setter.
Also note, if you're injecting into a constructor and it is clean by the arguments, you may omit #Autowired annotation, Spring will figure it out.
Call its method findOne.
So, you can do one of the following:
Inject the userRepository into the #RestController constructor (as shown above). I would do that.
Inject the userRepository into the #Service (internally #Component) class that will do this sorts of thing for you. Maybe you can play with it to create an annotation.
p.s. Use #PostMapping instead of #RequestMapping(method = RequestMethod.POST)
p.p.s. If ever in doubt, go to the official documentation page and just press CTRL-F: http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/ Note the current word, that will always take you to the latest version.
p.p.p.s Each Spring project has its own .io webpage as well as quick Get Started Guides where you can quickly see the sample project with explanations expecting you to know nothing.
Hope that helps! :)
Don't forget to mark the answer as accepted if you wish
Using Jose's input, I took a bit of a different route.
Here's what I did:
I created a ValidatedUser class:
#RequestScope
#Component
public class ValidatedUser {
private UserEntity user;
public UserEntity getUser() {
return user;
}
public void setUser(UserEntity user) {
this.user = user;
}
}
and I also created a wrapper class HttpRequestScopeConfig to capture all variables to use over the course of an Http Request to the api.
#Component
public class HttpRequestScopeConfig {
#Autowired
private ValidatedUser validatedUser;
...
public UserEntity getValidatedUser() {
return validatedUser.getUser();
}
public void setValidatedUser(UserEntity validatedUser) {
this.validatedUser.setUser(validatedUser);
}
...
}
In my UserExistsConstraintValidator (which is the impl of #UserExistsConstraint, I set the validatedUser in the httpRequestScopeConfig:
public class UserExistsConstraintValidator implements ConstraintValidator<UserExistsConstraint, Long> {
//private Log log = LogFactory.getLog(EmailExistsConstraintValidator.class);
#Autowired
private UserCommandService svc;
#Autowired
private HttpRequestScopeConfig httpRequestScope;
#Override
public void initialize(UserExistsConstraint userId) {
}
#Override
public boolean isValid(Long userIdField, ConstraintValidatorContext context) {
try {
UserEntity user = svc.findUserOfAnyStatus((Long) userIdField);
if (user != null) {
httpRequestScope.setValidatedUser(user);
return true;
}
} catch (Exception e) {
//log.error(e);
}
return false;
}
}
Now, I can access these variables throughout the rest of my service layers by autowiring HttpRequestScopeConfig where necessary.

HibernateTemplate not getting the object when called through TaskExecutor

I have a web service DocGenerationServiceImpl that inserts (for every format) a record in the table using DocRepository and object representing the record as DocFileDO. In the for-loop, I can get the id of the record that was created in the table. For each record, I will call the executor's execute method where DocGenTask will search for the record given the id. However, for example, there are 3 formats, the DocGenTask is able to get only the last record. The first 2 it cannot find. Although it's using hibernateTemplate. Can please advise?
#RestfulService
#Controller
#RequestMapping("/docs")
public class DocGenerationServiceImpl {
#Autowired
private TaskExecutor taskExecutor;
#Autowired
private DocRepository docRepository;
#RequestMapping(value = "/generate", method = RequestMethod.POST)
#ResponseBody
public String generatedDocFile(DOCParam param) {
for(String format : param.getFormatList()) {
DocFileDO docFileDO = new DocFileDO();
...
docRepository.saveDocFile(docFileDO);
log.debug("docFileDO id = " + docFileDO.getId());
DocGenTask task = new DocGenTask(docFileDO.getId());
task.setDocRepository(docRepository);
taskExecutor.execute(task);
}
}
}
#Repository
public class DocRepository {
#Autowired
private HibernateTemplate hibernateTemplate;
public DocFileDO saveDocFile(DocFileDO docFile) {
hibernateTemplate.save(docFile);
hibernateTemplate.flush();
return docFile;
}
public DocFileDO getDocFile(Long docFileId) {
return hibernateTemplate.get(DocFileDO.class, docFileId);
}
}
public class DocGenTask implements Runnable {
public void run() {
generate();
}
private void generate() {
DocFileDO docFileObj = docRepository.getDocFile(docFileId);
}
}
A couple of things
Don't use HibernateTemplate it should be considered deprecated as of Hibernate 3.0.1 (which was released somewhere in 2006). Use the SessionFactory directly and use the getCurrentSession() method to get a hibernate Session to operate on.
You don't have transactions setup (judging from the snippets), to work with a databse you need proper transaction setup.
Your controller is doing much, all of this should be inside a service.
The first refactor your repository
#Repository
public class DocRepository {
#Autowired
private SessionFactory sf;
public DocFileDO saveDocFile(DocFileDO docFile) {
Session session = sf.getCurrentSession();
session.save(docFile);
return docFile;
}
public DocFileDO getDocFile(Long docFileId) {
return sf.getCurrentSession().get(DocFileDO.class, docFileId);
}
}
Now your code will probably fail due to improper transaction setup. Add #Transactional to all the methods (or class) that need a transaction (like the saveDocFile method).
As mentioned you probably should move the code found in the controller to a service. The controller should be nothing more then a thin integration layer converting from the web to an internal representation of something and then kick off a service/business method somewhere. This service-/business-method is also your transactional unit-of-work it either all succeeds or all fails.

Spring Cache #Cacheable - not working while calling from another method of the same bean

Spring cache is not working when calling cached method from another method of the same bean.
Here is an example to explain my problem in clear way.
Configuration:
<cache:annotation-driven cache-manager="myCacheManager" />
<bean id="myCacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager" ref="myCache" />
</bean>
<!-- Ehcache library setup -->
<bean id="myCache"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:shared="true">
<property name="configLocation" value="classpath:ehcache.xml"></property>
</bean>
<cache name="employeeData" maxElementsInMemory="100"/>
Cached service :
#Named("aService")
public class AService {
#Cacheable("employeeData")
public List<EmployeeData> getEmployeeData(Date date){
..println("Cache is not being used");
...
}
public List<EmployeeEnrichedData> getEmployeeEnrichedData(Date date){
List<EmployeeData> employeeData = getEmployeeData(date);
...
}
}
Result :
aService.getEmployeeData(someDate);
output: Cache is not being used
aService.getEmployeeData(someDate);
output:
aService.getEmployeeEnrichedData(someDate);
output: Cache is not being used
The getEmployeeData method call uses cache employeeData in the second call as expected. But when the getEmployeeData method is called within the AService class (in getEmployeeEnrichedData), Cache is not being used.
Is this how spring cache works or am i missing something ?
I believe this is how it works. From what I remember reading, there is a proxy class generated that intercepts all requests and responds with the cached value, but 'internal' calls within the same class will not get the cached value.
From https://code.google.com/p/ehcache-spring-annotations/wiki/UsingCacheable
Only external method calls coming in through the proxy are
intercepted. This means that self-invocation, in effect, a method
within the target object calling another method of the target object,
will not lead to an actual cache interception at runtime even if the
invoked method is marked with #Cacheable.
Since Spring 4.3 the problem could be solved using self-autowiring over #Resource annotation:
#Component
#CacheConfig(cacheNames = "SphereClientFactoryCache")
public class CacheableSphereClientFactoryImpl implements SphereClientFactory {
/**
* 1. Self-autowired reference to proxified bean of this class.
*/
#Resource
private SphereClientFactory self;
#Override
#Cacheable(sync = true)
public SphereClient createSphereClient(#Nonnull TenantConfig tenantConfig) {
// 2. call cached method using self-bean
return self.createSphereClient(tenantConfig.getSphereClientConfig());
}
#Override
#Cacheable(sync = true)
public SphereClient createSphereClient(#Nonnull SphereClientConfig clientConfig) {
return CtpClientConfigurationUtils.createSphereClient(clientConfig);
}
}
The example below is what I use to hit the proxy from within the same bean, it is similar to #mario-eis' solution, but I find it a bit more readable (maybe it's not:-). Anyway, I like to keep the #Cacheable annotations at the service level:
#Service
#Transactional(readOnly=true)
public class SettingServiceImpl implements SettingService {
#Inject
private SettingRepository settingRepository;
#Inject
private ApplicationContext applicationContext;
#Override
#Cacheable("settingsCache")
public String findValue(String name) {
Setting setting = settingRepository.findOne(name);
if(setting == null){
return null;
}
return setting.getValue();
}
#Override
public Boolean findBoolean(String name) {
String value = getSpringProxy().findValue(name);
if (value == null) {
return null;
}
return Boolean.valueOf(value);
}
/**
* Use proxy to hit cache
*/
private SettingService getSpringProxy() {
return applicationContext.getBean(SettingService.class);
}
...
See also Starting new transaction in Spring bean
Here is what I do for small projects with only marginal usage of method calls within the same class. In-code documentation is strongly advidsed, as it may look strage to colleagues. But its easy to test, simple, quick to achieve and spares me the full blown AspectJ instrumentation. However, for more heavy usage I'd advice the AspectJ solution.
#Service
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
class AService {
private final AService _aService;
#Autowired
public AService(AService aService) {
_aService = aService;
}
#Cacheable("employeeData")
public List<EmployeeData> getEmployeeData(Date date){
..println("Cache is not being used");
...
}
public List<EmployeeEnrichedData> getEmployeeEnrichedData(Date date){
List<EmployeeData> employeeData = _aService.getEmployeeData(date);
...
}
}
If you call a cached method from same bean it will be treated as a private method and annotations will be ignored
Yes, the caching will not happen because of the reasons that were already mentioned in the other posts. However I would solve the problem by putting that method to its own class (service in this case). With that your code will be easier to maintain/test and understand.
#Service // or #Named("aService")
public class AService {
#Autowired //or how you inject your dependencies
private EmployeeService employeeService;
public List<EmployeeData> getEmployeeData(Date date){
employeeService.getEmployeeData(date);
}
public List<EmployeeEnrichedData> getEmployeeEnrichedData(Date date){
List<EmployeeData> employeeData = getEmployeeData(date);
...
}
}
#Service // or #Named("employeeService")
public class EmployeeService {
#Cacheable("employeeData")
public List<EmployeeData> getEmployeeData(Date date){
println("This will be called only once for same date");
...
}
}
In my Case I add variable :
#Autowired
private AService aService;
So I call the getEmployeeData method by using the aService
#Named("aService")
public class AService {
#Cacheable("employeeData")
public List<EmployeeData> getEmployeeData(Date date){
..println("Cache is not being used");
...
}
public List<EmployeeEnrichedData> getEmployeeEnrichedData(Date date){
List<EmployeeData> employeeData = aService.getEmployeeData(date);
...
}
}
It will use the cache in this case.
Better approach should be creating another service like ACachingService and call ACachingService.cachingMethod() instead of self Autowiring ( or any other approach trying to self inject). This way you do not fall into Circular dependency, which may be resulted in warning/error when upgrade to newer Spring ( Spring 2.6.6 in my case ) :
ERROR o.s.boot.SpringApplication - Application run failed
org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'webSecurityConfig':
Requested bean is currently in creation: Is there an unresolvable circular reference?
We looked at all the solutions here and decided to use a separate class for the cached methods because Spring 5 doesn't like circular dependencies.
Use static weaving to create proxy around your bean. In this case even 'internal' methods would work correctly
I use internal inner bean (FactoryInternalCache) with real cache for this purpose:
#Component
public class CacheableClientFactoryImpl implements ClientFactory {
private final FactoryInternalCache factoryInternalCache;
#Autowired
public CacheableClientFactoryImpl(#Nonnull FactoryInternalCache factoryInternalCache) {
this.factoryInternalCache = factoryInternalCache;
}
/**
* Returns cached client instance from cache.
*/
#Override
public Client createClient(#Nonnull AggregatedConfig aggregateConfig) {
return factoryInternalCache.createClient(aggregateConfig.getClientConfig());
}
/**
* Returns cached client instance from cache.
*/
#Override
public Client createClient(#Nonnull ClientConfig clientConfig) {
return factoryInternalCache.createClient(clientConfig);
}
/**
* Spring caching feature works over AOP proxies, thus internal calls to cached methods don't work. That's why
* this internal bean is created: it "proxifies" overloaded {#code #createClient(...)} methods
* to real AOP proxified cacheable bean method {#link #createClient}.
*
* #see Spring Cache #Cacheable - not working while calling from another method of the same bean
* #see Spring cache #Cacheable method ignored when called from within the same class
*/
#EnableCaching
#CacheConfig(cacheNames = "ClientFactoryCache")
static class FactoryInternalCache {
#Cacheable(sync = true)
public Client createClient(#Nonnull ClientConfig clientConfig) {
return ClientCreationUtils.createClient(clientConfig);
}
}
}
I would like to share what I think is the easiest approach:
Autowire the controller and use to call the method it instead of using the class context this.
The updated code would look like:
#Controller
public class TestController {
#Autowired TestController self;
#RequestMapping("/test")
public String testView(){
self.expensiveMethod();
return "test";
}
#Cacheable("ones")
public void expensiveMethod(){
System.out.println("Cache is not being used");
}
}
The default advice mode for processing caching annotation is “proxy”. At the startup of an application, all the caching annotations like #Caching, #Cacheable, #CacheEvict etc. are scanned and a target proxy class is generated for all of these classes. The proxy allows for intercepting the calls to these cacheable methods, which adds the caching advice/behavior.
So when we invoke the cacheable methods from the same class, as shown below, calls from the clients don’t get intercepted in a way that allows for caching advice to be added to them. Hence, every single time there is an unexpected cache miss.
Solution: Invoke the Cacheable methods from a different bean to use proxy class with caching advice.

Categories

Resources