加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

5、Spring Boot缓存

发布时间:2020-12-15 01:15:20 所属栏目:大数据 来源:网络整理
导读:1.JSR107 ??Java Caching定义了5个核心接口,分别是CachingProvider、CacheManager、Cache、Entry、Expiry。 ??CachingProvider:定义了创建、配置、获

1.JSR107

??Java Caching定义了5个核心接口,分别是CachingProvider、CacheManager、Cache、Entry、Expiry。

??CachingProvider:定义了创建、配置、获取、管理和控制多个CacheManager。一个应用可以在运行期访问多个CachingProvider。

??CacheManager:定义了创建、配置、获取、管理和控制多个唯一命名的Cache,这些Cache存在于CacheManager的上下文中。一个CacheManager仅被一个CachingProvider所拥有。

??Cache:是一个类似Map的数据结构并临时存储以Key为索引的值。一个Cache仅被一个CacheManager所拥有。

??Entry:是一个存储在Cache中的key-value对。

??Expiry:每一个存储在Cache中的条目有一个定义的有效期。一旦超过这个时间,条目为过期的状态。一旦过期,条目将不可访问、更新和删除。缓存有效期可以通过ExpiryPolicy设置。

?

2.Spring缓存抽象

??Spring从3.1开始定义了org.springframework.cache.Cache

org.springframework.cache.CacheManager接口来统一不同的缓存技术;

并支持使用JCache(JSR-107)注解简化我们开发;

?

??Cache接口为缓存的组件规范定义,包含缓存的各种操作集合;

??Cache接口下Spring提供了各种xxxCache的实现;如RedisCache、EhCacheCache、ConcurrentMapCache等;

??每次调用需要缓存功能的方法时,Spring会检查检查指定参数的指定的目标方法是否已经被调用过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回给用户。下次调用直接从缓存中获取。

??使用Spring缓存抽象时我们需要关注以下两点

确定方法需要被缓存以及他们的缓存策略

从缓存中读取之前缓存存储的数据

?

(1).搭建环境

1).导入数据库文件

com.hosystem.cache.bean.Department

package com.hosystem.cache.bean;

?

import java.io.Serializable;

?

public class Department implements Serializable {

???

???private Integer id;

???private String departmentName;

???

???

???public Department() {

??????super();

??????// TODO Auto-generated constructor stub

???}

???public Department(Integer id,String departmentName) {

??????super();

??????this.id = id;

??????this.departmentName = departmentName;

???}

???public Integer getId() {

??????return id;

???}

???public void setId(Integer id) {

??????this.id = id;

???}

???public String getDepartmentName() {

??????return departmentName;

???}

???public void setDepartmentName(String departmentName) {

??????this.departmentName = departmentName;

???}

???@Override

???public String toString() {

??????return "Department [id=" + id + ",departmentName=" + departmentName + "]";

???}

}

com.hosystem.cache.bean.Employee

package com.hosystem.cache.bean;

?

import java.io.Serializable;

?

public class Employee implements Serializable{

???

???private Integer id;

???private String lastName;

???private String email;

???private Integer gender; //性别?1??0

???private Integer dId;

???

???

???public Employee() {

??????super();

???}

?

???

???public Employee(Integer id,String lastName,String email,Integer gender,Integer dId) {

??????super();

??????this.id = id;

??????this.lastName = lastName;

??????this.email = email;

??????this.gender = gender;

??????this.dId = dId;

???}

???

???public Integer getId() {

??????return id;

???}

???public void setId(Integer id) {

??????this.id = id;

???}

???public String getLastName() {

??????return lastName;

???}

???public void setLastName(String lastName) {

??????this.lastName = lastName;

???}

???public String getEmail() {

??????return email;

???}

???public void setEmail(String email) {

??????this.email = email;

???}

???public Integer getGender() {

??????return gender;

???}

???public void setGender(Integer gender) {

??????this.gender = gender;

???}

???public Integer getdId() {

??????return dId;

???}

???public void setdId(Integer dId) {

??????this.dId = dId;

???}

???@Override

???public String toString() {

??????return "Employee [id=" + id + ",lastName=" + lastName + ",email=" + email + ",gender=" + gender + ",dId="

????????????+ dId + "]";

???}

}

(2).注解使用

?

com.hosystem.cache.service.EmployeeService

package com.hosystem.cache.service;

?

import com.hosystem.cache.bean.Employee;

import com.hosystem.cache.mapper.EmployeeMapper;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.cache.annotation.*;

import org.springframework.stereotype.Service;

?

@Service

@CacheConfig(cacheNames = "emp")

public class EmployeeService {

?

????@Autowired

????EmployeeMapper employeeMapper;

?

????/**

?????* ?将方法的运行结果进行缓存;下次在调用相同的数据,直接从缓存中获取,不再调用方法;

?????*

?????* ?CacheManager管理多个cache组件,对缓存的真正CRUD操作在Cache组件中,每一个缓存组件有自己唯一一个名字

?????* ?工作原理:

?????* ?????1.自动配置类:CacheAutoConfiguration

?????* ?????2.缓存配置类:GenericCacheConfigurationJCacheCacheConfigurationEhCacheCacheConfigurationHazelcastCacheConfigurationInfinispanCacheConfigurationCouchbaseCacheConfigurationRedisCacheConfigurationCaffeineCacheConfigurationSimpleCacheConfigurationNoOpCacheConfiguration

?????* ?????????org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration

?????* ?????????org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration

?????* ?????????org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration

?????* ?????????org.springframework.boot.autoconfigure.cache.HazelcastCacheConfiguration

?????* ?????????org.springframework.boot.autoconfigure.cache.InfinispanCacheConfiguration

?????* ?????????org.springframework.boot.autoconfigure.cache.CouchbaseCacheConfiguration

?????* ?????????org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration

?????* ?????????org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration

?????* ?????????org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration

?????* ?????????org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration

?????* ?????3.配置类默认生效:SimpleCacheConfiguration

?????* ?????4.给容器中注册了一个cacheManager:ConcurrentMapCacheManager

?????* ?????5.可以获取、创建ConcurrentMapCache类型的缓存组件;它的作用是将数据保存在ConcurrentMap

?????*

?????* ?@Cacheable 运行流程:

?????* ?????1.方法运行之前,先去查找Cache(缓存组件),按照cacheNames指定的名字获取;(CacheManager先获取相应的缓存)第一次获取缓存如果没有该缓存则会自动创建

?????* ?????2.Cache中查找缓存的内容,使用一个key,默认为方法的参数;

?????* ???????(1).key是按照某种策略生成的;默认是使用keyGenerator生成的,默认使用SimpleKeyGenerator生成key

?????* ???????(2).默认使用SimpleKeyGenerator生成key默认策略:若无参数,key = new SimpleKey();|如果有单个参数,key=参数值;|如果有多个参数,key = new SimpleKey(params);

?????* ?????3.若为查找到缓存就调用方法

?????* ?????4.将方法返回的结果,放入缓存中

?????* ?????@Cacheable 标注的方法执行之前先来检查缓存中有没有这个数据,默认按照参数的值作为key查找缓存,如果缓存不存在,则运行方法并将结果放入缓存

?????* ?核心:

?????* ?????1.使用CacheManager[ConcurrentMapCacheManager]按照名字获取cache[ConcurrentHashMapCache]组件

?????* ?????2.key使用keyGenerator生成,默认是SimpleKeyGenerator

?????*

?????* ?属性:valuecacheNameskeykeyGeneratorcacheManagercacheResolverconditionunlesssync

?????* ?????value/cacheNames:指定缓存组件的名字

?????* ?????key:缓存数据使用的key,可以用它指定参数。默认是使用方法参数的值

?????* ?????????SpEL: #id:参数id的值 ?#a0 #p0 #root.args[0]

?????* ?????keyGenerator:key生成器;可以指定key生成器组件id;

?????* ?????????注:keyGeneratorkey只能二选一

?????* ?????cacheManager:指定缓存管理器

?????* ?????cacheResolver:指定获取解析器

?????* ?????condition:指定符合条件情况下缓存

?????* ?????unless:否定缓存;unless指定条件为true,方法返回值不会被缓存;可以获取结果进行判断

?????* ?????sync:是否使用异步模式

?????*/

????//cacheNames = "emp":

????//condition = "#id>0":只有当id>0的时候再进行缓存

????//condition = "#a0>1":只有当第一个参数>1时候才进行缓存

????//unless = "#result==null":当返回结果为空时不进行缓存

????//unless = "#a0==2":如果第一个参数的结果为2,则结果不缓存

????//key = "#root.methodName+'['+#id+']'"

????//keyGenerator = "myKeyGenerator":自定义key

????@Cacheable(cacheNames = "emp"/*,condition = "#a0>1",unless = "#a0==2"*/)

????public Employee getEmp(Integer id){

????????System.out.println("查询"+id+"号员工");

????????Employee emp = employeeMapper.getEmpById(id);

????????return emp;

????}

?

????/**

?????* ?@CachePut:调用方法同时更新缓存数据

?????* ?修改数据库某个数据 同时更新缓存

?????*

?????* ?运行时间:

?????* ???1.先调用方法

?????* ???2.将方法的结果缓存起来

?????*

?????* ?测试步骤:

?????* ???1.查询1号员工;查询到的结果会放在缓存中 key:1 value:lastName:张三

?????* ???2.查询结果照旧

?????* ???3.更新1号员工信息[emp?id=1&lastName=zhangs&gender=0];将方法的返回值也放进缓存中 key:传入的employee对象 值:返回的employee对象

?????* ???4.查询1号员工;查询结果为未更新前的数据[1号员工的信息没有在缓存中更新]

?????* ?????key = "#employee.id":使用传入参数的员工id进行更新

?????* ?????key = "#result.id":使用返回后的id

?????* ?????:@Cacheablekey是不能够使用#result

?????*/

????@CachePut(value = "emp",key = "#result.id")

????public Employee updateEmp(Employee employee){

????????System.out.println("update" + employee);

????????employeeMapper.updateEmp(employee);

????????return employee;

????}

?

????/**

?????* ?@CacheEvict:缓存清除

?????*/

????//key = "#id":指定key删除缓存

????//allEntries = true:删除缓存中所有数据 默认参数为false

????//beforeInvocation=false:缓存的清除是否在方法之前执行 默认是false,即清除缓存操作在方法执行之后执行 如果方法出现异常缓存就不会清除

????//beforeInvocation = true:清除缓存操作在方法执行之前执行 如果方法出现异常缓存也会清除

????@CacheEvict(value = "emp"/*,key = "#id"*//*,allEntries = true*/,beforeInvocation = true)

????public void deleteEmp(Integer id){

????????System.out.println("delete"+id);

// ???????employeeMapper.deleteEmpById(id);

????????int i = 10/0;

????}

?

????@Caching(

????????cacheable = ?{

????????????????@Cacheable(value="emp",key="#lastName")

????????},

????????put = {

??????????????@CachePut(value = "emp",key = "#result.id"),

??????????????@CachePut(value = "emp",key = "#result.email")

????????}

????)

????public Employee getEmpByLastName(String lastName){

????????return employeeMapper.getEmpByLastName(lastName);

????}

}

