最近在学习redis,在网上查了些文章,利用他人已有的知识,总结写下了这篇文章,大部分内容还是引用别人的文章内容。经过测试发现spring-data-redis现在有的版本只能支持reids 2.6和2.8版本,更高版本尚未支持。还是直接使用jedis比较灵活。
redis
- redis安装
redis的安装过程在以前的博文中已经详细介绍 linux下安装redis并自启动
- jedis
jedis下载地址:https://github.com/xetorthio/jedis
jedis社区地址:https://groups.google.com/forum/#!forum/jedis_redis
最简单的Jedis访问redis
- jedis官方示例
1、键值读取
Jedis jedis = new Jedis("redis服务器ip地址", 6379);
jedis.set("foo", "123456");
String value = jedis.get("foo");
System.out.println("____________value="+value);
2、集群读取
Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>();
//Jedis Cluster will attempt to discover cluster nodes automatically
jedisClusterNodes.add(new HostAndPort("127.0.0.1", 7379));
JedisCluster jc = new JedisCluster(jedisClusterNodes);
jc.set("foo", "bar");
String value = jc.get("foo");
jedis 池和sharding基本示例
redis的基本配置信息
将以下内容放在redis.properties或者文件中,后面有关的.properties文件的内容都跟下面一样的内容。
redis.host=192.168.1.100
redis.port=6379
redis.pass=123456
redis.default.db=0
redis.timeout=100000//客户端超时时间单位是毫秒
redis.maxActive=300// 最大连接数
redis.maxIdle=100//最大空闲数
redis.maxWait=1000//最大建立连接等待时间
redis.testOnBorrow=true
redis.testOnReturn=true;
java读取配置文件
ResourceBundle bundle = ResourceBundle.getBundle("redis");
if (bundle == null) {
throw new IllegalArgumentException(
"[redis.properties] is not found!");
}
redis池
jedisc池需要commons-pool.jar的支持。
- 池对象的获取与回收
在没有使用spring-data-redis的情况下,需要手工获取池对象,并在使用完毕后放回对象池中。
在使用redis池,需要通过以下代码方式从pool中获取资源。
jedisPool.getResource()
资源使用完毕后需要放入pool中
jedisPool.returnResource(jedis);
具体的示例代码
public class MyJedisPool {
// jedis池
private static JedisPool pool;
// 静态代码初始化池配置
static {
// 加载redis配置文件
ResourceBundle bundle = ResourceBundle.getBundle("redis"); if (bundle == null) {
throw new IllegalArgumentException("[redis.properties] is not found!");
} // 创建jedis池配置实例
JedisPoolConfig config = new JedisPoolConfig(); // 设置池配置项值
config.setMaxActive(Integer.valueOf(bundle.getString("redis.pool.maxActive")));
config.setMaxIdle(Integer.valueOf(bundle.getString("redis.pool.maxIdle")));
config.setMaxWait(Long.valueOf(bundle.getString("redis.pool.maxWait")));
config.setTestOnBorrow(Boolean.valueOf(bundle.getString("redis.pool.testOnBorrow")));
config.setTestOnReturn(Boolean.valueOf(bundle.getString("redis.pool.testOnReturn")));
//根据配置实例化jedis池
pool = new JedisPool(config, bundle.getString("redis.ip"), Integer.valueOf(bundle.getString("redis.port")));
} /**
* 测试jedis池方法
*/
public static void test1() {
// 从jedis池中获取一个jedis实例
Jedis jedis = pool.getResource(); // 获取jedis实例后可以对redis服务进行一系列的操作
jedis.set("name", "xmong");
System.out.println(jedis.get("name")); jedis.del("name");
System.out.println(jedis.exists("name")); // 释放对象池,即获取jedis实例使用后要将对象还回去
pool.returnResource(jedis);
}
}
redis分布式
- 分布式配置信息
#redis1服务器ip #
Redis1.ip=172.30.5.113 #redis2服务器ip #
Redis2.ip=172.30.5.117 #redis服务器端口号#
redis.port=6379
- 分布式读取方法
public class MyJedisPool {
// jedis池
private static JedisPool pool; // shardedJedis池
private static ShardedJedisPool shardPool; // 静态代码初始化池配置 static {
// 加载redis配置文件
ResourceBundle bundle = ResourceBundle.getBundle("redis"); if (bundle == null) {
throw new IllegalArgumentException("[redis.properties] is not found!");
} // 创建jedis池配置实例
JedisPoolConfig config = new JedisPoolConfig(); // 设置池配置项值
config.setMaxActive(Integer.valueOf(bundle.getString("redis.pool.maxActive")));
config.setMaxIdle(Integer.valueOf(bundle.getString("redis.pool.maxIdle")));
config.setMaxWait(Long.valueOf(bundle.getString("redis.pool.maxWait")));
config.setTestOnBorrow(Boolean.valueOf(bundle.getString("redis.pool.testOnBorrow")));
config.setTestOnReturn(Boolean.valueOf(bundle.getString("redis.pool.testOnReturn"))); // 根据配置实例化jedis池
// pool = new JedisPool(config, bundle.getString("redis.ip"),
// Integer.valueOf(bundle.getString("redis.port"))); // 创建多个redis共享服务
JedisShardInfo jedisShardInfo1 = new JedisShardInfo(bundle.getString("redis1.ip"), Integer.valueOf(bundle.getString("redis.port")));
JedisShardInfo jedisShardInfo2 = new JedisShardInfo(bundle.getString("redis2.ip"), Integer.valueOf(bundle.getString("redis.port"))); List<JedisShardInfo> list = new LinkedList<JedisShardInfo>();
list.add(jedisShardInfo1);
list.add(jedisShardInfo2); // 根据配置文件,创建shared池实例
shardPool = new ShardedJedisPool(config, list);
} /**
* 测试jedis池方法
*/
public static void test1() {
// 从jedis池中获取一个jedis实例
Jedis jedis = pool.getResource(); // 获取jedis实例后可以对redis服务进行一系列的操作
jedis.set("name", "xmong");
System.out.println(jedis.get("name")); jedis.del("name");
System.out.println(jedis.exists("name")); // 释放对象池,即获取jedis实例使用后要将对象还回去
pool.returnResource(jedis);
} /**
* 测试shardedJedis池方法
*/
public static void test2() {
// 从shard池中获取shardJedis实例
ShardedJedis shardJedis = shardPool.getResource();
// 向redis服务插入两个key-value对象
shardJedis.set("aaa", "xmong_aaa");
System.out.println(shardJedis.get("aaa")); shardJedis.set("zzz", "xmong_zzz");
System.out.println(shardJedis.get("zzz")); // 释放资源
shardPool.returnResource(shardJedis);
} public static void main(String[] args) {
// test1();//执行test1方法
test2();// 执行test2方法
}
}
spinrg 整合redis
spring结合redis配置连接池
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxActive" value="32"></property>
<property name="maxIdle" value="6"></property>
<property name="maxWait" value="15000"></property>
<property name="minEvictableIdleTimeMillis" value="300000"></property>
<property name="numTestsPerEvictionRun" value="3"></property>
<property name="timeBetweenEvictionRunsMillis" value="60000"></property>
<property name="whenExhaustedAction" value="1"></property>
</bean>
<bean id="jedisPool" class="redis.clients.jedis.JedisPool" destroy-method="destroy">
<!-- config -->
<constructor-arg ref="jedisPoolConfig"></constructor-arg>
<!-- host -->
<constructor-arg value="127.0.0.1"></constructor-arg>
<!-- port -->
<constructor-arg value="6379"></constructor-arg>
<!-- timeout -->
<constructor-arg value="15000"></constructor-arg>
<!-- password -->
<constructor-arg value="0123456"></constructor-arg>
<!-- database index -->
<constructor-arg value="12"></constructor-arg>
</bean>
测试类
public static void main(String[] args) {
//resources/beans.xml
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:beans.xml");
JedisPool jedisPool = (JedisPool)context.getBean("jedisPool");
Jedis client = jedisPool.getResource();
try{
client.select(0);
client.set("k1", "v1");
System.out.println(client.get("k1"));
}catch(Exception e){
e.printStackTrace();
}finally{
jedisPool.returnResource(client);//must be
}
}
new ClassPathXmlApplicationContext("classpath:beans.xml");是直接读取beans.xml文件,因此需要将上面的配置内容放在beans.xml中,来获取bean对象实例。
spring 结合redis sharding
<context:property-placeholder location="classpath:redis.properties" />
<context:component-scan base-package="com.d.work.main">
</context:component-scan>
<context:component-scan base-package="com.d.work.redis">
</context:component-scan>
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxActive" value="50" />
<property name="maxIdle" value="8" />
<property name="maxWait" value="1000" />
<property name="testOnBorrow" value="true"/>
<property name="testOnReturn" value="true"/>
<!-- <property name="testWhileIdle" value="true"/> -->
</bean> <bean id="shardedJedisPool" class="redis.clients.jedis.ShardedJedisPool" scope="singleton">
<constructor-arg index="0" ref="jedisPoolConfig" />
<constructor-arg index="1">
<list>
<bean class="redis.clients.jedis.JedisShardInfo">
<constructor-arg name="host" value="${redis.host}" />
<constructor-arg name="port" value="${redis.port}" />
<constructor-arg name="timeout" value="${redis.timeout}" />
<constructor-arg name="weight" value="1" />
</bean>
</list>
</constructor-arg>
</bean>
Spring-data-redis
spring 提供jsmTemplement,jdbcTemplement,redisTemplement等类似模板。spring 通过context:property-placeholder实现导入配置文件,context:property-placeholder 标签用来导入properties文件。从而替换${redis.maxIdle}这样的变量。要使用spring-data-redis,需要下载spring-data-redis-1.5.1.RELEASE.jar
spring-data-redis针对redis提供了以下的特性
- 连接池自动管理
提供了一个高度封装的“RedisTemplate”类
- 针对jedis客户端中大量api进行了归类封装,将同一类型操作封装为operation接口
ValueOperations:简单K-V操作
SetOperations:set类型数据操作
ZSetOperations:zset类型数据操作
HashOperations:针对map类型的数据操作
ListOperations:针对list类型的数据操作
- 提供了对key的“bound”(绑定)便捷化操作API,可以通过bound封装指定的key,然后进行一系列的操作而无须“显式”的再次指定Key,即BoundKeyOperations:
BoundValueOperations
BoundSetOperations
BoundListOperations
BoundSetOperations
BoundHashOperations
- 将事务操作封装,有容器控制。
- 针对数据的“序列化/反序列化”,提供了多种可选择策略(RedisSerializer)
JdkSerializationRedisSerializer:POJO对象的存取场景,使用JDK本身序列化机制,将pojo类通过ObjectInputStream/ObjectOutputStream进行序列化操作,最终redis-server中将存储字节序列。是目前最常用的序列化策略。
StringRedisSerializer:Key或者value为字符串的场景,根据指定的charset对数据的字节序列编码成string,是“new String(bytes, charset)”和“string.getBytes(charset)”的直接封装。是最轻量级和高效的策略。
JacksonJsonRedisSerializer:jackson-json工具提供了javabean与json之间的转换能力,可以将pojo实例序列化成json格式存储在redis中,也可以将json格式的数据转换成pojo实例。因为jackson工具在序列化和反序列化时,需要明确指定Class类型,因此此策略封装起来稍微复杂。【需要jackson-mapper-asl工具支持】
OxmSerializer:提供了将javabean与xml之间的转换能力,目前可用的三方支持包括jaxb,apache-xmlbeans;redis存储的数据将是xml工具。不过使用此策略,编程将会有些难度,而且效率最低;不建议使用。【需要spring-oxm模块的支持】
针对“序列化和发序列化”中JdkSerializationRedisSerializer和StringRedisSerializer是最基础的策略,原则上,我们可以将数据存储为任何格式以便应用程序存取和解析(其中应用包括app,hadoop等其他工具),不过在设计时仍然不推荐直接使用“JacksonJsonRedisSerializer”和“OxmSerializer”,因为无论是json还是xml,他们本身仍然是String。
如果你的数据需要被第三方工具解析,那么数据应该使用StringRedisSerializer而不是JdkSerializationRedisSerializer。
如果你的数据格式必须为json或者xml,那么在编程级别,在redisTemplate配置中仍然使用StringRedisSerializer,在存储之前或者读取之后,使用“SerializationUtils”工具转换转换成json或者xml,请参见下文实例。
- 基于设计模式,和JMS开发思路,将pub/sub的API设计进行了封装,使开发更加便捷。
- spring-data-redis中,并没有对sharding提供良好的封装,如果你的架构是基于sharding,那么你需要自己去实现,这也是sdr和jedis相比,唯一缺少的特性。
spinrg-data-redis配置与调用
示例一
<bean id="propertyConfigurerRedis" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="order" value="1" />
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="locations">
<list>
<value>classpath:config/redis-manager-config.properties</value>
</list>
</property>
</bean> <!-- jedis pool配置 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxActive" value="${redis.maxActive}" />
<property name="maxIdle" value="${redis.maxIdle}" />
<property name="maxWait" value="${redis.maxWait}" />
<property name="testOnBorrow" value="${redis.testOnBorrow}" />
</bean> <!-- spring data redis -->
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="usePool" value="true"></property>
<property name="hostName" value="${redis.host}" />
<property name="port" value="${redis.port}" />
<property name="password" value="${redis.pass}" />
<property name="timeout" value="${redis.timeout}" />
<property name="database" value="${redis.default.db}"></property>
<constructor-arg index="0" ref="jedisPoolConfig" />
</bean> <bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory" />
</bean>
public class RedisBase { private StringRedisTemplate template; /**
* @return the template
*/
public StringRedisTemplate getTemplate() {
return template;
} /**
* @param template the template to set
*/
public void setTemplate(StringRedisTemplate template) {
this.template = template;
} }
示例二
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxActive" value="32"></property>
<property name="maxIdle" value="6"></property>
<property name="maxWait" value="15000"></property>
<property name="minEvictableIdleTimeMillis" value="300000"></property>
<property name="numTestsPerEvictionRun" value="3"></property>
<property name="timeBetweenEvictionRunsMillis" value="60000"></property>
<property name="whenExhaustedAction" value="1"></property>
</bean>
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" destroy-method="destroy">
<property name="poolConfig" ref="jedisPoolConfig"></property>
<property name="hostName" value="127.0.0.1"></property>
<property name="port" value="6379"></property>
<property name="password" value="0123456"></property>
<property name="timeout" value="15000"></property>
<property name="usePool" value="true"></property>
</bean>
<bean id="jedisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory"></property>
<property name="keySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
</property>
<property name="valueSerializer">
<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
</property>
</bean>
public class SpringDataRedisTestMain { /**
* @param args
*/
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-redis-beans.xml");
RedisTemplate redisTemplate = (RedisTemplate)context.getBean("jedisTemplate");
//其中key采取了StringRedisSerializer
//其中value采取JdkSerializationRedisSerializer
ValueOperations<String, User> valueOper = redisTemplate.opsForValue();
User u1 = new User("zhangsan",12);
User u2 = new User("lisi",25);
valueOper.set("u:u1", u1);
valueOper.set("u:u2", u2);
System.out.println(valueOper.get("u:u1").getName());
System.out.println(valueOper.get("u:u2").getName());
} /**
* 如果使用jdk序列化方式,bean必须实现Serializable,且提供getter/setter方法
* @author qing
*
*/
static class User implements Serializable{ /**
*
*/
private static final long serialVersionUID = -3766780183428993793L;
private String name;
private Date created;
private int age;
public User(){}
public User(String name,int age){
this.name = name;
this.age = age;
this.created = new Date();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
} } }
如果你使用过jedisPool连接池,在数据操作之前,你需要pool.getResource()即从连接池中获取“链接资源”(Jedis),在操作之后,你需要(必须)调用pool.returnResource()将资源归还个连接池。但是,spring-data-redis中,我们似乎并没有直接操作pool,那么spring是如何做到pool管理的呢??一句话:spring的“看门绝技”--callback。
public <T> T execute(RedisCallback<T> action):这个方法是redisTemplate中执行操作的底层方法,任何基于redisTemplate之上的调用(比如,valueOperations)最终都会被封装成RedisCallback,redisTemplate在execute方法中将会直接使用jedis客户端API进行与server通信,而且在如果使用了连接池,则会在操作之后执行returnSource。
java读取bean方式
方式一
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-redis-beans.xml");
RedisTemplate redisTemplate = (RedisTemplate)context.getBean("jedisTemplate");
方式二
ApplicationContext ctx = new FileSystemXmlApplicationContext("classpath:setup/applicationContext.xml");
cachedClient = (MemCachedClient)ctx.getBean("memcachedClient");
Spring-data-redis: pub/sub消息订阅
redis可以用来做消息订阅操作。
第一步配置
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxActive" value="32"></property>
<property name="maxIdle" value="6"></property>
<property name="maxWait" value="15000"></property>
<property name="minEvictableIdleTimeMillis" value="300000"></property>
<property name="numTestsPerEvictionRun" value="3"></property>
<property name="timeBetweenEvictionRunsMillis" value="60000"></property>
<property name="whenExhaustedAction" value="1"></property>
</bean>
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" destroy-method="destroy">
<property name="poolConfig" ref="jedisPoolConfig"></property>
<property name="hostName" value="127.0.0.1"></property>
<property name="port" value="6379"></property>
<property name="password" value="0123456"></property>
<property name="timeout" value="15000"></property>
<property name="usePool" value="true"></property>
</bean>
<bean id="jedisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory"></property>
<property name="defaultSerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
</property>
</bean> <bean id="topcMessageListener" class="com.sample.redis.sdr.TopicMessageListener">
<property name="redisTemplate" ref="jedisTemplate"></property>
</bean>
<bean id="topicContainer" class="org.springframework.data.redis.listener.RedisMessageListenerContainer" destroy-method="destroy">
<property name="connectionFactory" ref="jedisConnectionFactory"/>
<property name="taskExecutor"><!-- 此处有个奇怪的问题,无法正确使用其他类型的Executor -->
<bean class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler">
<property name="poolSize" value="3"></property>
</bean>
</property>
<property name="messageListeners">
<map>
<entry key-ref="topcMessageListener">
<bean class="org.springframework.data.redis.listener.ChannelTopic">
<constructor-arg value="user:topic"/>
</bean>
</entry>
</map>
</property>
</bean>
第二部:发布消息
String channel = "user:topic";
//其中channel必须为string,而且“序列化”策略也是StringSerializer
//消息内容,将会根据配置文件中指定的valueSerializer进行序列化
//本例中,默认全部采用StringSerializer
//那么在消息的subscribe端也要对“发序列化”保持一致。
redisTemplate.convertAndSend(channel, "from app 1");
第三部:消息接收
public class TopicMessageListener implements MessageListener { private RedisTemplate redisTemplate; public void setRedisTemplate(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
} @Override
public void onMessage(Message message, byte[] pattern) {
byte[] body = message.getBody();//请使用valueSerializer
byte[] channel = message.getChannel();
//请参考配置文件,本例中key,value的序列化方式均为string。
//其中key必须为stringSerializer。和redisTemplate.convertAndSend对应
String itemValue = (String)redisTemplate.getValueSerializer().deserialize(body);
String topic = (String)redisTemplate.getStringSerializer().deserialize(channel);
//...
}
}
spring-data-redis 事物处理
以下示例显示spring-data-redis实现事物处理,并通过回调方法实现返回数据
//execute a transaction
List<Object> txResults = redisTemplate.execute(new SessionCallback<List<Object>>() {
public List<Object> execute(RedisOperations operations) throws DataAccessException {
operations.multi();
operations.opsForSet().add("key", "value1"); // This will contain the results of all ops in the transaction
return operations.exec();
}
});
System.out.println("Number of items added to set: " + txResults.get(0));
Spring-data-redis: serializer实例
sdr提供了4种内置的serializer:
1、JdkSerializationRedisSerializer:使用JDK的序列化手段(serializable接口,ObjectInputStrean,ObjectOutputStream),数据以字节流存储
2、StringRedisSerializer:字符串编码,数据以string存储
3、JacksonJsonRedisSerializer:json格式存储
4、OxmSerializer:xml格式存储
其中JdkSerializationRedisSerializer和StringRedisSerializer是最基础的序列化策略,其中“JacksonJsonRedisSerializer”与“OxmSerializer”都是基于stirng存储,因此它们是较为“高级”的序列化(最终还是使用string解析以及构建java对象)。
RedisTemplate中需要声明4种serializer,默认为“JdkSerializationRedisSerializer”:
1) keySerializer :对于普通K-V操作时,key采取的序列化策略
2) valueSerializer:value采取的序列化策略
3) hashKeySerializer: 在hash数据结构中,hash-key的序列化策略
4) hashValueSerializer:hash-value的序列化策略
无论如何,建议key/hashKey采用StringRedisSerializer。
配置JdkSerializationRedisSerializer/StringRedisSerializer
<bean id="jedisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory"></property>
<property name="keySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
</property>
<property name="hashKeySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
</property>
<property name="valueSerializer">
<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
</property>
<property name="hashValueSerializer">
<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
</property>
</bean>
ValueOperations<String, User> valueOper = redisTemplate.opsForValue();
User user = new User("zhangsan",12);
valueOper.set("user:1", user);
System.out.println(valueOper.get("user:1").getName());
题外话
spring framework4.X.X版本与spring framework3.X.X有些区别:
spring framework 4.X.X需要jackjson2.0,同时 org.springframework.http.converter.json.MappingJacksonHttpMessageConverter改为 MappingJacksonHttpMessageConverter已经改为MappingJackson2HttpMessageConverter
spring framework 4.X.X配置中使用
<mvc:annotation-driven/>
不再需要手工配置bean
<bean class ="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" >
<property name="messageConverters">
<list>
<ref bean="mappingJackson2HttpMessageConverter" />
</list>
</property>
</bean> <bean name="mappingJackson2HttpMessageConverter"
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=UTF-8</value>
</list>
</property>
</bean>
参考资料
http://snowolf.iteye.com/blog/1630697
http://my.oschina.net/gccr/blog/307725
http://blog.163.com/asd_wll/blog/static/210310402013654528316/
http://wenku.baidu.com/link?url=fuS8aw93_4_Qvv8WBgazt5eZGiDhv1Np5vCyB8qBUVdWIUxI47IaA5opzI3vwhWth7MrF1KiJn_o1aBvWmFdeNxbmbcSnyCTEd54C0iLLEC
http://my.oschina.net/gccr/blog/307725
http://shift-alt-ctrl.iteye.com/blog/1886831
http://www.cnblogs.com/liuling/p/2014-4-19-04.html
http://www.cnblogs.com/tankaixiong/p/3660075.html
http://blog.csdn.net/neubuffer/article/details/17003909
http://blog.csdn.net/liuzhigang1237/article/details/8283797
http://shift-alt-ctrl.iteye.com/blog/1887370
http://shift-alt-ctrl.iteye.com/blog/1887473
http://shift-alt-ctrl.iteye.com/blog/1887644
http://shift-alt-ctrl.iteye.com/blog/1887700
http://shift-alt-ctrl.iteye.com/blog/1886831
http://shift-alt-ctrl.iteye.com/blog/1885910
http://www.open-open.com/lib/view/open1385173126448.html
http://blog.csdn.net/A_lele123/article/details/43406547
http://javacrazyer.iteye.com/blog/1840161
http://redis.readthedocs.org/en/2.4/index.html