Friday, January 20, 2017

Spring Cache
You may set exprire time in CacheManager by using RedisCacheManager.setExpires or RedisCacheManager.setDefaultExpiration
All you (should) need is a "custom" RedisCacheManager extending the SDR CacheManager implementation,

        protected <T> T handleErrors(Exception e) throws Exception {
            if (e instanceof <some RedisConnection Exception type>) {
                // log the connection problem
                return null;
            else if (<something different>) { // act appropriately }
            else {
                throw e;
logger name="org.springframework.cache" level="trace"
Cache entry for key 'Page request [number: 0, size 20, sort: null]' found in cache 'persons'

No cache entry for key
Cache entry for key found in cache

@Cacheable(value="books", key="#isbn", unless = "#result == null")
public Book findBookFromCache(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
    return null;

  • Looks up in the cache based on key
  • if found return the result from cache
  • if not found evaluates method that return null
  • return value is matched with unless condition (which always return true)
  • null value do not get cached
  • null value is returned to caller
In contrast to the @Cacheable annotation, CachePut does not cause the advised method to be skipped. Rather, it always causes the method to be invoked and its result to be stored in the associated cache. Note that Java8's Optional return types are automatically handled and its content is stored in the cache if present.

keyGenerator Mutually exclusive with the key attribute.
@Cacheable(value = "employee", condition = "#age < 25")
 @Cacheable(value = "userCache", unless = "#result != null")
      put = {
            @CachePut(value = "userCache", key = "'username:' + #result.username", condition = "#result != null"),
            @CachePut(value = "userCache", key = "", condition = "#result != null")

@EnableCaching(mode = AdviceMode.ASPECTJ)
In science we don't believe and trust, we measure and benchmark. 
first of all, check out the core implementation in CacheAspectSupport - it's actually quite complex. Secondly, is it really that slow? Do the math - you typically use Spring in business applications where database, network and external APIs are the bottleneck. What latencies do you typically see? Milliseconds? Tens or hundreds of milliseconds? Now add an overhead of 2 μs (worst case scenario). For caching database queries or REST calls this is completely negligible. It doesn't matter which technique you choose.

But if you are caching very low-level methods close to the metal, like CPU-intensive, in-memory computations, Spring abstraction layer might be an overkill. The bottom line: measure!
Actually Redis is a key/value store and if you use the same key for the same store you just override the old value with new one. And it doesn't matter how much RedisTemplates (or even connectionFactorys) you have, if the real Redis server is the same.
Now how to help you with your task:
You should have different kyes for different domain objects: e.g. studentsaddresses. Since you are going to store domain objects with their own keys it looks like Map value is for you. I mean under key students a map of Students should be stored ,and the same for Addresses.
which may have same ids (starting with 1 for each table). I read some where it can be achieved by using keys like, 'student:{id}', 'address:{id}', etc. I am still searching for the solution
The easiest workaround is to provide the name of the method as the key:
@Cacheable(value="usercache", key = "#root.methodName")
public string sayHello(){
return "test"
This would set sayHello as the key.
If you really need a static key, you should define a static variable in the class, and use
public static final String MY_KEY = "mykey";

@Cacheable(value="usercache", key = "")
public string sayHello(){
return "test"
You can find here the list of SPEL expressions that you can use in your key.

methodNameroot objectThe name of the method being invoked#root.methodName
methodroot objectThe method being
targetroot objectThe target object being
targetClassroot objectThe class of the target being invoked#root.targetClass
argsroot objectThe arguments (as array) used for invoking the target#root.args[0]
cachesroot objectCollection of caches against which the current method is executed#root.caches[0].name
argument nameevaluation contextName of any of the method arguments. If for some reason the names are not available (e.g. no debug information), the argument names are also available under the #a<#arg> where #arg stands for the argument index (starting from 0).#iban or #a0 (one can also use #p0 or #p<#arg> notation as an alias).
resultevaluation contextThe result of the method call (the value to be cached). Only available in unless expressions, cache put expressions (to compute the key), or cache evict expressions (when beforeInvocation is false). For supported wrappers such as Optional#result refers to the actual object, not the wrapper.#result

@Cacheable(value="bookCache", key="isbn + '_' + checkWarehouse + '_' + includeUsed")

java.lang.IllegalArgumentException: Null key returned for cache operation (maybe you are using named params on classes without debug info?)

- Not use right param name in @CacheXXX annotation
- Not param names are available - javac compiler
  • getting parameter names is possible if debug information is included during compilation. See this answer for more details
  • otherwise getting parameter names is not possible
  • getting parameter type is possible, using method.getParameterTypes()
For the sake of writing autocomplete functionality for an editor (as you stated in one of the comments) there are a few options:
  • use arg0arg1arg2 etc.
  • use intParamstringParamobjectTypeParam, etc.
  • use a combination of the above - the former for non-primitive types, and the latter for primitive types.
  • don't show argument names at all - just the types.
Parameter names are available if you have told the compiler to include them (compile with debug information). Spring has ParameterNameDiscoverer which can help you obtain the names. The default implementation uses asm ClassReader to do so.
With javac you should include the -g argument to include debug information. With eclipse I think it is there by default.
Some frameworks use this. For example spring-mvc has @RequestParam which defaults to the param name, if resolvable. It also supports explicit naming - @RequestParam("foo") in case no debug information is provided.
private Cache myCache;
See also examples how to use Spring EL inside the @Value() if you are interested.
<bean id="cache" class="net.sf.ehcache.Cache" factory-bean="cacheManager" factory-method="getCache">
    <constructor-arg value="CacheNameHere" />          
And my java class
private net.sf.ehcache.Cache cache;
All you need to do is to extend CachingConfigurerSupport and override errorHandler(). Now, whenever you are performing a GET operation ,like our findById(Integer id) , and the same error occurs,then Spring would bypass the Cache layer and execute the method.

@Cacheable maps to handleCacheGetError(RuntimeException ex, Cache cache, Object key) .
@CacheEvict maps to handleCacheEvictError(RuntimeException ex, Cache cache, Object key) .
@CachePut maps to handleCachePutError(RuntimeException ex, Cache cache, Object arg1, Object arg2)
You can use a similar strategy with other Cache backends that Spring provides.
As from Spring Framework 4.1, there is a CacheErrorHandler that you can implement to handle such exceptions. Refer to the javadoc for more details.
The above code presumes that Redis was completely down, and unpersisted, and therefore would be empty when it becomes available. In the event that Redis was only temporarily unreachable, or if Redis went down but the caches were persisted, updates to the database would still have gone through and the cache entries could be stale. Perhaps it should "remember" cache keys received while Redis was unavailable, and evict them when Redis becomes available
There is a nasty trick you can use to override this called inject yourself:
public class YourClass {
    private YourClass instance;

    public String method1() {
          // now you go through the cache again
          return instance.method2();

    public String method2() {
          return "2";
Here is what I do for small projects with only marginal usage of method calls within the same class. In-code documentation is strongly advidsed, as it may look strage to colleagues. But its easy to test, simple, quick to achieve and spares me the full blown AspectJ instrumentation. However, for more heavy usage I'd advice the AspectJ solution.
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
class AService {

    private final AService _aService;

    public AService(AService aService) {
        _aService = aService;

    public List<EmployeeData> getEmployeeData(Date date){
        ..println("Cache is not being used");

    public List<EmployeeEnrichedData> getEmployeeEnrichedData(Date date){
        List<EmployeeData> employeeData = _aService.getEmployeeData(date);

public String findValue(String name) {
    Setting setting = settingRepository.findOne(name);
    if(setting == null){
        return null;
    return setting.getValue();

public Boolean findBoolean(String name) {
    String value = getSpringProxy().findValue(name);
    if (value == null) {
        return null;
    return Boolean.valueOf(value);

 * Use proxy to hit cache 
private SettingService getSpringProxy() {
    return applicationContext.getBean(SettingService.class);

Another simple and good solution: move @Cacheable annotation to lower (DAO) level. Problem remain at inside DAO class, but solved for Service.

Atomic counters
Operations that require that the key and the value are given every time an operation is performed. These operations are handy when we have to execute a single operation by using a key and a value.
Operations that are bound to a specific key that is given only once. We should use this approach when we have to perform multiple operations by using the same key.
public RedisAtomicLong redisAtomicLong() {
  return new RedisAtomicLong("contact", redisConnectionFactory());
This will add @Class property to the JSON to understand the type, which helps Jackson to deserialize, so no need to explicitly map the model on the configuration class.
  public RedisCacheManager cacheManager() {
    RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate());
    return redisCacheManager;

GenericToStringSerializer:使用Spring转换服务进行序列化。在网上没有找到什么例子,使用方法和StringRedisSerializer相比,StringRedisSerializer只能直接对String类型的数据进行操作,如果要被序列化的数据不是String类型的,需要转换成String类型,例如:String.valueOf()。但使用GenericToStringSerializer的话,不需要进行转换,直接由String帮我们进行转换。但这样的话,也就定死了序列化前和序列化后的数据类型,例如:template.setValueSerializer(new GenericToStringSerializer<Long>(Long.class));
BoundListOperations<String, Object>cart=redisTemplate.boundListOps("cart");

对于使用 @Cacheable 注解的方法,每个缓存的 key 生成策略默认使用的是参数名+参数值,比如以下方法:
@Cacheable(value="commonCache")// 使用了一个缓存名叫 accountCache
public Account getAccountByName(String userName) {
// 方法内部实现不考虑缓存逻辑,直接实现业务
System.out.println("real query account."+userName);
return getFromDB(userName);
username取值为zhenghuasheng时,key为userName-zhenghuasheng,一般情况下没啥问题,二般情况如方法 key 取值相等然后参数名也一样的时候就出问题了,如:
public LoginData getLoginData(String userName){

这个方法的缓存也将保存于 key 为 users~keys 的缓存下。对于 userName 取值为 “zheghuasheng” 的缓存,key 也为 “userName-zhenghuasheng”,将另外一个方法的缓存覆盖掉。
解决办法是使用自定义缓存策略,对于同一业务(同一业务逻辑处理的方法,哪怕是集群/分布式系统),生成的 key 始终一致,对于不同业务则不一致
@Cacheable(value="users", key="#userid.toString() + #username.toString()")  
public User findByUsername(String username, String userid)  

@Cacheable(value="books", key="#isbn.rawNumber")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

@Cacheable(value="users", key="#p0")
public User find(Integer id) {

@Cacheable(value="users", key="")
public User find(User user) {
Redis 优势?
 public Object get(final String key) {
  Object result = null;
  ValueOperations<Serializable, Object> operations = redisTemplate
  result = operations.get(key);
  return result;

  * 写入缓存
  * @param key
  * @param value
  * @return
 public boolean set(final String key, Object value) {
  boolean result = false;
  try {
   ValueOperations<Serializable, Object> operations = redisTemplate
   operations.set(key, value);
   result = true;
  } catch (Exception e) {
  return result;
  项目中使用spring Redis cache做为cache客户端。
  spring redis cache中RedisCache是整个spring cache的领域模型,对应一个cache块的操作类。
 put与get方法运行时会查看是否存在lock锁,存在则会sleep 300毫秒。这个过程会一直继续,直到redis服务器上不存在锁时才会进行相应的get与put操作。

  1.     public RedisTemplate<String, String> redisTemplate(  
  2.             RedisConnectionFactory factory) {  
  3.         StringRedisTemplate template = new StringRedisTemplate(factory);  
  4.         Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);  
  5.         ObjectMapper om = new ObjectMapper();  
  6.         om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);  
  7.         om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);  
  8.         jackson2JsonRedisSerializer.setObjectMapper(om);  
  9.         template.setValueSerializer(jackson2JsonRedisSerializer);  
  10.         template.afterPropertiesSet();  
  11.         return template;  
  12.     }  
  1. 要缓存的 Java 对象必须实现 Serializable 接口,因为 Spring 会将对象先序列化再存入 Redis,比如本文中的 类,如果不实现 Serializable 的话将会遇到类似这种错误:nested exception is java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type []]。
  2. 缓存的生命周期我们可以配置,然后托管 Spring CacheManager,不要试图通过 redis-cli 命令行去管理缓存。比如 provinceCities 方法的缓存,某个省份的查询结果会被以 key-value 的形式存放在 Redis,key 就是我们刚才自定义生成的 key,value 是序列化后的对象,这个 key 会被放在 key 名为 provinceCities~keys key-value 存储中,参考下图"provinceCities 方法在 Redis 中的缓存情况"。可以通过 redis-cli 使用 del 命令将 provinceCities~keys 删除,但每个省份的缓存却不会被清除。
  3. CacheManager 必须设置缓存过期时间,否则缓存对象将永不过期,这样做的原因如上,避免一些野数据“永久保存”。此外,设置缓存过期时间也有助于资源利用最大化,因为缓存里保留的永远是热点数据。
  4. 缓存适用于读多写少的场合,查询时缓存命中率很低、写操作很频繁等场景不适宜用缓存。

No comments:

Post a Comment


Review (554) System Design (293) System Design - Review (189) Java (178) Coding (75) Interview-System Design (65) Interview (60) Book Notes (59) Coding - Review (59) to-do (45) Knowledge (39) Linux (39) Interview-Java (35) Knowledge - Review (32) Database (30) Design Patterns (29) Product Architecture (28) Big Data (27) Soft Skills (27) Miscs (25) MultiThread (25) Concurrency (24) Cracking Code Interview (24) Career (22) Interview - Review (21) Java - Code (21) Operating System (21) Distributed (20) Interview Q&A (20) OOD Design (20) System Design - Practice (19) Security (17) Algorithm (15) How to Ace Interview (15) Brain Teaser (14) Google (13) Linux - Shell (13) Spark (13) Spring (13) Code Quality (12) How to (12) Interview-Database (12) Interview-Operating System (12) Redis (12) Tools (12) Architecture Principles (11) Company - LinkedIn (11) Testing (11) Resource (10) Solr (10) Amazon (9) Cache (9) Search (9) Web Dev (9) Architecture Model (8) Better Programmer (8) Company - Uber (8) Interview - MultiThread (8) Java67 (8) Math (8) OO Design principles (8) SOLID (8) Scalability (8) Cassandra (7) Git (7) Interview Corner (7) JVM (7) Java Basics (7) Machine Learning (7) NoSQL (7) C++ (6) Design (6) File System (6) Highscalability (6) How to Better (6) Kafka (6) Network (6) Restful (6) Trouble Shooting (6) CareerCup (5) Code Review (5) Company - Facebook (5) Hash (5) How to Interview (5) JDK Source Code (5) JavaScript (5) Leetcode (5) Must Known (5) Be Architect (4) Big Fata (4) C (4) Company Product Architecture (4) Data structures (4) Design Principles (4) Facebook (4) GeeksforGeeks (4) Generics (4) Google Interview (4) Hardware (4) JDK8 (4) Optimization (4) Product + Framework (4) Shopping System (4) Source Code (4) Web Service (4) node.js (4) Back-of-Envelope (3) Company - Pinterest (3) Company - Twiiter (3) Company - Twitter (3) Consistent Hash (3) GOF (3) Game Design (3) GeoHash (3) Growth (3) Guava (3) Interview-Big Data (3) Interview-Linux (3) Interview-Network (3) Java EE Patterns (3) Javarevisited (3) Map Reduce (3) Math - Probabilities (3) Performance (3) Puzzles (3) Python (3) Resource-System Desgin (3) Scala (3) UML (3) geeksquiz (3) AI (2) API Design (2) AngularJS (2) Behavior Question (2) Bugs (2) Coding Interview (2) Company - Netflix (2) Crawler (2) Cross Data Center (2) Data Structure Design (2) Database-Shard (2) Debugging (2) Docker (2) Elasticsearch (2) Garbage Collection (2) Go (2) Hadoop (2) Html (2) Interview - Soft Skills (2) Interview-Miscs (2) Interview-Web (2) JDK (2) Logging (2) POI (2) Papers (2) Programming (2) Project Practice (2) Random (2) Software Desgin (2) System Design - Feed (2) Thread Synchronization (2) Video (2) ZooKeeper (2) reddit (2) Ads (1) Advanced data structures (1) Algorithm - Review (1) Android (1) Approximate Algorithms (1) Base X (1) Bash (1) Books (1) C# (1) CSS (1) Chrome (1) Client-Side (1) Cloud (1) CodingHorror (1) Company - Yelp (1) Counter (1) DSL (1) Dead Lock (1) Difficult Puzzles (1) Distributed ALgorithm (1) Eclipse (1) Facebook Interview (1) Function Design (1) Functional (1) GoLang (1) How to Solve Problems (1) ID Generation (1) IO (1) Important (1) Internals (1) Interview - Dropbox (1) Interview - Project Experience (1) Interview Tips (1) Interview-Brain Teaser (1) Interview-How (1) Interview-Mics (1) Interview-Process (1) Jeff Dean (1) Joda (1) LeetCode - Review (1) Library (1) LinkedIn (1) LintCode (1) Mac (1) Micro-Services (1) Mini System (1) MySQL (1) Nigix (1) NonBlock (1) Process (1) Productivity (1) Program Output (1) Programcreek (1) Quora (1) RPC (1) Raft (1) RateLimiter (1) Reactive (1) Reading (1) Reading Code (1) Refactoring (1) Resource-Java (1) Resource-System Design (1) Resume (1) SQL (1) Sampling (1) Shuffle (1) Slide Window (1) Spotify (1) Stability (1) Storm (1) Summary (1) System Design - TODO (1) Tic Tac Toe (1) Time Management (1) Web Tools (1) algolist (1) corejavainterviewquestions (1) martin fowler (1) mitbbs (1)

Popular Posts