欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Spring Boot集成Redis实现缓存机制(从零开始学Spring Boot)

程序员文章站 2024-03-02 08:40:16
本文章牵涉到的技术点比较多:spring data jpa、redis、spring mvc,spirng cache,所以在看这篇文章的时候,需要对以上这些技术点有一定的...

本文章牵涉到的技术点比较多:spring data jpa、redis、spring mvc,spirng cache,所以在看这篇文章的时候,需要对以上这些技术点有一定的了解或者也可以先看看这篇文章,针对文章中实际的技术点在进一步了解(注意,您需要自己下载redis server到您的本地,所以确保您本地的redis可用,这里还使用了mysql数据库,当然你也可以内存数据库进行测试)。这篇文章会提供对应的eclipse代码示例,具体大体的分如下几个步骤:

(1)新建java maven project;
(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 maven project;

(2)在pom.xml中添加相应的依赖包;

在maven中添加相应的依赖包,主要有:spring boot 父节点依赖;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.0 http://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>
  <!-- spring boot 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启动类;
 *
 * @author angel(qq:412887952)
 * @version v.0.1
 */
@springbootapplication
public class app {
  /**
  * -javaagent:.\lib\springloaded-1.2.4.release.jar -noverify
  * @param args
  */
  public static void main(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:port pairs
#spring.redis.sentinel.nodes=
########################################################
### java persistence api 自动进行建表
########################################################
# specify the dbms
spring.jpa.database = mysql
# show or not log for each sql query
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 to the 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;
import org.springframework.cache.cachemanager;
import org.springframework.cache.annotation.cachingconfigurersupport;
import org.springframework.cache.annotation.enablecaching;
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.core.redistemplate;
/**
 * redis 缓存配置;
 *
 * 注意:rediscacheconfig这里也可以不用继承:cachingconfigurersupport,也就是直接一个普通的class就好了;
 *
 * 这里主要我们之后要重新实现 key的生成策略,只要这里修改keygenerator,其它位置不用修改就生效了。
 *
 * 普通使用普通类的方式的话,那么在使用@cacheable的时候还需要指定keygenerator的名称;这样编码的时候比较麻烦。
 *
 * @author angel(qq:412887952)
 * @version v.0.1
 */
@configuration
@enablecaching//启用缓存,这个注解很重要;
publicclass rediscacheconfig extends cachingconfigurersupport {
 /**
  * 缓存管理器.
  * @param redistemplate
  * @return
  */
 @bean
 public cachemanager cachemanager(redistemplate<?,?> redistemplate) {
  cachemanager cachemanager = new rediscachemanager(redistemplate);
  returncachemanager;
 }
 /**
  * redis模板操作类,类似于jdbctemplate的一个类;
  *
  * 虽然cachemanager也能获取到cache对象,但是操作起来没有那么灵活;
  *
  * 这里在扩展下:redistemplate这个类不见得很好操作,我们可以在进行扩展一个我们
  *
  * 自己的缓存类,比如:redisstorage类;
  *
  * @param factory : 通过spring进行注入,参数在application.properties进行配置;
  * @return
  */
 @bean
 public redistemplate<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);
  returnredistemplate;
 }
}

在以上代码有很详细的注释,在这里还是在简单的提下:

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
publicclass demoinfo implements serializable{
 privatestaticfinallongserialversionuid = 1l;
 @id@generatedvalue
 privatelongid;
 private string name;
 private string pwd;
 publiclong getid() {
  returnid;
 }
 publicvoid setid(longid) {
  this.id = id;
 }
 public string getname() {
  returnname;
 }
 publicvoid setname(string name) {
  this.name = name;
 }
 public string getpwd() {
  returnpwd;
 }
 publicvoid setpwd(string pwd) {
  this.pwd = pwd;
 }
 @override
 public string tostring() {
  return"demoinfo [id=" + id + ", name=" + name + ", pwd=" + pwd + "]";
 }
}

(7)编写demoinforepository持久化类;

       demoinforepository使用spirng data jpa实现:

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
 */
publicinterface demoinforepository 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
 */
publicinterface demoinfoservice {
 public demoinfo findbyid(longid);
 publicvoid deletefromcache(longid);
 void test();
}
com.kfit.service.impl.demoinfoserviceimpl:
package com.kfit.service.impl;
import javax.annotation.resource;
import org.springframework.cache.annotation.cacheevict;
import org.springframework.cache.annotation.cacheable;
import org.springframework.data.redis.core.redistemplate;
import org.springframework.data.redis.core.valueoperations;
import org.springframework.stereotype.service;
import com.kfit.bean.demoinfo;
import com.kfit.repository.demoinforepository;
import com.kfit.service.demoinfoservice;
/**
 *
 *demoinfo数据处理类
 *
 * @author angel(qq:412887952)
 * @version v.0.1
 */
@service
publicclass demoinfoserviceimpl implements demoinfoservice {
 @resource
 private demoinforepository demoinforepository;
 @resource
 private redistemplate<string,string> redistemplate;
 @override
 publicvoid 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 demoinfo findbyid(longid) {
  system.err.println("demoinfoserviceimpl.findbyid()=========从数据库中进行获取的....id="+id);
  returndemoinforepository.findone(id);
 }
 @cacheevict(value="demoinfo")
 @override
 publicvoid deletefromcache(longid) {
  system.out.println("demoinfoserviceimpl.delete().从缓存中删除.");
 }
}

(9)编写demoinfocontroller类;

package com.kfit.controller;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.stereotype.controller;
import org.springframework.web.bind.annotation.requestmapping;
import org.springframework.web.bind.annotation.responsebody;
import com.kfit.bean.demoinfo;
import com.kfit.service.demoinfoservice;
/**
 * 测试类.
 * @author angel(qq:412887952)
 * @version v.0.1
 */
@controller
publicclass 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(longid){
  demoinfoservice.deletefromcache(id);
  return"ok";
 }
 @requestmapping("/test1")
 public@responsebody string test1(){
  demoinfoservice.test();
  system.out.println("demoinfocontroller.test1()");
  return"ok";
 }
}

