spring及springboot整合redis

前言:

redis是一種nosql數(shù)據(jù)庫(kù),以<key,value>的形式存儲(chǔ)數(shù)據(jù),其速度相比于MySQL之類(lèi)的數(shù)據(jù)庫(kù),相當(dāng)于內(nèi)存讀寫(xiě)與硬盤(pán)讀寫(xiě)的差別,所以常常用作緩存。接下來(lái)就分別看看在spring項(xiàng)目和springboot項(xiàng)目中如何使用redis(項(xiàng)目都基于SSM)。


歡迎大家關(guān)注我的公眾號(hào) javawebkf,目前正在慢慢地將簡(jiǎn)書(shū)文章搬到公眾號(hào),以后簡(jiǎn)書(shū)和公眾號(hào)文章將同步更新,且簡(jiǎn)書(shū)上的付費(fèi)文章在公眾號(hào)上將免費(fèi)。


一、整合前提:

前提是你已經(jīng)安裝redis且支持遠(yuǎn)程連接,redis的安裝這里不再贅述,有需要的可以參考我的另一篇文章:centos 7.3上安裝redis。這里主要講講如何判斷及設(shè)置redis支持遠(yuǎn)程連接。

1、判斷你的redis是否支持遠(yuǎn)程連接:
①:在centos中輸入如下命令找到redis-cli:
whereis redis-cli

②:根據(jù)返回的目錄找到redis-cli,再執(zhí)行如下命令:
redis-cli -h 192.168.1.100

③:192.168.1.100就是你的虛擬機(jī)ip地址。若這一步報(bào)錯(cuò)connect refused,可能是防火墻沒(méi)有開(kāi)放6379端口。執(zhí)行如下命令查看開(kāi)放的端口:
firewall-cmd --zone=public --list-ports

④:若確實(shí)沒(méi)有6379,那就執(zhí)行如下命令添加:
firewall-cmd --zone=public --add-port=6379/tcp --permanent

⑤:然后重啟防火墻:
firewall-cmd --reload

⑥:開(kāi)放了6379端口后再次執(zhí)行:
redis-cli -h 192.168.1.100
出現(xiàn)如下結(jié)果:

image.png

⑦:這樣并不能說(shuō)明支持遠(yuǎn)程連接,我們輸入ping,如果返回pong,說(shuō)明才是支持遠(yuǎn)程連接的。

image.png

2、設(shè)置支持遠(yuǎn)程連接:
如果沒(méi)有返回pong,而是報(bào)錯(cuò)了,執(zhí)行如下操作:
①:先關(guān)閉redis:
redis-cli shutdown

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

③:根據(jù)返回的目錄,用vim打開(kāi)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:

點(diǎn)下載源碼。
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數(shù)據(jù)庫(kù)需要的依賴,引入以上兩個(gè)即可。
注意:
注意這兩個(gè)jar包的版本搭配,有些版本搭配可能會(huì)報(bào)錯(cuò),這兩個(gè)版本搭配親測(cè)可用。

2、編寫(xiě)RedisUtil工具類(lèi):

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;
    }

}

這個(gè)類(lèi)等下要在spring-redis.xml中配置。該類(lèi)繼承了spring的cache,提供了對(duì)緩存的一些基本操作的方法(get、del等),還定義了一個(gè)name成員變量,在需要用緩存的地方直接用注解@Cacheable(value="name的值")即可。

3、連接參數(shù)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工具類(lèi) -->
                <bean class="com.zhu.redis.util.RedisUtil">
                    <property name="redisTemplate" ref="redisTemplate"/>
                    <!-- name屬性的值自己隨便寫(xiě),等下在注解當(dāng)中用 -->
                    <property name="name" value="common"/>
                </bean>
            </set>
        </property>
    </bean>
    <!-- 開(kāi)啟注解,這個(gè)非常重要,否則等下使用緩存注解不生效 -->
    <cache:annotation-driven cache-manager="cacheManager"/>

注意:
我這里并沒(méi)有引入redis.properties,是因?yàn)槲以趕pring-dao.xml中引入jdbc.properties時(shí)一起引入了,所以這里無(wú)需再引入。
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,實(shí)體類(lèi)必須實(shí)現(xiàn)序列化接口(implements Serializable),否則會(huì)拋java.io.NotSerializableException異常。

6、測(cè)試:
首先看一下數(shù)據(jù)庫(kù):

image.png

然后用junit測(cè)試:
BaseTest.java:(用來(lái)加載配置文件)

@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());
    }

}

看運(yùn)行結(jié)果:

image.png

從圖中可以看到這一次是從MySQL數(shù)據(jù)庫(kù)查詢的,且RedisUtil已經(jīng)運(yùn)行,因?yàn)榇蛴〕隽藀ut key。

