Spring整合Redis緩存

 通常而言目前的數據庫分類有幾種,包括 SQL/NSQL,,關係數據庫,鍵值數據庫等等 等,分類的標準也不以,Redis本質上也是一種鍵值數據庫的,但它在保持鍵值數據庫簡單快捷特點的同時,又吸收了部分關係數據庫的優點。從而使它的位置處於關係數據庫和鍵值數 據庫之間。Redis不僅能保存Strings類型的數據,還能保存Lists類型(有序)和Sets類型(無序)的數據,而且還能完成排序(SORT) 等高級功能,在實現INCR,SETNX等功能的時候,保證了其操作的原子性,除此以外,還支持主從複製等功能。

現在很流行用redis做緩存,本項目也先簡單地將redis緩存整合到SSM項目之中,主要有以下幾步:

1.pom.xml中引入jar包

	<!--redis-->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
            <version>1.6.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.8.0</version>
        </dependency>

2.新建spring-redis.xml配置文件

要啓用緩存支持,我們需要在spring的配置文件中進行配置。Redis 不是應用的共享內存,它只是一個內存服務器,就像 MySql 似的,我們需要將應用連接到它並使用某種“語言”進行交互,因此我們還需要一個連接工廠以及一個 Spring 和 Redis 對話要用的 RedisTemplate,這些都是 Redis 緩存所必需的配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:cache="http://www.springframework.org/schema/cache"
	xsi:schemaLocation="http://www.springframework.org/schema/beans      
                        http://www.springframework.org/schema/beans/spring-beans-4.2.xsd      
                        http://www.springframework.org/schema/context      
                        http://www.springframework.org/schema/context/spring-context-4.2.xsd      
                        http://www.springframework.org/schema/mvc      
                        http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd  
                        http://www.springframework.org/schema/cache   
                        http://www.springframework.org/schema/cache/spring-cache-4.2.xsd">

	<!-- 緩存的層級-->
 	<context:component-scan base-package="com.lw.common.utils" />
	
	<context:property-placeholder location="classpath:redis.properties" />

	<!-- redis 相關配置 -->
	<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
		<property name="maxIdle" value="${redis.maxIdle}" />
		<property name="maxWaitMillis" value="${redis.maxWait}" />
		<property name="testOnBorrow" value="${redis.testOnBorrow}" />
	</bean>

	<!-- redis單節點數據庫連接配置 -->
	<bean id="JedisConnectionFactory"
		class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
		<property name="hostName" value="${redis.host}" />
		<property name="port" value="${redis.port}" />
		<property name="password" value="${redis.pass}" />
		<property name="poolConfig" ref="poolConfig" />
	</bean>

	<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
		<property name="connectionFactory" ref="JedisConnectionFactory" />
	</bean>
</beans>    
redis.properties中設置了redis服務器的所需的參數:

redis.host=127.0.0.1
redis.port=6379
redis.pass=password
#控制一個pool最多有多少個狀態爲idle(空閒)的jedis實例
redis.maxIdle=100
redis.maxActive=300
# 表示當borrow(引入)一個jedis實例時,最大的等待時間,如果超過等待時間(毫秒),則直接拋出JedisConnectionException;    
redis.maxWait=3000  
# 在borrow一個jedis實例時,是否提前進行validate操作;如果爲true,則得到的jedis實例均是可用的    
redis.testOnBorrow=true  
配置完成後將配置文件引入到Spring配置文件中
<!-- 引入同文件夾下的redis屬性配置文件 -->
<import resource="spring-redis.xml" />

3.定義redisCache類來實現對redis的操作
package com.lw.common.cache;

import java.util.List;
import java.util.Set;

import javax.annotation.Resource;

import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;


@Component
public class RedisCache {

  public final static String CAHCENAME = "cache";// 緩存名
  public final static int CAHCETIME = 60;// 默認緩存時間

  @Resource
  private RedisTemplate redisTemplate;
//  private SimpleCacheManager cacheManager;

  public  boolean putCache(String key, T obj) {
   final byte[] bkey = key.getBytes();
   final byte[] bvalue = ProtoStuffSerializerUtil.serialize(obj);
   boolean result = redisTemplate.execute(new RedisCallback() {
    @Override
    public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
     return connection.setNX(bkey, bvalue);
    }
   });
   return result;
  }

  public  void putCacheWithExpireTime(String key, T obj, final long expireTime) {
   final byte[] bkey = key.getBytes();
   final byte[] bvalue = ProtoStuffSerializerUtil.serialize(obj);
   redisTemplate.execute(new RedisCallback() {
    @Override
    public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
     connection.setEx(bkey, expireTime, bvalue);
     return true;
    }
   });
  }

  public  boolean putListCache(String key, List objList) {
   final byte[] bkey = key.getBytes();
   final byte[] bvalue = ProtoStuffSerializerUtil.serializeList(objList);
   boolean result = redisTemplate.execute(new RedisCallback() {
    @Override
    public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
     return connection.setNX(bkey, bvalue);
    }
   });
   return result;
  }

  public  boolean putListCacheWithExpireTime(String key, List objList, final long expireTime) {
   final byte[] bkey = key.getBytes();
   final byte[] bvalue = ProtoStuffSerializerUtil.serializeList(objList);
   boolean result = redisTemplate.execute(new RedisCallback() {
    @Override
    public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
     connection.setEx(bkey, expireTime, bvalue);
     return true;
    }
   });
   return result;
  }

  public  T getCache(final String key, Class targetClass) {
   byte[] result = redisTemplate.execute(new RedisCallback() {
    @Override
    public byte[] doInRedis(RedisConnection connection) throws DataAccessException {
     return connection.get(key.getBytes());
    }
   });
   if (result == null) {
    return null;
   }
   return ProtoStuffSerializerUtil.deserialize(result, targetClass);
  }

  public  List getListCache(final String key, Class targetClass) {
   byte[] result = redisTemplate.execute(new RedisCallback() {
    @Override
    public byte[] doInRedis(RedisConnection connection) throws DataAccessException {
     return connection.get(key.getBytes());
    }
   });
   if (result == null) {
    return null;
   }
   return ProtoStuffSerializerUtil.deserializeList(result, targetClass);
  }

  /**
   * 精確刪除key
   * 
   * @param key
   */
  public void deleteCache(String key) {
   redisTemplate.delete(key);
  }

  /**
   * 模糊刪除key
   * 
   * @param pattern
   */
  public void deleteCacheWithPattern(String pattern) {
   Set keys = redisTemplate.keys(pattern);
   redisTemplate.delete(keys);
  }

  /**
   * 清空所有緩存
   */
  public void clearCache() {
   deleteCacheWithPattern(RedisCache.CAHCENAME+"|*");
  }

}
其中ProtoStuffSerializerUtil爲一個序列化和反序列化對象的工具類
package com.lw.common.utils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;

import com.dyuproject.protostuff.LinkedBuffer;
import com.dyuproject.protostuff.ProtostuffIOUtil;
import com.dyuproject.protostuff.Schema;
import com.dyuproject.protostuff.runtime.RuntimeSchema;

/**
 * 序列話工具
 */
public class ProtoStuffSerializerUtil {

	/**
	 * 序列化對象
	 * @param obj
	 * @return
	 */
	public static  byte[] serialize(T obj) {
		if (obj == null) {
			throw new RuntimeException("序列化對象(" + obj + ")!");
		}
		@SuppressWarnings("unchecked")
		Schema schema = (Schema) RuntimeSchema.getSchema(obj.getClass());
		LinkedBuffer buffer = LinkedBuffer.allocate(1024 * 1024);
		byte[] protostuff = null;
		try {
			protostuff = ProtostuffIOUtil.toByteArray(obj, schema, buffer);
		} catch (Exception e) {
			throw new RuntimeException("序列化(" + obj.getClass() + ")對象(" + obj + ")發生異常!", e);
		} finally {
			buffer.clear();
		}
		return protostuff;
	}

