Spring Boot 集成 Redis 實現緩存機制


 本文章牽涉到的技術點比較多:spring Data JPA、Redis、Spring MVC,Spirng Cache,所以在看這篇文章的時候,需要對以上這些技術點有一定的了解或者也可以先看看這篇文章,針對文章中實際的技術點在進一步了解(注意,您需要自己下載Redis Server到您的本地,所以確保您本地的Redis可用,這里還使用了MySQL數據庫,當然你也可以內存數據庫進行測試)。這篇文章會提供對應的Eclipse代碼示例,具體大體的分如下幾個步驟:

(1)新建Java MavenProject;

(2)在pom.xml中添加相應的依賴包;

(3)編寫Spring Boot啟動類;

(4)配置application.properties;

(5)編寫RedisCacheConfig配置類;

(6)編寫DemoInfo測試實體類;

(7)編寫DemoInfoRepository持久化類;

(8)編寫DemoInfoService類;

(9)編寫DemoInfoController類;

(10)測試代碼是否正常運行了

(11)自定義緩存key;

 

       接下來我們看看具體每個步驟具體的操作吧。
(1)新建Java Maven Project;

       這個步驟就不細說,新建一個spring-boot-redis Java mavenproject;

 

(2)在pom.xml中添加相應的依賴包;

在Maven中添加相應的依賴包,主要有:springboot 父節點依賴;spring boot web支持;緩存服務spring-context-support;添加redis支持;JPA操作數據庫;mysql 數據庫驅動,具體pom.xml文件如下:

<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0http://maven.apache.org/xsd/maven-4.0.0.xsd">

 <modelVersion>4.0.0</modelVersion>

 

 <groupId>com.kfit</groupId>

 <artifactId>spring-boot-redis</artifactId>

 <version>0.0.1-SNAPSHOT</version>

 <packaging>jar</packaging>

 

 <name>spring-boot-redis</name>

 <url>http://maven.apache.org</url>

 

 <properties>

   <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

   <!-- 配置JDK編譯版本. -->

       <java.version>1.8</java.version>

 </properties>

 

 <!-- spring boot 父節點依賴,

              引入這個之后相關的引入就不需要添加version配置,

               spring boot會自動選擇最合適的版本進行添加。

       -->

       <parent>

              <groupId>org.springframework.boot</groupId>

              <artifactId>spring-boot-starter-parent</artifactId>

              <version>1.3.3.RELEASE</version>

       </parent>

 

 

 <dependencies>

         

          <dependency>

                     <groupId>junit</groupId>

                     <artifactId>junit</artifactId>

                     <scope>test</scope>

              </dependency>

 

              <!-- springboot web支持:mvc,aop... -->

              <dependency>

                     <groupId>org.springframework.boot</groupId>

                     <artifactId>spring-boot-starter-web</artifactId>

              </dependency>

             

              <!--

                     包含支持UI模版(Velocity,FreeMarker,JasperReports),

                     郵件服務,

                     腳本服務(JRuby),

                     緩存Cache(EHCache),

                     任務計划Scheduling(uartz)。

               -->

              <dependency>

             <groupId>org.springframework</groupId>

             <artifactId>spring-context-support</artifactId>

           </dependency>

 

              <!-- 添加redis支持-->

              <dependency>

            <groupId>org.springframework.boot</groupId>

           <artifactId>spring-boot-starter-redis</artifactId>

        </dependency>

             

              <!-- JPA操作數據庫. -->

              <dependency>

             <groupId>org.springframework.boot</groupId>

             <artifactId>spring-boot-starter-data-jpa</artifactId>

           </dependency>

             

              <!-- mysql數據庫驅動. -->

              <dependency>

             <groupId>mysql</groupId>

             <artifactId>mysql-connector-java</artifactId>

           </dependency>

          

           <!-- 單元測試. -->

              <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-test</artifactId>

            <scope>test</scope>

        </dependency>

             

 </dependencies>

</project>

