2

I am searching for a way to read and parse a lot of data when the spring boot app is starting and be able to use these data later in other classes.

I started with a class DataRepository.java and annotated it with @Service to be able to inject it later. I'm planning to read the data here and to inject it in any other class I need the data.

But how can I achieve to parse the data just once and at app startup? The spring boot app should only be reachable if the parsing is done.

user2170547
  • 361
  • 1
  • 3
  • 15
  • Possible duplicate of [Running code after Spring Boot starts](https://stackoverflow.com/questions/27405713/running-code-after-spring-boot-starts) – jreznot Aug 10 '18 at 11:03

4 Answers4

2

You can use ContextStartedEvent and handle it:

@Component
public class ContextStartedListener implements ApplicationListener<ContextStartedEvent> {
    @Override
    public void onApplicationEvent(ContextStartedEvent cse) {
        System.out.println("Handling context start event. ");
    }
}

See also: https://www.baeldung.com/spring-events

jreznot
  • 2,694
  • 2
  • 35
  • 53
2

Your approach with @Service is 100% appropriate.

By default all beans are singletons, so if you parse data on bean creation (in constructor) it will be parsed only once, and this info can be used in other beans by simple injection.

Please note that if during data parsing you have to use other beans, you should be confident that all beans are completely constructed. For that you should use approach proposed by @jreznot: https://stackoverflow.com/a/51783858/5289288

Max Farsikov
  • 2,451
  • 19
  • 25
  • 1
    Ahh ok, I just generated a random value as test data and injected my service in different controllers and the value is always the same so I'm more than convinced. Thank you! – user2170547 Aug 10 '18 at 10:44
  • This is not answer for > But how can I achieve to parse the data just once and at app startup? – jreznot Aug 10 '18 at 10:49
  • @jreznot since bean is singleton, it's constructor is called only once, so all the code is ran only once. Is that clearer? – Max Farsikov Aug 10 '18 at 10:53
1

By default all beans in spring context are singletons. Spring guarantees that it will creates a bean just ones during context loading. For example if you will have few contexts in your application it creates one instance for every context.

If you have just one context you can use these approaches:

  • initialize data in constructor. Data will initialized and ready to use just after bean's instance creation.

    @Component
    public class DataRepository {
    
       public DataRepository() {
           ... init data
       }
    }
    
  • use @Bean annotation withinit method. Allows you don't stick to Spring in your data repository and initialize data after all beans were created.

     public class DataRepository {
    
          public void init() {
                ... init data
          }
     }
    
    @Configuration
    public class DataRepositoryConfiguration {
    
         @Bean(initMethod = "init")
         public DataRepository dataRepository() {
            return new DataRepository();
         }
    
  • use @Bean annotation and invoke init method. Allows you don't stick to Spring in your data repository, but @Autowired field will uninitialized.

    public class DataRepository {
    
          public void init() {
                ... init data
          }
     }
    
    @Configuration
    public class DataRepositoryConfiguration {
         @Bean
         public DataRepository dataRepository() { 
            DataRepository dr = new new DataRepository();
            dr.init();
            return dr;
         }
    
    }
    
  • use @PostConstruct annotation. Initialize data after all beans was created.

    public class DataRepository {
    
       @PostConstruct
       public void init() {
          ... init data
       }
    }
    

Exception thrown during initializing will stop Spring's context initializing

Vlad Bochenin
  • 3,007
  • 1
  • 20
  • 33
  • 1
    You cannot use beans from `@PostConstruct`. There is no guarantee that dependencies of the class are initialized. – jreznot Aug 10 '18 at 10:50
  • @jreznot Agree, but it fair for `@Autowired` fields, but not for `@Autowire` constructor. Original question was about initializing data just ones. – Vlad Bochenin Aug 10 '18 at 10:54
0

You can use PostConstruct on any bean. For example

@Component
class DataLoad {

 ......
 ......


  @PostConstruct
  public void parseData() {

    ...... do your stuff here.......
  }
}

With this the code inside parseData will be called only once. This is a very common way to do things in scenarios like when you want to load some configuration data from database at the start of the application and do it only once. In these cases you can @Autowired the repository class to the same class and use that in your @PostConstruct method and get data

pvpkiran
  • 25,582
  • 8
  • 87
  • 134
  • You cannot use beans from `@PostConstruct`. There is no guarantee that dependencies of the class are initialized. – jreznot Aug 10 '18 at 10:48
  • 1
    when you add that as a dependency in the class (using `@Autowired`), spring will intialize that first. – pvpkiran Aug 10 '18 at 10:55
  • There is no order of execution defined for beans both having `@PostConstruct` – jreznot Aug 10 '18 at 10:56