Spring 提供了对缓存功能的抽象:即容许绑定不一样的缓存解决方案(如Caffeine、Ehcache等),但自己不直接提供缓存功能的实现。它支持注解方式使用缓存,很是方便。html
SpringBoot在annotation的层面实现了数据缓存的功能,基于Spring的AOP技术。全部的缓存配置只是在annotation层面配置,像声明式事务同样。java
Spring定义了CacheManager和Cache接口统一不一样的缓存技术。其中CacheManager是Spring提供的各类缓存技术的抽象接口。而Cache接口包含缓存的各类操做。git
Cache接口下Spring提供了各类xxxCache的实现,如RedisCache,EhCacheCache ,ConcurrentMapCache等;github
针对不一样的缓存技术,须要实现不一样的cacheManager,Spring定义了以下的cacheManger实现。正则表达式
CacheManger | 描述 |
---|---|
SimpleCacheManager | 使用简单的Collection来存储缓存,主要用于测试 |
ConcurrentMapCacheManager | 使用ConcurrentMap做为缓存技术(默认),须要显式的删除缓存,无过时机制 |
NoOpCacheManager | 仅测试用途,不会实际存储缓存 |
EhCacheCacheManager | 使用EhCache做为缓存技术,之前在hibernate的时候常常用 |
GuavaCacheManager | 使用google guava的GuavaCache做为缓存技术(1.5版本已不建议使用) |
CaffeineCacheManager | 是使用Java8对Guava缓存的重写,spring5(springboot2)开始用Caffeine取代guava |
HazelcastCacheManager | 使用Hazelcast做为缓存技术 |
JCacheCacheManager | 使用JCache标准的实现做为缓存技术,如Apache Commons JCS |
RedisCacheManager | 使用Redis做为缓存技术 |
常规的SpringBoot已经为咱们自动配置了EhCache、Collection、Guava、ConcurrentMap等缓存,默认使用ConcurrentMapCacheManager
。SpringBoot的application.properties配置文件,使用spring.cache前缀的属性进行配置。redis
开始使用前须要导入依赖算法
spring-boot-starter-cache 为基础依赖,其余依赖根据使用不一样的缓存技术选择加入,默认状况下使用 ConcurrentMapCache不须要引用任何依赖spring
<!-- 基础依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- 使用 ehcache --> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> </dependency> <!-- 使用 caffeine https://mvnrepository.com/artifact/com.github.ben-manes.caffeine/caffeine --> <dependency> <groupId>com.github.ben-manes.caffeine</groupId> <artifactId>caffeine</artifactId> <version>2.6.0</version> </dependency> <!-- 使用 redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
spring.cache.type= #缓存的技术类型,可选 generic,ehcache,hazelcast,infinispan,jcache,redis,guava,simple,none spring.cache.cache-names= #应用程序启动建立缓存的名称,必须将全部注释为@Cacheable缓存name(或value)罗列在这里,否者:Cannot find cache named 'xxx' for Builder[xx] caches=[sysItem] | key='' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false'
#如下根据不一样缓存技术选择配置 spring.cache.ehcache.config= #EHCache的配置文件位置 spring.caffeine.spec= #caffeine类型建立缓存的规范。查看CaffeineSpec了解更多关于规格格式的细节
spring.cache.infinispan.config= #infinispan的配置文件位置 spring.cache.jcache.config= #jcache配置文件位置 spring.cache.jcache.provider= #当多个jcache实现类时,指定选择jcache的实现类
下面是缓存公用接口注释,使用与任何缓存技术数据库
1,@EnableCaching:在启动类注解@EnableCaching开启缓存 apache
@SpringBootApplication @EnableCaching //开启缓存 public class DemoApplication{ public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
2,@Cacheable:配置了findByName函数的返回值将被加入缓存。同时在查询时,会先从缓存中获取,若不存在才再发起对数据库的访问。
该注解主要有下面几个参数:
public class BotRelationServiceImpl implements BotRelationService { @Override @Cacheable(value = {"newJob"},key = "#p0") public List<NewJob> findAllLimit(int num) { return botRelationRepository.findAllLimit(num); } ..... }
3,@CachePut:主要针对方法配置,可以根据方法的请求参数对其结果进行缓存,和 @Cacheable 不一样的是,它每次都会触发真实方法的调用 。简单来讲就是用户更新缓存数据。但须要注意的是该注解的value 和 key 必须与要更新的缓存相同,也就是与@Cacheable 相同。示例:
@CachePut(value = "newJob", key = "#p0") //按条件更新缓存 public NewJob updata(NewJob job) { NewJob newJob = newJobDao.findAllById(job.getId()); newJob.updata(job); return job; }
4,@CacheEvict:配置于函数上,一般用在删除方法上,用来从缓存中移除相应数据。除了同@Cacheable同样的参数以外,它还有下面两个参数:
@Cacheable(value = "emp",key = "#p0.id") public NewJob save(NewJob job) { newJobDao.save(job); return job; } //清除一条缓存,key为要清空的数据 @CacheEvict(value="emp",key="#id") public void delect(int id) { newJobDao.deleteAllById(id); } //方法调用后清空全部缓存 @CacheEvict(value="accountCache",allEntries=true) public void delectAll() { newJobDao.deleteAll(); } //方法调用前清空全部缓存 @CacheEvict(value="accountCache",beforeInvocation=true) public void delectAll() { newJobDao.deleteAll(); }
5,@CacheConfig: 统一配置本类的缓存注解的属性,在类上面统必定义缓存的名字,方法上面就不用标注了,当标记在一个类上时则表示该类全部的方法都是支持缓存的
@CacheConfig(cacheNames = {"myCache"}) public class BotRelationServiceImpl implements BotRelationService { @Override @Cacheable(key = "targetClass + methodName +#p0")//此处没写value public List<BotRelation> findAllLimit(int num) { return botRelationRepository.findAllLimit(num); } ..... }
Spring Cache提供了一些供咱们使用的SpEL上下文数据,下表直接摘自Spring官方文档:
名称 | 位置 | 描述 | 示例 |
---|---|---|---|
methodName | root对象 | 当前被调用的方法名 | #root.methodname |
method | root对象 | 当前被调用的方法 | #root.method.name |
target | root对象 | 当前被调用的目标对象实例 | #root.target |
targetClass | root对象 | 当前被调用的目标对象的类 | #root.targetClass |
args | root对象 | 当前被调用的方法的参数列表 | #root.args[0] |
caches | root对象 | 当前方法调用使用的缓存列表 | #root.caches[0].name |
Argument Name | 执行上下文 | 当前被调用的方法的参数,如findArtisan(Artisan artisan),能够经过#artsian.id得到参数 | #artsian.id |
result | 执行上下文 | 方法执行后的返回值(仅当方法执行后的判断有效,如 unless cacheEvict的beforeInvocation=false) | #result |
注意:
1.当咱们要使用root对象的属性做为key时咱们也能够将“#root”省略,由于Spring默认使用的就是root对象的属性。 如
@Cacheable(key = "targetClass + methodName +#p0")
2.使用方法参数时咱们能够直接使用“#参数名”或者“#p参数index”。 如:
@Cacheable(value="users", key="#id")
@Cacheable(value="users", key="#p0")
SpEL提供了多种运算符
类型 | 运算符 |
---|---|
关系 | <,>,<=,>=,==,!=,lt,gt,le,ge,eq,ne |
算术 | +,- ,* ,/,%,^ |
逻辑 | &&,||,!,and,or,not,between,instanceof |
条件 | ?: (ternary),?: (elvis) |
正则表达式 | matches |
其余类型 | ?.,?[…],![…],^[…],$[…] |
以上的知识点适合你遗忘的时候来查阅,下面正式进入学习!
Spring boot默认使用的是SimpleCacheConfiguration,即便用ConcurrentMapCacheManager来实现缓存,ConcurrentMapCache实质是一个ConcurrentHashMap集合对象java内置,因此无需引入其余依赖,也没有额外的配置
ConcurrentMapCache的自动装配声明在SimpleCacheConfiguration中,若是须要也可对它进行额外的装配
//注册1个id为cacheManager,类型为ConcurrentMapCacheManager的bean
@Bean public ConcurrentMapCacheManager cacheManager() { ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager(); //实例化ConcurrentMapCacheManager List<String> cacheNames = this.cacheProperties.getCacheNames(); //读取配置文件,若是配置有spring.cache.cache-names=xx,xx,则进行配置cacheNames,默认是没有配置的 if (!cacheNames.isEmpty()) { cacheManager.setCacheNames(cacheNames); } return this.customizerInvoker.customize(cacheManager); //调用CacheManagerCustomizers#customize 进行个性化设置,在该方法中是遍历其持有的List }
Caffeine是使用Java8对Guava缓存的重写版本,在Spring Boot 2.0中将取代,基于LRU算法实现,支持多种缓存过时策略。具体查看这里 http://www.javashuo.com/article/p-oiglzozi-ge.html,
1,Caffeine参数说明:
2,导入依赖
<!-- 使用 caffeine https://mvnrepository.com/artifact/com.github.ben-manes.caffeine/caffeine --> <dependency> <groupId>com.github.ben-manes.caffeine</groupId> <artifactId>caffeine</artifactId> <version>2.6.0</version> </dependency>
三、Caffeine配置
(1)经过配置文件来设置Caffeine
spring: cache: cache-names: outLimit,notOutLimit caffeine: spec: initialCapacity=50,maximumSize=500,expireAfterWrite=5s,refreshAfterWrite=7s # type: caffeine
(2)经过bean装配
@Bean() @Primary public CacheManager cacheManagerWithCaffeine() { CaffeineCacheManager cacheManager = new CaffeineCacheManager(); Caffeine caffeine = Caffeine.newBuilder() .initialCapacity(100) //cache的初始容量值 .maximumSize(1000) //maximumSize用来控制cache的最大缓存数量,maximumSize和maximumWeight不能够同时使用, .maximumWeight(100) //控制最大权重 .expireAfter(customExpireAfter) //自定义过时 .refreshAfterWrite(5, TimeUnit.SECONDS); //使用refreshAfterWrite必需要设置cacheLoader cacheManager.setCaffeine(caffeine); cacheManager.setCacheLoader(cacheLoader); //缓存加载方案 cacheManager.setCacheNames(getNames()); //缓存名称列表 cacheManager.setAllowNullValues(false); return cacheManager; }
(3)配置文件结合Bean装配
@Value("${caffeine.spec}") private String caffeineSpec;
@Bean(name = "caffeineSpec") public CacheManager cacheManagerWithCaffeineFromSpec(){ CaffeineSpec spec = CaffeineSpec.parse(caffeineSpec); Caffeine caffeine = Caffeine.from(spec); // 或使用 Caffeine caffeine = Caffeine.from(caffeineSpec); CaffeineCacheManager cacheManager = new CaffeineCacheManager(); cacheManager.setCaffeine(caffeine); cacheManager.setCacheNames(getNames()); return cacheManager; }
4,实现cacheLoader
CacheLoader是cache的一种加载策略,key不存在或者key过时之类的均可以经过CacheLoader来自定义得到/从新得到数据。使用refreshAfterWrite必需要设置cacheLoader
@Configuration public class CacheConfig {
@Bean public CacheLoader<Object, Object> cacheLoader() { CacheLoader<Object, Object> cacheLoader = new CacheLoader<Object, Object>() { @Override public Object load(Object key) throws Exception { return null; } // 达到配置文件中的refreshAfterWrite所指定的时候回处罚这个事件方法 @Override public Object reload(Object key, Object oldValue) throws Exception { return oldValue; //能够在这里处理从新加载策略,本例子,没有处理从新加载,只是返回旧值。 } }; return cacheLoader; }
}
CacheLoader实质是一个监听,处上述load与reload还包含,expireAfterCreate,expireAfterUpdate,expireAfterRead等能够很灵活的配置CacheLoader,具体详细配置可查看这里 https://www.jianshu.com/p/15d0a9ce37dd,详细原理查看:http://www.javashuo.com/article/p-oiglzozi-ge.html
EhCache 是一个纯Java的进程内缓存框架,具备快速、精干等特色,是Hibernate中默认CacheProvider。Ehcache是一种普遍使用的开源Java分布式缓存。主要面向通用缓存,Java EE和轻量级容器。它具备内存和磁盘存储,缓存加载器,缓存扩展,缓存异常处理程序,一个gzip缓存servlet过滤器,支持REST和SOAP api等特色。
1,导入依赖
引入springboot-cache
和ehcache
。须要注意,EhCache
不须要配置version
,SpringBoot的根pom已经集成了。
<dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> </dependency>
2,加入配置:
spring.cache.type=ehcache # 配置ehcache缓存
spring.cache.ehcache.config=classpath:/ehcache.xml # 指定ehcache配置文件路径 ,能够不用写,由于默认就是这个路径,SpringBoot会自动扫描
3,ehcache配置文件
EhCache的配置文件ehcache.xml只须要放到类路径下面,SpringBoot会自动扫描
<ehcache> <!-- 磁盘存储:指定一个文件目录,当EHCache把数据写到硬盘上时,将把数据写到这个文件目录下 path:指定在硬盘上存储对象的路径 path能够配置的目录有: user.home(用户的家目录) user.dir(用户当前的工做目录) java.io.tmpdir(默认的临时目录) ehcache.disk.store.dir(ehcache的配置目录) 绝对路径(如:d:\\ehcache) 查看路径方法:String tmpDir = System.getProperty("java.io.tmpdir"); --> <diskStore path="java.io.tmpdir" /> <!-- defaultCache:默认的缓存配置信息,若是不加特殊说明,则全部对象按照此配置项处理 maxElementsInMemory:设置了缓存的上限,最多存储多少个记录对象 eternal:表明对象是否永不过时 (指定true则下面两项配置需为0无限期) timeToIdleSeconds:最大的发呆时间 /秒 timeToLiveSeconds:最大的存活时间 /秒 overflowToDisk:是否容许对象被写入到磁盘 说明:下列配置自缓存创建起600秒(10分钟)有效 。 在有效的600秒(10分钟)内,若是连续120秒(2分钟)未访问缓存,则缓存失效。 就算有访问,也只会存活600秒。 --> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="600" timeToLiveSeconds="600" overflowToDisk="true" /> <!-- 按缓存名称的不一样管理策略 --> <cache name="myCache" maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="600" overflowToDisk="true" /> </ehcache>
4,装配
SpringBoot会为咱们自动配置 EhCacheCacheManager
这个Bean,若是想自定义设置一些个性化参数时,经过Java Config形式配置。
@Configuration @EnableCaching public class CacheConfig { @Bean public CacheManager cacheManager() { return new EhCacheCacheManager(ehCacheCacheManager().getObject()); } @Bean public EhCacheManagerFactoryBean ehCacheCacheManager() { EhCacheManagerFactoryBean cmfb = new EhCacheManagerFactoryBean(); cmfb.setConfigLocation(new ClassPathResource("ehcache.xml")); cmfb.setShared(true); return cmfb; } }
1,Redis 优点
2, 导入依赖
就只须要这一个依赖!不须要spring-boot-starter-cache
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
当你导入这一个依赖时,SpringBoot的CacheManager就会使用RedisCache。
Redis使用模式使用pool2链接池,在须要时引用下面的依赖
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-pool2 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.6.2</version> </dependency>
4,配置Redis
spring.redis.database=1 # Redis数据库索引(默认为0) spring.redis.host=127.0.0.1 # Redis服务器地址 spring.redis.port=6379 # Redis服务器链接端口 spring.redis.password= # Redis服务器链接密码(默认为空) spring.redis.pool.max-active=1000 # 链接池最大链接数(使用负值表示没有限制) spring.redis.pool.max-wait=-1 # 链接池最大阻塞等待时间(使用负值表示没有限制) spring.redis.pool.max-idle=10 # 链接池中的最大空闲链接 spring.redis.pool.min-idle=2 # 链接池中的最小空闲链接 spring.redis.timeout=0 # 链接超时时间(毫秒)
若是你的Redis这时候已经能够启动程序了。
5,装配
若是须要自定义缓存配置能够经过,继承CachingConfigurerSupport类,手动装配,若是一切使用默认配置可没必要
(1) 装配序列化类型
@Bean public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory connectionFactory) { // 配置redisTemplate RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(connectionFactory); redisTemplate.setKeySerializer(new StringRedisSerializer());//key序列化 redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());//value序列化 redisTemplate.afterPropertiesSet(); return redisTemplate; }
(2) 装配过时时间
/** * 经过RedisCacheManager配置过时时间 * * @param redisConnectionFactory * @return */ @Bean public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) { RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofHours(1)); // 设置缓存有效期一小时 return RedisCacheManager .builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory)) .cacheDefaults(redisCacheConfiguration).build(); }
(3) 一个比较完整的装配类 demo
/** * 自定义缓存配置文件,继承 CachingConfigurerSupport * Created by huanl on 2017/8/22. */ @Configuration @EnableCaching public class RedisConfig extends CachingConfigurerSupport{ public RedisConfig() { super(); } /** * 指定使用哪种缓存 * @param redisTemplate * @return */ @Bean public CacheManager cacheManager(RedisTemplate<?,?> redisTemplate) { RedisCacheManager rcm = new RedisCacheManager(redisTemplate); return rcm; } /** * 指定默认的key生成方式 * @return */ @Override public KeyGenerator keyGenerator() { KeyGenerator keyGenerator = new KeyGenerator() { @Override public Object generate(Object o, Method method, Object... objects) { StringBuilder sb = new StringBuilder(); sb.append(o.getClass().getName()); sb.append(method.getName()); for (Object obj : objects) { sb.append(obj.toString()); } return sb.toString(); } }; return keyGenerator; } @Override public CacheResolver cacheResolver() { return super.cacheResolver(); } @Override public CacheErrorHandler errorHandler() { return super.errorHandler(); } /** * redis 序列化策略 ,一般状况下key值采用String序列化策略 * StringRedisTemplate默认采用的是String的序列化策略,保存的key和value都是采用此策略序列化保存的。StringRedisSerializer * RedisTemplate默认采用的是JDK的序列化策略,保存的key和value都是采用此策略序列化保存的。JdkSerializationRedisSerializer * @param factory * @return */ @Bean public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory){ RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(factory); // // 使用Jackson2JsonRedisSerialize 替换默认序列化 // Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); // ObjectMapper om = new ObjectMapper(); // om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); // om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); // jackson2JsonRedisSerializer.setObjectMapper(om); // // // //设置value的序列化方式 // redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); // //设置key的序列化方式 // redisTemplate.setKeySerializer(new StringRedisSerializer()); // redisTemplate.setHashKeySerializer(new StringRedisSerializer()); // redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); //使用fastJson做为默认的序列化方式 GenericFastJsonRedisSerializer genericFastJsonRedisSerializer = new GenericFastJsonRedisSerializer(); redisTemplate.setDefaultSerializer(genericFastJsonRedisSerializer); redisTemplate.setValueSerializer(genericFastJsonRedisSerializer); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(genericFastJsonRedisSerializer); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.afterPropertiesSet(); return redisTemplate; } /** * 转换返回的object为json * @return */ @Bean public HttpMessageConverters fastJsonHttpMessageConverters(){ // 一、须要先定义一个converter 转换器 FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter(); // 二、添加fastJson 的配置信息,好比:是否要格式化返回的json数据 FastJsonConfig fastJsonConfig = new FastJsonConfig(); fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat); // 三、在convert 中添加配置信息 fastConverter.setFastJsonConfig(fastJsonConfig); // 四、将convert 添加到converters当中 HttpMessageConverter<?> converter = fastConverter; return new HttpMessageConverters(converter); } }
6,模板编程
除了使用注解,Spring boot集成 Redis 客户端jedis。封装Redis 链接池,以及操做模板,能够方便的显示的在方法的代码中处理缓存对象
@Autowired private StringRedisTemplate stringRedisTemplate;//操做key-value都是字符串 @Autowired private RedisTemplate redisTemplate;//操做key-value都是对象 @Autowired private RedisCacheManager redisCacheManager; /** * Redis常见的五大数据类型: * stringRedisTemplate.opsForValue();[String(字符串)] * stringRedisTemplate.opsForList();[List(列表)] * stringRedisTemplate.opsForSet();[Set(集合)] * stringRedisTemplate.opsForHash();[Hash(散列)] * stringRedisTemplate.opsForZSet();[ZSet(有序集合)] */ public void test(){ stringRedisTemplate.opsForValue().append("msg","hello"); Cache emp = redisCacheManager.getCache("emp"); emp.put("111", "222"); }