Javaweb——Spring Boot 系列(14)数据缓存相关技术

一、为什么需要数据缓存

  • 实际生活场景中,web 应用的后台数据库都不会像开发环境中为了测试功能,在数据库就添加了几百条甚至更少的数据记录那样,实际场景中的后台数据库的数据量往往数以万计,因此数据库往往存放存储介质中,一般是大容量的硬盘,不会直接存放在内存中。
  • 然而,无论硬盘多么好,其读写速度都比不过内存的读写速度,一旦我们访问数据或提交数据,特别是大量数据时,势必因为硬盘读写速度的跟不上,导致整个程序停留在数据库查询或提交数据这一个环节,给用户带来一种程序卡顿的体验,这势必会导致用户的流失。
  • 为了避免因为直接与硬盘交互导致程序运行缓慢,势必应用一种介于硬盘和内存之间对读写速度进行处理的东西,这就是缓存。
  • 一般位于硬盘和内存之间有两级缓存,与内存直接挂钩的采用高速缓存,其读写速率接近内存;而与硬盘挂钩的采用低速缓存。

二、Spring 对缓存的支持

  • Spring 在 cache 中定义了 CacheManager 和 Cache 两个类来统一不同的缓存技术,前者是各种缓存技术的抽象接口,后者是缓存的各种操作(增加、删除、获得缓存)。

1、Spring 支持的缓存技术

  • Spring 用不同的 CacheManager 对不同的缓存技术进行管理,Spring 支持的缓存技术如下:
    缓存方式 对应的 CacheManager 描述
    Collection SimpleCacheManager 使用简单的 Collection 来存储缓存,主要用于测试
    ConcurrentMap ConcurrentMapCacheManager 使用 ConcurrentMap 来存储缓存
    NoOpCacheManager 仅测试用途,不会实际存储缓存
    EhCache EhCacheCacheManager 使用 EhCache 做为缓存技术
    GuavaCache GuavaCacheManager 使用 Google Guava 的 GuavaCache 作为缓存技术
    Hazelcast HazelcastCacheManager 使用 Hazelcast 作为缓存技术
    JCache JCacheCacheManager 支持 JCache(JSR-107)标准的实现作为缓存技术,如 Apache Commons JCS
    Redis RedisCacheManager 使用 Redis 作为缓存技术

2、缓存注解

  • Spring 提供了 4 个注解来声明缓存规则,如下:
    注解 作用
    @Cacheable 在执行注解了该注解的方法前,查看缓存中是否有目标数据,有则直接返回数据,否则调用方法并将方法的返回值放入缓存中
    @CachePut 将方法的返回值放入缓存中;该注解的属性要与 @Cacheable 注解的属性保持一致
    @CacheEvict 将一条或多条数据从缓存中删除
    @Caching 组合多个注解策略在一个方法上
  • 这几个注解都有一个 value 属性用于指定缓存名称,也都有一个 key 属性指定数据在缓存中的存储的键。

3、如何开启 Spring 对声明式缓存的支持

  • 要开启声明式缓存的支持,操作颇为简单,只需在配置类上使用 @EnableCaching 注解就可以了。

4、Spring Boot 对缓存的支持

  • Spring Boot 在 autoconfigure.cache 中对各种 CacheManager 进行了许多自动配置,在不做任何额外配置的情况下,默认使用的是 SimpleCacheConfiguration(ConcurrentMap),当然我们可以在 application.properties 中使用前缀 spring.cache 加相关具体属性来进行修改配置,如下:
    spring.cache.type= #可选 generic、ehcache、hazelcast、infinispan、jcache、redis、guava、simple、none
    spring.cache.cache-name= #程序启动时创建缓存名称
    spring.cache.ehcache.config= # ehcache 配置文件的地址
    spring.cache.hazelcast.config= # 同上
    spring.cache.infinispan.config= #同上
    spring.cache.jcache.config= #同上
    spring.cache.jcache.provider= # 当多个 jcache 实现在类路径的时候,指定 jcache 实现
    spring.cache.guava.spec= # guava specs
    

三、项目示例

  • 以简单的项目测试数据缓存。

1、新建项目和配置

  • 使用 IDEA 新建一个 Spring Boot 项目,初始依赖选择 Cache、JPA 和 Web,并手动添加 Oracle 数据库的驱动包 ojdbc6.jar,POM 文件如下:
    <?xml version="1.0" encoding="UTF-8"?>
    <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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>1.3.0.M2</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.pyc</groupId>
        <artifactId>mycache</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>mycache</name>
        <packaging>jar</packaging>
        <description>Demo project for Spring Boot</description>
    
        <properties>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>com.oracle</groupId>
                <artifactId>ojdbc6</artifactId>
                <version>11.2.0.2.0</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-cache</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-jpa</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
    
  • application.properties 中的内容先保持和上一篇的一样。

2、Entity 和 Repository

  • 仍旧用 Person 这个数据表,因此实体类和实体类的 Repository 和上一篇的一样。

3、Business Service

  • 在这一项目中,同样要用业务服务的逻辑,一个服务接口和接口实现类。

3.1、Service Interface

  • 首先是一个服务接口,是一个虚类,代码如下:
    package com.pyc.mycache.service;
    
    import com.pyc.mycache.domain.Person;
    
    public interface DemoService {
        public Person save(Person person);
        public void remove(Long id);
        public Person findOne(Person person);
    }
    
  • 定义了三个方法,分别对应将数据写入缓存、从缓存中删除数据和从缓存中查找某个数据三种操作。

3.2、Service Implements

  • 对上面的服务接口进行具体实现,代码如下:
    package com.pyc.mycache.service.impl;
    
    import com.pyc.mycache.dao.PersonRepository;
    import com.pyc.mycache.domain.Person;
    import com.pyc.mycache.service.DemoService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cache.annotation.CacheEvict;
    import org.springframework.cache.annotation.CachePut;
    import org.springframework.cache.annotation.Cacheable;
    import org.springframework.stereotype.Service;
    
    @Service
    public class DemoServiceImpl implements DemoService {
    
        @Autowired
        PersonRepository personRepository;
    
        @Override
        // 缓存新增的或更新的数据到缓存,缓存名称为 people,数据的 key 时 person 的 id
        // Caching new or updated data to the cache,
        //the cache name is people, and the key of the data is the person's id
        @CachePut(value = "people", key = "#person.id")
        public Person save(Person person) {
            Person p = personRepository.save(person);
            System.out.println("为id,key 为:" + p.getId() + "的数据做了缓存");
            return p;
        }
    
        @Override
        // 从缓存 people 中删除 key 为 id 的数据
        // delete the data from cache called people where key value equal of the parameter id
        @CacheEvict(value = "people")
        public void remove(Long id) {
            System.out.println("删除了id, key 为" + id + "的数据缓存");
            personRepository.delete(id);
        }
    
        @Override
        // 缓存 key 为 person.id 的数据到缓存 people 中
        // Caching the data that key is parameter person's id into cache called people
        @Cacheable(value = "people", key = "#person.id")
        public Person findOne(Person person) {
            Person p = personRepository.findOne(person.getId());
            System.out.println("为 id,key 为" + p.getId() + "的数据做了缓存");
            return p;
        }
    }
    

4、Controller

  • 同样的,View 层无法对上文的接口方法直接调用,须得在控制层中生成相应的 URL 才能访问。
    package com.pyc.mycache.web;
    
    import com.pyc.mycache.domain.Person;
    import com.pyc.mycache.service.DemoService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class CacheController {
        @Autowired
        DemoService demoService;
    
        @RequestMapping("/put")
        public Person put(Person person){
            return demoService.save(person);
        }
    
        @RequestMapping("/able")
        public Person cacheable(Person person){
            return demoService.findOne(person);
        }
    
        @RequestMapping("/evit")
        public String evit(Long id){
            demoService.remove(id);
            return "ok";
        }
    }
    

5、注解入口类开启缓存支持

  • 在入口类上使用 @EnableCaching 开启缓存支持
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cache.annotation.EnableCaching;
    
    @SpringBootApplication
    @EnableCaching
    public class MycacheApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(MycacheApplication.class, args);
        }
    
    }
    

6、运行测试

  • 先对 @Cacheable 进行测试,访问 localhost:8080/able?id=42
  • 控制台输出如下图:
    在这里插入图片描述
  • 浏览器获得返回的数据
    在这里插入图片描述
  • 再次访问同样的路径,控制台不在输出对某某数据做了缓存的字样。
  • 测试 @CachePut,访问 localhost:8080/put?name=ty&address=sf&age=22
  • 控制台提示
    在这里插入图片描述
  • 再测试 @CacheEvit,访问 localhost:8080/evit?id=64,同样的控制太输出提示信息:
    在这里插入图片描述
  • 初步测试,功能正常。

四、转换缓存技术

  • 让项目从 Spring Boot 默认的缓存技术转换为其他缓存技术,例如 EhCache、Guava 或 Redis 的操作都比较简单,只需对 POM 文件添加相关依赖,以及用个 XML 文档对该缓存技术进行配置即可。
  • 例如 EhCache,先在 POM 文件中增加如下代码:
    <dependency>
    	<groupId>net.sf.ehcache</groupId>
    	<artifactId>ehcache</artifactId>
    </dependency>
    
  • 再在 Resources 目录下新建一个 ehcache.xml 文件,编辑内容如下:
    <?xml version="1.0" encoding="UTF-8"?>
    <ehcache>
        <cache name="people" maxElementsInMemory="1000"/>
    </ehcache>
    
  • 其他的缓存技术的切换也差不多是这个步骤。

上一篇
下一篇

发布了184 篇原创文章 · 获赞 24 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_42896653/article/details/104265369
今日推荐