Spring boot Post-construct method cached value update - spring

In my micro-service spring boot application I have used #PostConstruct annotation to initialize and cache some data as below,
#Microservice
#EnableRetry
#EnableCaching
public class OrderMicroService {
public static void main(final String[] args) {
SpringApplication.run(OrderMicroService.class, args);
}
#Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("ServiceCode");
}
}
My #Postconstruct method as below,
#Service
public class ServiceCodeImpl implements ServiceCode {
#PostConstruct
public void populateMap(){
private static HashMap<String,Orders.Service> serviceCache=new HashMap<String,Services.Service>();
// Rest call here to get some xml from other interface and caching here
serviceCache.put(put something here);
}
}
Is their any way that i can update the cached data automatically and manually [because the .xml file getting from other interface could be modified sometime but not very often]. on any change i need to update my cache.

Related

Spring Boot #Component doesn't create Beans

Since according to the docs #Component registers beans for the Spring container I'm trying to create a simple example of dependency injection using the following code:
package pl.playground;
//...
#SpringBootApplication
public class PlaygroundApplication {
#Autowired
private static Building building;
public static void main(String[] args) {
building.setBuildingSize(12L);
System.out.println(building.monthlyHeatingCost());
}
}
package pl.playground.facade;
//...
#Component
public class Building {
private HeatingService service;
private Long buildingSize;
#Autowired
public Building(HeatingService service) {
this.service = service;
}
public Double monthlyHeatingCost() {
return service.getMonthlyHeatingCost(buildingSize);
}
// getters & setters...
}
package pl.playground.service;
public interface HeatingService {
Double getMonthlyHeatingCost(Long size);
}
package pl.playground.service;
//...
#Component
public class HeatingServiceImpl implements HeatingService {
private final Double CUBIC_PRICE = 2.3;
public HeatingServiceImpl() {}
#Override
public Double getMonthlyHeatingCost(Long size) {
return size * CUBIC_PRICE;
}
}
It builds and runs, but there is a NullPointerException at building.setBuildingSize(12L);. However the one below works without any issues:
//PlaygroundApplication.java
package pl.playground;
//...
#SpringBootApplication
public class PlaygroundApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
Building building = context.getBean(Building.class);
building.setBuildingSize(12L);
System.out.println(building.monthlyHeatingCost());
}
}
package pl.playground.config;
//...
#Configuration
public class Config {
#Bean
public Building building(HeatingService service) {
return new Building(service);
}
#Bean
public HeatingServiceImpl heatingServiceImpl() {
return new HeatingServiceImpl();
}
}
The rest is the same as before.
Why is #Component not creating Beans?
It is working the way I think it should when used inside a #Controller of a web app, does that make a difference? How does exactly #Bean and #Component differ?
What am I failing to understand?
EDIT
Consider the following scenario:
package pl.playground;
//...
#SpringBootApplication
public class ExampleApplication {
public static void main(String[] args) {
SpringApplication.run(ExampleApplication.class, args);
}
}
package pl.playground.controller;
//...
#Controller
public class Controller {
private Facade facade;
#Autowired
public Controller(Facade facade) {
this.facade = facade;
}
#GetMapping("/")
public String getIndexPage(Model model) {
return "index";
}
}
package pl.playground.facade;
//...
#Component
public class Facade {
private PostsService postService;
private UserService userService;
private TagService tagService;
#Autowired
public Facade(PostsService retrieve, UserService user, TagService tag) {
this.postService = retrieve;
this.userService = user;
this.tagService = tag;
}
//...
}
I don't need #Configuration here for it to work. That's my concern.
The problem with your code is that you are trying to #Autowire on a static field. You simply cannot do that. Look here: Can you use #Autowired with static fields?
It fails to work because the PlaygroundApplication class is not being created and managed by spring. The injection works only inside instances managed by spring. You can treat class annotated with #SpringBootApplication as configuration classes. Spring creates instances of those classes and injection works inside them but only on instance fields.
The second example shows the correct way to access spring beans from main method of the application.
Well. I used your original question and is working without any issues. #cezary-butler pointed out in the comments you can autowire into PlaygroundApplication but you can get hold of it easily in the static main method using context.getBean(Building.class)
#SpringBootApplication
public class PlaygroundApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context =
SpringApplication.run(PlaygroundApplication.class);
Building building = context.getBean(Building.class);
building.setBuildingSize(12L);
System.out.println(building.monthlyHeatingCost());
}
}
Here is the sample repo https://github.com/kavi-kanap/stackoverflow-63072236
TLDR;
A Spring context needs to be created before any bean can be injected. In the first scenario, just the fact of having a #SpringBootApplication decorator does not ensure a context in the scope of the class it decorates.
SpringApplication.run(ExampleApplication.class, args); instantiates a context (and e.g. a web server among other things)
var context = new AnnotationConfigApplicationContext(Config.class); instantiates a scoped context
Thus the first example had null inside of Building as there was no context with the bean to inject.

