1

I'm using Spring and I would like to cache some data before starting my app.

I found some solutions in other posts to use @PostConstruct to call my @Service method (which are annotated @Cacheable), eg. How to load @Cache on startup in spring? I did that but when after application start I call REST endpoint which calls this service method again it's sending database request another time (so it's not cached yet). When I send request to endpoint for the second time data is cached.

Conclusion is that calling Service method on @PostConstruct didn't cause caching data from database.

Is it possible to cache data before starting app? How can I do that? Below is my code fragments.

@RestController
class MyController {

    private static final Logger logger = LoggerFactory.getLogger(MyController.class);

    @Autowired
    MyService service;

    @PostConstruct
    void init() {
        logger.debug("MyController @PostConstruct started");
        MyObject o = service.myMethod("someString");
        logger.debug("@PostConstruct: " + o);
    }

    @GetMapping(value = "api/{param}")
    MyObject myEndpoint(@PathVariable String param) {
        return service.myMethod(param);
    }

}


@Service
@CacheConfig(cacheNames = "myCache")
class MyServiceImpl implements MyService {

    @Autowired
    MyDAO dao;

    @Cacheable(key = "{ #param }")
    @Override
    public MyObject myMethod(String param) {
        return dao.findByParam(param);
    }
}


interface MyService {
    MyObject myMethod(String param);
}


@Repository
interface MyDAO extends JpaRepository<MyObject, Long> {
    MyObject findByParam(String param);
}


@SpringBootApplication
@EnableConfigurationProperties
@EnableCaching
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }

    @Primary
    @Bean
    public CacheManager jdkCacheManager() {
        return new ConcurrentMapCacheManager("myCache");
    }
}
Community
  • 1
  • 1
user3626048
  • 706
  • 4
  • 19
  • 52
  • 1
    Trying to do this in an `@PostConstruct` is generally a bad idea it might run to early (no proxies have been created and thus no AOP has been applied). It is better to do things like this in an `ApplicationListener` that listens for `ContextRefreshedEvent`s. That is the trigger telling you everything has been started and you can now invoke the caching method. – M. Deinum Jan 17 '17 at 14:52
  • @M.Deinum ok but I need to initiate cache before REST endpoints are started and listening for requests. How can I do that? – user3626048 Jan 17 '17 at 15:34
  • 1
    As soon as the dispatcherservlet is up it is listening... As stated use an `ApplicationListener` to have safe way. – M. Deinum Jan 17 '17 at 16:15
  • @M.Deinum thanks, that solved my problem! – user3626048 Jan 18 '17 at 17:36

2 Answers2

0

Try using ApplicationStartedEvent instead of @PostConstruct annotation.

I had the same problem and this small change fixed it.

You should add @EventListener(classes = ApplicationStartedEvent.class) on top of your method and pass ApplicationStartedEvent event as a parameter.

Example:

@EventListener(classes = ApplicationStartedEvent.class)
void init(ApplicationStartedEvent event) {
    MyObject o = service.myMethod("someString");
}
-1

@PostConstruct will work in your case. the method with @PostConstruct annotation will be called just after the method bean was instantiated.

But if you depend on other beans and you went your method to be called after the application context has fully started? you can create a new bean like that :

@Component
public class MyListener 
        implements ApplicationListener<ContextRefreshedEvent> {

    public void onApplicationEvent(ContextRefreshedEvent event) {
        //Here call your method of cache
        // Your methode will be called after the application context has fully started
    }
}