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;
        }
    }
  • 相关阅读:
    runtime-给系统已有类添加属性
    解决自定义leftBarButtonItem返回手势失效的方法
    类和对象
    内存拷贝
    响应者链
    属性
    懒加载
    封装思想
    屏幕旋转
    block
  • 原文地址:https://www.cnblogs.com/onlymate/p/7890813.html
Copyright © 2011-2022 走看看