再運(yùn)行一次:

image.png

可以看到這次沒(méi)有打印sql語(yǔ)句,且打印了get key,說(shuō)明緩存已經(jīng)生效。

把AreaService.java中的@Cacheable(value="common")注釋掉,把@CacheEvict(value="common")放開(kāi),
再次運(yùn)行:

image.png

可以看到又打印出了sql語(yǔ)句,且輸出了del key,說(shuō)明已經(jīng)清除了緩存,再次從數(shù)據(jù)庫(kù)中查找。

這樣就完成了spring與redis的整合。

三、springboot整合redis:

springboot整合redis就簡(jiǎn)單了,只需要簡(jiǎn)單配置就行了。

1、引依賴:

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

springboot整合redis,只需引入這一個(gè)依賴即可。

2、在application.properties中配置redis:

spring.redis.host=192.168.1.100
spring.redis.port=6379
#連接池最大連接數(shù)
spring.redis.jedis.pool.max-active=8
#最小空閑連接
spring.redis.jedis.pool.min-idle=0
#最大阻塞等待時(shí)間,負(fù)值表示沒(méi)有限制
spring.redis.jedis.pool.max-wait=-1
#最大空閑連接
spring.redis.jedis.pool.max-idle=8
#連接超時(shí)時(shí)間(毫秒)
spring.redis.timeout=20

3、在springboot啟動(dòng)類(lèi)上加上@EnableCaching注解,如下圖:

image.png

4、接下來(lái)就可以使用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、測(cè)試:
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());
        }
    }

}

第一次運(yùn)行:

image.png

打印出了sql語(yǔ)句,是從數(shù)據(jù)庫(kù)查詢的。

第二次運(yùn)行:

image.png

沒(méi)有sql語(yǔ)句,是從緩存中讀取的。

@Cacheable(value = "common")注釋掉,把@CacheEvict(value="common")放開(kāi),
再運(yùn)行:

image.png

又是從數(shù)據(jù)庫(kù)中讀取的,說(shuō)明redis清除成功。

總結(jié):

spring整合redis:
總的來(lái)說(shuō)就是引依賴、編寫(xiě)RedisUtil、編寫(xiě)redis.properties、在spring-redis.xml中配置,最后在需要使用的地方用注解就行了。
spring boot整合redis:
總的來(lái)說(shuō)就是引依賴、在application.properties中配置、在啟動(dòng)類(lèi)上加@EnableCaching注解,然后在需要使用的地方用注解就行了。
注意事項(xiàng):
1、首先你的redis得支持遠(yuǎn)程連接。
2、實(shí)體類(lèi)必須實(shí)現(xiàn)序列化接口。
3、redis的注解是@Cacheable(value = "?",key="?")這樣的key、value形式,value必須自己指定,可以隨便寫(xiě),key可以不寫(xiě),不寫(xiě)會(huì)自動(dòng)生成。
4、如果要使用緩存的方法的參數(shù)是引用類(lèi)型,比如方法是getStudent(Student stu)那么該引用類(lèi)型的必須重寫(xiě)toString方法,即Student實(shí)體類(lèi)必須重寫(xiě)toString方法,否則使用緩存時(shí)會(huì)報(bào)錯(cuò)can not convert com.zhu.entity.Student toString。
5、若項(xiàng)目發(fā)布到服務(wù)器上,一開(kāi)始redis可以正常使用,一段時(shí)間后redis不能正常使用,用redis-cli -h xx.xxx.x.xxx -p 6379命令連接顯示connection timeout,訪問(wèn)項(xiàng)目中用到了redis的路由會(huì)報(bào)錯(cuò)could not get a resource from pool,可按如下步驟解決:
先kill掉redis的服務(wù):
ps aux | grep redis
kill -9 redis服務(wù)的pid
換端口開(kāi)啟redis服務(wù):
redis-server redis.conf --port 6000 &
這里指定的6000端口,也可以是其他的,&表示后臺(tái)啟動(dòng)。再用redis-cli -h xx.xxx.x.xxx -p 6000就可以連接上了。然后把項(xiàng)目中的redis配置的端口換成6000,重新發(fā)布項(xiàng)目即可。
項(xiàng)目說(shuō)明:
1、springboot是springboot2.x,springboot 1.5整合redis與這個(gè)不一樣。
2、上述兩個(gè)項(xiàng)目之所以會(huì)打印查詢的sql語(yǔ)句,是因?yàn)樵趍ybatis-config.xml中進(jìn)行了如下配置:

    <settings>
        <!-- 打印查詢語(yǔ)句 -->
        <setting name="logImpl" value="STDOUT_LOGGING" />
    </settings>

以上內(nèi)容屬于個(gè)人筆記整理,如有錯(cuò)誤,歡迎批評(píng)指正!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容