SpringBoot和缓存

Cache缓存快速使用:

启动类:

package com.atguigu.cache;

import org.apache.ibatis.annotations.Mapper;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

/**
 * 一、搭建基本环境
 * 1.导入数据库文件 创建出department 和 employee表
 * 2.创建javaBean封装数据
 * 3.整合MyBatis操作数据库
 * 1.配置数据源信息
 * 2.使用注解版的MyBatis
 * 1).@MapperScan指定需要扫描的mapper接口所在的包
 * 二、快速体验缓存
 * 步骤:
 * 1.开启基于注解的缓存@EnableCaching
 * 2.标注缓存注解即可
 * @Cacheable
 * @CacheEvict
 * @EnableCaching
 */
@EnableCaching
@MapperScan("com.atguigu.cache.mapper")
@SpringBootApplication
public class CacheApplication {

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

}

Controller:

package com.atguigu.cache.controller;

import com.atguigu.cache.bean.Employee;
import com.atguigu.cache.service.EmployeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author 犀角
 * @date 2020/12/20 20:19
 * @description
 */
@RestController
public class EmployeeController {

    @Autowired
    EmployeeService employeeService;



    @RequestMapping("/emp/{id}")
    public Employee getEmployee(@PathVariable("id") Integer id){
        Employee emp = employeeService.getEmp(id);
        return emp;
    }
}

Mapper:

package com.atguigu.cache.mapper;

import com.atguigu.cache.bean.Employee;
import org.apache.ibatis.annotations.*;

/**
 * @author 犀角
 * @date 2020/12/20 19:36
 * @description
 */
@Mapper
public interface EmployeeMapper {

    @Select("SELECT * FROM employee WHERE id = #{id}")
    public Employee getEmpById(Integer id);

    @Update("UPDAT employee SET lastName=#{lastName},email=#{email},gender=#{gender},d_id=#{dId} WHERE id=#{id}")
    public void updateEmp(Employee employee);

    @Delete("DELETE FROM employee WHERE id=#{id}")
    public void deleteEmpById(Integer id);

    @Insert("INSERT INTO employee(lastName,email,gender,d_id) VALUES (#{lastName},#{email},#{gender},#{dId})")
    public void insertEmployee(Employee employee);

}

service:

package com.atguigu.cache.service;

import com.atguigu.cache.bean.Employee;
import org.springframework.cache.annotation.Cacheable;

/**
 * @author 犀角
 * @date 2020/12/20 20:15
 * @description
 */
public interface EmployeeService {



    public Employee getEmp(Integer id);
}

serviceImpl:

package com.atguigu.cache.service.Impl;

import com.atguigu.cache.bean.Employee;
import com.atguigu.cache.mapper.EmployeeMapper;
import com.atguigu.cache.service.EmployeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

/**
 * @author 犀角
 * @date 2020/12/20 20:16
 * @description
 */
@Service
public class EmployeeServiceImpl implements EmployeeService {

    @Autowired
    EmployeeMapper employeeMapper;


    /**
     * 将方法的运行结果进行缓存,以后再要相同的数据,直接从缓存中获取,不用调用方法
     * CacheManager管理多个Cache组件的,对缓存的真正CRUD操作在Cache组件中,每一个缓存组件有自己唯一一个名字;
     *
     * 几个属性
     *      cacheNames/value:指定缓存的名称;
     *      key:缓存数据时的key;可以用它来指定。默认是使用方法参数的值 1-方法的返回值
     *              编写SpEL; #id;参数id的值 #a0 #p0 #root.args[0]
     *      keyGenerator: key的生成器;可以自己指定key的生成器的组件id
     *              key/keyGenerator:二选一使用
     *      cacheManager:指定缓存管理器; 或者指定cacheResolver  指定获取解析器
     *      candition:指定符合条件的情况下才缓存;
            *      ,condition = "#id>0"
            *      unless:否定缓存;当unless条件为true,方法的返回值就不会被缓存;可以获取到结果进行判断 ,unless = "#result == null"
            *      sync:是否使用异步
     * @param id
     * @return
             */
    @Override
    @Cacheable(cacheNames = {"emp"})
    public Employee getEmp(Integer id) {
        Employee emp = employeeMapper.getEmpById(id);
        return emp;
    }
}

Bean:

package com.atguigu.cache.bean;

public class Employee {

	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 + "]";
	}



}

