Spring Boot integration Redis caching mechanism of Learn Spring Boot from scratch

  • 2020-06-15 09:09:59
  • OfStack

This article involves more technical point: spring Data JPA, Redis, Spring MVC, Spirng Cache, so in this article, need to have 1 set of knowledge of these technical points or you can first take a look at this article, aiming at actual technical points in the article in further understanding (note that you need to download the Redis Server to your local, so make sure your local Redis available, here also USES MySQL database, of course you also can be tested memory database). This article will provide the corresponding Eclipse code sample, which can be divided into the following steps:

(1) Create new Java Maven Project;
(2) Add corresponding dependency package in ES20en.xml;
(3) Write Spring Boot startup class;
(4) Configuration application. properties;
(5) Write RedisCacheConfig configuration class;
(6) Write DemoInfo test entity class;
(7) Write DemoInfoRepository persistent class;
(8) Write DemoInfoService class;
(9) Write DemoInfoController class;
(10) Test whether the code runs normally
(11) Custom cache key;

(1) New Java Maven Project;

I won't go into details about this step. Create a new spring-ES50en-ES51en Java maven project.

(2) Add the corresponding dependency package in ES58en.xml;

Corresponding dependency packages are added to Maven, including: spring boot parent node dependency; spring boot web support; Caching service ES68en-ES69en-ES70en; Add redis support; JPA operates the database; mysql database driver, specific ES74en. xml file as follows:


<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>
 <!--  configuration JDK Compile version . -->
 <java.version>1.8</java.version>
 </properties>
 <!-- spring boot  Parent node dependency ,
   Once you introduce this, you don't need to add the related introduction version The configuration, 
  spring boot It automatically selects the most appropriate version to add. 
 -->
 <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 Support: mvc,aop... -->
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <!--
    Contains support UI Template ( Velocity . FreeMarker . JasperReports ), 
    Mail service, 
    Script service (JRuby) . 
    The cache Cache ( EHCache ), 
    Task plan Scheduling ( uartz ). 
  -->
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context-support</artifactId>
  </dependency>
  <!--  add redis support -->
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-redis</artifactId>
  </dependency>
  <!-- JPA Operational database . -->
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>
  <!-- mysql  Database driven . -->
  <dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
  </dependency>
  <!--  Unit testing . -->
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-test</artifactId>
   <scope>test</scope>
  </dependency>
 </dependencies>
</project>

Above is the complete pom.xml file, each with a simple comment.

(3) Write Spring Boot startup class (ES86en. kfit. App);


package com.kfit;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
 * Spring Boot Start the class ;
 *
 * @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) Configure ES93en. properties;

Here is mainly to configure two resources, the first is the database basic information; The second is the redis configuration; The third is the configuration of JPA;


Src/main/resouces/application.properties : 
########################################################
###datasource  configuration MySQL The data source. 
########################################################
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 Basic configuration; 
########################################################
# 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  Build tables automatically 
########################################################
# 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) Write RedisCacheConfig configuration class;

Caching has several main classes to implement: Its 1 is CacheManager cache manager; Its 2 is the concrete operation implementation class; Its 3 is the CacheManager factory class (which can be injected using configuration file configuration or implemented by coding); Its 4 is the cache key production strategy (of course, Spring comes with its own generation strategy, but when viewed on the Redis client, it is serializedkey, which is confusing to our eyes, so we will use the cache strategy here first).


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  Cache configuration ;
 *
 *  Note: RedisCacheConfig You can also do without inheritance here: CachingConfigurerSupport Which is direct 1 An ordinary Class It is good; 
 *
 *  This is where we're going to have to reimplement it later  key As long as it is modified here KeyGenerator , other locations will take effect without modification. 
 *
 *  If you normally use a normal class, then use it @Cacheable You also need to specify KeyGenerator The name of the ; It's a bit of a hassle to code in this way. 
 *
 * @author Angel(QQ:412887952)
 * @version v.0.1
 */
@Configuration
@EnableCaching// Cache enabled, that's an important annotation; 
publicclass RedisCacheConfig extends CachingConfigurerSupport {
 /**
  *  Cache manager .
  * @param redisTemplate
  * @return
  */
 @Bean
 public CacheManager cacheManager(RedisTemplate<?,?> redisTemplate) {
  CacheManager cacheManager = new RedisCacheManager(redisTemplate);
  returncacheManager;
 }
 /**
  * redis Template operation class , Similar to the jdbcTemplate the 1 A class ;
  *
  *  although CacheManager You can also get it Cache Objects, but not as flexible to operate; 
  *
  *  Here under the extension: RedisTemplate This class is not very easy to use, we can extend it 1 A we 
  *
  *  Your own cache class, such as: RedisStorage class ;
  *
  * @param factory :  through Spring Carry out the injection, parameters in application.properties To configure; 
  * @return
  */
 @Bean
 public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
  RedisTemplate<String, String> redisTemplate = new RedisTemplate<String, String>();
  redisTemplate.setConnectionFactory(factory);
  //key Serialization mode ; Otherwise there will be messy code ; ) , But if there is a method Long Such as the String Type, type conversion error will be reported; 
  // So there's no self-definition key When generating a policy, the following code suggests not to do so, either by not configuring it or by implementing it yourself ObjectRedisSerializer
  // or JdkSerializationRedisSerializer Serialization mode ;
//  RedisSerializer<String> redisSerializer = new StringRedisSerializer();//Long The type must not display an exception message ;
//  redisTemplate.setKeySerializer(redisSerializer);
//  redisTemplate.setHashKeySerializer(redisSerializer);
  returnredisTemplate;
 }
}