(10)测试代码是否正常运行了

启动应用程序,访问地址:

查看控制台可以查看:

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]

如果你看到以上的打印信息的话,那么说明缓存成功了。

访问地址:

random1=0.9985031320746356
demoinfocontroller.test1()

二次访问:

loaded=demoinfo [id=1, name=张三, pwd=123456]
cached=demoinfo [id=1, name=张三, pwd=123456]
loaded2=demoinfo [id=2, name=张三, pwd=123456]

这时候所有的数据都是执行缓存的。

这时候执行删除动作:

然后在访问:

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 keygenerator keygenerator() {
  system.out.println("rediscacheconfig.keygenerator()");
  returnnew keygenerator() {
   @override
   public object generate(object o, method method, object... objects) {
    // this will generate a unique key of the class name, the method name
    //and all method parameters appended.
    stringbuilder sb = new stringbuilder();
    sb.append(o.getclass().getname());
    sb.append(method.getname());
    for (object obj : objects) {
     sb.append(obj.tostring());
    }
    system.out.println("keygenerator=" + sb.tostring());
    returnsb.tostring();
   }
  };
 }

 这时候在redis的客户端查看key的话还是序列化的肉眼看到就是乱码了,那么我改变key的序列方式,这个很简单,redis底层已经有具体的实现类了,我们只需要配置下:

//key序列化方式;(不然会出现乱码;),但是如果方法上有long等非string类型的话,会报类型转换错误;
//所以在没有自己定义key生成策略的时候,以下这个代码建议不要这么写,可以不配置或者自己实现objectredisserializer
//或者jdkserializationredisserializer序列化方式;
    redisserializer<string> redisserializer = new stringredisserializer();//long类型不可以会出现异常信息;
    redistemplate.setkeyserializer(redisserializer);
    redistemplate.sethashkeyserializer(redisserializer);

综上以上分析:rediscacheconfig类的方法调整为:

package com.kfit.config;
import java.lang.reflect.method;
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.core.redistemplate;
import org.springframework.data.redis.serializer.redisserializer;
import org.springframework.data.redis.serializer.stringredisserializer;
/**
 * redis 缓存配置;
 *
 * 注意:rediscacheconfig这里也可以不用继承:cachingconfigurersupport,也就是直接一个普通的class就好了;
 *
 * 这里主要我们之后要重新实现 key的生成策略,只要这里修改keygenerator,其它位置不用修改就生效了。
 *
 * 普通使用普通类的方式的话,那么在使用@cacheable的时候还需要指定keygenerator的名称;这样编码的时候比较麻烦。
 *
 * @author angel(qq:412887952)
 * @version v.0.1
 */
@configuration
@enablecaching//启用缓存,这个注解很重要;
publicclass rediscacheconfig extends cachingconfigurersupport {
  /**
   * 缓存管理器.
   * @param redistemplate
   * @return
   */
  @bean
  public cachemanager cachemanager(redistemplate<?,?> redistemplate) {
    cachemanager cachemanager = new rediscachemanager(redistemplate);
    returncachemanager;
  }
  /**
   * redistemplate缓存操作类,类似于jdbctemplate的一个类;
   *
   * 虽然cachemanager也能获取到cache对象,但是操作起来没有那么灵活;
   *
   * 这里在扩展下:redistemplate这个类不见得很好操作,我们可以在进行扩展一个我们
   *
   * 自己的缓存类,比如:redisstorage类;
   *
   * @param factory : 通过spring进行注入,参数在application.properties进行配置;
   * @return
   */
  @bean
  public redistemplate<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);
    returnredistemplate;
  }
  /**
   * 自定义key.
   * 此方法将会根据类名+方法名+所有参数的值生成唯一的一个key,即使@cacheable中的value属性一样,key也会不一样。
   */
  @override
  public keygenerator keygenerator() {
    system.out.println("rediscacheconfig.keygenerator()");
    returnnew keygenerator() {
      @override
      public object generate(object o, method method, object... objects) {
       // this will generate a unique key of the class name, the method name
       //and all method parameters appended.
       stringbuilder sb = new stringbuilder();
       sb.append(o.getclass().getname());
       sb.append(method.getname());
       for (object obj : objects) {
         sb.append(obj.tostring());
       }
       system.out.println("keygenerator=" + sb.tostring());
       returnsb.tostring();
      }
    };
  }
}

这时候在访问地址:

这时候看到的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下节进行简单的介绍,这节的东西实在是太多了,到这里就打住吧,剩下的就需要靠你们自己进行扩展了。

以上所述是小编给大家介绍的spring boot集成redis实现缓存机制(从零开始学spring boot),希望对大家有所帮助