缓存_1"/>
SpringBoot高级_与缓存_1
一、JSR-107规范
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设置。
一个应用里面可以有多个缓存提供者(CachingProvider),一个缓存提供者可以获取到多个缓存管理器(CacheManager),一个缓存管理器管理着不同的缓存(Cache),缓存中是一个个的缓存键值对(Entry),每个entry都有一个有效期(Expiry)。缓存管理器和缓存之间的关系有点类似于数据库中连接池和连接的关系。
开发中使用JSR-107需要导入包:一般不直接使用JSR-107开发,因为JSR-107仅仅定义了接口,而没有实现
<dependency><groupId>javax.cache</groupId><artifactId>cache-api</artifactId><version>1.1.0</version>
</dependency>·
二、Spring缓存抽象
1、简介
Spring从3.1开始定义了org.springframework.cache.Cache和
org.springframework.cache.CacheManager接口来统一不同的缓存技术;并支持使用JCache(JSR-107)注解简化我们开发。
Cache接口为缓存的组件规范定义,包含缓存的各种操作(增删改查)集合。Cache接口下Spring提供了各种XxxCache的实现,如RedisCache、EhCacheCache、ConcurrentMapCache等。每次调用需要缓存功能的方法时,Spring会检查指定参数的指定的目标方法是否已经被调用过,如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回给用户。下次调用直接从缓存中获取。
使用Spring缓存抽象时我们需要关注以下两点:
①确定方法需要被缓存以及他们的缓存策略
②从缓存中读取之前缓存存储的数据
说明:
①@Cacheable标注在方法上,表示该方法的结果需要被缓存起来,缓存的键由keyGenerator的策略决定,缓存的值的形式则由serialize序列化策略决定(序列化还是json格式);标注上该注解之后,在缓存时效内再次调用该方法时将不会调用方法本身而是直接从缓存获取结果
②@CachePut也标注在方法上,和@Cacheable相似也会将方法的返回值缓存起来,不同的是标注@CachePut的方法每次都会被调用,而且每次都会将结果缓存起来,适用于对象的更新
2、基本环境搭建
①创建SpringBoot应用:选中Cache、Mysql、Mybatis、Web模块,pom如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns=".0.0" xmlns:xsi=""xsi:schemaLocation=".0.0 .0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>1.5.18.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.bdm</groupId><artifactId>springboot-cache</artifactId><version>0.0.1-SNAPSHOT</version><name>springboot-cache</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>1.3.2</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>
②创建数据库表
SET FOREIGN_KEY_CHECKS=0;DROP TABLE IF EXISTS `department`;
CREATE TABLE `department` (`id` int(11) NOT NULL AUTO_INCREMENT,`departmentName` varchar(255) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;DROP TABLE IF EXISTS `employee`;
CREATE TABLE `employee` (`id` int(11) NOT NULL AUTO_INCREMENT,`lastName` varchar(255) DEFAULT NULL,`email` varchar(255) DEFAULT NULL,`gender` int(2) DEFAULT NULL,`d_id` int(11) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
③创建表对应的实体Bean
@Data
public class Employee {private Integer id;private String lastName;private String email;private Integer gender; //性别 1男 0女private Integer dId;
}@Data
public class Department {private Integer id;private String departmentName;
}
④整合mybatis操作数据库
数据源配置:驱动可以不写,SpringBoot会根据连接自动判断
spring.datasource.url=jdbc:mysql://localhost:3306/springboot_cache
spring.datasource.username=root
spring.datasource.password=120288
#spring.datasource.driver-class-name=com.mysql.jdbc.Driver#开启驼峰命名
mybatis.configuration.map-underscore-to-camel-case=true
使用注解版Mybatis:使用@MapperScan指定mapper接口所在的包
@SpringBootApplication
@MapperScan(basePackages = "com.bdm.cache.mappers")
public class SpringbootCacheApplication {public static void main(String[] args) {SpringApplication.run(SpringbootCacheApplication.class, args);}
}
创建对应的mapper接口:使用@Mapper注解标注,标明是一个mybatis的mapper接口
@Mapper
public interface EmployeeMapper {@Select("SELECT * FROM employee WHERE id = #{id}")public Employee getEmpById(Integer id);@Insert("INSERT INTO employee(lastName,email,gender,d_id) VALUES(#{lastName},#{email},#{gender},#{dId})")public void insertEmp(Employee employee);@Update("UPDATE 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);
}
编写Service:
@Service
public class EmployeeService {@AutowiredEmployeeMapper employeeMapper;public Employee getEmpById(Integer id){Employee emp = employeeMapper.getEmpById(id);return emp;}
}
编写Controller:
@RestController
public class EmployeeController {@AutowiredEmployeeService employeeService;@GetMapping("/emp/{id}")public Employee getEmp(@PathVariable("id") Integer id){return employeeService.getEmpById(id);}
}
测试
3、@Cacheable初体验
测试之前可以先配置一下Logger日志,让控制台将Sql打印出来:
logging.level.com.bdm.cache.mappers=debug
①开启基于注解的缓存功能:主启动类标注@EnableCaching
@SpringBootApplication
@MapperScan(basePackages = "com.bdm.cache.mappers")
@EnableCaching //开启基于注解的缓存
public class SpringbootCacheApplication {public static void main(String[] args) {SpringApplication.run(SpringbootCacheApplication.class, args);}
}
②标注缓存相关注解:@Cacheable、CacheEvict、CachePut
a、@Cacheable:将方法运行的结果进行缓存,以后再获取相同的数据时,直接从缓存中获取,不再调用方法
@Cacheable(cacheNames = {"emp"})
public Employee getEmpById(Integer id){Employee emp = employeeMapper.getEmpById(id);return emp;
}
注:
①既满足condition又满足unless条件的也不进行缓存
②使用异步模式进行缓存时(sync=true):unless条件将不被支持
可用的SpEL表达式见下表:
三、缓存工作原理
1、自动配置类:CacheAutoConfiguration,通过CacheAutoConfiguration导入的CacheConfigurationImportSelector会向数组中添加一些缓存的配置类全类名
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
3、默认生效的配置类:SimpleCacheConfiguration
4、SimpleCacheConfiguration给容器中注册了一个CacheManager:ConcurrentMapCacheManager
@Configuration
@ConditionalOnMissingBean({CacheManager.class})
@Conditional({CacheCondition.class})
class SimpleCacheConfiguration {private final CacheProperties cacheProperties;private final CacheManagerCustomizers customizerInvoker;SimpleCacheConfiguration(CacheProperties cacheProperties, CacheManagerCustomizers customizerInvoker) {this.cacheProperties = cacheProperties;this.customizerInvoker = customizerInvoker;}@Beanpublic ConcurrentMapCacheManager cacheManager() {ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();List<String> cacheNames = this.cacheProperties.getCacheNames();if (!cacheNames.isEmpty()) {cacheManager.setCacheNames(cacheNames);}return (ConcurrentMapCacheManager)this.customizerInvoker.customize(cacheManager);}
}
5、通过ConcurrentMapCacheManager可以获取和创建ConcurrentMapCache类型的缓存组件:ConcurrentMapCache的作用是数据保存在ConcurrentMap中
6、@Cacheable运行流程:
①方法运行之前,先去查询Cache(缓存组件),按照cacheNames指定的名字获取(CacheManager先获取相应的缓存,第一次获取缓存如果没有Cache组件会自动创建)
②去Cache中查找缓存的内容,使用的key默认就是方法的参数:
key默认是使用keyGenerator生成的,默认使用的是SimpleKeyGenerator
SimpleKeyGenerator生成key的默认策略:
如果没有参数:key = new SimpleKey();
如果有一个参数:key = 参数的值
如果有多个参数:key = new SimpleKey(params);
③没有查到缓存就调用目标方法
④将目标方法返回的结果放进缓存中
总结:@Cacheable标注的方法在执行之前会先检查缓存中有没有这个数据,默认按照参数的值为key查询缓存,如果没有就运行方法并将结果放入缓存,以后再来调用时直接使用缓存中的数据。
核心:
1️⃣使用CacheManager(ConcurrentMapCacheManager)按照名字得到Cache(ConcurrentMapCache)组件
2️⃣key使用keyGenerator生成,默认使用SimpleKeyGenerator
四、@Cacheable的其他属性
1、cacheNames/value:指定缓存组件的名字,将方法的返回结果放在哪个缓存中,值是数组的方式,可以指定多个缓存
2、key:缓存数据使用的key,默认使用方法参数的值,可使用SpEL表达式计算(如key="#root.methodName+’[’+ #id +’]’")
3、keyGenerator:key的生成器,可以自定义key的生成器,key/keyGenerator二选一使用
自定义主键生成器:此处的KeyGenerator 是cache相关的接口,注意不要导错接口
@Configuration
public class MyCacheConfig {@Bean("myKeyGenerator")public KeyGenerator keyGenerator() {return new KeyGenerator() {@Overridepublic Object generate(Object o, Method method, Object... objects) {return method.getName() + "[" + Arrays.asList(objects) + "]";}};}
}
使用自定义的KeyGenerator:
@Cacheable(cacheNames = {"emp"},keyGenerator = "myKeyGenerator",,condition = "#id > 1 and #id < 10")
public Employee getEmpById(Integer id){Employee emp = employeeMapper.getEmpById(id);return emp;
}
4、 cacheManager:指定缓存管理器,或者cacheResolver指定获取解析器
5、condition:指定符合条件的情况下才缓存
6、unless:否定缓存,当unless指定的条件为true,方法的返回值就不会被缓存,可以获取到结果进行判断
7、sync:是否使用异步模式缓存
更多推荐
SpringBoot高级_与缓存_1
发布评论