There are detailed comments in the above code, which are briefly mentioned here:

RedisCacheConfig here can also do without inheritance: CachingConfigurerSupport, that is, directly 1 ordinary Class is good; The main point here is to re-implement the key build strategy, as long as you change KeyGenerator here, other places will take effect without changes. If you are using a normal class, you will need to specify the name of KeyGenerator when using @Cacheable. It's a bit of a hassle to code in this way.

(6) Write DemoInfo test entity class;

Write a test entity class: com kfit. bean. DemoInfo:


package com.kfit.bean;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
/**
 *  Test the entity class, whatever ;
 * @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) Write DemoInfoRepository persistent class;

DemoInfoRepository Spirng Data JPA


com.kfit.repository.DemoInfoRepository : 
package com.kfit.repository;
import org.springframework.data.repository.CrudRepository;
import com.kfit.bean.DemoInfo;
/**
 * DemoInfo The persistent classes 
 * @author Angel(QQ:412887952)
 * @version v.0.1
 */
publicinterface DemoInfoRepository extends CrudRepository<DemoInfo,Long> {
}

(8) Write DemoInfoService class;

To write DemoInfoService, there are two technical aspects. The first is to use ES153en@ES154en annotation mode and RedisTemplate object for operation. The specific code is as follows:

com.kfit.service.DemoInfoService:


package com.kfit.service;
import com.kfit.bean.DemoInfo;
/**
 * demoInfo  The service interface 
 * @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 Data processing class 
 *
 * @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") // The cache , It's not specified here key.
 @Override
 public DemoInfo findById(longid) {
  System.err.println("DemoInfoServiceImpl.findById()========= Obtained from a database ....id="+id);
  returndemoInfoRepository.findOne(id);
 }
 @CacheEvict(value="demoInfo")
 @Override
 publicvoid deleteFromCache(longid) {
  System.out.println("DemoInfoServiceImpl.delete(). Remove from cache .");
 }
}

(9) Write DemoInfoController class;


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;
/**
 *  The test class .
 * @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) Test whether the code runs normally

Start the application, access address: http: / / 127.0.0.1:8080 / test

View console can view:

DemoInfoServiceImpl ()======== id = 1
loaded=DemoInfo [id=1, name= 3, pwd=123456]
cached=DemoInfo [id=1, name= 3, pwd=123456]
DemoInfoServiceImpl. findById()======= id = 2
loaded2=DemoInfo [id=2, name= 3, pwd=123456]

If you see the print above, the cache is successful.

Access address: http: / / 127.0.0.1:8080 / test1

random1=0.9985031320746356
DemoInfoController.test1()

2 visit: http: / / 127.0.0.1:8080 / test

loaded=DemoInfo [id=1, name= 3, pwd=123456]
cached=DemoInfo [id=1, name= sheet 3, pwd=123456]
loaded2=DemoInfo [id=2, name= 3, pwd=123456]

At this point, all data is cached.

At that time the delete action: http: / / 127.0.0.1:8080 / delete & # 63; id = 1

Then, on a visit to: http: / / 127.0.0.1:8080 / test

DemoInfoServiceImpl. findById()======= id = 1
loaded=DemoInfo [id=1, name= 3, pwd=123456]
cached=DemoInfo [id=1, name= 3, pwd=123456]
loaded2=DemoInfo [id=2, name= 3, pwd=123456]

(11) Custom cache key;

In com. kfit. config. RedisCacheConfig class rewrite the keyGenerator CachingConfigurerSupport, specific implementation code is as follows:


/**
  *  The custom key.
  *  This method will be based on the class name + The method name + The values of all parameters generate only 1 the 1 a key, Even if the @Cacheable In the value attribute 1 The sample, key Also will not 1 The sample. 
  */
 @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();
   }
  };
 }

At this time, if I look at key on the client side of redis, the serialization of key will be a mess to the naked eye, so I will change the sequence of key, this is very simple, redis has specific implementation classes at the bottom, we only need to configure:


//key Serialization mode ; Otherwise there will be messy code ; ) , But if there is a method Long Such as the String Type, type conversion error will be reported; 
// So there's no self-definition key When generating a policy, the following code suggests not to do so, either by not configuring it or by implementing it yourself ObjectRedisSerializer
// or JdkSerializationRedisSerializer Serialization mode ;
    RedisSerializer<String> redisSerializer = new StringRedisSerializer();//Long The type must not display an exception message ;
    redisTemplate.setKeySerializer(redisSerializer);
    redisTemplate.setHashKeySerializer(redisSerializer);

To sum up, the method of RedisCacheConfig class is adjusted as follows:


package com.kfit;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
 * Spring Boot Start the class ;
 *
 * @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);
  }
}
0

On a visit to address at this time: http: / / 127.0.0.1:8080 / test

At that time see Key is: com. kfit. service. impl. DemoInfoServiceImplfindById1

Print the message in the console as follows:

(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= 3, pwd=123456]
(5) keyGenerator = com. kfit. service. impl. DemoInfoServiceImplfindById1
(6) cached=DemoInfo [id=1, name= 3, 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= 3, pwd=123456]

In the next section @ES380en, @CacheEvict will give a brief introduction. There are too many things in this section, so I will stop here, and the rest is up to you to expand.


Related articles: