spring及springboot整合redis

作者: 贪挽懒月 | 来源:发表于2018-07-09 13:47 被阅读117次

    前言:

    redis是一种nosql数据库,以<key,value>的形式存储数据,其速度相比于MySQL之类的数据库,相当于内存读写与硬盘读写的差别,所以常常用作缓存。接下来就分别看看在spring项目和springboot项目中如何使用redis(项目都基于SSM)。

    一、整合前提:

    前提是你已经安装redis且支持远程连接,redis的安装这里不再赘述,有需要的可以参考我的另一篇文章:centos 7.3上安装redis这里主要讲讲如何判断及设置redis支持远程连接。

    1、判断你的redis是否支持远程连接:
    ①:在centos中输入如下命令找到redis-cli:
    whereis redis-cli

    ②:根据返回的目录找到redis-cli,再执行如下命令:
    redis-cli -h 192.168.1.100

    ③:192.168.1.100就是你的虚拟机ip地址。若这一步报错connect refused,可能是防火墙没有开放6379端口。执行如下命令查看开放的端口:
    firewall-cmd --zone=public --list-ports

    ④:若确实没有6379,那就执行如下命令添加:
    firewall-cmd --zone=public --add-port=6379/tcp --permanent

    ⑤:然后重启防火墙:
    firewall-cmd --reload

    ⑥:开放了6379端口后再次执行:
    redis-cli -h 192.168.1.100
    出现如下结果:

    image.png

    ⑦:这样并不能说明支持远程连接,我们输入ping,如果返回pong,说明才是支持远程连接的。

    image.png

    2、设置支持远程连接:
    如果没有返回pong,而是报错了,执行如下操作:
    ①:先关闭redis:
    redis-cli shutdown

    ②:找到redis的配置文件:
    whereis redis.conf

    ③:根据返回的目录,用vim打开redis.conf:
    vim redis.conf
    bind 127.0.0.1改成bind 0.0.0.0,
    再把protected-mode=yesyes改为no,

    image.png

    ④:然后保存退出,重启redis,再用redis-cli连接,输入ping,就能返回pong了。

    二、spring整合redis:

    1、引依赖:
    pom.xml:

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

    除了ssm整合以及mysql数据库需要的依赖,引入以上两个即可。
    注意:
    注意这两个jar包的版本搭配,有些版本搭配可能会报错,这两个版本搭配亲测可用。

    2、编写RedisUtil工具类:

    package com.zhu.redis.util;
    
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.util.concurrent.Callable;
    
    import org.springframework.cache.Cache;
    import org.springframework.cache.support.SimpleValueWrapper;
    import org.springframework.data.redis.connection.RedisConnection;
    import org.springframework.data.redis.core.RedisCallback;
    import org.springframework.data.redis.core.RedisTemplate;
    
    public class RedisUtil implements Cache {
    
        private RedisTemplate<String, Object> redisTemplate;
        private String name;
    
        public RedisTemplate<String, Object> getRedisTemplate() {
            return redisTemplate;
        }
    
        public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
            this.redisTemplate = redisTemplate;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getName() {
            return this.name;
        }
    
        public 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 e) {
                e.printStackTrace();
            }
            return bytes;
        }
    
        public 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 (Exception e) {
                e.printStackTrace();
            }
            return obj;
        }
    
        @Override
        public Object getNativeCache() {
            return this.redisTemplate;
        }
    
        @Override
        public ValueWrapper get(Object key) {
            System.out.println("--------------------------- get key ----------------------------");
            final String keyf = key.toString();
            Object object = null;
            object = redisTemplate.execute(new RedisCallback<Object>() {
                public Object doInRedis(RedisConnection connection) {
                    byte[] key = keyf.getBytes();
                    byte[] value = connection.get(key);
                    if (value == null) {
                        return null;
                    }
                    return toObject(value);
                }
            });
            return (object != null ? new SimpleValueWrapper(object) : null);
        }
    
        @Override
        public void put(Object key, Object value) {
            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) {
                    byte[] keyb = keyf.getBytes();
                    byte[] valueb = toByteArray(valuef);
                    connection.set(keyb, valueb);
                    if (liveTime > 0) {
                        connection.expire(keyb, liveTime);
                    }
                    return 1L;
                }
            });
    
        }
    
        @Override
        public void evict(Object key) {
            System.out.println("-------------------------- del key ---------------------------");
            final String keyf = key.toString();
            redisTemplate.execute(new RedisCallback<Long>() {
                public Long doInRedis(RedisConnection connection) {
                    return connection.del(keyf.getBytes());
                }
            });
    
        }
    
        @Override
        public void clear() {
            System.out.println("-------------------- clear key -------------------------");
            redisTemplate.execute(new RedisCallback<String>() {
                public String doInRedis(RedisConnection connection) {
                    connection.flushDb();
                    return "ok";
                }
            });
    
        }
    
        @Override
        public ValueWrapper putIfAbsent(Object key, Object value) {
            // TODO Auto-generated method stub
            return null;
        }
    
        @Override
        public <T> T get(Object key, Class<T> type) {
            // 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中配置。该类继承了spring的cache,提供了对缓存的一些基本操作的方法(get、del等),还定义了一个name成员变量,在需要用缓存的地方直接用注解@Cacheable(value="name的值")即可。

    3、连接参数redis.properties:
    redis.properties:

    redis.host=192.168.1.100
    redis.port=6379
    redis.dbIndex=0
    redis.expiration=3000
    redis.maxIdle=300
    redis.maxActive=600
    redis.maxWait=1000
    redis.testOnBorrow=true
    

    4、在spring-redis.xml中整合:

    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
            <property name="maxIdle" value="${redis.maxIdle}" /> 
            <property name="maxTotal" value="${redis.maxActive}" />
            <property name="maxWaitMillis" value="${redis.maxWait}" />
            <property name="testOnBorrow" value="${redis.testOnBorrow}" />
        </bean>
    
        <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="database" value="${redis.dbIndex}" />
            <property name="poolConfig" ref="poolConfig" />
        </bean>
    
        <bean id="redisTemplate"
            class="org.springframework.data.redis.core.RedisTemplate">
            <property name="connectionFactory" ref="jedisConnectionFactory"></property>
        </bean>
        
        <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
            <property name="caches">
                <set>
                    <!-- 自定义的RedisUtil工具类 -->
                    <bean class="com.zhu.redis.util.RedisUtil">
                        <property name="redisTemplate" ref="redisTemplate"/>
                        <!-- name属性的值自己随便写,等下在注解当中用 -->
                        <property name="name" value="common"/>
                    </bean>
                </set>
            </property>
        </bean>
        <!-- 开启注解,这个非常重要,否则等下使用缓存注解不生效 -->
        <cache:annotation-driven cache-manager="cacheManager"/>
    

    注意:
    我这里并没有引入redis.properties,是因为我在spring-dao.xml中引入jdbc.properties时一起引入了,所以这里无需再引入。
    spring-dao.xml中引入.properties文件代码:

        <bean
            class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="locations">
                <list>
                    <value>classpath:jdbc.properties</value>
                    <value>classpath:redis.properties</value>
                </list>
            </property>
        </bean>
    

    5、在service层使用redis:

    @Service
    public class AreaServiceImpl implements AreaService {
        @Autowired
        private AreaDao areaDao;
        
        @Cacheable(value="common") //加入缓存
        //@CacheEvict(value="common") //清除缓存
        @Override
        public List<Area> getAreaList() {
            return areaDao.queryArea();
        }
    
    }
    

    注意:
    要使用redis,实体类必须实现序列化接口(implements Serializable),否则会抛java.io.NotSerializableException异常。

    6、测试:
    首先看一下数据库:

    image.png

    然后用junit测试:
    BaseTest.java:(用来加载配置文件)

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration({ "classpath:spring/spring-dao.xml","classpath:spring/spring-service.xml","classpath:spring/spring-redis.xml"})
    public class BaseTest {
    
    }
    

    AreaServiceTest.java:

    public class AreaServiceTest extends BaseTest {
        
        @Autowired
        private AreaService areaService;
        
        @Test
        public void test() {
            List<Area> areaList = areaService.getAreaList();
            for(Area area : areaList) {
                System.out.println(area.getAreaName());
            }
            System.out.println(areaList.size());
        }
    
    }
    

    看运行结果:

    image.png
    从图中可以看到这一次是从MySQL数据库查询的,且RedisUtil已经运行,因为打印出了put key。

    再运行一次:

    image.png

    可以看到这次没有打印sql语句,且打印了get key,说明缓存已经生效。

    把AreaService.java中的@Cacheable(value="common")注释掉,把@CacheEvict(value="common")放开,
    再次运行:

    image.png
    可以看到又打印出了sql语句,且输出了del key,说明已经清除了缓存,再次从数据库中查找。

    这样就完成了spring与redis的整合。

    三、springboot整合redis:

    springboot整合redis就简单了,只需要简单配置就行了。

    1、引依赖:

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-redis</artifactId>
                <version>1.4.7.RELEASE</version>
            </dependency>
    

    springboot整合redis,只需引入这一个依赖即可。

    2、在application.properties中配置redis:

    spring.redis.host=192.168.1.100
    spring.redis.port=6379
    #连接池最大连接数
    spring.redis.jedis.pool.max-active=8
    #最小空闲连接
    spring.redis.jedis.pool.min-idle=0
    #最大阻塞等待时间,负值表示没有限制
    spring.redis.jedis.pool.max-wait=-1
    #最大空闲连接
    spring.redis.jedis.pool.max-idle=8
    #连接超时时间(毫秒)
    spring.redis.timeout=20
    

    3、在springboot启动类上加上@EnableCaching注解,如下图:

    image.png

    4、接下来就可以使用redis缓存了:
    在需要使用的地方加上注解即可。
    AreaServiceImpl.java:

    @Service
    public class AreaServiceImpl implements AreaService {
    
        @Autowired
        private AreaDao areaDao;
    
    
        @Override
        @Transactional
        @Cacheable(value = "common")
        //@CacheEvict(value="common")
        public List<Area> getAreaList() {
            return areaDao.queryArea();
        }
    
    }
    

    5、测试:
    AreaServiceTest.java:

    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class AreaServiceTest {
        
        @Autowired
        private AreaService areaService;
        
        @Test
        public void test() {
            List<Area> areaList = areaService.getAreaList();
            for(Area area : areaList) {
                System.out.println(area.getAreaName());
            }
        }
    
    }
    

    第一次运行:

    image.png

    打印出了sql语句,是从数据库查询的。

    第二次运行:

    image.png
    没有sql语句,是从缓存中读取的。

    @Cacheable(value = "common")注释掉,把@CacheEvict(value="common")放开,
    再运行:

    image.png
    又是从数据库中读取的,说明redis清除成功。

    总结:

    spring整合redis:
    总的来说就是引依赖、编写RedisUtil、编写redis.properties、在spring-redis.xml中配置,最后在需要使用的地方用注解就行了。
    spring boot整合redis:
    总的来说就是引依赖、在application.properties中配置、在启动类上加@EnableCaching注解,然后在需要使用的地方用注解就行了。
    注意事项:
    1、首先你的redis得支持远程连接。
    2、实体类必须实现序列化接口。
    3、redis的注解是@Cacheable(value = "?",key="?")这样的key、value形式,value必须自己指定,可以随便写,key可以不写,不写会自动生成。
    4、如果要使用缓存的方法的参数是引用类型,比如方法是getStudent(Student stu)那么该引用类型的必须重写toString方法,即Student实体类必须重写toString方法,否则使用缓存时会报错can not convert com.zhu.entity.Student toString
    项目说明:
    1、springboot是springboot2.x,springboot 1.5整合redis与这个不一样。
    2、上述两个项目之所以会打印查询的sql语句,是因为在mybatis-config.xml中进行了如下配置:

        <settings>
            <!-- 打印查询语句 -->
            <setting name="logImpl" value="STDOUT_LOGGING" />
        </settings>
    

    以上内容属于个人笔记整理,如有错误,欢迎批评指正!

    相关文章

      网友评论

        本文标题:spring及springboot整合redis

        本文链接:https://www.haomeiwen.com/subject/dcxzuftx.html