微信公众号开发教程第19篇——使用Spring+Redis方式保存access_token

使用Spring+Redis方式保存access_token长期有效

除了使用properties和servlet方式之外还有好的方法,希望读者朋友留言分享经验。

示例使用SpringMVC+Redis方式,逻辑流程适用于memcached,这里用到了spring-data-redis-1.7.5.RELEASE.jar

Spring的配置省略,并非本节重点,主要开启注解配置,并在其Spring-context.xml中引用到spring-redis.xml

<!-- 引入同文件夹下的redis属性配置 -->
<import resource="spring-redis.xml"/>

以下就是redis配置,并设置AccessToken的源码了

redis.properties

redis.host=127.0.0.1
redis.port=6379
redis.pass=ibloger.net_redis

redis.maxIdle=200
redis.maxTotal=512
redis.maxWaitMillis=3000
redis.testOnBorrow=true

spring-redis.xml配置

<!-- 扫描redis.properties文件-->
<context:property-placeholder location="classpath:config/redis.properties" />

<!-- 读取redis开始 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
    <property name="maxTotal" value="${redis.maxTotal}" />         <!--最大分配的对象数 -->
    <property name="maxIdle" value="${redis.maxIdle}" />           <!--最大能够保持idel空闲状态的对象数 -->
    <property name="maxWaitMillis" value="${redis.maxWaitMillis}" /> <!--最大等待时间ms -->
    <property name="testOnBorrow" value="${redis.testOnBorrow}" />   <!-- 当调用borrow Object方法时,在获取连接的时候是否进行检查有效性 -->
</bean>

<!-- redis连接工厂类 -->
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
    p:host-name="${redis.host}" p:port="${redis.port}" p:pool-config-ref="jedisPoolConfig" />

<!-- redis模板类 -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate" p:connection-factory-ref="jedisConnectionFactory" />

RedisBaseDao基类

package net.ibloger.wechat.redis.dao;

/**
 * 设置一个Redis基类,根据泛型+反射整理,实现通用方法
 * <br>
 * keyId代表Class类名全路径 + "_" + keyId  例如:key为id,那么到实现类中,就是 net.ibloger.demo.User_id
 * <br>
 * 两个方法没有提供
 *   一:没有提供修改方法,set中key相同替换相当于更新
 *   二:没有提供添加集合方法,因为 T 不能确定某一个参数值,所以集合可以在自定义的dao中实现
 * @param <T>
 */
public interface RedisBaseDao<T>{
    
    /**
     * 根据keyId查询实体
     * @param cls
     * @param keyId 以什么参数作为keyid
     * @return
     */
    public T getEntityByKey(final Class<T> cls, final String keyId);
    
    /**
     * 新增实体
     * @param entity
     * @param keyId
     */
    public void addEntity(final T entity, final String keyId);
    
    /**
     * 新增实体(设置过期时间)
     * @param entity
     * @param keyId
     * @param seconds 多少秒后过期
     */
    public void addEntity(final T entity, final String keyId, long seconds);

    /**
     * 根据keyId删除实体
     * @param cls
     * @param keyId
     */
    public void deleteEntityByKey(final Class<T> cls, final String keyId);

}

RedisBaseDaoImpl实现类

package net.ibloger.wechat.redis.dao;

import java.io.Serializable;

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.data.redis.serializer.RedisSerializer;
import org.springframework.util.Assert;

import com.google.gson.Gson;

/**
 * 实现Redis通用基类
 * 
 * @author http://www.ibloger.net
 * @param <T>
 */
public class RedisBaseDaoImpl<T> implements RedisBaseDao<T> {
    
    @Resource
    protected RedisTemplate<Serializable, Serializable> redisTemplate;

    /**
     * 获取 RedisSerializer
     */
    protected RedisSerializer<String> getStringSerializer() {
        return redisTemplate.getStringSerializer();
    }

    @Override
    public T getEntityByKey(final Class<T> cls, final String keyId) {
        return redisTemplate.execute(new RedisCallback<T>() {
            @Override
            public T doInRedis(RedisConnection connection) throws DataAccessException {
                byte[] key = getStringSerializer().serialize(cls.getName() + "_" + keyId);
                if (connection.exists(key)) {
                    byte[] value = connection.get(key);
                    String json = getStringSerializer().deserialize(value);
                    return new Gson().fromJson(json, cls);
                }
                return null;
            }
        });
    }

    @Override
    public void addEntity(final T entity, final String keyId) {
        Assert.notNull(entity);
        redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                byte[] key =  getStringSerializer().serialize(entity.getClass().getName() + "_" + keyId);
                byte[] name = getStringSerializer().serialize(new Gson().toJson(entity));
                connection.set(key,name);
                return null;
            }
        });
    }
    
    @Override
    public void addEntity(final T entity, final String keyId, final long seconds) {
        Assert.notNull(entity);
        redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                byte[] key =  getStringSerializer().serialize(entity.getClass().getName() + "_" + keyId);
                byte[] name = getStringSerializer().serialize(new Gson().toJson(entity));
                connection.setEx(key, seconds, name);
                System.out.println("connection.getClientName(): "+connection.getClientName());
                return null;
            }
            
            
        });
    }

    @Override
    public void deleteEntityByKey(Class<T> cls, String keyId) {
        redisTemplate.delete(cls.getName() + "_" + keyId);
    }

}

RedisKeyConfig,保存key配置

package net.ibloger.wechat.redis;

/**
 * Redis服务器存储Key的配置
 * @author X-rapido
 *
 */
public class RedisKeyConfig {
	
	/**
	 * 微信消