一、Cache缓存的作用

 

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.、确定方法需要被缓存以及他们的缓存策略

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

 

二、几个重要概念&缓存注解

Cache缓存接口,定义缓存操作。实现有:RedisCache、EhCacheCache、 ConcurrentMapCache等
CacheManager

 缓存管理器,管理各种缓存(Cache)组件

@Cacheable

主要针对方法配置,能够根据方法的请求参数对其结果进行缓存

@CacheEvict清空缓存
@CachePut

 保证方法被调用,又希望结果被缓存。

@EnableCaching

开启基于注解的缓存

keyGenerator

缓存数据时key生成策略

serialize缓存数据时value序列化策略
@Cacheable/@CachePut/@CacheEvict 主要的参数
value

缓存的名称,在 spring 配置文件中定义,必须指定 至少一个

 

例如: @Cacheable(value=”mycache”) 或者 @Cacheable(value={”cache1”,”cache2”}

 

key

缓存的 key,可以为空,如果指定要按照 SpEL 表达 式编写,如果不指定,则缺省按照方法的所有参数 进行组合

 

例如: @Cacheable(value=”testcache”,key=”#userName”
condition

缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存/清除缓存,在 调用方法之前之后都能判断

 

例如: @Cacheable(value=”testcache”,condition=”#userNam e.length()>2”)

 

allEntries (@CacheEvict )

 

是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存

 

例如:@CachEvict(value=”testcache”,allEntries=true)

 

beforeInvocation (@CacheEvict)

 

是否在方法执行前就清空,缺省为 false,如果指定 为 true,则在方法还没有执行的时候就清空缓存, 缺省情况下,如果方法执行抛出异常,则不会清空 缓存

 

例如:@CachEvict(value=”testcache”, beforeInvocation=true)
unless (@CachePut) (@Cacheable)用于否决缓存的,不像condition,该表达式只在方 法执行之后判断,此时可以拿到返回值result进行判 断。条件为true不会缓存,fasle才缓存例如:@Cacheable(value=”testcache”,unless=”#result == null”)
Cache SpEL available metadata
名字位置描述实例
methodName root object当前被调用的方法名 #root.methodName
method root object当前被调用的方法 #root.method.name
target root object当前被调用的目标对象 #root.target
targetClass root object当前被调用的目标对象类 #root.targetClass
args root object当前被调用的方法的参数列表 #root.args[0]
caches root object

 当前方法调用使用的缓存列表(如@Cacheable(value={"cache1", "cache2"})),则有两个cache。

 

#root.caches[0].name

 

argument name

 

evaluation context

方法参数的名字. 可以直接 #参数名 ,也可以使用 #p0或#a0 的 形式,0代表参数的索引。

 

#a0、#p0
resultevaluation context

方法执行后的返回值(仅当方法执行之后的判断有效,如 ‘unless’,’cache put’的表达式 ’cache evict’的表达式 beforeInvocation=false)

 

#result

 

三、缓存实现原理

将方法的运行结果进行缓存;以后再要相同的数据,直接从缓存中获取,不用调用方法,CacheManager管理多个Cache组件的,对缓存的真正CRUD操作在Cache组件中,每一个缓存组件有自己唯一一个名字。

详细原理:

1)、查看自动配置类:CacheAutoConfiguration

2)、断点跟踪可知默认配置了:
     *   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.GuavaCacheConfiguration
     *   org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration【默认】
     *   org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration

  默认SimpleCacheConfiguration生效

3)、给容器中注册了一个CacheManager:ConcurrentMapCacheManager;

4)、获取和创建ConcurrentMapCache类型的缓存组件,他的作用将数据保存在ConcurrentMap中;

5)、 运行流程:

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

  去Cache中查找缓存的内容,使用一个key,默认就是方法的参数,key是按照某种策略生成的,默认是使用keyGenerator生成的,默认使用SimpleKeyGenerator生成key,SimpleKeyGenerator生成key的默认策略:如果没有参数:key=new SimpleKey();如果有一个参数:key=参数的值;如果有多个参数:key=new SimpleKey(params);
  没有查到缓存就调用目标方法,将目标方法返回的结果,放进缓存中,@Cacheable标注的方法执行之前先来检查缓存中有没有这个数据,默认按照参数的值作为key去查询缓存,如果没有就运行方法并将结果放入缓存,以后再来调用就可以直接使用缓存中的数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值