本文內容參考網絡,侵刪java
本系列文章將整理到我在GitHub上的《Java面試指南》倉庫,更多精彩內容請到個人倉庫裏查看git
https://github.com/h2pl/Java-Tutorialgithub
喜歡的話麻煩點下Star哈web
文章首發於個人我的博客:面試
www.how2playlife.comredis
該系列博文會告訴你什麼是分佈式系統,這對後端工程師來講是很重要的一門學問,咱們會逐步瞭解常見的分佈式技術、以及一些較爲常見的分佈式系統概念,同時也須要進一步瞭解zookeeper、分佈式事務、分佈式鎖、負載均衡等技術,以便讓你更完整地瞭解分佈式技術的具體實戰方法,爲真正應用分佈式技術作好準備。spring
若是對本系列文章有什麼建議,或者是有什麼疑問的話,也能夠關注公衆號【Java技術江湖】聯繫做者,歡迎你參與本系列博文的創做和修訂。數據庫
本文轉載自 linkedkeeper.comapache
Spring Boot 熟悉後,集成一個外部擴展是一件很容易的事,集成Redis也很簡單,看下面步驟配置:後端
1、添加pom依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-redis</artifactId> </dependency>
2、建立 RedisClient.java
注意該類存放的package
package org.springframework.data.redis.connection.jedis; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.UnsupportedEncodingException; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.Jedis; import redis.clients.jedis.Protocol; import redis.clients.jedis.exceptions.JedisException; /** * 工具類 RedisClient * 由於本類中獲取JedisPool調用的是JedisConnectionFactory中protected修飾的方法fetchJedisConnector() * 因此該類須要與JedisConnectionFactory在同一個package中 * * @author 單紅宇(CSDN CATOOP) * @create 2017年4月9日 */ public class RedisClient { private static Logger logger = LoggerFactory.getLogger(RedisClient.class); private JedisConnectionFactory factory; public RedisClient(JedisConnectionFactory factory) { super(); this.factory = factory; } /** * put操做(存儲序列化對象)+ 生效時間 * * @param key * @param value * @return */ public void putObject(final String key, final Object value, final int cacheSeconds) { if (StringUtils.isNotBlank(key)) { redisTemplete(key, new RedisExecute<Object>() { @Override public Object doInvoker(Jedis jedis) { try { jedis.setex(key.getBytes(Protocol.CHARSET), cacheSeconds, serialize(value)); } catch (UnsupportedEncodingException e) { } return null; } }); } } /** * get操做(獲取序列化對象) * * @param key * @return */ public Object getObject(final String key) { return redisTemplete(key, new RedisExecute<Object>() { @Override public Object doInvoker(Jedis jedis) { try { byte[] byteKey = key.getBytes(Protocol.CHARSET); byte[] byteValue = jedis.get(byteKey); if (byteValue != null) { return deserialize(byteValue); } } catch (UnsupportedEncodingException e) { return null; } return null; } }); } /** * setex操做 * * @param key * 鍵 * @param value * 值 * @param cacheSeconds * 超時時間,0爲不超時 * @return */ public String set(final String key, final String value, final int cacheSeconds) { return redisTemplete(key, new RedisExecute<String>() { @Override public String doInvoker(Jedis jedis) { if (cacheSeconds == 0) { return jedis.set(key, value); } return jedis.setex(key, cacheSeconds, value); } }); } /** * get操做 * * @param key * 鍵 * @return 值 */ public String get(final String key) { return redisTemplete(key, new RedisExecute<String>() { @Override public String doInvoker(Jedis jedis) { String value = jedis.get(key); return StringUtils.isNotBlank(value) && !"nil".equalsIgnoreCase(value) ? value : null; } }); } /** * del操做 * * @param key * 鍵 * @return */ public long del(final String key) { return redisTemplete(key, new RedisExecute<Long>() { @Override public Long doInvoker(Jedis jedis) { return jedis.del(key); } }); } /** * 獲取資源 * * @return * @throws JedisException */ public Jedis getResource() throws JedisException { Jedis jedis = null; try { jedis = factory.fetchJedisConnector(); } catch (JedisException e) { logger.error("getResource.", e); returnBrokenResource(jedis); throw e; } return jedis; } /** * 獲取資源 * * @return * @throws JedisException */ public Jedis getJedis() throws JedisException { return getResource(); } /** * 歸還資源 * * @param jedis * @param isBroken */ public void returnBrokenResource(Jedis jedis) { if (jedis != null) { jedis.close(); } } /** * 釋放資源 * * @param jedis * @param isBroken */ public void returnResource(Jedis jedis) { if (jedis != null) { jedis.close(); } } /** * 操做jedis客戶端模板 * * @param key * @param execute * @return */ public <R> R redisTemplete(String key, RedisExecute<R> execute) { Jedis jedis = null; try { jedis = getResource(); if (jedis == null) { return null; } return execute.doInvoker(jedis); } catch (Exception e) { logger.error("operator redis api fail,{}", key, e); } finally { returnResource(jedis); } return null; } /** * 功能簡述: 對實體Bean進行序列化操做. * * @param source * 待轉換的實體 * @return 轉換以後的字節數組 * @throws Exception */ public static byte[] serialize(Object source) { ByteArrayOutputStream byteOut = null; ObjectOutputStream ObjOut = null; try { byteOut = new ByteArrayOutputStream(); ObjOut = new ObjectOutputStream(byteOut); ObjOut.writeObject(source); ObjOut.flush(); } catch (IOException e) { e.printStackTrace(); } finally { try { if (null != ObjOut) { ObjOut.close(); } } catch (IOException e) { ObjOut = null; } } return byteOut.toByteArray(); } /** * 功能簡述: 將字節數組反序列化爲實體Bean. * * @param source * 須要進行反序列化的字節數組 * @return 反序列化後的實體Bean * @throws Exception */ public static Object deserialize(byte[] source) { ObjectInputStream ObjIn = null; Object retVal = null; try { ByteArrayInputStream byteIn = new ByteArrayInputStream(source); ObjIn = new ObjectInputStream(byteIn); retVal = ObjIn.readObject(); } catch (Exception e) { e.printStackTrace(); } finally { try { if (null != ObjIn) { ObjIn.close(); } } catch (IOException e) { ObjIn = null; } } return retVal; } interface RedisExecute<T> { T doInvoker(Jedis jedis); } }
3、建立Redis配置類
RedisConfig.java
package com.shanhy.example.redis; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.connection.jedis.RedisClient; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.StringRedisSerializer; /** * Redis配置 * * @author 單紅宇(CSDN catoop) * @create 2016年9月12日 */ @Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(JedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<String, Object>(); template.setConnectionFactory(factory); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new RedisObjectSerializer()); template.afterPropertiesSet(); return template; } @Bean public RedisClient redisClient(JedisConnectionFactory factory){ return new RedisClient(factory); } } RedisObjectSerializer.java package com.shanhy.example.redis; import org.springframework.core.convert.converter.Converter; import org.springframework.core.serializer.support.DeserializingConverter; import org.springframework.core.serializer.support.SerializingConverter; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.SerializationException; /** * 實現對象的序列化接口 * @author 單紅宇(365384722) * @myblog http://blog.csdn.net/catoop/ * @create 2017年4月9日 */ public class RedisObjectSerializer implements RedisSerializer<Object> { private Converter<Object, byte[]> serializer = new SerializingConverter(); private Converter<byte[], Object> deserializer = new DeserializingConverter(); static final byte[] EMPTY_ARRAY = new byte[0]; @Override public Object deserialize(byte[] bytes) { if (isEmpty(bytes)) { return null; } try { return deserializer.convert(bytes); } catch (Exception ex) { throw new SerializationException("Cannot deserialize", ex); } } @Override public byte[] serialize(Object object) { if (object == null) { return EMPTY_ARRAY; } try { return serializer.convert(object); } catch (Exception ex) { return EMPTY_ARRAY; } } private boolean isEmpty(byte[] data) { return (data == null || data.length == 0); } }
4、建立測試方法
下面代碼隨便放一個Controller裏
@Autowired private RedisTemplate<String, Object> redisTemplate; /** * 緩存測試 * * @return * @author SHANHY * @create 2016年9月12日 */ @RequestMapping("/redisTest") public String redisTest() { try { redisTemplate.opsForValue().set("test-key", "redis測試內容", 2, TimeUnit.SECONDS);// 緩存有效期2秒 logger.info("從Redis中讀取數據:" + redisTemplate.opsForValue().get("test-key").toString()); TimeUnit.SECONDS.sleep(3); logger.info("等待3秒後嘗試讀取過時的數據:" + redisTemplate.opsForValue().get("test-key")); } catch (InterruptedException e) { e.printStackTrace(); } return "OK"; }
5、配置文件配置Redis
application.yml
spring: # Redis配置 redis: host: 192.168.1.101 port: 6379 password: # 鏈接超時時間(毫秒) timeout: 10000 pool: max-idle: 20 min-idle: 5 max-active: 20 max-wait: 2
這樣就完成了Redis的配置,能夠正常使用 redisTemplate 了。
atoop/article/details/71275331
RedisKeys.java
package com.shanhy.example.redis; import java.util.HashMap; import java.util.Map; import javax.annotation.PostConstruct; import org.springframework.stereotype.Component; /** * 方法緩存key常量 * * @author SHANHY */ @Component public class RedisKeys { // 測試 begin public static final String _CACHE_TEST = "_cache_test";// 緩存key public static final Long _CACHE_TEST_SECOND = 20L;// 緩存時間 // 測試 end // 根據key設定具體的緩存時間 private Map<String, Long> expiresMap = null; @PostConstruct public void init(){ expiresMap = new HashMap<>(); expiresMap.put(_CACHE_TEST, _CACHE_TEST_SECOND); } public Map<String, Long> getExpiresMap(){ return this.expiresMap; } }
CachingConfig.java
package com.shanhy.example.redis; import java.lang.reflect.Method; import java.util.ArrayList; 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.interceptor.KeyGenerator; import org.springframework.cache.interceptor.SimpleKeyGenerator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.core.RedisTemplate; /** * 註解式環境管理 * * @author 單紅宇(CSDN catoop) * @create 2016年9月12日 */ @Configuration @EnableCaching public class CachingConfig extends CachingConfigurerSupport { /** * 在使用@Cacheable時,若是不指定key,則使用找個默認的key生成器生成的key * * @return * * @author 單紅宇(CSDN CATOOP) * @create 2017年3月11日 */ @Override public KeyGenerator keyGenerator() { return new SimpleKeyGenerator() { /** * 對參數進行拼接後MD5 */ @Override public Object generate(Object target, Method method, Object... params) { StringBuilder sb = new StringBuilder(); sb.append(target.getClass().getName()); sb.append(".").append(method.getName()); StringBuilder paramsSb = new StringBuilder(); for (Object param : params) { // 若是不指定,默認生成包含到鍵值中 if (param != null) { paramsSb.append(param.toString()); } } if (paramsSb.length() > 0) { sb.append("_").append(paramsSb); } return sb.toString(); } }; } /** * 管理緩存 * * @param redisTemplate * @return */ @Bean public CacheManager cacheManager(RedisTemplate<String, Object> redisTemplate, RedisKeys redisKeys) { RedisCacheManager rcm = new RedisCacheManager(redisTemplate); // 設置緩存默認過時時間(全局的) rcm.setDefaultExpiration(1800);// 30分鐘 // 根據key設定具體的緩存時間,key統一放在常量類RedisKeys中 rcm.setExpires(redisKeys.getExpiresMap()); List<String> cacheNames = new ArrayList<String>(redisKeys.getExpiresMap().keySet()); rcm.setCacheNames(cacheNames); return rcm; } }
TestService.java
package com.shanhy.example.service; import org.apache.commons.lang3.RandomStringUtils; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import com.shanhy.example.redis.RedisKeys; @Service public class TestService { /** * 固定key * * @return * @author SHANHY * @create 2017年4月9日 */ @Cacheable(value = RedisKeys._CACHE_TEST, key = "'" + RedisKeys._CACHE_TEST + "'") public String testCache() { return RandomStringUtils.randomNumeric(4); } /** * 存儲在Redis中的key自動生成,生成規則詳見CachingConfig.keyGenerator()方法 * * @param str1 * @param str2 * @return * @author SHANHY * @create 2017年4月9日 */ @Cacheable(value = RedisKeys._CACHE_TEST) public String testCache2(String str1, String str2) { return RandomStringUtils.randomNumeric(4); } }
說明一下,其中 @Cacheable 中的 value 值是在 CachingConfig的cacheManager 中配置的,那裏是爲了配置咱們的緩存有效時間。其中 methodKeyGenerator 爲 CachingConfig 中聲明的 KeyGenerator。
另外,Cache 相關的註解還有幾個,你們能夠了解下,不過咱們經常使用的就是 @Cacheable,通常狀況也能夠知足咱們的大部分需求了。還有 @Cacheable 也能夠配置表達式根據咱們傳遞的參數值判斷是否須要緩存。
注: TestService 中 testCache 中的 mapper.get 你們不用關心,這裏面我只是訪問了一下數據庫而已,你只須要在這裏作本身的業務代碼便可。
下面代碼,隨便放一個 Controller 中
package com.shanhy.example.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.connection.jedis.RedisClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.shanhy.example.service.TestService; /** * 測試Controller * * @author 單紅宇(365384722) * @myblog http://blog.csdn.net/catoop/ * @create 2017年4月9日 */ @RestController @RequestMapping("/test") public class TestController { private static final Logger LOG = LoggerFactory.getLogger(TestController.class); @Autowired private RedisClient redisClient; @Autowired private TestService testService; @GetMapping("/redisCache") public String redisCache() { redisClient.set("shanhy", "hello,shanhy", 100); LOG.info("getRedisValue = {}", redisClient.get("shanhy")); testService.testCache2("aaa", "bbb"); return testService.testCache(); } }
至此完畢!
最後說一下,這個 @Cacheable 基本是能夠放在全部方法上的,Controller 的方法上也是能夠的(這個我沒有測試 ^_^)。