Friday 27 September 2019

spring cache: Register custom key generator globally

In my previous post, I explained how to create a key generator to store the employee details into cache. In this post I am going to explain how to register a key generator globally that is used by all the caches.

By extending CachingConfigurerSupport class, we can register cache manager and KeyGenerator.

@Configuration
@EnableCaching
public class AppConfig extends CachingConfigurerSupport {
 @Bean
 @Override
 public CacheManager cacheManager() {
  List<ConcurrentMapCache> caches = Arrays.asList(new ConcurrentMapCache("myEmployeeCache"),
    new ConcurrentMapCache("myOrgCache"));
  SimpleCacheManager cacheManager = new SimpleCacheManager();
  cacheManager.setCaches(caches);
  return cacheManager;
 }

 @Override
 public KeyGenerator keyGenerator() {
  return new CacheKeyGenerator();
 }
}

Find the below working application.

Employee.java    
package com.sample.app.model;

public class Employee {
 private int id;
 private String firstName;
 private String lastName;

 public Employee(int id, String firstName, String lastName) {
  this.id = id;
  this.firstName = firstName;
  this.lastName = lastName;
 }

 public int getId() {
  return id;
 }

 public void setId(int id) {
  this.id = id;
 }

 public String getFirstName() {
  return firstName;
 }

 public void setFirstName(String firstName) {
  this.firstName = firstName;
 }

 public String getLastName() {
  return lastName;
 }

 public void setLastName(String lastName) {
  this.lastName = lastName;
 }

 @Override
 public String toString() {
  return "Employee [id=" + id + ", firstName=" + firstName + ", lastName=" + lastName + "]";
 }

}

Organization.java
package com.sample.app.model;

public class Organization {
 private int id;
 private String name;
 private String description;

 public Organization(int id, String name, String description) {
  super();
  this.id = id;
  this.name = name;
  this.description = description;
 }

 public int getId() {
  return id;
 }

 public void setId(int id) {
  this.id = id;
 }

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }

 public String getDescription() {
  return description;
 }

 public void setDescription(String description) {
  this.description = description;
 }

 @Override
 public String toString() {
  return "Organization [id=" + id + ", name=" + name + ", description=" + description + "]";
 }

}

EmployeeService.java
package com.sample.app.service;

import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import com.sample.app.model.Employee;
import java.util.*;

@Service
@CacheConfig(cacheNames = "myEmployeeCache")
public class EmployeeService {

 private static List<Employee> emps = new ArrayList<>();

 static {
  emps.add(new Employee(1, "Ram", "Kota"));
  emps.add(new Employee(2, "Raj", "Majety"));
  emps.add(new Employee(3, "PTR", "Navakotla"));
  emps.add(new Employee(4, "Krishna", "Boppana"));
 }

 @Cacheable
 public Employee getEmployeeById(int id) {
  System.out.println("getEmployeeById() is called");

  for (Employee emp : emps) {
   if (id == emp.getId()) {
    return emp;
   }
  }
  return null;
 }

 @Cacheable
 public Employee getEmployeeByFirstName(String name) {
  System.out.println("getEmployeeByFirstName() invoked");

  for (Employee emp : emps) {
   if (name.equals(emp.getFirstName())) {
    return emp;
   }
  }
  return null;
 }
}

OrganizationService.java
package com.sample.app.service;

import java.util.ArrayList;
import java.util.List;

import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import com.sample.app.model.Organization;

@Service
@CacheConfig(cacheNames = "myOrgCache")
public class OrganizationService {
 private static final List<Organization> orgs = new ArrayList<>();

 static {
  Organization org1 = new Organization(1, "ABC Corp", "Hello ABC Corp");
  Organization org2 = new Organization(2, "DEF Corp", "Hello DEF Corp");
  Organization org3 = new Organization(3, "GHI Corp", "Hello GHI Corp");
  Organization org4 = new Organization(4, "JKL Corp", "Hello JKL Corp");
  Organization org5 = new Organization(5, "MNO Corp", "Hello MNO Corp");

  orgs.add(org1);
  orgs.add(org2);
  orgs.add(org3);
  orgs.add(org4);
  orgs.add(org5);
 }

 @Cacheable
 public Organization getById(int id) {
  System.out.println("getById() called");
  for (Organization org : orgs) {
   if (id == org.getId()) {
    return org;
   }
  }

  return null;
 }

 @Cacheable
 public Organization getByName(String name) {
  System.out.println("getByName() called");
  for (Organization org : orgs) {
   if (org.getName().equals(name)) {
    return org;
   }
  }

  return null;
 }

}