com.hosystem.cache.service.DeptService

package com.hosystem.cache.service;

?

import com.hosystem.cache.bean.Department;

import com.hosystem.cache.mapper.DepartmentMapper;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.cache.annotation.Cacheable;

import org.springframework.stereotype.Service;

?

@Service

public class DeptService {

?

????@Autowired

????DepartmentMapper departmentMapper;

?

????/**

?????* ?缓存的数据能存入redis

?????* ?第二次从缓存中查询就不能恢复反序列化

?????* ?存的是deptjson;cachemanager默认使用RedisTemplate<Object,Employee>操作Redis

?????*

?????* @param id

?????* @return

?????*/

????@Cacheable(cacheNames = "dept")

????public Department getDeptById(Integer id){

????????System.out.println("查询部门"+id);

????????Department mapper = departmentMapper.getDeptById(id);

????????return mapper;

????}

}

?

?

?

?

?

com.hosystem.cache.controller.EmployeeController

package com.hosystem.cache.controller;

?

?

import com.hosystem.cache.bean.Employee;

import com.hosystem.cache.service.EmployeeService;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.RestController;

?

@RestController

public class EmployeeController {

?

????@Autowired

????EmployeeService employeeService;

?

????@GetMapping("/emp/{id}")

????public Employee getEmployee(@PathVariable("id") Integer id){

????????Employee employee = employeeService.getEmp(id);

????????return employee;

????}

?

????@GetMapping("/emp")

????public Employee update(Employee employee){

????????Employee emp = employeeService.updateEmp(employee);

????????return emp;

????}

?

????@GetMapping("/delemp")

????public String deleteEmp(Integer id){

????????employeeService.deleteEmp(id);

????????return "success";

????}

?

????@GetMapping("/emp/lastname/{lastName}")

????public Employee getEmpByLastName(@PathVariable("lastName") String lastName){

????????return employeeService.getEmpByLastName(lastName);

????}

}

com.hosystem.cache.Springboot01CacheApplication

package com.hosystem.cache;

?

import org.mybatis.spring.annotation.MapperScan;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.cache.annotation.EnableCaching;

?

/**

?* ?搭建环境

?* ?1. 导入数据库文件 ?创建departmentemployee

?* ?2. 创建javaBean封装数据

?* ?3. 整合mybatis操作数据库

?* ????1).配置数据源

?* ????2).使用注解版mybatis

?* ???????(1).@MapperScan指定需要扫描的mapper接口所在的包

?*

?* ?使用缓存

?* ?1. 开启注解缓存 @EnableCaching

?* ?2. 标注缓存注解

?* ????@Cacheable:针对方法配置,能够根据方法的请求参数对其结果进行缓存

?* ????@CacheEvict:清空缓存

?* ????@CachePut:保证方法被调用,又希望结果被缓存

?*

?* 默认使用的ConcurrentMapCacheManager--->ConcurrentMapCache 将数据保存在ConcurrentMap<Object,Object>

?* 开发中常使用其它缓存中间件:Redismemcahced

?*

?* 整合Redis作为缓存

?* Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。

?* 1.安装docker:https://www.cnblogs.com/HOsystem/p/13789551.html

?* 2.安装Redis(通过docker):https://www.cnblogs.com/HOsystem/p/13850049.html

?* 3.配置Redis

?* 4.测试缓存

?* ???原理:CacheManager===Cache 缓存组件来实际给缓存中存储数据

?* ???(1).引入redisstarter,容器中保存的是org.springframework.data.redis.cache.RedisCacheManager

?* ???(2).org.springframework.data.redis.cache.RedisCacheManager帮忙创建org.springframework.data.redis.cache.RedisCache作为缓存组件;

?* ??????org.springframework.data.redis.cache.RedisCache通过操作redis缓存数据的

?* ???(3).默认保存数据k-v都是object 利用序列化保存;如何保存为json;

?* ??????1).引入了redisstartercachemanager变为RedisCacheManage

?* ??????2).默认创建RedisCacheManage操作redis的时候使用的是RedisTemplate<Object,Object>

?* ??????3).RedisTemplate<Object,Object>默认使用jdk的序列化机制

?* ???(4).自定义CacheManager

?*

?*/

@MapperScan("com.hosystem.cache.mapper")

@EnableCaching

@SpringBootApplication

public class Springboot01CacheApplication {

?

???public static void main(String[] args) {

??????SpringApplication.run(Springboot01CacheApplication.class,args);

???}

?

}

?

(3).docker

1).安装docker

https://www.cnblogs.com/HOsystem/p/13789551.html

2).安装redis

https://www.cnblogs.com/HOsystem/p/13850049.html

3).测试Redis

com.hosystem.cache.Springboot01CacheApplicationTests

package com.hosystem.cache;

?

import com.hosystem.cache.bean.Employee;

import com.hosystem.cache.mapper.EmployeeMapper;

import com.sun.xml.internal.ws.api.ha.StickyFeature;

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.test.context.SpringBootTest;

import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.data.redis.core.StringRedisTemplate;

import org.springframework.data.redis.serializer.RedisSerializer;

import org.springframework.lang.Nullable;

?

@SpringBootTest

class Springboot01CacheApplicationTests {

?

???@Autowired

???EmployeeMapper employeeMapper;

?

???@Autowired

???StringRedisTemplate stringRedisTemplate; ?//操作k-v是字符串形式

?

???@Autowired

???RedisTemplate redisTemplate; ??//k-v都是对象

?

???@Autowired

???RedisTemplate<Object,Employee> empRedisTemplate;

?

???/**

????* ????Redis常见五大数据类型

????* ???????String(字符串)List(列表)Hash(散列)Set(集合)ZSet(有序集合)

????* ???????stringRedisTemplate.opsForValue():String(字符串)

????* ???????stringRedisTemplate.opsForList():List(列表)

????* ???????stringRedisTemplate.opsForHash():Hash(散列)

????* ???????stringRedisTemplate.opsForSet():Set(集合)

????* ???????stringRedisTemplate.opsForZSet():ZSet(有序集合)

????*/

???@Test

???public void test01(){

??????//redis保存数据

// ???stringRedisTemplate.opsForValue().append("msg","hello");

??????String msg = stringRedisTemplate.opsForValue().get("msg");

??????System.out.println(msg);

?

// ???stringRedisTemplate.opsForList().leftPush("mylist","1");

// ???stringRedisTemplate.opsForList().leftPush("mylist","2");

???}

?

???//测试保存对象

???@Test

???public void test02(){

??????Employee empById = employeeMapper.getEmpById(1);

??????//默认保存对象,使用jdk序列化机制,序列化后的数据保存到redis

// ???redisTemplate.opsForValue().set("emp-01",empById);

??????//1.将数据以json的方式保存

?????????//(1).将对象转为json

?????????//(2).redisTemplate默认序列化规则;自定义默认序列化规则

????????????// ???private RedisSerializer keySerializer = null;

????????????// ???private RedisSerializer valueSerializer = null;

????????????// ???private RedisSerializer hashKeySerializer = null;

????????????// ???private RedisSerializer hashValueSerializer = null;

????????????// ???private RedisSerializer<String> stringSerializer = RedisSerializer.string();

??????empRedisTemplate.opsForValue().set("emp-01",empById);

???}

?

?

?

???@Test

???public void contextLoads() {

??????Employee empById = employeeMapper.getEmpById(1);

??????System.out.println(empById);

???}

?

}

docker启动redis失败

Error response from daemon: Cannot start container 53fe1fcb2e05214c6f853ef2fe9f65539e69fdc7d6a454bfb073c10c2fba82dd: iptables failed: iptables -t nat -A DOCKER -p tcp -d 0/0 --dport 6379 -j DNAT --to-destination 172.17.0.3:6379 !?-i docker0: iptables: No chain/target/match by that name.

我们首先对iptables进行防火墙规则配置 允许6379端口可以访问

docker启动redis失败

[root@pluto sysconfig]# docker run -d -p 6379:6379 --name myredis redis

Error response from daemon: Conflict. The name "myredis" is already in use by container 53fe1fcb2e05. You have to delete (or rename) that container to be able to reuse that name.

[root@pluto sysconfig]# docker ps -a

CONTAINER ID ???????IMAGE ??????????????????????COMMAND ???????????????CREATED ????????????STATUS ?????????????????????PORTS ??????????????NAMES

53fe1fcb2e05 ???????redis ??????????????????????"docker-entrypoint.s ??2 minutes ago ??????????????????????????????????????????????????????myredis ??????

[root@pluto sysconfig]# docker rm 53fe1fcb2e05

(4).自定义CacheManager

* ??整合Redis作为缓存

* ?Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。

* ?1.安装docker:https://www.cnblogs.com/HOsystem/p/13789551.html

* ?2.安装Redis(通过docker):https://www.cnblogs.com/HOsystem/p/13850049.html

* ?3.配置Redis

* ?4.测试缓存

* ????原理:CacheManager===Cache 缓存组件来实际给缓存中存储数据

* ????(1).引入redisstarter,容器中保存的是org.springframework.data.redis.cache.RedisCacheManager

* ????(2).org.springframework.data.redis.cache.RedisCacheManager帮忙创建org.springframework.data.redis.cache.RedisCache作为缓存组件;

* ???????org.springframework.data.redis.cache.RedisCache通过操作redis缓存数据的

* ????(3).默认保存数据k-v都是object 利用序列化保存;如何保存为json;

* ???????1).引入了redisstartercachemanager变为RedisCacheManage

* ???????2).默认创建RedisCacheManage操作redis的时候使用的是RedisTemplate<Object,Object>

* ???????3).RedisTemplate<Object,Object>默认使用jdk的序列化机制

* ????(4).自定义CacheManager

@Autowired

StringRedisTemplate stringRedisTemplate;

?

?

@Autowired

RedisTemplate redisTemplate;

?

pom.xml

<?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>2.3.4.RELEASE</version>

??????<relativePath?<!-- lookup parent from repository -->

?

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读