zoukankan      html  css  js  c++  java
  • 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;
        }
    }
  • 相关阅读:
    CSS盒子模型
    getContextPath、getServletPath、getRequestURI、request.getRealPath的区别
    MYSQL中的CASE WHEN END AS
    单点登录的精华总结
    git&github
    June 21st 2017 Week 25th Wednesday
    June 20th 2017 Week 25th Tuesday
    June 19th 2017 Week 25th Monday
    June 18th 2017 Week 25th Sunday
    June 17th 2017 Week 24th Saturday
  • 原文地址:https://www.cnblogs.com/onlymate/p/7890813.html
Copyright © 2011-2022 走看看