上面是完整的pom.xml文件,每個里面都進行了簡單的注釋。

 

(3)編寫Spring Boot啟動類(com.kfit.App);

package com.kfit;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

/**

 * Spring Boot啟動類;

 *

 * @authorAngel(QQ:412887952)

 * @version v.0.1

 */

 

@SpringBootApplication

public class App {

       /**

        *-javaagent:.\lib\springloaded-1.2.4.RELEASE.jar -noverify

        * @param args

        */

       public static voidmain(String[] args) {

              SpringApplication.run(App.class,args);

       }

}

 

(4)配置application.properties;

這里主要是配置兩個資源,第一就是數據庫基本信息;第二就是redis配置;第三就是JPA的配置;

Src/main/resouces/application.properties:

########################################################

###datasource  配置MySQL數據源;

########################################################

spring.datasource.url =jdbc:mysql://localhost:3306/test

spring.datasource.username = root

spring.datasource.password = root

spring.datasource.driverClassName =com.mysql.jdbc.Driver

spring.datasource.max-active=20

spring.datasource.max-idle=8

spring.datasource.min-idle=8

spring.datasource.initial-size=10

 

 

 

########################################################

###REDIS (RedisProperties) redis基本配置;

########################################################

# database name

spring.redis.database=0

# server host1

spring.redis.host=127.0.0.1  

# server password

#spring.redis.password=

#connection port

spring.redis.port=6379

# pool settings ...

spring.redis.pool.max-idle=8

spring.redis.pool.min-idle=0

spring.redis.pool.max-active=8

spring.redis.pool.max-wait=-1

# name of Redis server

#spring.redis.sentinel.master=

# comma-separated list of host:portpairs

#spring.redis.sentinel.nodes=

 

 

########################################################

### Java Persistence Api 自動進行建表

########################################################

# Specify the DBMS

spring.jpa.database = MYSQL

# Show or not log for each sqlquery

spring.jpa.show-sql = true

hibernate ddl auto (create,create-drop, update)

spring.jpa.hibernate.ddl-auto = update

# Naming strategy

spring.jpa.hibernate.naming-strategy =org.hibernate.cfg.ImprovedNamingStrategy

# stripped before adding them tothe entity manager)

spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect

 

 

 

(5)編寫RedisCacheConfig配置類;

       緩存主要有幾個要實現的類:其一就是CacheManager緩存管理器;其二就是具體操作實現類;其三就是CacheManager工廠類(這個可以使用配置文件配置的進行注入,也可以通過編碼的方式進行實現);其四就是緩存key生產策略(當然Spring自帶生成策略,但是在Redis客戶端進行查看的話是系列化的key,對於我們肉眼來說就是感覺是亂碼了,這里我們先使用自帶的緩存策略)。

com.kfit.config/RedisCacheConfig:

package com.kfit.config;

 

importorg.springframework.cache.CacheManager;

importorg.springframework.cache.annotation.CachingConfigurerSupport;

importorg.springframework.cache.annotation.EnableCaching;

importorg.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

importorg.springframework.data.redis.cache.RedisCacheManager;

importorg.springframework.data.redis.connection.RedisConnectionFactory;

importorg.springframework.data.redis.core.RedisTemplate;

 

/**

 * redis 緩存配置;

 *

 * 注意:RedisCacheConfig這里也可以不用繼承 :CachingConfigurerSupport,也就是直接一個普通的Class就好了;

 *

 * 這里主要我們之后要重新實現 key的生成策略,只要這里修改KeyGenerator,其它位置不用修改就生效了。

 *

 * 普通使用普通類的方式的話,那么在使用@Cacheable的時候還需要指定KeyGenerator的名稱;這樣編碼的時候比較麻煩。

 *

 * @author Angel(QQ:412887952)

 * @version v.0.1

 */

@Configuration

@EnableCaching //啟用緩存,這個注解很重要;

public class RedisCacheConfig extendsCachingConfigurerSupport {

      

      

       /**

        * 緩存管理器.

        * @param redisTemplate

        * @return

        */

