背景
在平時項目中,可能會有某個條件的查詢,會多次進到db里面去查,這樣就會重復的查詢相同的數據,但是我們的數據又不是需要更改及顯示的,這時候就可以用到
方法的緩存了。例如在我們調用微信小程序時,需要獲取access_token,並且其有效時間為7200秒,過期后再次獲取,我們就可以把獲取access_token的方法作為
緩存。以下為我實現的過程記錄。
1、重寫 RedisSerializer 中的 serialize 和 deserialize

1 public class GenericFastJson2JsonRedisSerializer<T> implements RedisSerializer<T> { 2 public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); 3 public GenericFastJson2JsonRedisSerializer() { 4 super(); 5 } 6 @Override 7 public byte[] serialize(T t) throws SerializationException { 8 if (t == null) { 9 return new byte[0]; 10 } 11 FastJsonWraper<T> wraperSet =new FastJsonWraper<>(t); 12 return JSON.toJSONString(wraperSet, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET); 13 } 14 @Override 15 public T deserialize(byte[] bytes) throws SerializationException { 16 if (bytes == null || bytes.length <= 0) { 17 return null; 18 } 19 String deserializeStr = new String(bytes, DEFAULT_CHARSET); 20 FastJsonWraper<T> wraperGet=JSON.parseObject(deserializeStr,FastJsonWraper.class); 21 return wraperGet.getValue(); 22 } 23 }
2、實現緩存的獲取和設置

1 public class RedisCache implements Cache { 2 3 private Logger logger = Logger.getLogger(RedisCache.class); 4 private RedisTemplate<String, Object> redisTemplate; 5 6 private String name; 7 8 private String resultType; 9 10 private long expireInSencods; 11 12 public static final String KEY_PREFIX = "wx:"; 13 14 15 @Override 16 public Object getNativeCache() { 17 return this.redisTemplate; 18 } 19 20 21 @Override 22 public ValueWrapper get(Object key) { 23 if (logger.isDebugEnabled()) { 24 logger.debug("------緩存獲取-------" + key.toString()); 25 } 26 final String keyf = KEY_PREFIX + key.toString(); 27 Object object = null; 28 object = redisTemplate.execute(new RedisCallback<Object>() { 29 @Override 30 public Object doInRedis(RedisConnection connection) throws DataAccessException { 31 byte[] key = keyf.getBytes(); 32 byte[] value = connection.get(key); 33 if (value == null) { 34 if (logger.isDebugEnabled()) { 35 logger.debug("------緩存不存在-------"); 36 } 37 return null; 38 } 39 try { 40 Class<?> class1 = null; 41 if (StringUtils.isNotEmpty(resultType)) { 42 class1 = Class.forName(resultType); 43 } else { 44 class1 = String.class; 45 } 46 String resultJson = new String(value, Charset.forName("utf-8")); 47 Object result = JSONObject.parseObject(resultJson, class1); 48 return result; 49 } catch (ClassNotFoundException e) { 50 e.printStackTrace(); 51 } 52 return null; 53 } 54 }); 55 ValueWrapper obj = (object != null ? new SimpleValueWrapper(object) : null); 56 if (logger.isDebugEnabled()) { 57 logger.debug("------獲取到內容-------" + obj); 58 } 59 return obj; 60 } 61 62 63 @Override 64 public <T> T get(Object key, Class<T> type) { 65 // TODO Auto-generated method stub 66 return null; 67 } 68 69 70 @Override 71 public <T> T get(Object key, Callable<T> valueLoader) { 72 // TODO Auto-generated method stub 73 return null; 74 } 75 76 77 @Override 78 public void put(Object key, Object value) { 79 if (logger.isDebugEnabled()) { 80 logger.debug("-------加入緩存------"); 81 logger.debug("key----:" + key); 82 logger.debug("key----:" + value); 83 } 84 final String keyString = KEY_PREFIX + key.toString(); 85 final Object valuef = value; 86 redisTemplate.execute(new RedisCallback<Long>() { 87 @Override 88 public Long doInRedis(RedisConnection connection) throws DataAccessException { 89 byte[] keyb = keyString.getBytes(); 90 String valuejson = JSONObject.toJSONString(valuef); 91 byte[] valueb = valuejson.getBytes(Charset.forName("utf-8")); 92 connection.set(keyb, valueb); 93 if (expireInSencods > 0) { 94 connection.expire(keyb, expireInSencods); 95 } 96 return 1L; 97 } 98 }); 99 } 100 101 102 @Override 103 public ValueWrapper putIfAbsent(Object key, Object value) { 104 // TODO Auto-generated method stub 105 return null; 106 } 107 108 109 @Override 110 public void evict(Object key) { 111 if (logger.isDebugEnabled()) { 112 logger.debug("-------緩存刪除------"); 113 } 114 final String keyf = KEY_PREFIX + key.toString(); 115 redisTemplate.execute(new RedisCallback<Long>() { 116 @Override 117 public Long doInRedis(RedisConnection connection) throws DataAccessException { 118 return connection.del(keyf.getBytes()); 119 } 120 121 }); 122 } 123 124 125 @Override 126 public void clear() { 127 if (logger.isDebugEnabled()) { 128 logger.debug("-------緩存清理------"); 129 } 130 redisTemplate.execute(new RedisCallback<String>() { 131 @Override 132 public String doInRedis(RedisConnection connection) throws DataAccessException { 133 connection.flushDb(); 134 return "ok"; 135 } 136 }); 137 } 138 139 /** 140 * @return redisTemplate 141 */ 142 public RedisTemplate<String, Object> getRedisTemplate() { 143 return redisTemplate; 144 } 145 146 /** 147 * @param redisTemplate the redisTemplate to set 148 */ 149 public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) { 150 this.redisTemplate = redisTemplate; 151 } 152 153 /** 154 * @param name the name to set 155 */ 156 public void setName(String name) { 157 this.name = name; 158 } 159 160 /** 161 * @return name 162 */ 163 @Override 164 public String getName() { 165 return name; 166 } 167 168 /** 169 * @return resultType 170 */ 171 public String getResultType() { 172 return resultType; 173 } 174 175 /** 176 * @param resultType the resultType to set 177 */ 178 public void setResultType(String resultType) { 179 this.resultType = resultType; 180 } 181 182 /** 183 * @return expireInSencods 184 */ 185 public long getExpireInSencods() { 186 return expireInSencods; 187 } 188 189 /** 190 * @param expireInSencods the expireInSencods to set 191 */ 192 public void setExpireInSencods(long expireInSencods) { 193 this.expireInSencods = expireInSencods; 194 } 195 196 }
3、在spring-redis.xml中配置 spring mvc自定義緩存,bean 中的class為我們緩存實現類的包路徑,屬性均為緩存實現類中的屬性。

1 <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager"> 2 <property name="caches"> 3 <set> 4 <bean class="xxxx.xxx.xxx.RedisCache"> 5 <property name="redisTemplate" ref="redisTemplate"/> 6 <property name="name" value="wechatapp"/> 7 <property name="expireInSencods" value="7200"/> 8 <property name="resultType" 9 value="xxxx.xx.xxx.xx"/> 10 </bean> 11 </set> 12 </property> 13 </bean>
4、啟用緩存
<cache:annotation-driven
cache-manager="cacheManager"/>
5、在代碼中使用緩存,使用注解 @Cacheable,其中緩存名字cacheNames需要與我們spring redis中配置bean的<property name="name" value="xxx"/>對應起來

@Cacheable(cacheNames = CACHE_NAME,key = "#appid+':'+#secret",unless = "#result == null") @Override public WeChatAppResponseDto getAccessToken(String appid, String secret) { }
然后執行代碼可以發現,在第一次進入的時候,進入方法內部,然后返回結果,再次訪問就沒有進入方法內部了,可以打斷點在緩存獲取方法中查看,后面的都直接在緩存中獲取了。其實通過引入的相關依賴也可以看出來,該注解功能是利用AOP來實現的。