Spring与Redis的实现

前言

  Redis作为缓存还是相当不错的,一定程度上缓解了数据库的IO操作,具体不多说,具体网上查找资料。

实战

不多说,直接上代码。

第一步:所需要的依赖

 

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

第二步:Redis配置文件及其相关文件

公用参数配置

#============================#
#==== Redis settings ====#
#============================#
#redis 服务器 IP
redis.host=127.0.0.1
#redis 服务器端口
redis.port=6379
#redis 密码
redis.pass=

#redis 支持16个数据库(相当于不同用户)可以使不同的应用程序数据彼此分开同时又存储在相同的实例上
redis.dbIndex=0
#redis 缓存数据过期时间单位秒
redis.expiration=3000
#控制一个 pool 最多有多少个状态为 idle 的jedis实例(连接池中空闲的连接数)
redis.maxIdle=300
redis.minIdle=5
#控制一个 pool 可分配多少个jedis实例(连接池中最大连接数)
redis.maxActive=600
#当borrow一个jedis实例时,最大的等待时间,如果超过等待时间,则直接抛出JedisConnectionException;
redis.maxWait=1000
#在borrow一个jedis实例时,是否提前进行alidate操作;如果为true,则得到的jedis实例均是可用的;
redis.testOnBorrow=true

 

方法一

采取手动配置Redis缓存,一定要记得开启缓存

spring-redis.xml

<?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:context="http://www.springframework.org/schema/context"
    xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx" 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.3.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd 
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd 
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
    http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
    
    <description>redis 相关类 Spring 托管</description>
    
    <!-- 开启缓存 -->
    <cache:annotation-driven />
    
    <!--载入 redis 配置文件-->
    <context:property-placeholder location="classpath:redis.properties" ignore-unresolvable="true"/>

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

    <!-- 配置JedisConnectionFactory -->
    <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="database" value="${redis.dbIndex}"/>
        <property name="poolConfig" ref="poolConfig"/>
    </bean>

    <!-- SDR默认采用的序列化策略有两种,一种是String的序列化策略,一种是JDK的序列化策略。
        StringRedisTemplate默认采用的是String的序列化策略,保存的key和value都是采用此策略序列化保存的。 
        RedisTemplate默认采用的是JDK的序列化策略,保存的key和value都是采用此策略序列化保存的。 
        就是因为序列化策略的不同,即使是同一个key用不同的Template去序列化,结果是不同的。所以根据key去删除数据的时候就出现了删除失败的问题。
    -->
    <!-- redis 序列化策略 ,通常情况下key值采用String序列化策略, -->
    <!-- 如果不指定序列化策略,StringRedisTemplate的key和value都将采用String序列化策略; -->
    <!-- 但是RedisTemplate的key和value都将采用JDK序列化 这样就会出现采用不同template保存的数据不能用同一个template删除的问题 -->
    <!-- 配置RedisTemplate -->
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory" ref="jedisConnectionFactory" />
        <property name="keySerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
        </property>
        <!-- <property name="valueSerializer" ref="stringRedisSerializer" /> value值如果是对象,这不能用stringRedisSerializer,报类型转换错误-->
        <!-- <property name="valueSerializer">
            hex(十六进制)的格式
            <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
        </property> -->
        <property name="valueSerializer">
            <!-- json的格式,要注意实体属性名有没有‘_’,如user_name,有的话要加注解 ,@JsonNaming会将userName处理为user_name
                   @JsonSerialize
                @JsonNaming(PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy.class) 
               -->
            <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer" />
        </property>
    </bean>

    <!-- spring自己的缓存管理器,这里定义了缓存位置名称 ,即注解中的value -->
    <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
        <property name="caches">
            <set>
                <!-- 这里可以配置多个redis -->
                <bean class="com.only.mate.utils.RedisCache">
                    <property name="redisTemplate" ref="redisTemplate" />
                    <property name="name" value="default" />
                </bean>
                <bean class="com.only.mate.utils.RedisCache">
                    <property name="redisTemplate" ref="redisTemplate" />
                    <property name="name" value="common" /> <!-- common名称要在类或方法的注解中使用 -->
                </bean>
                <bean class="com.only.mate.utils.RedisCache">
                    <property name="redisTemplate" ref="redisTemplate" />
                    <property name="name" value="user" /> <!-- common名称要在类或方法的注解中使用 -->
                </bean>
            </set>
        </property>
    </bean>
    <!-- 配置RedisCacheManager -->
    <!-- <bean id="redisCacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">
        <constructor-arg name="redisOperations" ref="redisTemplate" />
        <property name="defaultExpiration" value="${redis.expiration}" />
    </bean> -->
    <!-- 配置RedisCacheConfig -->
    <!-- <bean id="redisCacheConfig" class="com.only.mate.utils.RedisCacheConfig"> 
        <constructor-arg ref="jedisConnectionFactory"/> <constructor-arg ref="redisTemplate"/> 
        <constructor-arg ref="redisCacheManager"/> </bean> -->
</beans>

 

RedisCache.java

package com.only.mate.utils;

import java.util.concurrent.Callable;

import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;
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;