       @Bean

       public CacheManagercacheManager(RedisTemplate<?,?> redisTemplate) {

              CacheManagercacheManager = newRedisCacheManager(redisTemplate);

              return cacheManager;

       }

 

      

       /**

        * redis模板操作類,類似於jdbcTemplate的一個類;

        *

        * 雖然CacheManager也能獲取到Cache對象,但是操作起來沒有那么靈活;

        *

        * 這里在擴展下:RedisTemplate這個類不見得很好操作,我們可以在進行擴展一個我們

        *

        * 自己的緩存類,比如:RedisStorage類;

        *

        * @param factory : 通過Spring進行注入,參數在application.properties進行配置;

        * @return

        */

       @Bean

       publicRedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {

              RedisTemplate<String,String> redisTemplate = new RedisTemplate<String, String>();

              redisTemplate.setConnectionFactory(factory);

             

              //key序列化方式;(不然會出現亂碼;),但是如果方法上有Long等非String類型的話,會報類型轉換錯誤;

              //所以在沒有自己定義key生成策略的時候,以下這個代碼建議不要這么寫,可以不配置或者自己實現ObjectRedisSerializer

              //或者JdkSerializationRedisSerializer序列化方式;

//           RedisSerializer<String>redisSerializer = new StringRedisSerializer();//Long類型不可以會出現異常信息;

//           redisTemplate.setKeySerializer(redisSerializer);

//           redisTemplate.setHashKeySerializer(redisSerializer);

             

              return redisTemplate;

       }

 

}

在以上代碼有很詳細的注釋,在這里還是在簡單的提下:

RedisCacheConfig這里也可以不用繼承:CachingConfigurerSupport,也就是直接一個普通的Class就好了;這里主要我們之后要重新實現 key的生成策略,只要這里修改KeyGenerator,其它位置不用修改就生效了。普通使用普通類的方式的話,那么在使用@Cacheable的時候還需要指定KeyGenerator的名稱;這樣編碼的時候比較麻煩。

 

 

(6)編寫DemoInfo測試實體類;

       編寫一個測試實體類:com.kfit.bean.DemoInfo:

package com.kfit.bean;

 

import java.io.Serializable;

import javax.persistence.Entity;

import javax.persistence.GeneratedValue;

import javax.persistence.Id;

/**

 * 測試實體類,這個隨便;

 * @author Angel(QQ:412887952)

 * @version v.0.1

 */

@Entity

public class DemoInfo  implements Serializable{

       private static final long serialVersionUID = 1L;

       @Id @GeneratedValue

       private long id;

       private String name;

       private String pwd;

       public long getId() {

              return id;

       }

       public void setId(long id) {

              this.id = id;

       }

       public StringgetName() {

              return name;

       }

       public void setName(String name) {

              this.name = name;

       }

       public String getPwd(){

              return pwd;

       }

       public void setPwd(String pwd) {

              this.pwd = pwd;

       }

      

       @Override

       public StringtoString() {

              return "DemoInfo [id=" + id + ",name=" + name + ", pwd=" + pwd + "]";

       }

}

 

(7)編寫DemoInfoRepository持久化類;

       DemoInfoRepository使用Spirng DataJPA實現:

com.kfit.repository.DemoInfoRepository:

package com.kfit.repository;

 

import org.springframework.data.repository.CrudRepository;

 

import com.kfit.bean.DemoInfo;

 

/**

 * DemoInfo持久化類

 * @author Angel(QQ:412887952)

 * @version v.0.1

 */

public interfaceDemoInfoRepository extends CrudRepository<DemoInfo,Long> {

 

}

 

(8)編寫DemoInfoService類;

       編寫DemoInfoService,這里有兩個技術方面,第一就是使用Spring @Cacheable注解方式和RedisTemplate對象進行操作,具體代碼如下:

com.kfit.service.DemoInfoService:

package com.kfit.service;

 

import com.kfit.bean.DemoInfo;

 

/**

 * demoInfo 服務接口

 * @author Angel(QQ:412887952)

 * @version v.0.1

 */

public interface DemoInfoService{

      

       public DemoInfofindById(long id);

      

       public voiddeleteFromCache(long id);

 

       void test();

}

 

 

com.kfit.service.impl.DemoInfoServiceImpl:

package com.kfit.service.impl;

 

import javax.annotation.Resource;

 

importorg.springframework.cache.annotation.CacheEvict;

importorg.springframework.cache.annotation.Cacheable;

importorg.springframework.data.redis.core.RedisTemplate;

importorg.springframework.data.redis.core.ValueOperations;

importorg.springframework.stereotype.Service;

 

import com.kfit.bean.DemoInfo;

importcom.kfit.repository.DemoInfoRepository;

import com.kfit.service.DemoInfoService;

 

/**

 *

 *DemoInfo數據處理類

 *

 * @author Angel(QQ:412887952)

 * @version v.0.1

 */

@Service

public class DemoInfoServiceImpl implements DemoInfoService{

      

       @Resource

       privateDemoInfoRepository demoInfoRepository;

      

       @Resource

       privateRedisTemplate<String,String> redisTemplate;

      

       @Override

       public void test(){

              ValueOperations<String,String>valueOperations = redisTemplate.opsForValue();

              valueOperations.set("mykey4", "random1="+Math.random());

              System.out.println(valueOperations.get("mykey4"));

       }

      

       //keyGenerator="myKeyGenerator"

       @Cacheable(value="demoInfo") //緩存,這里沒有指定key.

       @Override

       public DemoInfofindById(long id) {

              System.err.println("DemoInfoServiceImpl.findById()=========從數據庫中進行獲取的....id="+id);

              return demoInfoRepository.findOne(id);

       }

      

       @CacheEvict(value="demoInfo")

       @Override

       public void deleteFromCache(long id) {

              System.out.println("DemoInfoServiceImpl.delete().從緩存中刪除.");

       }

      

}

 

 

(9)編寫DemoInfoController類;

package com.kfit.controller;

 

importorg.springframework.beans.factory.annotation.Autowired;

importorg.springframework.stereotype.Controller;

importorg.springframework.web.bind.annotation.RequestMapping;

importorg.springframework.web.bind.annotation.ResponseBody;

 

import com.kfit.bean.DemoInfo;

import com.kfit.service.DemoInfoService;

 

/**

 * 測試類.

 * @author Angel(QQ:412887952)

 * @version v.0.1

 */

@Controller

public class DemoInfoController {

      

       @Autowired

        DemoInfoService demoInfoService;

        

      

       @RequestMapping("/test")

       public @ResponseBody String test(){

           DemoInfo loaded = demoInfoService.findById(1);

System.out.println("loaded="+loaded);

DemoInfo cached = demoInfoService.findById(1);

           System.out.println("cached="+cached);

           loaded = demoInfoService.findById(2);

           System.out.println("loaded2="+loaded);

           return "ok";

       }

      

      

       @RequestMapping("/delete")

       public @ResponseBody String delete(long id){

           demoInfoService.deleteFromCache(id);

           return "ok";

       }

      

       @RequestMapping("/test1")

       public @ResponseBody String test1(){

           demoInfoService.test();

           System.out.println("DemoInfoController.test1()");

           return "ok";

       }

      

}

 

(10)測試代碼是否正常運行了

 

啟動應用程序,訪問地址:http://127.0.0.1:8080/test

查看控制台可以查看:

DemoInfoServiceImpl.findById()=========從數據庫中進行獲取的....id=1

loaded=DemoInfo [id=1, name=張三, pwd=123456]

cached=DemoInfo [id=1, name=張三, pwd=123456]

DemoInfoServiceImpl.findById()=========從數據庫中進行獲取的....id=2

loaded2=DemoInfo [id=2, name=張三, pwd=123456]

如果你看到以上的打印信息的話,那么說明緩存成功了。

 

訪問地址:http://127.0.0.1:8080/test1

random1=0.9985031320746356

DemoInfoController.test1()

 

二次訪問:http://127.0.0.1:8080/test

loaded=DemoInfo [id=1, name=張三, pwd=123456]

cached=DemoInfo [id=1, name=張三, pwd=123456]

loaded2=DemoInfo [id=2, name=張三, pwd=123456]

這時候所有的數據都是執行緩存的。

 

這時候執行刪除動作:http://127.0.0.1:8080/delete?id=1

然后在訪問:http://127.0.0.1:8080/test

DemoInfoServiceImpl.findById()=========從數據庫中進行獲取的....id=1

loaded=DemoInfo [id=1, name=張三, pwd=123456]

cached=DemoInfo [id=1, name=張三, pwd=123456]

loaded2=DemoInfo [id=2, name=張三, pwd=123456]

 

(11)自定義緩存key;

在com.kfit.config.RedisCacheConfig類中重寫CachingConfigurerSupport中的keyGenerator ,具體實現代碼如下:

 

/**

        * 自定義key.

        * 此方法將會根據類名+方法名+所有參數的值生成唯一的一個key,即使@Cacheable中的value屬性一樣,key也會不一樣。

        */

       @Override

       public KeyGeneratorkeyGenerator() {

              System.out.println("RedisCacheConfig.keyGenerator()");

              return new KeyGenerator(){

                     @Override

                     public Objectgenerate(Object o, Method method, Object... objects) {

                            // This willgenerate a unique key of the class name, the method name

                            //and allmethod parameters appended.

                            StringBuildersb = newStringBuilder();

                            sb.append(o.getClass().getName());

                            sb.append(method.getName());

                            for (Object obj : objects) {

                                   sb.append(obj.toString());

                            }

                            System.out.println("keyGenerator=" + sb.toString());

                            return sb.toString();

                     }

              };

       }

 

這時候在redis的客戶端查看key的話還是序列化的肉眼看到就是亂碼了,那么我改變key的序列方式,這個很簡單,redis底層已經有具體的實現類了,我們只需要配置下:

//key序列化方式;(不然會出現亂碼;),但是如果方法上有Long等非String類型的話,會報類型轉換錯誤;

//所以在沒有自己定義key生成策略的時候,以下這個代碼建議不要這么寫,可以不配置或者自己實現ObjectRedisSerializer

//或者JdkSerializationRedisSerializer序列化方式;

              RedisSerializer<String>redisSerializer = newStringRedisSerializer();//Long類型不可以會出現異常信息;

              redisTemplate.setKeySerializer(redisSerializer);

              redisTemplate.setHashKeySerializer(redisSerializer);

綜上以上分析:RedisCacheConfig類的方法調整為:

package com.kfit.config;

 

import java.lang.reflect.Method;

 

importorg.springframework.cache.CacheManager;

importorg.springframework.cache.annotation.CachingConfigurerSupport;

importorg.springframework.cache.annotation.EnableCaching;

importorg.springframework.cache.interceptor.KeyGenerator;

import org.springframework.context.annotation.Bean;

importorg.springframework.context.annotation.Configuration;

importorg.springframework.data.redis.cache.RedisCacheManager;

importorg.springframework.data.redis.connection.RedisConnectionFactory;

importorg.springframework.data.redis.core.RedisTemplate;

importorg.springframework.data.redis.serializer.RedisSerializer;

importorg.springframework.data.redis.serializer.StringRedisSerializer;

 

/**

 * redis 緩存配置;

 *

 * 注意:RedisCacheConfig這里也可以不用繼承 :CachingConfigurerSupport,也就是直接一個普通的Class就好了;

 *

 * 這里主要我們之后要重新實現 key的生成策略,只要這里修改KeyGenerator,其它位置不用修改就生效了。

 *

 * 普通使用普通類的方式的話,那么在使用@Cacheable的時候還需要指定KeyGenerator的名稱;這樣編碼的時候比較麻煩。

 *

 * @author Angel(QQ:412887952)

 * @version v.0.1

 */