CacheKeyGenerator.java
package com.sample.app.generator;

import java.lang.reflect.Method;

import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.stereotype.Component;

@Component("myCacheKeyGenerator")
public class CacheKeyGenerator implements KeyGenerator {

 @Override
 public Object generate(Object target, Method method, Object... params) {
  StringBuilder sb = new StringBuilder();

  if (target != null) {
   sb.append(target.getClass().getSimpleName()).append("-");
  }

  if (method != null) {
   sb.append(method.getName());
  }

  if (params != null) {
   for (Object param : params) {
    sb.append("-").append(param.getClass().getSimpleName()).append(":").append(param);
   }
  }
  return sb.toString();
 }

}

AppConfig.java
package com.sample.app.config;

import java.util.Arrays;
import java.util.List;

import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCache;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.sample.app.generator.CacheKeyGenerator;

@Configuration
@EnableCaching
public class AppConfig extends CachingConfigurerSupport {
 @Bean
 @Override
 public CacheManager cacheManager() {
  List<ConcurrentMapCache> caches = Arrays.asList(new ConcurrentMapCache("myEmployeeCache"),
    new ConcurrentMapCache("myOrgCache"));
  SimpleCacheManager cacheManager = new SimpleCacheManager();
  cacheManager.setCaches(caches);
  return cacheManager;
 }

 @Override
 public KeyGenerator keyGenerator() {
  return new CacheKeyGenerator();
 }
}

App.java
package com.sample.app;

import java.util.Collection;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;

import com.sample.app.model.Employee;
import com.sample.app.model.Organization;
import com.sample.app.service.EmployeeService;
import com.sample.app.service.OrganizationService;

@SpringBootApplication
public class App {

 @Autowired
 private EmployeeService empService;

 @Autowired
 private OrganizationService orgService;

 @Autowired
 private CacheManager cacheManager;

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

 public void getEmployeeAndOrgDetails() {

  Employee emp = empService.getEmployeeById(1);
  System.out.println(emp);
  emp = empService.getEmployeeByFirstName("Krishna");
  System.out.println(emp);

  Organization org = orgService.getById(3);
  System.out.println(org);
  org = orgService.getByName("GHI Corp");
  System.out.println(org);
 }

 @Bean
 public CommandLineRunner demo() {
  return (args) -> {
   printNativeCache();
   getEmployeeAndOrgDetails();

   printNativeCache();
   getEmployeeAndOrgDetails();
  };
 }

 public void printNativeCache() {
  System.out.println("\n**************************************");
  System.out.println("-- native cache --");
  Collection<String> cacheNames = cacheManager.getCacheNames();

  for (String cacheName : cacheNames) {
   Cache cache = cacheManager.getCache(cacheName);
   Map<String, Object> map = (Map<String, Object>) cache.getNativeCache();
   map.forEach((key, value) -> {
    System.out.println(key + " = " + value);
   });
  }

  System.out.println("**************************************\n");
 }
}

pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>com.sample.app</groupId>
 <artifactId>springCache</artifactId>
 <version>1</version>

 <parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.1.6.RELEASE</version>
 </parent>

 <properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 </properties>

 <dependencies>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter</artifactId>
  </dependency>

 </dependencies>
</project>

Total project structure looks like below.

Run App.java, you can see below messages in console.    
**************************************
-- native cache --
**************************************

getEmployeeById() is called
Employee [id=1, firstName=Ram, lastName=Kota]
getEmployeeByFirstName() invoked
Employee [id=4, firstName=Krishna, lastName=Boppana]
getById() called
Organization [id=3, name=GHI Corp, description=Hello GHI Corp]
getByName() called
Organization [id=3, name=GHI Corp, description=Hello GHI Corp]

**************************************
-- native cache --
EmployeeService-getEmployeeByFirstName-String:Krishna = Employee [id=4, firstName=Krishna, lastName=Boppana]
EmployeeService-getEmployeeById-Integer:1 = Employee [id=1, firstName=Ram, lastName=Kota]
OrganizationService-getById-Integer:3 = Organization [id=3, name=GHI Corp, description=Hello GHI Corp]
OrganizationService-getByName-String:GHI Corp = Organization [id=3, name=GHI Corp, description=Hello GHI Corp]
**************************************

Employee [id=1, firstName=Ram, lastName=Kota]
Employee [id=4, firstName=Krishna, lastName=Boppana]
Organization [id=3, name=GHI Corp, description=Hello GHI Corp]
Organization [id=3, name=GHI Corp, description=Hello GHI Corp]

You can download complete application from this link.
    
Previous                                                    Next                                                    Home

No comments:

Post a Comment