第九章、Spring Boot 缓存

本文介绍了SpringBoot中SpringCache的基本原理及其与Redis、EHCache的集成应用。详细讲解了注解@Cacheable、@CacheEvict和@CachePut的使用方法,并提供了完整的代码示例。

课时五十二、Spring Boot Spring Cache理论篇Spring Boot Spring Cache理论篇

Spring 3.1 引入了激动人心的基于注释(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案,而是一个对缓存使用的抽象,通过在既有代码中添加少量它定义的各种 annotation,就能够达到缓存方法返回对象的效果。(具体实现:例如EHCache、 OSCache、Redis等)

灵活性

Spring 的缓存技术具备相当的灵活性,不仅能够使用 SpEL(Spring Expression Language)来定义缓存的 key 和各种 condition,还提供开箱即用的缓存临时存储方案,也支持和主流的专业缓存例如 EHCache 集成。

特点

通过少量的配置 annotation 注释即可使得既有代码支持缓存
支持开箱即用 Out-Of-The-Box,即不用安装和部署额外第三方组件即可使用缓存
支持 Spring Express Language,能使用对象的任何属性或者方法来定义缓存的 key 和 condition
支持 AspectJ,并通过其实现任何方法的缓存支持
支持自定义 key 和自定义缓存管理者,具有相当的灵活性和扩展性

我们以前如何自己实现缓存的呢?

以前我们是在service层通过先判断缓存中是否存在缓存数据,如果存在直接返回数据,如果不存在从数据库中进行查询数据然后进行缓存。
那么我们会发现这种方式的代码耦合度太高。如果你之前的代码就是这么设计的,那么你可以考虑使用Spring Cache优化你的代码,你的代码将会变得优雅很多。

注解类Cacheable

注解类Cacheable

Cacheable 支持如下几个参数:

value:缓存位置名称,不能为空,如果使用EHCache,就是ehcache.xml中声明的cache的name
key:缓存的key,默认为空,既表示使用方法的参数类型及参数值作为key,支持SpEL
condition:触发条件,只有满足条件的情况才会加入缓存,默认为空,既表示全部都加入缓存,支持SpEL

    //将缓存保存进andCache,并使用参数中的userId加上一个字符串作为缓存的key
    Cacheable(value="andCache", Key="#userId+'findById'")
        public SystemUser findById(String userId){
        SystemUser user =(SystemUser)dao.findById(SystemUser.Class,userId);
        return user;
        }

例子

    //将缓存保存进andCache,并使用参数中的userId的长度保存到32是才保存进缓存,默认使用参数值及类型作为缓存的key
    Cacheable(value="andCache", Key="#userId.length<32")
        public boolean isResserved(String userId){
        System.out.println("hello andCache"+userId);
        return false;
        }

注解类@CacheEvict

@CacheEvict 支持如下几个参数:
value:缓存位置名称,不能为空
key:缓存的key,默认为空
condition:触发条件,只有满足条件的情况才会清除缓存,默认为空,支持SpEL
allEntries:true表示清除value中的全部缓存,默认为false
 

例子

    // 清楚掉制定key的缓存
     @CacheEvict(value = "andCache",key = "#user.userId+'findById'")
     public void modifyUserRole(SystemUser user){
        System.out.println("hello andCache delete"+user.getUserId());
    }

    //全部清除缓存
    @CacheEvict(value = "andCache",allEntries = true)
    public void setReservedUsers(){
         System.out.println("hello andCache deleteall");
    }

注解类@CachePut

@CachePut 注释,这个注释可以确保方法被执行, 同时方法的返回值也被记录到缓存中,实现缓存与数据库的同步更新,理解为update语句。

课时五十三、Spring Boot集成Redis实现缓存机制

前言

Spring Boot集成Redis实现缓存机制

本节课牵涉到的技术点比较多:Spring Data JPA、Redis、Spring MVC,Spirng Cache,所以在本视频的时候,需要对以上这些技术点有一定的了解或者也可以先看看本视频,针对本视频实际的技术点在进一步了解

注意:需要下载Redis Server到本地,所以确保本地的Redis可用。这里还使用了MySql数据库,当然你也可以使用内存数据库进行测试

课程大纲

(1)新建Java Maven Project

新建一个项目:spring-boot-redis

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

<!-- 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>
 

(3)编写Spring Boot启动类

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }

(4)配置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配置类

/**
 * redis : 
 ---------------------
 1/RedisFactory : redis工厂. --- 通过application.properties配置文件,spring boot会自动创建,我们引入使用即可。
 2/RedisTempalte: redis操作类.
 3/ReidisManager:redis的管理类.
 
 * 
 * @author Angel -- 守护天使
 * @version v.0.1
 * @date 2017年9月21日
 */
@Configuration//这是配置类.

@EnableCaching //开启缓存.
public class RedisCacheConfig {
    
    @Bean
    public RedisTemplate<String,String> redisTempalte(RedisConnectionFactory redisConnectionFactory){
        RedisTemplate<String, String> redisTemplate = new RedisTemplate<String, String>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        return redisTemplate;
    }
    