Spring Ioc Beans management

I have a question about spring IoC management. I created Bean in:
#SpringBootApplication
public class Application {
public static void main(String[] args) {....}
#Bean
public XmlMapper xmlMapper() {
return new XmlMapper();
}
}
These beans work fine as expected. But Default ObjectMapper get overridden and
#RestController try to parse the request and expect that payload is XML.
Can anyone explain why this happens?
XmlMapper is a sub class of ObjectMapper so if you declare one bean of this type, Spring will use that one and inject it where needed.
If you want to still use basic ObjectMapper elsewhere. You can declare another bean ObjectMapper. You may have to indicate it as primary.
#SpringBootApplication
public class Application {
public static void main(String[] args) {....}
#Bean
public XmlMapper xmlMapper() {
return new XmlMapper();
}
#Bean
#Primary //Not sure if needed
public ObjectMapper objectMapper() {
return new ObjectMapper();
}
}

Spring Boot insert sample data into database upon startup

What is the right way for creating test data upon server startup and inserting them into the database (I'm using a JPA/JDBC backed Postgres instance).
Preferably in form of creating Entities and having them persisted through a Repository interface rather than writing plain SQL code. Something like RoR's Rake db:seed helper.
If the framework exposes a hook for doing stuff when all the beans have been injected and the database is ready, that could also work.
You can catch ApplicationReadyEvent then insert demo data, for example:
#Component
public class DemoData {
#Autowired
private final EntityRepository repo;
#EventListener
public void appReady(ApplicationReadyEvent event) {
repo.save(new Entity(...));
}
}
Or you can implement CommandLineRunner or ApplicationRunner, to load demo data when an application is fully started:
#Component
public class DemoData implements CommandLineRunner {
#Autowired
private final EntityRepository repo;
#Override
public void run(String...args) throws Exception {
repo.save(new Entity(...));
}
}
#Component
public class DemoData implements ApplicationRunner {
#Autowired
private final EntityRepository repo;
#Override
public void run(ApplicationArguments args) throws Exception {
repo.save(new Entity(...));
}
}
Or even implement them like a Bean right in your Application (or other 'config') class:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public CommandLineRunner demoData(EntityRepository repo) {
return args -> {
repo.save(new Entity(...));
}
}
}
From Spring documentation: http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/reference/htmlsingle/#howto-database-initialization
Initialize a database using Hibernate
A file named import.sql in the root of the classpath will be executed on startup if Hibernate creates the schema from scratch (that is if the ddl-auto property is set to create or create-drop). This can be useful for demos and for testing if you are careful, but probably not something you want to be on the classpath in production. It is a Hibernate feature (nothing to do with Spring).
You can do like this
#SpringBootApplication
public class H2Application {
public static void main(String[] args) {
SpringApplication.run(H2Application.class, args);
}
#Bean
CommandLineRunner init (StudentRepo studentRepo){
return args -> {
List<String> names = Arrays.asList("udara", "sampath");
names.forEach(name -> studentRepo.save(new Student(name)));
};
}
}