	/**
	 * 反序列化對象
	 * @param paramArrayOfByte
	 * @param targetClass
	 * @return
	 */
	public static  T deserialize(byte[] paramArrayOfByte, Class targetClass) {
		if (paramArrayOfByte == null || paramArrayOfByte.length == 0) {
			throw new RuntimeException("反序列化對象發生異常,byte序列爲空!");
		}
		T instance = null;
		try {
			instance = targetClass.newInstance();
		} catch (InstantiationException | IllegalAccessException e) {
			throw new RuntimeException("反序列化過程中依據類型創建對象失敗!", e);
		}
		Schema schema = RuntimeSchema.getSchema(targetClass);
		ProtostuffIOUtil.mergeFrom(paramArrayOfByte, instance, schema);
		return instance;
	}

	/**
	 * 序列化列表
	 * @param objList
	 * @return
	 */
	public static  byte[] serializeList(List objList) {
		if (objList == null || objList.isEmpty()) {
			throw new RuntimeException("序列化對象列表(" + objList + ")參數異常!");
		}
		@SuppressWarnings("unchecked")
		Schema schema = (Schema) RuntimeSchema.getSchema(objList.get(0).getClass());
		LinkedBuffer buffer = LinkedBuffer.allocate(1024 * 1024);
		byte[] protostuff = null;
		ByteArrayOutputStream bos = null;
		try {
			bos = new ByteArrayOutputStream();
			ProtostuffIOUtil.writeListTo(bos, objList, schema, buffer);
			protostuff = bos.toByteArray();
		} catch (Exception e) {
			throw new RuntimeException("序列化對象列表(" + objList + ")發生異常!", e);
		} finally {
			buffer.clear();
			try {
				if (bos != null) {
					bos.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		return protostuff;
	}

	/**
	 * 反序列化列表
	 * @param paramArrayOfByte
	 * @param targetClass
	 * @return
	 */
	public static  List deserializeList(byte[] paramArrayOfByte, Class targetClass) {
		if (paramArrayOfByte == null || paramArrayOfByte.length == 0) {
			throw new RuntimeException("反序列化對象發生異常,byte序列爲空!");
		}

		Schema schema = RuntimeSchema.getSchema(targetClass);
		List result = null;
		try {
			result = ProtostuffIOUtil.parseListFrom(new ByteArrayInputStream(paramArrayOfByte), schema);
		} catch (IOException e) {
			throw new RuntimeException("反序列化對象列表發生異常!", e);
		}
		return result;
	}

}
工具類又需要引入額外的jar包
<dependency>
	<groupId>com.dyuproject.protostuff</groupId>
	<artifactId>protostuff-core</artifactId>
	<version>1.0.8</version>
</dependency>

<dependency>
	<groupId>com.dyuproject.protostuff</groupId>
	<artifactId>protostuff-runtime</artifactId>
	<version>1.0.8</version>
</dependency>
至此Spring整合redis緩存的配置工作便準備完成,下面便可在service層使用
package com.lw.service;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.lw.common.utils.RedisCache;
import com.lw.dao.UserMapper;
import com.lw.entity.User;

@Service
public class UserService {
  
  private static final Logger log = LoggerFactory.getLogger(UserService.class);
  
  @Resource
  private UserMapper userMapper;
  @Resource
  private RedisCache redisCache;
  
  public User getUserById(int userId) {
    String cache_key = RedisCache.CAHCENAME+"|getUserById"+userId;
    User user = redisCache.getCache(cache_key, User.class);
    if(user != null){
      log.info("get cache with key:"+cache_key);
    }else {
      user = userMapper.selectByPrimaryKey(userId);
      redisCache.putCacheWithExpireTime(cache_key, user, RedisCache.CAHCETIME);
      log.info("put cache with key:"+cache_key);
    }
    return user;
  }
}
當系統獲取用戶信息時,先從緩存中查找,如果緩存中由數據便直接返回,如果沒有便從數據庫中獲取,並將結果存入到緩存中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章