Search code examples
springspring-mvcspring-cache

Spring Cache in MVC - Possible to lookup with autowiring?


I see that when the application starts up my singleton cache is created

DEBUG Creating CGLIB proxy: target source is SingletonTargetSource for target object [com.abc.xyz.util.CacheUtil@14e3dd3] DEBUG Unable to apply any optimizations to advised method: public java.util.Map

But how do I lookup the value using autowiring as when I attempt, it does not hit the singleton created and creates a new instance of CacheUtil.

CacheUtil.java [This class is annotated with @Component]

    public Map getSelectOptions(String codeType) {
        System.out.println("Cache Breached!!!");
        HashMap selectOpts = new HashMap();
        Vector<MyTableDO> vCodeMap = null;
        vCodeMap = MyTableDO.getCodesFromDatabase(codeType, "LookupCacheUtil"); 


        if(vCodeMap == null || vCodeMap.size() == 0) return selectOpts;

        vCodeMap.forEach(codeMap -> selectOpts.put(codeMap.getCodeValue(), codeMap.getCodeDesc()));

        return selectOpts;
    }

My spring config xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <context:component-scan base-package="com.abc.xyz" />
    <context:annotation-config />
    <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>

   <bean id="cacheUtil" class="com.abc.xyz.util.CacheUtil" />
</beans>

Class Invoking the Cached method

  @Autowired
  @Qualifier("cacheUtil")
  protected CacheUtil cacheUtil;

  public Map getSelectOptions(String codeType) {

      AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MyApplication.class); 
      //ctx.refresh();
      CacheUtil lkp = (CacheUtil) ctx.getBean(CacheUtil.class); 
      ctx.close();   

      System.out.println("App Context lookupCacheUtil -"+lkp); // Not the same object of Spring Cache and comes to be new instance on every call
      System.out.println("Autowired lookupCacheUtil -"+cacheUtil); // Always comes to be NULL

      return lkp.getSelectOptions(codeType);  
  }

}

MyApplication class

@SpringBootApplication
@EnableCaching
public class MyApplication extends SpringBootServletInitializer{


    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(MyApplication.class);
    }

    @Override
    public void onStartup(ServletContext container) {
        XmlWebApplicationContext context = new XmlWebApplicationContext();
        context.setConfigLocation("/WEB-INF/config/spring-servlet.xml");

        //using servlet 3 api to dynamically create spring dispatcher servlet
        ServletRegistration.Dynamic dispatcher = container.addServlet("spring", new DispatcherServlet(context));
        dispatcher.setLoadOnStartup(2);
        dispatcher.addMapping("/");
    }

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

}

Solution

  • On detailed analysis, my understanding of Autowired become more refined. Thanks to this link.

    In my case, I had autowired 'CacheUtil' on a form bean. It appears that the form beans are not being managed by spring or at least in this case. The same autowired works normally in a controller which is managed by Spring.

    So I to work around by fetching the Spring Cache 'Proxy' version of CacheUtil from the Application Context. Below code snippet should help (method getInstance()):

    import org.springframework.beans.BeansException;
    import org.springframework.cache.annotation.Cacheable;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.stereotype.Component;
    
    @Component("MyCache")
    public class CacheUtil implements ApplicationContextAware{
    
        private static ApplicationContext appContext;
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            // TODO Auto-generated method stub
            appContext = applicationContext;
        }
        /**
         * Method to fetch the shared instance of the Spring Cache Object that helps reach the 
         * Cache that resides in Application Context.
         * 
         * @return Singleton shared instance of the Spring Cache Proxy of this class CacheUtil 
         */
        public static CacheUtil getInstance() {
            CacheUtil appCache = appContext.getBean("MyCache", CacheUtil.class);        
            if(appCache != null) return appCache;
    
            return new CacheUtil();
        }