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的源码:
看到有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的成员不能序列化或者说序列化的结果为默认值。原因参考:将对象序列化和反序列化。