主要學習https://github.com/thinkgem/jeesite。一下代碼均參考於此並稍作修改。
1.jedis
首先,需要添加jedis:
<!--jedis--> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.8.0</version> </dependency>
2.applicationContext-jedis.xml
然后,springmvc完成基本配置。添加jedispool的bean即可。在spring容器中添加applicationContext-jedis.xml:
在applicationContext-jedis.xml中添加:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd"> <!-- 加載配置屬性文件 --> <context:property-placeholder ignore-unresolvable="true" location="classpath:db.properties" /> <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="maxIdle" value="300"/> <!--最大能夠保持idel狀態的對象數--> <property name="maxTotal" value="60000"/><!--最大分配的對象數--> <property name="testOnBorrow" value="true"/><!--當調用borrow Oject方法時,是否進行有效性檢查--> </bean> <bean id="jedisPool" class="redis.clients.jedis.JedisPool"> <constructor-arg index="0" ref="jedisPoolConfig"/> <constructor-arg index="1" value="${redis.host}"/> <constructor-arg index="2" value="${redis.port}" type="int"/> <constructor-arg index="3" value="${redis.timeout}" type="int"/> <constructor-arg index="4" value="${redis.auth}"/> </bean> </beans>
注解:參考的源碼中的jedisPool配置只有三個參數:config,host,port。我復制后的結果總是getResource失敗,因為我的redis添加了auth,所以猜測是不是沒通過auth的原因。於是打開JedisPool的源碼:

1 package redis.clients.jedis; 2 3 import java.net.URI; 4 5 import org.apache.commons.pool2.impl.GenericObjectPool; 6 import org.apache.commons.pool2.impl.GenericObjectPoolConfig; 7 8 import redis.clients.jedis.exceptions.JedisException; 9 import redis.clients.util.JedisURIHelper; 10 import redis.clients.util.Pool; 11 12 public class JedisPool extends Pool<Jedis> { 13 14 public JedisPool() { 15 this(Protocol.DEFAULT_HOST, Protocol.DEFAULT_PORT); 16 } 17 18 public JedisPool(final GenericObjectPoolConfig poolConfig, final String host) { 19 this(poolConfig, host, Protocol.DEFAULT_PORT, Protocol.DEFAULT_TIMEOUT, null, 20 Protocol.DEFAULT_DATABASE, null); 21 } 22 23 public JedisPool(String host, int port) { 24 this(new GenericObjectPoolConfig(), host, port, Protocol.DEFAULT_TIMEOUT, null, 25 Protocol.DEFAULT_DATABASE, null); 26 } 27 28 public JedisPool(final String host) { 29 URI uri = URI.create(host); 30 if (JedisURIHelper.isValid(uri)) { 31 String h = uri.getHost(); 32 int port = uri.getPort(); 33 String password = JedisURIHelper.getPassword(uri); 34 int database = JedisURIHelper.getDBIndex(uri); 35 this.internalPool = new GenericObjectPool<Jedis>(new JedisFactory(h, port, 36 Protocol.DEFAULT_TIMEOUT, Protocol.DEFAULT_TIMEOUT, password, database, null), 37 new GenericObjectPoolConfig()); 38 } else { 39 this.internalPool = new GenericObjectPool<Jedis>(new JedisFactory(host, 40 Protocol.DEFAULT_PORT, Protocol.DEFAULT_TIMEOUT, Protocol.DEFAULT_TIMEOUT, null, 41 Protocol.DEFAULT_DATABASE, null), new GenericObjectPoolConfig()); 42 } 43 } 44 45 public JedisPool(final URI uri) { 46 this(new GenericObjectPoolConfig(), uri, Protocol.DEFAULT_TIMEOUT); 47 } 48 49 public JedisPool(final URI uri, final int timeout) { 50 this(new GenericObjectPoolConfig(), uri, timeout); 51 } 52 53 public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, 54 int timeout, final String password) { 55 this(poolConfig, host, port, timeout, password, Protocol.DEFAULT_DATABASE, null); 56 } 57 58 public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, final int port) { 59 this(poolConfig, host, port, Protocol.DEFAULT_TIMEOUT, null, Protocol.DEFAULT_DATABASE, null); 60 } 61 62 public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, final int port, 63 final int timeout) { 64 this(poolConfig, host, port, timeout, null, Protocol.DEFAULT_DATABASE, null); 65 } 66 67 public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, 68 int timeout, final String password, final int database) { 69 this(poolConfig, host, port, timeout, password, database, null); 70 } 71 72 public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, 73 int timeout, final String password, final int database, final String clientName) { 74 this(poolConfig, host, port, timeout, timeout, password, database, clientName); 75 } 76 77 public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, 78 final int connectionTimeout, final int soTimeout, final String password, final int database, 79 final String clientName) { 80 super(poolConfig, new JedisFactory(host, port, connectionTimeout, soTimeout, password, 81 database, clientName)); 82 } 83 84 public JedisPool(final GenericObjectPoolConfig poolConfig, final URI uri) { 85 this(poolConfig, uri, Protocol.DEFAULT_TIMEOUT); 86 } 87 88 public JedisPool(final GenericObjectPoolConfig poolConfig, final URI uri, final int timeout) { 89 this(poolConfig, uri, timeout, timeout); 90 } 91 92 public JedisPool(final GenericObjectPoolConfig poolConfig, final URI uri, 93 final int connectionTimeout, final int soTimeout) { 94 super(poolConfig, new JedisFactory(uri, connectionTimeout, soTimeout, null)); 95 } 96 97 @Override 98 public Jedis getResource() { 99 Jedis jedis = super.getResource(); 100 jedis.setDataSource(this); 101 return jedis; 102 } 103 104 /** 105 * @deprecated starting from Jedis 3.0 this method will not be exposed. 106 * Resource cleanup should be done using @see {@link redis.clients.jedis.Jedis#close()} 107 */ 108 @Override 109 @Deprecated 110 public void returnBrokenResource(final Jedis resource) { 111 if (resource != null) { 112 returnBrokenResourceObject(resource); 113 } 114 } 115 116 /** 117 * @deprecated starting from Jedis 3.0 this method will not be exposed. 118 * Resource cleanup should be done using @see {@link redis.clients.jedis.Jedis#close()} 119 */ 120 @Override 121 @Deprecated 122 public void returnResource(final Jedis resource) { 123 if (resource != null) { 124 try { 125 resource.resetState(); 126 returnResourceObject(resource); 127 } catch (Exception e) { 128 returnBrokenResource(resource); 129 throw new JedisException("Could not return the resource to the pool", e); 130 } 131 } 132 } 133 }
看到有password的參數配置,如果沒有配置的話默認為null。到這一步我便沒有往下深入看了,因為我連接的redis中有auth,原諒我的不求甚解。於是,我接着配置timeout和auth。timeout直接還是源碼的默認值。后面的代碼測試通過。在這里我了解到spring的bean注入的幾個參數含義:比如property表示屬性注入,constructor表示構造函數的參數注入。
為了更清楚的表達,redis要設置db,配置文件參數也做一下改動:
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="maxIdle" value="300" /> <!-- 最大能夠保持idel狀態的對象數 --> <property name="maxTotal" value="60000" /> <!-- 最大分配的對象數 --> <property name="testOnBorrow" value="true" /> <!-- 當調用borrow Object方法時,是否進行有效性檢查 --> </bean> <bean id="jedisPool" class="redis.clients.jedis.JedisPool"> <constructor-arg name="poolConfig" ref="jedisPoolConfig" /> <constructor-arg name="host" value="${redis.host}" /> <constructor-arg name="port" value="${redis.port}" type="int" /> <constructor-arg name="timeout" value="${redis.timeout}" type="int" /> <constructor-arg name="password" value="#{'${redis.password}'!=''?'${redis.password}':null}" /> <constructor-arg name="database" value="${redis.db.index}" type="int" /> </bean>
最后一項參數是選擇redis的db,我認為通常默認連接的都是redis的0,那么我們的開發環境為了不沖突,應該另外設置。但JedisPool並沒有只有指定db的構造函數,所以選擇了這個構造函數。唯一的問題是,默認我們的redis是沒有密碼的,那么這里也填null而不是空字符串哦。所以,這里使用spring spEL表達式來填充空。對應的配置文件如下:
#redis settings redis.keyPrefix=wz redis.host=127.0.0.1 redis.port=6379 redis.timeout=2000 #注意,如果沒有password,此處不設置值,但這一項要保留 redis.password= redis.db.index=1
3. JedisUtil
3.1 getResource
上面設置好了JedisPool,這里就要獲取jedis。然后就可以利用jedis進行操作了。
1 /** 2 * 獲取資源 3 * @return 4 */ 5 public static Jedis getResource() { 6 Jedis jedis = null; 7 try { 8 jedis = jedisPool.getResource(); 9 logger.debug("getResource:{}",jedis); 10 } catch (Exception e) { 11 logger.error("getResource:{}",e); 12 if (jedis!=null) 13 jedis.close(); 14 throw e; 15 } 16 return jedis; 17 }
但是,為了更加自定義的設置db,這里也可以加一個db的選擇:
public static Jedis getResource() throws JedisException { Jedis jedis = null; try { jedis = jedisPool.getResource(); jedis.select(Integer.parseInt(DB_INDEX)); // logger.debug("getResource.", jedis); } catch (JedisException e) { logger.warn("getResource.", e); returnBrokenResource(jedis); throw e; } return jedis; }
3.1.1設置prefix
為了我們的key與其他app不沖突,我們最后為我們key統一增加一個標識,這種做法類似選擇一個表。
private static String setPrefix(String key) { key=KEY_PREFIX+"_"+key; return key; }
在任何使用到redis的地方,配置key的prefix。比如get 和 set:
public static String get(String key) { key = setPrefix(key); String value = null; Jedis jedis = null; try { jedis = getResource(); if (jedis.exists(key)) { value = jedis.get(key); value = StringUtils.isNotBlank(value) && !"nil".equalsIgnoreCase(value) ? value : null; logger.debug("get {} = {}", key, value); } } catch (Exception e) { logger.warn("get {} = {}", key, value, e); } finally { returnResource(jedis); } return value; }
public static String set(String key, String value, int cacheSeconds) { key = setPrefix(key); String result = null; Jedis jedis = null; try { jedis = getResource(); result = jedis.set(key, value); if (cacheSeconds != 0) { jedis.expire(key, cacheSeconds); } logger.debug("set {} = {}", key, value); } catch (Exception e) { logger.warn("set {} = {}", key, value, e); } finally { returnResource(jedis); } return result; }
3.2 Object對象的緩存
通過使用jedis基本可以完成任何操作了。這里添加一個緩存對象的功能。java對象的緩存利用序列化實現,因此,需要緩存的對象必須實現了serializable接口。關於如何序列化,參考:將對象序列化和反序列化。
1 /** 2 * 設置緩存 3 * @param key String 4 * @param value Object對象 5 * @param cacheSeconds 超時時間,0為不超時 6 * @return 7 */ 8 public static String setObject(String key,Object value,int cacheSeconds){ 9 String result = null; 10 Jedis jedis = null; 11 try { 12 jedis = getResource(); 13 result = jedis.set(getBytesKey(key),toBytes(value)); 14 if (cacheSeconds!=0){ 15 jedis.expire(key,cacheSeconds); 16 } 17 logger.debug("setObject {}={}",key,value); 18 } catch (Exception e) { 19 logger.warn("setObject {} 失敗:{}",key,e); 20 } finally { 21 jedis.close(); 22 } 23 return result; 24 } 25 /** 26 * 獲取緩存 27 * @param key 28 * @return 對象(反序列化) 29 */ 30 public static Object getObject(String key){ 31 Object value = null; 32 Jedis jedis = null; 33 try { 34 jedis = getResource(); 35 byte[] bytes = jedis.get(getBytesKey(key)); 36 value = toObject(bytes); 37 logger.debug("getObject {}={}",key,value); 38 } catch (Exception e) { 39 logger.warn("getObject {}錯誤:{}",key,e.getMessage()); 40 e.printStackTrace(); 41 } finally { 42 jedis.close(); 43 } 44 return value; 45 } 46 /** 47 * 將key轉換為byte[] 48 * @param object 49 * @return 50 */ 51 private static byte[] getBytesKey(Object object) { 52 if(object instanceof String){ 53 return StringUtils.getBytes((String) object); 54 }else { 55 return ObjectUtils.serialize(object); 56 } 57 } 58 59 /** 60 * Object轉換為byte[]類型 61 * @param value Object對象 62 * @return byte[]數組 63 */ 64 private static byte[] toBytes(Object value) { 65 return ObjectUtils.serialize(value); 66 } 67 68 /** 69 * byte[]轉換為object 70 * @param bytes 71 * @return 72 */ 73 private static Object toObject(byte[] bytes) { 74 return ObjectUtils.unserialize(bytes); 75 }
3.3 ObjectList對象緩存
我們平時用到的list基本都是ObjectList,即list的元素為object而不是String。這樣就需要特定方法來緩存了。
采用同樣的方式,將object序列化為字節數組,然后存儲起來。取出的時候再反序列化,因此object必須實現了serializable接口,而且static的成員不能序列化或者說序列化的結果為默認值。原因參考:將對象序列化和反序列化。

1 /** 2 * 獲取list緩存 3 * @param key 4 * @return 5 */ 6 public static List<String> getList(String key){ 7 key = addDatabaseName(key); 8 List<String> value = null; 9 Jedis jedis = null; 10 try { 11 jedis = getResource(); 12 value = jedis.lrange(key, 0, -1); 13 logger.debug("getList {}={}",key,value); 14 } catch (Exception e) { 15 logger.warn("getList {}失敗:{}",key,e); 16 e.printStackTrace(); 17 } finally { 18 jedis.close(); 19 } 20 return value; 21 } 22 23 /** 24 * 獲取list緩存,元素是object 25 * @param key 26 * @return 27 */ 28 public static List<Object> getObjectList(String key){ 29 key = addDatabaseName(key); 30 List<Object> value = null; 31 Jedis jedis = null; 32 try { 33 jedis = getResource(); 34 List<byte[]> list = jedis.lrange(getBytesKey(key), 0, -1); 35 value = Lists.newArrayList(); 36 for (byte[] bytes : list) { 37 value.add(toObject(bytes)); 38 } 39 logger.debug("getObjectList {}={}",key,value); 40 }catch (Exception e){ 41 logger.warn("getObjectList {} 失敗:{}",key,e); 42 e.printStackTrace(); 43 }finally { 44 jedis.close(); 45 } 46 return value; 47 } 48 49 /** 50 * 設置list緩存 51 * @param key 52 * @param value 53 * @param cacheSeconds 54 * @return 55 */ 56 public static long setList(String key,List<String> value,int cacheSeconds){ 57 key = addDatabaseName(key); 58 long result = 0; 59 Jedis jedis = null; 60 try { 61 jedis = getResource(); 62 jedis.del(key); 63 String[] arr = new String[value.size()]; 64 value.toArray(arr); 65 result = jedis.rpush(key,arr); 66 if (cacheSeconds!=0){ 67 jedis.expire(key,cacheSeconds); 68 } 69 logger.debug("setList {}={}",key,value); 70 }catch (Exception e){ 71 logger.warn("setList {} 錯誤:",key,e); 72 e.printStackTrace(); 73 }finally { 74 jedis.close(); 75 } 76 return result; 77 } 78 79 /** 80 * 設置list緩存,list的元素為object 81 * @param key 82 * @param value 83 * @param cacheSeconds 84 * @return 85 */ 86 public static long setObjectList(String key,List<Object> value ,int cacheSeconds){ 87 key = addDatabaseName(key); 88 long result = 0; 89 Jedis jedis = null; 90 try { 91 jedis = getResource(); 92 jedis.del(key); 93 ArrayList<byte[]> list = Lists.newArrayList(); 94 for (Object o : value) { 95 list.add(toBytes(o)); 96 } 97 byte[] []arr = new byte[list.size()][]; 98 list.toArray(arr); 99 result = jedis.rpush(getBytesKey(key),arr); 100 if(cacheSeconds!=0){ 101 jedis.expire(key,cacheSeconds); 102 } 103 logger.debug("setObjectList {}={}",key,value); 104 }catch (Exception e){ 105 logger.warn("setObjectList {} 錯誤:{}",key,e); 106 e.printStackTrace(); 107 } 108 return result; 109 }