    /**
     * 将我们定义的redis 的 redisTempalte(缓存操作类),设置到spring cahcheManager对象中,
     * 这样的话,spring 就可以使用cacaheManager对我们的对象进行缓存控制。
     * @param redisTempalte
     * @return
     */
    @Bean
    public CacheManager cacheManager(RedisTemplate redisTempalte){
        CacheManager cacheManager = new RedisCacheManager(redisTempalte);
        return cacheManager;
    }
    
}

(6)编写Student测试实体类

@Entity
public class Student implements Serializable{
    
    private static final long serialVersionUID = 1L;
    @Id @GeneratedValue(strategy=GenerationType.AUTO)
    private int id; //主键.
    private String name;
    private int gender;
    private Date registerTime;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getGender() {
        return gender;
    }
    public void setGender(int gender) {
        this.gender = gender;
    }
    public Date getRegisterTime() {
        return registerTime;
    }
    public void setRegisterTime(Date registerTime) {
        this.registerTime = registerTime;
    }
    
}
 

(7)编写StudentInfoRepository持久化类


public interface StudentRepository extends CrudRepository<Student, Integer>{

}

(8)编写StudentService类

@Service
public class StudentService {
    
    @Autowired
    private StudentRepository studentRepository;
    
    //开启事务.
    @Transactional
    public void save(Student student){
        student.setRegisterTime(new Date());
        studentRepository.save(student);
    }

    @Cacheable(value="student")
    public Student findById(int id) {
        System.out.println("(未使用缓存)-->进入到了findById,id="+id);
        return studentRepository.findOne(id);
    }
    
    @CacheEvict(value="student")
    public void deleteCache(int id) {
        //这里只清空缓存,实际数据库没有删除。
//        studentRepository.delete(id);
    }
    @Cacheable(value="student1")
    public List<Student> findByAll() {
        return (List<Student>) studentRepository.findAll();
    }


    //@CachePut
    //public void update();
}

(9)编写StudentController类

@RestController
@RequestMapping("/student")
public class StudentController {
    
    @Autowired
    private StudentService studentService;
    
    
    //?name=张三&gender=1
    @RequestMapping("/save")
    public Student save(Student student){
        studentService.save(student);
        return student;
    }
    
    @RequestMapping("/findById")
    public Student findById(int id){
        System.out.println("StudentController.findById----id="+id+",开始获取数据");
        return studentService.findById(id);
    }
    @RequestMapping("/findByAll")
    public List<Student> findByAll(){

        return studentService.findByAll();
    }
    @RequestMapping("/deleteCache")
    public String deleteCache(int id){
        System.out.println("StudentController.deleteCache----id="+id+",开始请缓存");
        studentService.deleteCache(id);
        return "ok";
    }
    
    
}

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

编外话:全部数据缓存

课时五十四、Spring Boot Redis版本问题

使用1.5版本引入redis就不能成功,自动下载到本地maven库里面的也是一个”unknown“的文件夹,使用1.3、1.4的就OK...,如下的配置方式:


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

    <!-- 添加redis支持
        不同spring-boot的版本是不一样的,
        1.4+ 之前的是:spring-boot-starter-redis
        1.4+ spring-boot-starter-redis修改为了:spring-boot-starter-data-redis
        1.5+ 直接废弃了.  需要使用:spring-boot-starter-data-redis
     -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-redis</artifactId>
        </dependency>

课时五十五、Spring Boot集成EHCache实现缓存机制

(1)新建Maven Java Project

新建一个工程名为spring-boot-ehcache的maven java project

(2)在pom.xml中加入依赖包

<!-- 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>
 
        <!-- 集成ehcache需要的依赖-->
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</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>

(3)编写Spring Boot启动类;

@SpringBootApplication 
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}

(4)配置application.properties;

########################################################
###datasource ,mysql\u6570\u636e\u5e93\u8fde\u63a5\u914d\u7f6e
########################################################
spring.datasource.url = jdbc:mysql://59818a0fa1d8a.bj.cdb.myqcloud.com:5169/test?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
spring.datasource.username = root
spring.datasource.password = sag18747231218
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
 
########################################################
### Java Persistence Api \uff0cJPA\u81ea\u52a8\u5efa\u8868\u64cd\u4f5c\u914d\u7f6e
########################################################
# 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)编写缓存配置类以及ehcache.xml配置文件;

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
    updateCheck="false">
    
    
    <!-- 
    diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下:
        user.home – 用户主目录
        user.dir  – 用户当前工作目录
        java.io.tmpdir – 默认临时文件路径
        
     -->
    <diskStore path="java.io.tmpdir/Tmp_EhCache" />
    
    <!-- 
        defaultCache: 默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。
     -->
     
     <!-- 
        name:缓存名称。 
        maxElementsInMemory:缓存最大数目 
        maxElementsOnDisk:硬盘最大缓存个数。  
        eternal:对象是否永久有效,一但设置了,timeout将不起作用。  
        overflowToDisk:是否保存到磁盘,当系统当机时
        timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。 
        timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
        diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.  
        diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。  
        diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。 
        memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。  
        clearOnFlush:内存数量最大时是否清除。 
         memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。 
             FIFO,first in first out,这个是大家最熟的,先进先出。 
             LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
             LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
    --> 
    <defaultCache 
        eternal="false" 
        maxElementsInMemory="1000"
        overflowToDisk="false" 
        diskPersistent="false" 
        timeToIdleSeconds="0"
        timeToLiveSeconds="600" 
        memoryStoreEvictionPolicy="LRU" />

    <cache 
        name="student"    
        eternal="false" 
        maxElementsInMemory="100"
        overflowToDisk="false" 
        diskPersistent="false" 
        timeToIdleSeconds="0"
        timeToLiveSeconds="300" 
        memoryStoreEvictionPolicy="LRU" />
        
</ehcache>

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
    updateCheck="false">
    
    
    <!-- 
    diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下:
        user.home – 用户主目录
        user.dir  – 用户当前工作目录
        java.io.tmpdir – 默认临时文件路径
        
     -->
    <diskStore path="java.io.tmpdir/Tmp_EhCache" />
    
    <!-- 
        defaultCache: 默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。
     -->
     
     <!-- 
        name:缓存名称。 
        maxElementsInMemory:缓存最大数目 
        maxElementsOnDisk:硬盘最大缓存个数。  
        eternal:对象是否永久有效,一但设置了,timeout将不起作用。  
        overflowToDisk:是否保存到磁盘,当系统当机时
        timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。 
        timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
        diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.  
        diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。  
        diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。 
        memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。  
        clearOnFlush:内存数量最大时是否清除。 
         memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。 
             FIFO,first in first out,这个是大家最熟的,先进先出。 
             LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
             LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
    --> 
    <defaultCache 
        eternal="false" 
        maxElementsInMemory="1000"
        overflowToDisk="false" 
        diskPersistent="false" 
        timeToIdleSeconds="0"
        timeToLiveSeconds="600" 
        memoryStoreEvictionPolicy="LRU" />

    <cache 
        name="student"    
        eternal="false" 
        maxElementsInMemory="100"
        overflowToDisk="false" 
        diskPersistent="false" 
        timeToIdleSeconds="0"
        timeToLiveSeconds="300" 
        memoryStoreEvictionPolicy="LRU" />
        
</ehcache>

(6)编写Student实体类进行测试;

@Entity
public class Student implements Serializable{
    
    private static final long serialVersionUID = 1L;
    @Id @GeneratedValue(strategy=GenerationType.AUTO)
    private int id; //主键.
    private String name;
    private int gender;
    private Date registerTime;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getGender() {
        return gender;
    }
    public void setGender(int gender) {
        this.gender = gender;
    }
    public Date getRegisterTime() {
        return registerTime;
    }
    public void setRegisterTime(Date registerTime) {
        this.registerTime = registerTime;
    }
    
}
 

(7)编写持久类StudentRepository;

public interface StudentRepository extends CrudRepository<Student, Integer> {

}
 

(8)编写处理类StudentService

@Service
public class StudentService {
    
    @Autowired
    private StudentRepository studentRepository;
    
    @Transactional //开启事务.
    @Cacheable(value="student",key="'student'+#student.id")
    public void save(Student student){
        studentRepository.save(student);
    }
    
    @Cacheable(value="student",key="'student'+#id")
    public Student findById(int id){
        System.out.println("不使用缓存,查询数据,并且返回。");
        return studentRepository.findOne(id);
    }
    
    @CacheEvict(value="student",key="'student'+#id")
    public void deleteCache(int id){
        //这里不做具体的数据库删除,只清空缓存.
        
    }
    
}

(9)编写StudentController测试类

@RestController
@RequestMapping("/student")
public class StudentController {
    
    @Autowired
    private StudentService studentService;
    
    @RequestMapping("/save")
    public Student save(Student student){
        studentService.save(student);
        return student;
    }
    
    
    @RequestMapping("/findById")
    public Student findById(int id){
        System.out.println("进入到StudentController的findById");
        return studentService.findById(id);
    }
    
    @RequestMapping("/deleteCache")
    public String deleteCache(int id){
        System.out.println("进入到StudentController的deleteCache");
        studentService.deleteCache(id);
        return "ok";
    }
}
@RestController
@RequestMapping("/student")
public class StudentController {
    
    @Autowired
    private StudentService studentService;
    
    @RequestMapping("/save")
    public Student save(Student student){
        studentService.save(student);
        return student;
    }
    
    
    @RequestMapping("/findById")
    public Student findById(int id){
        System.out.println("进入到StudentController的findById");
        return studentService.findById(id);
    }
    
    @RequestMapping("/deleteCache")
    public String deleteCache(int id){
        System.out.println("进入到StudentController的deleteCache");
        studentService.deleteCache(id);
        return "ok";
    }
}
 

(10)运行测试

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码上分享

创作不易,感谢各位看官

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值