/**
 * 仔细需要写redis缓存的key和value的序列化操作
 * 此处还可做拓展:
 *     1.存储的时候判断该key值是否已经存在
 *     2.读取的时候判断key值是否存在
 *     3.拿取数据的时候判断有没有失效
 * @ClassName: RedisCache 
 * @Description: TODO
 * @author OnlyMate
 * @date 2017年11月24日 下午3:08:55
 */
@SuppressWarnings({ "unchecked"})
public class RedisCache implements Cache {

    private RedisTemplate<String, Object> redisTemplate;
    private String name;
    
    private RedisSerializer<Object> keySerializer;
    private RedisSerializer<Object> valueSerializer;
    
    public RedisTemplate<String, Object> getRedisTemplate() {
        return redisTemplate;
    }

    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
        keySerializer = (RedisSerializer<Object>) redisTemplate.getKeySerializer();
        valueSerializer = (RedisSerializer<Object>) redisTemplate.getValueSerializer();
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        // TODO Auto-generated method stub
        return this.name;
    }

    @Override
    public Object getNativeCache() {
        // TODO Auto-generated method stub
        return this.redisTemplate;
    }

    @Override
    public ValueWrapper get(Object key) {
        // TODO Auto-generated method stub
        System.out.println("get key");
        final String keyf = key.toString();
        Object object = null;
        object = redisTemplate.execute(new RedisCallback<Object>() {
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                byte[] key = keySerializer.serialize(keyf);
                byte[] value = connection.get(key);
                if (value == null) {
                    return null;
                }
                return valueSerializer.deserialize(value);
            }
        });
        return (object != null ? new SimpleValueWrapper(object) : null);
    }

    @Override
    public void put(Object key, Object value) {
        // TODO Auto-generated method stub
        System.out.println("put key");
        final String keyf = key.toString();
        final Object valuef = value;
        final long liveTime = 86400;
        redisTemplate.execute(new RedisCallback<Long>() {
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                byte[] keyb =keySerializer.serialize(keyf);
//                byte[] valueb = toByteArray(valuef);
                byte[] valueb = valueSerializer.serialize(valuef);
                connection.set(keyb, valueb);
                if (liveTime > 0) {
                    connection.expire(keyb, liveTime);
                }
                return 1L;
            }
        });
    }
    
    /**
     * 此处写死了序列化,无法用xml中定义redisTemplate的key和value对应的序列化
     * Title: evict
     * @see org.springframework.cache.Cache#evict(java.lang.Object)
     */
    /*private byte[] toByteArray(Object obj) {
        byte[] bytes = null;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(obj);
            oos.flush();
            bytes = bos.toByteArray();
            oos.close();
            bos.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return bytes;
    }

    private Object toObject(byte[] bytes) {
        Object obj = null;
        try {
            ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
            ObjectInputStream ois = new ObjectInputStream(bis);
            obj = ois.readObject();
            ois.close();
            bis.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        } catch (ClassNotFoundException ex) {
            ex.printStackTrace();
        }
        return obj;
    }*/

    @Override
    public void evict(Object key) {
        // TODO Auto-generated method stub
        System.out.println("del key");
        final String keyf = key.toString();
        redisTemplate.execute(new RedisCallback<Long>() {
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.del(keyf.getBytes());
            }
        });
    }

    @Override
    public void clear() {
        // TODO Auto-generated method stub
        System.out.println("clear key");
        redisTemplate.execute(new RedisCallback<String>() {
            public String doInRedis(RedisConnection connection) throws DataAccessException {
                connection.flushDb();
                return "ok";
            }
        });
    }

    @Override
    public <T> T get(Object key, Class<T> type) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public ValueWrapper putIfAbsent(Object key, Object value) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public <T> T get(Object key, Callable<T> valueLoader) {
        // TODO Auto-generated method stub
        return null;
    }

}

 

方法二

采用注解的形式开启缓存

spring-redis.xml

<?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:context="http://www.springframework.org/schema/context"
    xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd 
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd 
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
    
    <description>redis 相关类 Spring 托管</description>
    
    
    <!--载入 redis 配置文件-->
    <context:property-placeholder location="classpath:redis.properties" ignore-unresolvable="true"/>

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

    <!-- 配置JedisConnectionFactory -->
    <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="database" value="${redis.dbIndex}"/>
        <property name="poolConfig" ref="poolConfig"/>
    </bean>

    <!-- SDR默认采用的序列化策略有两种,一种是String的序列化策略,一种是JDK的序列化策略。
        StringRedisTemplate默认采用的是String的序列化策略,保存的key和value都是采用此策略序列化保存的。 
        RedisTemplate默认采用的是JDK的序列化策略,保存的key和value都是采用此策略序列化保存的。 
        就是因为序列化策略的不同,即使是同一个key用不同的Template去序列化,结果是不同的。所以根据key去删除数据的时候就出现了删除失败的问题。
    -->
    <!-- redis 序列化策略 ,通常情况下key值采用String序列化策略, -->
    <!-- 如果不指定序列化策略,StringRedisTemplate的key和value都将采用String序列化策略; -->
    <!-- 但是RedisTemplate的key和value都将采用JDK序列化 这样就会出现采用不同template保存的数据不能用同一个template删除的问题 -->
    <!-- 配置RedisTemplate -->
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory" ref="jedisConnectionFactory" />
        <property name="keySerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
        </property>
        <!-- <property name="valueSerializer" ref="stringRedisSerializer" /> value值如果是对象,这不能用stringRedisSerializer,报类型转换错误-->
        <!-- <property name="valueSerializer">
            hex(十六进制)的格式
            <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
        </property> -->
        <property name="valueSerializer">
            <!-- json的格式,要注意实体属性名有没有‘_’,如user_name,有的话要加注解 ,@JsonNaming会将userName处理为user_name
                   @JsonSerialize
                @JsonNaming(PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy.class) 
               -->
            <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer" />
        </property>
    </bean>

    <!-- 配置RedisCacheManager -->
    <bean id="redisCacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">
        <constructor-arg name="redisOperations" ref="redisTemplate" />
        <property name="defaultExpiration" value="${redis.expiration}" />
    </bean>
    <!-- 配置RedisCacheConfig -->
    <bean id="redisCacheConfig" class="com.only.mate.utils.RedisCacheConfig"> 
        <constructor-arg ref="jedisConnectionFactory"/> 
        <constructor-arg ref="redisTemplate"/> 
        <constructor-arg ref="redisCacheManager"/> 
    </bean>
</beans>

 

RedisCacheConfig.java

package com.only.mate.utils;

import java.lang.reflect.Method;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;

@Configuration  
@EnableCaching 
public class RedisCacheConfig extends CachingConfigurerSupport {
    protected final static Logger log = LoggerFactory.getLogger(RedisCacheConfig.class);

    private volatile JedisConnectionFactory mJedisConnectionFactory;
    private volatile RedisTemplate<String, String> mRedisTemplate;
    private volatile RedisCacheManager mRedisCacheManager;

    public RedisCacheConfig() {
        super();
        log.info("RedisCacheConfig()");
    }

    public RedisCacheConfig(JedisConnectionFactory mJedisConnectionFactory, RedisTemplate<String, String> mRedisTemplate, RedisCacheManager mRedisCacheManager) {
        super();
        this.mJedisConnectionFactory = mJedisConnectionFactory;
        this.mRedisTemplate = mRedisTemplate;
        this.mRedisCacheManager = mRedisCacheManager;
        log.info("RedisCacheConfig(a,b,c)");
    }

    public JedisConnectionFactory redisConnectionFactory() {
        return mJedisConnectionFactory;
    }

    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory cf) {
        return mRedisTemplate;
    }

    public CacheManager cacheManager(RedisTemplate<?, ?> redisTemplate) {
        return mRedisCacheManager;
    }

    @Bean
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object o, Method method, Object... objects) {
                StringBuilder sb = new StringBuilder();
                sb.append(o.getClass().getName());
                sb.append(method.getName());
                for (Object obj : objects) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }
}

第三步:在Spring的配置文件中引入spring-reids.xml

<import resource="classpath:spring-redis.xml" />

第四步:在Java代码中调用

下面注释掉的代码中,是手动缓存的操作

package com.only.mate.service.impl;

import java.io.Serializable;

import org.apache.ibatis.annotations.CacheNamespace;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSON;
import com.only.mate.entity.User;
import com.only.mate.repository.UserMapper;
import com.only.mate.service.UserService;

@Service
@Transactional
@CacheConfig(cacheNames="user")
public class UserServiceImpl implements UserService {
    /*@Autowired
    private UserDao userDao;
    
    @Override
    public User findOne(String username) {
        return userDao.getUserByUserName(username);
    }

    @Override
    public void save(User user) {
        userDao.saveUser(user);
    }*/
    /*@Autowired
    protected RedisTemplate<Serializable, Serializable> redisTemplate;*/
    
    @Autowired
    private UserMapper userMapper;
    
    @Override
    @Cacheable(value="user", key="#username")
    public User findOne(String username) {
        User user = userMapper.getUserByUserName(username);
        //使用最基本的方式
        /*redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                connection.set(redisTemplate.getStringSerializer().serialize("user." + user.getUserName()),
                        redisTemplate.getStringSerializer().serialize(JSON.toJSONString(user)));
                return null;
            }
        });*/
        //利用了StringRdisTemplate的特性
        /*redisTemplate.opsForValue().set("user." + user.getUserName(), JSON.toJSONString(user));*/
        
        //利用了StringRdisTemplate的特性 通过绑定的方式
        /*BoundValueOperations<Serializable, Serializable> bound = redisTemplate.boundValueOps("user." + user.getUserName());
        bound.append(JSON.toJSONString(user));
        //bound.append(JSON.toJSONString(user));//追加,和StringBuilder的append一样功能
        */        
        return user;
    }
    
    @Override
    @CachePut(value="user", key="#user.userName")
    public User save(User user) {
        Integer userId = userMapper.insert(user);
        if("zhangsan".equals(user.getUserName())){
            throw new RuntimeException();
        }
        return user;
    }
}

 

posted @ 2017-11-24 15:24  路途寻码人  阅读(467)  评论(0编辑  收藏  举报