ssm框架之前已经搭建过了,这里不再做代码复制工作。
这里主要是利用redis去做mybatis的二级缓存,mybaits映射文件中所有的select都会刷新已有缓存,如果不存在就会新建缓存,所有的insert,update操作都会更新缓存。
redis的好处也显而易见,可以使系统的数据访问性能更高。本节只是展示了整合方法和效果,后面会补齐redis集群、负载均衡和session共享的文章。
下面就开始整合工作:
后台首先启动redis-server(后台启动与远程连接linux服务的方法都需要改redis.conf文件),启动命令“./src/redis-server ./redis.conf”
我这里是windows系统下开发的,推荐一个可视化工具“Redis Desktop manager”,需要远程连接linux下的redis,需要linux下开启端口对外开放(具体方法是修改/etc/sysconfig/iptables文件,增加对外端口开发命令)。
以上操作都完成后,即可远程连接成功了,如图:
现在还没有缓存记录,下面进入代码阶段,首先在pom.xml中增加需要的redis jar包
1 <dependency> 2 <groupId>redis.clients</groupId> 3 <artifactId>jedis</artifactId> 4 <version>2.9.0</version> 5 </dependency> 6 7 <dependency> 8 <groupId>org.springframework.data</groupId> 9 <artifactId>spring-data-redis</artifactId>10 <version>1.6.2.RELEASE</version>11 </dependency>12 13 <dependency>14 <groupId>org.mybatis</groupId>15 <artifactId>mybatis-ehcache</artifactId>16 <version>1.0.0</version>17 </dependency>18 <!-- 添加druid连接池包 -->19 <dependency>20 <groupId>com.alibaba</groupId>21 <artifactId>druid</artifactId>22 <version>1.0.24</version>23 </dependency>
pom.xml写好后,还需要新增两个配置文件:redis.properties
redis.host=192.168.0.109 redis.port=6379 redis.pass=123456 redis.maxIdle=200 redis.maxActive=1024 redis.maxWait=10000 redis.testOnBorrow=true
其中字段也都很好理解,再加入配置文件:spring-redis.xml
1 <beans xmlns="http://www.springframework.org/schema/beans" 2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns:p="http://www.springframework.org/schema/p" 4 xmlns:mvc="http://www.springframework.org/schema/mvc" 5 xmlns:util="http://www.springframework.org/schema/util" 6 xmlns:aop="http://www.springframework.org/schema/aop" 7 xmlns:context="http://www.springframework.org/schema/context" 8 xmlns:task="http://www.springframework.org/schema/task" 9 xsi:schemaLocation="http://www.springframework.org/schema/beans10 http://www.springframework.org/schema/beans/spring-beans-4.3.xsd11 http://www.springframework.org/schema/util12 http://www.springframework.org/schema/util/spring-util-4.3.xsd13 http://www.springframework.org/schema/mvc14 http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd15 http://www.springframework.org/schema/aop16 http://www.springframework.org/schema/aop/spring-aop-4.3.xsd17 http://www.springframework.org/schema/context18 http://www.springframework.org/schema/context/spring-context-4.3.xsd">19 20 21 <!-- 连接池基本参数配置,类似数据库连接池 -->22 <context:property-placeholder location="classpath*:redis.properties" />23 24 <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">25 <property name="maxTotal" value="${redis.maxActive}"/>26 <property name="maxIdle" value="${redis.maxIdle}" />27 <property name="testOnBorrow" value="${redis.testOnBorrow}"/>28 </bean>29 30 <!-- 连接池配置,类似数据库连接池 -->31 <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" >32 <property name="hostName" value="${redis.host}"></property>33 <property name="port" value="${redis.port}"></property>34 <property name="password" value="${redis.pass}"></property>35 <property name="poolConfig" ref="poolConfig"></property> 36 </bean>37 38 <!-- 调用连接池工厂配置 -->39 <!-- <bean id="redisTemplate" class=" org.springframework.data.redis.core.RedisTemplate">40 <property name="jedisConnectionFactory" ref="jedisConnectionFactory"></property>41 42 如果不配置Serializer,那么存储的时候智能使用String,如果用User类型存储,那么会提示错误User can't cast to String!!! 43 <property name="keySerializer"> 44 <bean 45 class="org.springframework.data.redis.serializer.StringRedisSerializer" /> 46 </property> 47 <property name="valueSerializer"> 48 <bean 49 class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" /> 50 </property> 51 </bean> -->52 <bean id="redisCacheTransfer" class="com.cjl.util.RedisCacheTransfer">53 <property name="jedisConnectionFactory" ref="jedisConnectionFactory" />54 </bean>55 </beans>
配置文件写好后,就开始java代码的编写:
JedisClusterFactory.java
1 package com.cjl.util; 2 3 import java.util.HashSet; 4 import java.util.Properties; 5 import java.util.Set; 6 import java.util.regex.Pattern; 7 8 import org.apache.commons.pool2.impl.GenericObjectPoolConfig; 9 import org.springframework.beans.factory.FactoryBean; 10 import org.springframework.beans.factory.InitializingBean; 11 import org.springframework.core.io.Resource; 12 13 import redis.clients.jedis.HostAndPort; 14 import redis.clients.jedis.JedisCluster; 15 16 public class JedisClusterFactory implements FactoryBean<JedisCluster>, InitializingBean { 17 18 private Resource addressConfig; 19 private String addressKeyPrefix; 20 21 private JedisCluster jedisCluster; 22 private Integer timeout; 23 private Integer maxRedirections; 24 private GenericObjectPoolConfig genericObjectPoolConfig; 25 26 private Pattern p = Pattern.compile("^.+[:]\\d{1,5}\\s*$"); 27 28 public JedisCluster getObject() throws Exception { 29 return jedisCluster; 30 } 31 32 public Class<? extends JedisCluster> getObjectType() { 33 return (this.jedisCluster != null ? this.jedisCluster.getClass() : JedisCluster.class); 34 } 35 36 public boolean isSingleton() { 37 return true; 38 } 39 40 private Set<HostAndPort> parseHostAndPort() throws Exception { 41 try { 42 Properties prop = new Properties(); 43 prop.load(this.addressConfig.getInputStream()); 44 45 Set<HostAndPort> haps = new HashSet<HostAndPort>(); 46 for (Object key : prop.keySet()) { 47 48 if (!((String) key).startsWith(addressKeyPrefix)) { 49 continue; 50 } 51 52 String val = (String) prop.get(key); 53 54 boolean isIpPort = p.matcher(val).matches(); 55 56 if (!isIpPort) { 57 throw new IllegalArgumentException("ip 或 port 不合法"); 58 } 59 String[] ipAndPort = val.split(":"); 60 61 HostAndPort hap = new HostAndPort(ipAndPort[0], Integer.parseInt(ipAndPort[1])); 62 haps.add(hap); 63 } 64 65 return haps; 66 } catch (IllegalArgumentException ex) { 67 throw ex; 68 } catch (Exception ex) { 69 throw new Exception("解析 jedis 配置文件失败", ex); 70 } 71 } 72 73 public void afterPropertiesSet() throws Exception { 74 Set<HostAndPort> haps = this.parseHostAndPort(); 75 76 jedisCluster = new JedisCluster(haps, timeout, maxRedirections, genericObjectPoolConfig); 77 78 } 79 80 public void setAddressConfig(Resource addressConfig) { 81 this.addressConfig = addressConfig; 82 } 83 84 public void setTimeout(int timeout) { 85 this.timeout = timeout; 86 } 87 88 public void setMaxRedirections(int maxRedirections) { 89 this.maxRedirections = maxRedirections; 90 } 91 92 public void setAddressKeyPrefix(String addressKeyPrefix) { 93 this.addressKeyPrefix = addressKeyPrefix; 94 } 95 96 public void setGenericObjectPoolConfig(GenericObjectPoolConfig genericObjectPoolConfig) { 97 this.genericObjectPoolConfig = genericObjectPoolConfig; 98 } 99 100 }
RedisCache.java
1 package com.cjl.util; 2 3 import java.util.concurrent.locks.ReadWriteLock; 4 import java.util.concurrent.locks.ReentrantReadWriteLock; 5 6 import org.apache.ibatis.cache.Cache; 7 import org.slf4j.Logger; 8 import org.slf4j.LoggerFactory; 9 import org.springframework.data.redis.connection.jedis.JedisConnection; 10 import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; 11 import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; 12 import org.springframework.data.redis.serializer.RedisSerializer; 13 14 import redis.clients.jedis.exceptions.JedisConnectionException; 15 16 public class RedisCache implements Cache { 17 private static final Logger logger = LoggerFactory.getLogger(RedisCache.class); 18 19 private static JedisConnectionFactory jedisConnectionFactory; 20 21 private final String id; 22 23 private final ReadWriteLock rwl = new ReentrantReadWriteLock(); 24 25 26 public RedisCache(final String id) { 27 if (id == null) { 28 throw new IllegalArgumentException("Cache instances require an ID"); 29 } 30 logger.debug("MybatisRedisCache:id=" + id); 31 this.id = id; 32 } 33 34 /** 35 * 清空所有缓存 36 */ 37 public void clear() { 38 rwl.readLock().lock(); 39 JedisConnection connection = null; 40 try { 41 connection = jedisConnectionFactory.getConnection(); 42 connection.flushDb(); 43 connection.flushAll(); 44 } catch (JedisConnectionException e) { 45 e.printStackTrace(); 46 } finally { 47 if (connection != null) { 48 connection.close(); 49 } 50 rwl.readLock().unlock(); 51 } 52 } 53 54 public String getId() { 55 return this.id; 56 } 57 58 /** 59 * 获取缓存总数量 60 */ 61 public int getSize() { 62 int result = 0; 63 JedisConnection connection = null; 64 try { 65 connection = jedisConnectionFactory.getConnection(); 66 result = Integer.valueOf(connection.dbSize().toString()); 67 logger.info("添加mybaits二级缓存数量:" + result); 68 } catch (JedisConnectionException e) { 69 e.printStackTrace(); 70 } finally { 71 if (connection != null) { 72 connection.close(); 73 } 74 } 75 return result; 76 } 77 78 public void putObject(Object key, Object value) { 79 rwl.writeLock().lock(); 80 81 JedisConnection connection = null; 82 try { 83 connection = jedisConnectionFactory.getConnection(); 84 RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer(); 85 connection.set(SerializeUtil.serialize(key), SerializeUtil.serialize(value)); 86 logger.info("添加mybaits二级缓存key=" + key + ",value=" + value); 87 } catch (JedisConnectionException e) { 88 e.printStackTrace(); 89 } finally { 90 if (connection != null) { 91 connection.close(); 92 } 93 rwl.writeLock().unlock(); 94 } 95 } 96 97 public Object getObject(Object key) { 98 // 先从缓存中去取数据,先加上读锁 99 rwl.readLock().lock();100 Object result = null;101 JedisConnection connection = null;102 try {103 connection = jedisConnectionFactory.getConnection();104 RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();105 result = serializer.deserialize(connection.get(serializer.serialize(key)));106 logger.info("命中mybaits二级缓存,value=" + result);107 108 } catch (JedisConnectionException e) {109 e.printStackTrace();110 } finally {111 if (connection != null) {112 connection.close();113 }114 rwl.readLock().unlock();115 }116 return result;117 }118 119 public Object removeObject(Object key) {120 rwl.writeLock().lock();121 122 JedisConnection connection = null;123 Object result = null;124 try {125 connection = jedisConnectionFactory.getConnection();126 RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();127 result = connection.expire(serializer.serialize(key), 0);128 } catch (JedisConnectionException e) {129 e.printStackTrace();130 } finally {131 if (connection != null) {132 connection.close();133 }134 rwl.writeLock().unlock();135 }136 return result;137 }138 139 public static void setJedisConnectionFactory(JedisConnectionFactory jedisConnectionFactory) {140 RedisCache.jedisConnectionFactory = jedisConnectionFactory;141 }142 143 public ReadWriteLock getReadWriteLock() {144 // TODO Auto-generated method stub145 return rwl;146 }147 148 }
RedisCacheTransfer.java
1 package com.cjl.util; 2 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; 5 6 /** 7 * 静态注入中间类 8 */ 9 public class RedisCacheTransfer {10 @Autowired11 public void setJedisConnectionFactory(JedisConnectionFactory jedisConnectionFactory) {12 RedisCache.setJedisConnectionFactory(jedisConnectionFactory);13 }14 15 }
SerializeUtil.java
1 package com.cjl.util; 2 3 import java.io.ByteArrayInputStream; 4 import java.io.ByteArrayOutputStream; 5 import java.io.ObjectInputStream; 6 import java.io.ObjectOutputStream; 7 8 /** 9 * 10 * @author cjl11 *12 */13 public class SerializeUtil {14 /**15 * 序列化16 */17 public static byte[] serialize(Object object) {18 ObjectOutputStream oos = null;19 ByteArrayOutputStream baos = null;20 try {21 // 序列化22 baos = new ByteArrayOutputStream();23 oos = new ObjectOutputStream(baos);24 oos.writeObject(object);25 byte[] bytes = baos.toByteArray();26 return bytes;27 } catch (Exception e) {28 e.printStackTrace();29 }30 return null;31 }32 33 /**34 *反序列化35 */36 public static Object unserialize(byte[] bytes) {37 if (bytes !=null) {38 ByteArrayInputStream bais = null;39 try {40 // 反序列化41 bais = new ByteArrayInputStream(bytes);42 ObjectInputStream ois = new ObjectInputStream(bais);43 return ois.readObject();44 } catch (Exception e) {45 46 }47 } 48 return null;49 }50 }
所有东西准备齐全后还需要修改映射文件
要使mybaits缓存生效,还需如上图这样开启二级缓存。配置文件还需要在web.xml中加载生效
一切准备就绪后,启动服务
启动成功后,点击员工表单可以触发查询所有员工的方法,第一次进行查询语句可以看到mybatis打印了查询语句,并在redis服务器中更新了一条缓存
我们清空控制台再次点击查询员工按钮执行查询方法,可以看到没有执行查询语句,证明第二次查询直接从缓存中取值,没有连接mysql进行查询。
以上整合基本已经完成,如有不对的地方希望大家能够指出,后面会补上负载均衡和session共享。
原文地址:http://blog.51cto.com/13531815/2050773