How to inject property values into Spring Boot beans

In my spring boot application, i'am trying to inject variable's value from the config file application.properties to my java class and i'm getting a null value.
here is the configuration of my application.properties file:
myapp.username=user#user.com
myapp.password=user
here is where i call the configuration entries:
#Component
public class MyClass{
#Value("${myapp.username}")
public String username;
#Value("${myapp.password}")
public String password;
public static void main(String[] args) {
System.out.println(password);
}
}
I hope there someone how did deal with the same problem, thanks.
you can use this example add bean to your config like this :
#Configuration
#ComponentScan(basePackages = "youpackagebase")
#PropertySource(value = { "classpath:application.properties" })
public class AppConfig {
/*
* PropertySourcesPlaceHolderConfigurer Bean only required for #Value("{}") annotations.
* Remove this bean if you are not using #Value annotations for injecting properties.
*/
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
and in your bean :
#Component
public class NetClient {
#Value("${bigwater.api_config.url.login}")
public String url_login;
Best Regards
You are not even letting the Spring Boot container to boot (initialize) as you are are writing the code directly under main.
You should have an Application class as shown below to launch the Spring boot container properly, look here.
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
As far as my understanding you wanted to execute your code once the container is started, so follow the below steps:
Add the above Application class and then in your NetClient component class add a #Postconstruct method & this method will be called automatically once the bean is ready, refer the code below:
#Component
public class NetClient {
#Value("${bigwater.api_config.url.login}")
public String url_login;
#Value("${bigwater.api_config.url.ws}")
public static String url_ws;
#Value("${bigwater.api_config.username}")
public String username;
#Value("${bigwater.api_config.password}")
public String password;
#Postconstruct
public void init() {
//place all of your main(String[] args) method code here
}
//Add authentification() method here
}

PlayFramework: Depedencies are not inject using Spring and got NullPointerException

When i try to integrate Spring-Dependency-Injection in Play-framework with Java 8. In controller the dependencies are not injected. I am using spring stereo-type annotations. Get
Follwowing is my code:
Configuration:
public class GlobalConfiguration extends GlobalSettings{
private AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
#Override
public void onStart(Application app) {
super.onStart(app);
// AnnotationConfigApplicationContext can only be refreshed once, but we do it here even though this method
// can be called multiple times. The reason for doing during startup is so that the Play configuration is
// entirely available to this application context.
applicationContext.scan("com.harmeetsingh13.controllers", "com.harmeetsingh13.service.impl", "com.harmeetsingh13.dao.impl");
applicationContext.refresh();
// This will construct the beans and call any construction lifecycle methods e.g. #PostConstruct
applicationContext.start();
}
#Override
public void onStop(Application app) {
// This will call any destruction lifecycle methods and then release the beans e.g. #PreDestroy
applicationContext.close();
super.onStop(app);
}
#Override
public <A> A getControllerInstance(Class<A> clazz) throws Exception {
return applicationContext.getBean(clazz);
}
}
Controller:
#Component
public class UserController extends Controller{
#Autowired
private UserService userService;
public Result findUserById(Integer userId) {
Optional<User> user = userService.findUserById(userId);
if(user.isPresent()){
}
return null;
}
}
Service:
#Service
public class UserServiceImpl implements UserService {
#Autowired
private UserDao userDao;
#Override
public Optional<User> findUserById(int id) {
List<User> users = userDao.getAllUsers();
return users.stream().filter(user -> user.id == id).findFirst();
}
}
This is the link where i found sample application
This is really my stupid mistake. In play-framework we always need to put the custom global configuration file in project app folder at root and play-framework always find to search Global file name at app folder root and load into the memory. In my case, my GlobalConfiguration file are not loaded in the memory and default configuration are used by play-framework. For Global-Settings click on this link for more information

Resources