@Configuration

@EnableCaching //啟用緩存,這個注解很重要;

public class RedisCacheConfig extendsCachingConfigurerSupport {

      

       /**

        * 緩存管理器.

        * @param redisTemplate

        * @return

        */

       @Bean

       public CacheManagercacheManager(RedisTemplate<?,?> redisTemplate) {

              CacheManagercacheManager = newRedisCacheManager(redisTemplate);

              return cacheManager;

       }

 

      

       /**

        * RedisTemplate緩存操作類,類似於jdbcTemplate的一個類;

        *

        * 雖然CacheManager也能獲取到Cache對象,但是操作起來沒有那么靈活;

        *

        * 這里在擴展下:RedisTemplate這個類不見得很好操作,我們可以在進行擴展一個我們

        *

        * 自己的緩存類,比如:RedisStorage類;

        *

        * @param factory : 通過Spring進行注入,參數在application.properties進行配置;

        * @return

        */

       @Bean

       publicRedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {

              RedisTemplate<String,String> redisTemplate = new RedisTemplate<String, String>();

              redisTemplate.setConnectionFactory(factory);

             

              //key序列化方式;(不然會出現亂碼;),但是如果方法上有Long等非String類型的話,會報類型轉換錯誤;

              //所以在沒有自己定義key生成策略的時候,以下這個代碼建議不要這么寫,可以不配置或者自己實現ObjectRedisSerializer

              //或者JdkSerializationRedisSerializer序列化方式;

              RedisSerializer<String>redisSerializer = newStringRedisSerializer();//Long類型不可以會出現異常信息;

              redisTemplate.setKeySerializer(redisSerializer);

              redisTemplate.setHashKeySerializer(redisSerializer);

             

              return redisTemplate;

       }

      

       /**

        * 自定義key.

        * 此方法將會根據類名+方法名+所有參數的值生成唯一的一個key,即使@Cacheable中的value屬性一樣,key也會不一樣。

        */

       @Override

       public KeyGeneratorkeyGenerator() {

              System.out.println("RedisCacheConfig.keyGenerator()");

              return new KeyGenerator(){

                     @Override

                     public Objectgenerate(Object o, Method method, Object... objects) {

                            // This willgenerate a unique key of the class name, the method name

                            //and allmethod parameters appended.

                            StringBuildersb = newStringBuilder();

                            sb.append(o.getClass().getName());

                            sb.append(method.getName());

                            for (Object obj : objects) {

                                   sb.append(obj.toString());

                            }

                            System.out.println("keyGenerator=" + sb.toString());

                            return sb.toString();

                     }

              };

       }

 

}

 

這時候在訪問地址:http://127.0.0.1:8080/test

這時候看到的Key就是:com.kfit.service.impl.DemoInfoServiceImplfindById1

在控制台打印信息是:

(1)keyGenerator=com.kfit.service.impl.DemoInfoServiceImplfindById1

(2)DemoInfoServiceImpl.findById()=========從數據庫中進行獲取的....id=1

(3)keyGenerator=com.kfit.service.impl.DemoInfoServiceImplfindById1

(4)loaded=DemoInfo[id=1, name=張三, pwd=123456]

(5)keyGenerator=com.kfit.service.impl.DemoInfoServiceImplfindById1

(6)cached=DemoInfo[id=1, name=張三, pwd=123456]

(7)keyGenerator=com.kfit.service.impl.DemoInfoServiceImplfindById2

(8)keyGenerator=com.kfit.service.impl.DemoInfoServiceImplfindById2

(10)DemoInfoServiceImpl.findById()=========從數據庫中進行獲取的....id=2

(11)loaded2=DemoInfo[id=2, name=張三, pwd=123456]

其中@Cacheable,@CacheEvict下節進行簡單的介紹,這節的東西實在是太多了,到這里就打住吧,剩下的就需要靠你們自己進行擴展了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM