SpringBoot企业级开发教程第6章 SpringBoot缓存管理
SpringBootCache使用与整合

SpringBootCache使⽤与整合Spring 提供了对缓存功能的抽象:即允许绑定不同的缓存解决⽅案(如Caffeine、Ehcache等),但本⾝不直接提供缓存功能的实现。
它⽀持注解⽅式使⽤缓存,⾮常⽅便。
SpringBoot在annotation的层⾯实现了数据缓存的功能,基于Spring的AOP技术。
所有的缓存配置只是在annotation层⾯配置,像声明式事务⼀样。
Spring定义了CacheManager和Cache接⼝统⼀不同的缓存技术。
其中CacheManager是Spring提供的各种缓存技术的抽象接⼝。
⽽Cache接⼝包含缓存的各种操作。
Cache接⼝下Spring提供了各种xxxCache的实现,如RedisCache,EhCacheCache ,ConcurrentMapCache等;⼀,缓存技术类型与CacheManger针对不同的缓存技术,需要实现不同的cacheManager,Spring定义了如下的cacheManger实现。
CacheManger描述SimpleCacheManager使⽤简单的Collection来存储缓存,主要⽤于测试ConcurrentMapCacheManager使⽤ConcurrentMap作为缓存技术(默认),需要显式的删除缓存,⽆过期机制NoOpCacheManager仅测试⽤途,不会实际存储缓存EhCacheCacheManager使⽤EhCache作为缓存技术,以前在hibernate的时候经常⽤GuavaCacheManager使⽤google guava的GuavaCache作为缓存技术(1.5版本已不建议使⽤)CaffeineCacheManager是使⽤Java8对Guava缓存的重写,spring5(springboot2)开始⽤Caffeine取代guavaHazelcastCacheManager使⽤Hazelcast作为缓存技术JCacheCacheManager使⽤JCache标准的实现作为缓存技术,如Apache Commons JCSRedisCacheManager使⽤Redis作为缓存技术常规的SpringBoot已经为我们⾃动配置了EhCache、Collection、Guava、ConcurrentMap等缓存,默认使⽤ConcurrentMapCacheManager。
springboot中使用自定义两级缓存的方法

springboot中使⽤⾃定义两级缓存的⽅法⼯作中⽤到了springboot的缓存,使⽤起来挺⽅便的,直接引⼊redis或者ehcache这些缓存依赖包和相关缓存的starter依赖包,然后在启动类中加⼊@EnableCaching注解,然后在需要的地⽅就可以使⽤@Cacheable和@CacheEvict使⽤和删除缓存了。
这个使⽤很简单,相信⽤过springboot缓存的都会玩,这⾥就不再多说了。
美中不⾜的是,springboot使⽤了插件式的集成⽅式,虽然⽤起来很⽅便,但是当你集成ehcache的时候就是⽤ehcache,集成redis的时候就是⽤redis。
如果想两者⼀起⽤,ehcache作为本地⼀级缓存,redis作为集成式的⼆级缓存,使⽤默认的⽅式据我所知是没法实现的(如果有⾼⼈可以实现,⿇烦指点下我)。
毕竟很多服务需要多点部署,如果单独选择ehcache可以很好地实现本地缓存,但是如果在多机之间共享缓存⼜需要⽐较费时的折腾,如果选⽤集中式的redis缓存,因为每次取数据都要⾛⽹络,总感觉性能不会太好。
本话题主要就是讨论如何在springboot的基础上,⽆缝集成ehcache和redis作为⼀⼆级缓存,并且实现缓存同步。
为了不要侵⼊springboot原本使⽤缓存的⽅式,这⾥⾃⼰定义了两个缓存相关的注解,如下@Target({ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)public @interface Cacheable {String value() default "";String key() default "";//泛型的Class类型Class<?> type() default Exception.class;}@Target({ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)public @interface CacheEvict {String value() default "";String key() default "";}如上两个注解和spring中缓存的注解基本⼀致,只是去掉了⼀些不常⽤的属性。
深度理解springboot集成cache缓存之源码解析

深度理解springboot集成cache缓存之源码解析⼀、案例准备1.创建数据表(employee表)2.创建Employee实体类封装数据库中的数据@AllArgsConstructor@NoArgsConstructor@Data@ToStringpublic class Employee {private Integer id;private String lastName;private String email;private Integer gender; //1.男 2.⼥private Integer dId;}3.编写EmployeeMapper接⼝(DAO测通)@Mapperpublic interface EmployeeMapper {@Select("select * from employee where id=#{id}")Employee getEmpById(Integer id);}4.编写EmployeeService接⼝及其EmployeeServiceImpl实现类@Servicepublic class EmployeeServiceImpl implements EmployeeService {@Autowiredprivate EmployeeMapper employeeMapper;@Override@Cacheable(cacheNames = "emp",key = "#id",condition = "#id>0")public Employee getEmp(Integer id) {System.out.println("正在查询id为"+id+"号的员⼯");Employee emp = employeeMapper.getEmpById(id);return emp;}}5.编写EmployeeController类@RestControllerpublic class EmpController {@Autowiredprivate EmployeeService employeeService;@GetMapping("/emp/{id}")public Employee getEmployee(@PathVariable("id") Integer id){Employee emp = employeeService.getEmp(id);return emp;}}6.启动访问http://localhost:8080/emp/1成功!-----------------------------------------------分割线-------------------------------------------------------------⼆、⼯作原理分析1.查看springboot启动时,导⼊了哪些缓存组件通过以往springboot相关的⾃动配置类可知与缓存相关的⾃动配置类为CacheAutoConfiguration@Import:向容器中导⼊⼀些组件(通常导⼊的选择器以ImportSelector结尾) ctrl+右键查看CacheConfigurationImportSelector源码static class CacheConfigurationImportSelector implements ImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {CacheType[] types = CacheType.values();String[] imports = new String[types.length];for (int i = 0; i < types.length; i++) {imports[i] = CacheConfigurations.getConfigurationClass(types[i]);}return imports;}}}打上断点,debug模式下运⾏放⾏,查看return imports的结果导⼊的组件如下* "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.NoOpCacheConfiguration"* "org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration"2.是哪个缓存配置类⽣效呢?根据当前的场景进⾏分析有两种⽅法可以得出哪个缓存配置类⽣效第⼀种:源码分析(该⽅法只做简单说明)随便打开⼀个缓存配置类,例如第⼀个GenericCacheConfiguration,查看源码如下@Configuration(proxyBeanMethods = false)@ConditionalOnBean(Cache.class)@ConditionalOnMissingBean(CacheManager.class)@Conditional(CacheCondition.class)class GenericCacheConfiguration {@BeanSimpleCacheManager cacheManager(CacheManagerCustomizers customizers, Collection<Cache> caches) {SimpleCacheManager cacheManager = new SimpleCacheManager();cacheManager.setCaches(caches);return customizers.customize(cacheManager);}}根据类上的@ConditionalOnBean(Cache.class)@ConditionalOnMissingBean(CacheManager.class)@Conditional(CacheCondition.class)注解进⾏判断该类是否⽣效这些都是@Conditional注解的衍⽣注解,该注解是Spring4新推出的注解,判断是否满⾜某种条件,如果满⾜则给容器注册bean第⼆种:查看⾃动配置报告在application.properties配置⽂件中添加debug=true运⾏发现这⼏个组件中只有SimpleCacheConfiguration⽣效了3.分析核⼼类的作⽤进去查看SimpleCacheConfiguration的源码@Configuration(proxyBeanMethods = false)@ConditionalOnMissingBean(CacheManager.class)@Conditional(CacheCondition.class)class SimpleCacheConfiguration {@BeanConcurrentMapCacheManager cacheManager(CacheProperties cacheProperties,CacheManagerCustomizers cacheManagerCustomizers) {ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();List<String> cacheNames = cacheProperties.getCacheNames();if (!cacheNames.isEmpty()) {cacheManager.setCacheNames(cacheNames);}return cacheManagerCustomizers.customize(cacheManager);}}可以看出给容器中注册了⼀个ConcurrentMapCacheManager缓存管理器查看源码分析创建Cache的具体细节这⼀段代码是创建Cache的核⼼#⾸先通过该类中的cacheMap属性获取缓存,参数为缓存名字(key-value)#然后进⾏判断,如果cache为null,则上锁;再次获取如果为null,则根据本类中的createConcurrentMapCache⽅法创建Cache,然后将其放到缓存中protected Cache createConcurrentMapCache(String name) {SerializationDelegate actualSerialization = this.isStoreByValue() ? this.serialization : null;return new ConcurrentMapCache(name, new ConcurrentHashMap(256), this.isAllowNullValues(), actualSerialization);}}可以看出返回时创建了ConcurrentMapCache对象,进去查看源码lookup⽅法的作⽤是从缓存中获取数据put⽅法的作⽤是是保存数据到缓存中⾃此我们可以猜⼀下,ConcurrentMapCache该类的作⽤就是对缓存中的数据进⾏操作,如果缓存中没有数据,则从数据库查询,⼀并放到缓存中总的来说ConcurrentMapCacheManager类的作⽤就是,先判断是否有某缓存,如果没有就创建该缓存,ConcurrentMapCache类从数据库中进⾏查询,⼀并将数据存储到ConcurrentMap集合中4.运⾏流程根据上⾯打上的四个断点,debug模式下启动发现程序刚开始并没有⾛EmployeeServiceImpl的断点,⽽是⾛到了这个getCache⽅法,寻找name=emp的缓存因为没有名为emp的缓存,所以会创建名为emp的缓存,继续放⾏使⽤key去缓存中查找内容(key默认是⽅法的参数,浏览器访问的是1号员⼯信息,id=1即key=1),size=0,缓存中的数据为空继续放⾏直接调⽤业务⽅法去数据库中查询数据了,这是为什么呢,因为上个步骤在缓存中没有查询到数据,所以需要向数据库中要数据;继续放⾏上⼀步从数据库中查询出数据之后,将数据传回前端页⾯展⽰并使⽤put⽅法将其放⼊到缓存中此时缓存中已经有数据了,当我再次debug运⾏就不会再从数据库中查询数据了该博客仅为了记录⾃⼰的学习过程,理清技术点思路。
springboot(12)Redis作为SpringBoot项目数据缓存

springboot(12)Redis作为SpringBoot项⽬数据缓存简介: 在项⽬中设计数据访问的时候往往都是采⽤直接访问数据库,采⽤数据库连接池来实现,但是如果我们的项⽬访问量过⼤或者访问过于频繁,将会对我们的数据库带来很⼤的压⼒。
为了解决这个问题从⽽redis数据库脱颖⽽出,redis数据库出现时是以⾮关系数据库的光环展⽰在⼴⼤程序猿的⾯前的,后来redis的迭代版本⽀持了缓存数据、登录session状态(分布式session共享)等。
所以⼜被作为内存缓存的形式应⽤到⼤型企业级项⽬中。
本⽂⽬标 实现SpringBoot项⽬中整合⾮关系数据库Redis作为内存缓存框架,并测试数据读取源。
Redis的安装: 官⽹的仅⽀持Linux服务器的安装版本,由于开发⼈员都是在windows上⼯作,所以GitHub上的⽜⼈基于linux平台下的Redis实现了windows版本。
下载Windows版本Redis: 我们直接访问github⽹址:,下载最新的windows X64版本的压缩包。
开启Redis: 解压完成后我们来开启Redis数据库,Redis数据库的默认端⼝是6379,如果已经被其他应⽤程序占⽤,请⾃⾏修改redis.windows.conf 配置⽂件。
⼀、创建项⽬: 项⽬依赖pom.xml,我们添加缓存的⽀持需要两个依赖,⼀个是SpringBoot内部的缓存配置、另外则是我们的redis缓存。
<?xml version="1.0" encoding="UTF-8"?><project xmlns="/POM/4.0.0" xmlns:xsi="/2001/XMLSchema-instance"xsi:schemaLocation="/POM/4.0.0 /xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.yuqiyu</groupId><artifactId>chapter16</artifactId><version>0.0.1-SNAPSHOT</version><packaging>war</packaging><name>chapter16</name><description>Demo project for Spring Boot</description><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>1.5.2.RELEASE</version><relativePath/><!-- lookup parent from repository --></parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version></properties><dependencies><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><!--引⼊druid最新maven依赖--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.0.29</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-tomcat</artifactId><!--<scope>provided</scope>--></dependency><!-- 添加缓存⽀持 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency><!-- 添加Redis缓存⽀持 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-redis</artifactId><version>1.4.3.RELEASE</version></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>View Code 数据库对应的实体类:@Entity@Table(name = "t_user")public class UserEntity implements Serializable {@Id@GeneratedValue@Column(name = "t_id")private Long id;@Column(name = "t_name")private String name;@Column(name = "t_age")private int age;@Column(name = "t_address")private String address;public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getName() {return name;}public void setName(String name) { = name;}public int getAge() {return age;}this.age = age;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}}View Code 简单JPA接⼝public interface UserJPA extends JpaRepository<UserEntity,Long>{}⼆、配置⽂件application.yml,数据库连接池、jpa、本地的redis数据库连接到项⽬中spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8username: rootpassword: root#最⼤活跃数maxActive: 20#初始化数量initialSize: 1#最⼤连接等待超时时间maxWait: 60000#打开PSCache,并且指定每个连接PSCache的⼤⼩poolPreparedStatements: truemaxPoolPreparedStatementPerConnectionSize: 20#通过connectionProperties属性来打开mergeSql功能;慢SQL记录#connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000minIdle: 1timeBetweenEvictionRunsMillis: 60000minEvictableIdleTimeMillis: 300000validationQuery: select 1 from dualtestWhileIdle: truetestOnBorrow: falsetestOnReturn: false#配置监控统计拦截的filters,去掉后监控界⾯sql将⽆法统计,'wall'⽤于防⽕墙filters: stat, wall, log4jjpa:properties:hibernate:show_sql: trueformat_sql: true#配置redis数据库连接redis:host: 127.0.0.1port: 6379pool:max-idle: 20min-idle: 1max-active: 20max-wait: 60000database: 0 #默认是索引为的0的数据库三、创建⼀个业务逻辑服务类:UserService,我们在Service内添加redis的缓存⽀持 @CacheConfig:该注解是⽤来开启声明的类参与缓存,如果⽅法内的@Cacheable注解没有添加key值,那么会⾃动使⽤cahceNames 配置参数并且追加⽅法名。
Springboot整合ehcache缓存

Springboot整合ehcache缓存EhCache是⼀个⽐较成熟的Java缓存框架,最早从hibernate发展⽽来,是进程中的缓存系统,它提供了⽤内存,磁盘⽂件存储,以及分布式存储⽅式等多种灵活的cache 管理⽅案,快速简单。
Springboot对ehcache的使⽤⾮常⽀持,所以在Springboot中只需做些配置就可使⽤,且使⽤⽅式也简易。
在你的项⽬上配置以下⼏步即可使⽤⾸先,⽼规矩,pom.xml加依赖;<!-- Spring Boot 缓存⽀持启动器 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency><!-- Ehcache 坐标 --><dependency><groupId>net.sf.ehcache</groupId><artifactId>ehcache</artifactId></dependency>第⼆步,创建ehcache.xml配置⽂件位置:classpath⽬录下,即src/main/resources/ehcache.xml⽂件内容开发的时候可参考第⼀步导⼊的jar包,具体在哪呢,看下⾯:再看代码:<ehcache xmlns:xsi="/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd"><diskStore path="java.io.tmpdir"/><!--defaultCache:echcache的默认缓存策略 --><defaultCachemaxElementsInMemory="10000"eternal="false"timeToIdleSeconds="120"timeToLiveSeconds="120"maxElementsOnDisk="10000000"diskExpiryThreadIntervalSeconds="120"memoryStoreEvictionPolicy="LRU"><persistence strategy="localTempSwap"/></defaultCache><cache name="users"maxElementsInMemory="10000"eternal="false"timeToIdleSeconds="120"timeToLiveSeconds="120"maxElementsOnDisk="10000000"diskExpiryThreadIntervalSeconds="120"memoryStoreEvictionPolicy="LRU"><persistence strategy="localTempSwap"/></cache></ehcache> 说明:<diskStore path="java.io.tmpdir"/>这个是磁盘存储路径,当内存缓存满了的时候,就会往这⾥⾯放,java.io.tmdir是操作系统缓存的临时⽬录,不同操作系统缓存⽬录不⼀样。
SpringBoot缓存Caffeine使用解析

SpringBoot缓存Caffeine使⽤解析⽬录Redis和Caffeine的区别相同点不同点联系Spring Boot 缓存 Caffeine使⽤1.需要添加的依赖2.配置3.使⽤Caffeine缓存Caffeine其他常⽤注解⼿动添加、获取、删除缓存1.从缓存中获取数据2.向缓存中添加数据3.删除缓存中的数据Redis和Caffeine的区别相同点两个都是缓存的⽅式不同点redis是分布式缓存,通过⽹络将数据存储到redis服务器内存⾥caffeine是将数据存储在本地应⽤⾥caffeine和redis相⽐,没有了⽹络IO上的消耗联系⼀般将两者结合起来,形成⼀⼆级缓存。
使⽤流程⼤致如下:先去⼀级缓存中查找数据(caffeine-本地应⽤内),如果没有的话,去⼆级缓存中查找数据(redis-内存),再没有,再去数据库中查找数据(数据库-磁盘)Spring Boot 缓存 Caffeine使⽤1.需要添加的依赖<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency><dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId><version>2.8.6</version></dependency>2.配置在SpringBoot中配置Caffeine,控制缓存⾏为(例如过期时间,缓存⼤⼩限制等)import com.github.benmanes.caffeine.cache.Caffeine;import org.springframework.cache.CacheManager;import org.springframework.cache.annotation.EnableCaching;import org.springframework.cache.caffeine.CaffeineCacheManager;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import java.util.concurrent.TimeUnit;@Configuration@EnableCaching //开启缓存public class CaffeinConfig {@Bean//配置Caffeine缓存⾏为(例如到期,缓存⼤⼩限制等)public Caffeine caffeineConfig() {Caffeine caffeine = Caffeine.newBuilder().expireAfterWrite(60, TimeUnit.MINUTES).maximumSize(1000);return caffeine;}@Beanpublic CacheManager cacheManager(Caffeine caffeine) {CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();caffeineCacheManager.setCaffeine(caffeine);return caffeineCacheManager;}}Caffeine配置说明:initialCapacity=[integer]:初始的缓存空间⼤⼩maximumSize=[long]:缓存的最⼤条数maximumWeight=[long]:缓存的最⼤权重expireAfterAccess=[duration]:最后⼀次写⼊或访问后经过固定时间过期expireAfterWrite=[duration]:最后⼀次写⼊后经过固定时间过期refreshAfterWrite=[duration]:创建缓存或者最近⼀次更新缓存后经过固定的时间间隔,刷新缓存recordStats:开发统计功能注意:expireAfterWrite和expireAfterAccess同时存在时,以expireAfterWrite为准。
SpringBoot通过Cacheable注解完成redis缓存功能
SpringBoot通过Cacheable注解完成redis缓存功能⼀、pom⽂件引⼊所需jar<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId></dependency>⼆、yml⽂件配置1 spring:2 redis:3 host: 192.168.80.884 port: 63795 password:6 database: 67 timeout: 2000ms8 jedis:9 pool:10 max-idle: 2011 max-wait: 1000ms12 max-active: 100三、config配置import org.springframework.cache.CacheManager;import org.springframework.cache.annotation.CacheConfig;import org.springframework.cache.annotation.EnableCaching;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.cache.*;import org.springframework.data.redis.connection.RedisConnectionFactory;import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;import org.springframework.data.redis.serializer.RedisSerializationContext;import org.springframework.transaction.annotation.EnableTransactionManagement;import java.time.Duration;import java.util.Map;/*** @Description: redis缓存配置: 重载redisCacheManager实现过期时间可配置**/@Configuration@EnableTransactionManagement@CacheConfig@EnableCachingpublic class RedisCacheConfig {@Beanpublic CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {//初始化⼀个RedisCacheWriterRedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);//初始化⼀个RedisCacheConfigurationRedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig();//返回⼀个⾃定义的CacheManagerreturn new CustomizeTtlRedisCacheManager(redisCacheWriter, defaultCacheConfig);}}/*** @Description: 重载redisCacheManager-从cacheName中提取过期时间进⾏配置**/class CustomizeTtlRedisCacheManager extends RedisCacheManager {public CustomizeTtlRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {super(cacheWriter, defaultCacheConfiguration);}public CustomizeTtlRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, String... initialCacheNames) {super(cacheWriter, defaultCacheConfiguration, initialCacheNames);}public CustomizeTtlRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, boolean allowInFlightCacheCreation, String... initialCacheNames) {super(cacheWriter, defaultCacheConfiguration, allowInFlightCacheCreation, initialCacheNames);}public CustomizeTtlRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, Map<String, RedisCacheConfiguration> initialCacheConfigurations) {super(cacheWriter, defaultCacheConfiguration, initialCacheConfigurations);}public CustomizeTtlRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, Map<String, RedisCacheConfiguration> initialCacheConfigurations, boolean allowInFlightCacheCreation) { super(cacheWriter, defaultCacheConfiguration, initialCacheConfigurations, allowInFlightCacheCreation);}@Overrideprotected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) {Duration ttl = getTtlByName(name);if (ttl != null) {//证明在cacheName上使⽤了过期时间,需要修改配置中的ttlcacheConfig = cacheConfig.entryTtl(ttl);}//修改缓存key和value值的序列化⽅式cacheConfig = putePrefixWith(DEFAULT_CACHE_KEY_PREFIX).serializeValuesWith(DEFAULT_PAIR);return super.createRedisCache(name, cacheConfig);}/*** 缓存参数的分隔符* 数组元素0=缓存的名称* 数组元素1=缓存过期时间TTL*/private static final String DEFAULT_SEPARATOR = "#";/*** 通过name获取过期时间** @param name* @return*/private Duration getTtlByName(String name) {if (name == null) {return null;}//根据分隔符拆分字符串,并进⾏过期时间ttl的解析String[] cacheParams = name.split(DEFAULT_SEPARATOR);if (cacheParams.length > 1) {String ttl = cacheParams[1];if (!StringUtils.isEmpty(ttl)) {try {return Duration.ofSeconds(Long.parseLong(ttl));} catch (Exception e) {// TODO}}}return null;}/*** 默认的key前缀*/private static final CacheKeyPrefix DEFAULT_CACHE_KEY_PREFIX = cacheName -> cacheName + ":";/*** 默认序列化⽅式为json*/private static final RedisSerializationContext.SerializationPair<Object> DEFAULT_PAIR = RedisSerializationContext.SerializationPair .fromSerializer(new GenericJackson2JsonRedisSerializer());}四、使⽤注解/**cacheNames中的"#30"表⽰缓存过期时间为30秒 */@Cacheable(cacheNames = "testMethod#30", unless = "#result==null")public String testMethod(String param) {// TODO}五、打完收功~。
SpringBoot中的缓存支持(一)注解配置与EhCache使用
中的缓存支持(一)注解配置与 EhCache 使用随着时间的积累,应用的使用用户不断增加,数据规模 也越来越大,往往数据库查询操作会成为影响用户使用体验 的瓶颈,此时使用缓存往往是解决这一问题非常好的手段之Spring 3 开始提供了强大的基于注解的缓存支持,可以在 Spring Boot 中对于缓存的支持,提供了一系列的自动化 配置,使我们可以非常方便的使用缓存。
下面我们通过一个 简单的例子来展示,我们是如何给一个既有应用增加缓存功 能的。
快速入门 首先,下载样例工程 chapter3-2-2 。
本例通过 spring-data-jpa实现了对 User 用户表的一些操作,若没有这个基础,可以 先阅读 《使用 Spring-data-jpa 简化数据访问层》 一文对数据 访问有所基础。
准备工作 为了更好的理解缓存,我们先对该工程做一些简单的改造。
application.properties 文件中新增spring.jpa.properties.hibernate.show_sql=true ,开启 hibernate 对 sql 语句的打印Spring Boot 通过注解配置方式低侵入的给原有 Spring 应用增加缓存功 能,提高数据访问性能 台匕修改单元测试ApplicationTests ,初始化插入User 表一条用户名为AAA ,年龄为10 的数据。
并通过findByName 函数完成两次查询。
@RunWith(SpringJUnit4ClassRunner.class)@SpringApplicationConfiguration(Application.class) public class ApplicationTests {@Autowired private UserRepository userRepository;@Before public void before() {userRepository.save(new User("AAA", 10));@Testpublic void test() throws Exception {User u1 = userRepository.findByName("AAA");System.out.println(" 第一次查询:" +u1.getAge());User u2 = userRepository.findByName("AAA");System.out.println(" 第二次查询:" +u2.getAge());执行单元测试,我们可以在控制台中看到下面内容。
springboot(九)Cache缓存和Redis缓存
springboot(九)Cache缓存和Redis缓存1. Cache缓存1.1 缓存的概念&缓存注解Cache缓存接⼝,定义缓存操作。
实现有:RedisCache、EhCacheCache、ConcurrentMapCache等CacheManager缓存管理器,管理各种缓存(Cache)组件@Cacheable主要针对⽅法配置,能够根据⽅法的请求参数对其结果进⾏缓存@CacheEvict清空缓存@CachePut保证⽅法被调⽤,⼜希望结果被缓存。
@EnableCaching开启基于注解的缓存keyGenerator缓存数据时key⽣成策略serialize缓存数据时value序列化策略1.2 @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=”#userName.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进⾏判断。
SpringBoot+SpringCache实现两级缓存(Redis+Caffeine)
SpringBoot+SpringCache实现两级缓存(Redis+Caffeine)1. 缓存、两级缓存1.1 内容说明Spring cache:主要包含spring cache定义的接⼝⽅法说明和注解中的属性说明springboot+spring cache:rediscache实现中的缺陷caffeine简介spring boot+spring cache实现两级缓存使⽤缓存时的流程图1.2 Sping Cachespring cache是spring-context包中提供的基于注解⽅式使⽤的缓存组件,定义了⼀些标准接⼝,通过实现这些接⼝,就可以通过在⽅法上增加注解来实现缓存。
这样就能够避免缓存代码与业务处理耦合在⼀起的问题。
spring cache的实现是使⽤spring aop中对⽅法切⾯(MethodInterceptor)封装的扩展,当然spring aop也是基于Aspect来实现的。
spring cache核⼼的接⼝就两个:Cache和CacheManager1.2.1 Cache接⼝提供缓存的具体操作,⽐如缓存的放⼊,读取,清理,spring框架中默认提供的实现有1.2.2 CacheManager接⼝主要提供Cache实现bean的创建,每个应⽤⾥可以通过cacheName来对Cache进⾏隔离,每个CaheName对应⼀个Cache实现,spring框架中默认提供的实现与Cache的实现都是成对出现的1.2.3 常⽤的注解说明@Cacheable:主要应⽤到查询数据的⽅法上@CacheEvict:清除缓存,主要应⽤到删除数据的⽅法上@CachePut:放⼊缓存,主要⽤到对数据有更新的⽅法上@Caching:⽤于在⼀个⽅法上配置多种注解@EnableCaching:启⽤spring cache缓存,作为总的开关,在spring boot的启动类或配置类上需要加⼊次注解才会⽣效2.实战多级缓存的⽤法package com.xfgg.demo.config;import lombok.AllArgsConstructor;import com.github.benmanes.caffeine.cache.Caffeine;import org.springframework.cache.CacheManager;import org.springframework.cache.caffeine.CaffeineCacheManager;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import java.util.concurrent.TimeUnit;@Configuration@AllArgsConstructor//把定义的缓存加⼊到Caffeine中public class CacheConfig {@Beanpublic CacheManager cacheManager(){CaffeineCacheManager cacheManager = new CaffeineCacheManager();cacheManager.setCaffeine(Caffeine.newBuilder()//使⽤refreshAfterWrite必须要设置cacheLoader//在5分钟内没有创建/覆盖时,会移除该key,下次取的时候从loading中取【重点:失效、移除Key、失效后需要获取新值】.expireAfterWrite(5, TimeUnit.MINUTES)//初始容量.initialCapacity(10)//⽤来控制cache的最⼤缓存数量.maximumSize(150));return cacheManager;}}package com.xfgg.demo.config;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Bean;import org.springframework.data.redis.connection.RedisConnectionFactory;import org.springframework.data.redis.connection.RedisPassword;import org.springframework.data.redis.connection.RedisStandaloneConfiguration;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.serializer.StringRedisSerializer;//⽣成的redis连接public class RedisConfig<GenericObjectPoolConfig> {@Value("${spring.redis1.host}")private String host;@Value("${spring.redis1.port}")private Integer port;@Value("${spring.redis1.password}")private String password;@Value("${spring.redis1.database}")private Integer database;@Value("${spring.redis1.lettuce.pool.max-active}")private Integer maxActive;@Value("${spring.redis1.lettuce.pool.max-idle}")private Integer maxIdle;@Value("${spring.redis1.lettuce.pool.max-wait}")private Long maxWait;@Value("${spring.redis1.lettuce.pool.min-idle}")private Integer minIdle;@Beanpublic RedisStandaloneConfiguration redis1RedisConfig() {RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();config.setHostName(host);config.setPassword(RedisPassword.of(password));config.setPort(port);config.setDatabase(database);return config;}//配置序列化器@Beanpublic RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){RedisTemplate<String,Object>template=new RedisTemplate<>();//关联template.setConnectionFactory(factory);//设置key的序列化器template.setKeySerializer(new StringRedisSerializer());//设置value的序列化器template.setValueSerializer(new StringRedisSerializer());return template;}}⼀个使⽤cacheable注解,⼀个使⽤redistemplate进⾏缓存因为公司项⽬中⽤到的是jedis和jediscluster所以这⾥只是做个了解,没有写的很细到此这篇关于SpringBoot+SpringCache实现两级缓存(Redis+Caffeine)的⽂章就介绍到这了,更多相关SpringBoot SpringCache两级缓存内容请搜索以前的⽂章或继续浏览下⾯的相关⽂章希望⼤家以后多多⽀持!。