1

This is my ProductServiceImpl class.

public class ProductServiceImpl implements ProductService {

@Autowired
 GalaxyService gs ;

@PostConstruct
private void init() {
   int hash = Objects.hash("Galaxy");
    gs.updateByName(hash);

}

@Override
@Cacheable(value = "products", key = "T(java.util.Objects).hash(#p0)")
public String getByName(String name) {
    System.out.println("Inside method");
    slowLookupOperation();
    return name + " : " + name;
}

@CacheEvict(value = "products", allEntries = true)
public void refreshAllProducts() {
    //This method will remove all 'products' from cache, say as a result of flush API.
}

public void slowLookupOperation() {
    try {
        long time = 5000L;
        Thread.sleep(time);
    } catch (InterruptedException e) {
        throw new IllegalStateException(e);
    }
}}

Here is my GalaxyServiceImpl:

public class GalaxyServiceImpl implements GalaxyService {

@Override
@CachePut(value = "products", key = "#key")
public String updateByName(Integer key) {
    return "Oh My Galaxy- " + key;
}}

From init() method of ProductServiceImpl, I was updating a cache element. Looks like it is Spring cache is not caching that method.

But, I do it from my Main class, it was caching the method. Main class in below:

    public mainconstructor() {

        AbstractApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        ProductService service = (ProductService) context.getBean("productService");

        GalaxyService gs = (GalaxyService) context.getBean("galaxy");


     int hash = Objects.hash("Galaxy");
     gs.updateByName(hash);

        System.out.println("Galaxy S8 ->" + service.getByName("Galaxy"));

        ((AbstractApplicationContext) context).close();
    }

My application configuration class in below:

@EnableCaching
@Configuration
@ComponentScan(basePackages = {"com.websystique.spring", "com.samsung.gs8"})
public class AppConfig {

@Bean
public CacheManager cacheManager() {
    return new EhCacheCacheManager(ehCacheCacheManager().getObject());
}

@Bean
public EhCacheManagerFactoryBean ehCacheCacheManager() {
    EhCacheManagerFactoryBean factory = new EhCacheManagerFactoryBean();
    factory.setConfigLocation(new ClassPathResource("ehcache.xml"));
    factory.setShared(true);
    factory.setAcceptExisting(false);
    return factory;
}

@Bean
public GalaxyService galaxy() {
    GalaxyService gs = new GalaxyServiceImpl();
    return gs;
}}

Completes code available at- https://github.com/pijushcse/SpringCache

My question is what is the wrong from ProductServiceImpl, if I want to update a cache item? Why it works from Main why not from other class? TIA

pijushcse
  • 510
  • 9
  • 31
  • What other class isn't working? Please add more code. – Andrew S Jan 30 '18 at 18:56
  • I mean, in the Main class, When I wrote- call GalaxyService method which has a @CachePut annotation, it was caching the return value. But, If I do the same thing from ProductServiceImple's init() method, it was NOT caching. – pijushcse Jan 30 '18 at 18:59
  • `ProductServiceImpl.getByName(...)` is invoked only in `mainconstructor()`, which I'm assuming is in a `Main` class. _Why it works from Main why not from other class?_ What is the other class where it doesn't work? – Andrew S Jan 30 '18 at 19:11
  • The other class is, ProductServiceImpl . If you carefully look into its init() method, i was calling the GalaxyService method to update a cache. – pijushcse Jan 30 '18 at 19:47
  • I have pushed my code- https://github.com/pijushcse/SpringCache – pijushcse Jan 30 '18 at 20:12
  • The CacheXXXX annotations behave like AOP, which have some limitations on when they are invoked. When you call gs.updateByName(hash) from init(), the CachePut AOP is not triggered. Does this help: https://stackoverflow.com/questions/48408121/spring-caching-works-only-sometimes/48409165#48409165 – Ian Mc Jan 30 '18 at 20:12
  • Thanks, Ian Mc, I understood what you trying to say. In the link, they have mentioned three different approaches and I think my current code is following approach#1. I also tried aproach#3 which also didn't work – pijushcse Jan 30 '18 at 20:20
  • Maybe `T(java.util.Objects).hash(#p0)"` is not an `Integer`? – Andrew S Jan 30 '18 at 20:40
  • Well, Objects.hash(...) method always return integer. – pijushcse Jan 30 '18 at 20:46
  • Since you are confident that your approach should be working, I am suspicious of the fact your cache initialization is done in PostConstruct (that seems to be the only difference). Would it be worth a try to move the cache initialization timing to when the context is refreshed (the EventListener annotation)? – Ian Mc Jan 30 '18 at 20:51

2 Answers2

3

The quick answer is: It's normal. You should not initialize a cache in a @PostConstruct.

The long answer now. The CacheInterceptor is special. It gets created with initialized = false. Then, in a special callback SmartInitializingSingleton it goes in initialized = true. This callback is called after all beans have been initialized. Before that, it's not caching anything.

Bottom line, you can't pre-cache something in @PostConstruct.

The best solution is to indeed trigger caching by yourself. And it's probably cleaner anyway and it gives more flexibility. So the getBean makes sense.

Henri
  • 5,551
  • 1
  • 22
  • 29
0

I assume your product service isn't caching but galaxy is. You can't call the slowOperation and have it cached within the same class. caching impls. should wrap what needs cached. Extract a cache service that wraps the slow operating calls.

Darren Forsythe
  • 10,712
  • 4
  • 43
  • 54