在讲解Springboot相关组件以前,咱们先要了解在java的缓存插件体系,相似于咱们以前学习数据操做的数据源规范同样,也有一个标准,这个标准叫作JSR107,咱们能够经过在网上查询相关的信息。java
Java Caching定义了5个核心接口,分别是CachingProvider, CacheManager, Cache, Entry 和 Expiry。mysql
Spring从3.1开始定义了org.springframework.cache.Cache和org.springframework.cache.CacheManager(缓存管理器)接口来统一不一样的缓存技术;并支持使用JCache(JSR-107)注解简化咱们开发;web
Cache接口为缓存的组件规范定义,包含缓存的各类操做集合; Cache接口下Spring提供了各类xxxCache的实现;如RedisCache,EhCacheCache , ConcurrentMapCache等;redis
每次调用须要缓存功能的方法时,Spring会检查检查指定参数的指定的目标方法是否已经被调用过;若是有就直接从缓存中获取方法调用后的结果,若是没有就调用方法并缓存结果后返回给用户。下次调用直接从缓存中获取。 使用Spring缓存抽象时咱们须要关注如下两点;spring
在咱们开发应用程序中,比较经常使用的注解以下所示: ||| |-|-| |Cache |缓存接口,定义缓存操做。实现有:RedisCache、EhCacheCache、ConcurrentMapCache等| |CacheManager |缓存管理器,管理各类缓存(Cache)组件| |@Cacheable |主要针对方法配置,可以根据方法的请求参数对其结果进行缓存| |@CacheEvict |清空缓存| |@CachePut |保证方法被调用,又但愿结果被缓存。| |@EnableCaching |开启基于注解的缓存| |@keyGenerator |缓存数据时key生成策略| |@serialize |缓存数据时value序列化策略|sql
接下来咱们编写一个应用程序体验一下springboot中缓存的使用。docker
/* Navicat MySQL Data Transfer Source Server : docker Source Server Version : 50505 Source Host : 10.21.1.47:3306 Source Database : joyblack Target Server Type : MYSQL Target Server Version : 50505 File Encoding : 65001 Date: 2018-12-20 09:45:44 */ SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for `department` -- ---------------------------- DROP TABLE IF EXISTS `department`; CREATE TABLE `department` ( `id` int(11) NOT NULL, `department_name` varchar(30) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of department -- ---------------------------- INSERT INTO `department` VALUES ('1', '乡下冒险者公会'); INSERT INTO `department` VALUES ('2', '城市冒险者公会'); -- ---------------------------- -- Table structure for `user` -- ---------------------------- DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(11) NOT NULL, `user_name` varchar(20) NOT NULL, `login_name` varchar(20) NOT NULL, `department_id` int(11) NOT NULL DEFAULT 1, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of user -- ---------------------------- INSERT INTO `user` VALUES ('1', '阿库娅', 'akuya', '1'); INSERT INTO `user` VALUES ('2', '克里斯汀娜', 'crustina', '1'); INSERT INTO `user` VALUES ('3', '惠惠', 'huihui', '1');
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency>
spring: datasource: username: root password: 123456 url: jdbc:mysql://127.0.0.1:3306/joyblack?characterEncoding=utf8&serverTimezone=GMT driver-class-name: com.mysql.cj.jdbc.Driver server: port: 8090 mybatis: configuration: # 开启驼峰映射规则 map-underscore-to-camel-case: true
package com.zhaoyi.aweb.bean; public class User { private Integer id; private String loginName; private String userName; private Integer departmentId; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getLoginName() { return loginName; } public void setLoginName(String loginName) { this.loginName = loginName; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public Integer getDepartmentId() { return departmentId; } public void setDepartmentId(Integer departmentId) { this.departmentId = departmentId; } }
package com.zhaoyi.aweb.bean; public class Department { private Integer id; private String 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; } }
package com.zhaoyi.aweb.mapper; import com.zhaoyi.aweb.bean.User; import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; public interface UserMapper { @Insert(" insert into user(id, login_name, user_name, department_id) values (#{id}, #{loginName}, #{userName}, #{departmentId})") int insertUser(User user); @Delete("delete from user where id = #{id}") int deleteUser(Integer id); @Select("select * from user where id = #{id}") User getUserById(Integer id); @Update("update user set user_name = #{userName} where id = #{id}") int updateUser(User user); }
package com.zhaoyi.aweb.mapper; import com.zhaoyi.aweb.bean.Department; import org.apache.ibatis.annotations.*; public interface DepartmentMapper { // insert a derpartment. // @Options(useGeneratedKeys = true, keyProperty = "id") may you want get insert data generated id. @Insert("insert into department(id,department_name) values(#{id}, #{departmentName})") int insertDepartment(Department department); // delete a department by id. @Insert("delete from department where id = #{id}") int deleteDepartment(Integer id); // query a department by id. @Select("select * from department where id = #{id}") Department getDepartmentById(Integer id); // update a department information. @Update("update department set department_name=#{departmentName} where id=#{id}") int updateDepartment(Department department); }
package com.zhaoyi.aweb; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @MapperScan(value = "com.zhaoyi.aweb.mapper") @SpringBootApplication public class AwebApplication { public static void main(String[] args) { SpringApplication.run(AwebApplication.class, args); } }
package com.zhaoyi.aweb.controller; import com.zhaoyi.aweb.bean.User; import com.zhaoyi.aweb.mapper.UserMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class UserController { @Autowired private UserMapper userMapper; @RequestMapping("/user/insert") public User insertUser(User user){ userMapper.insertUser(user); return user; } @RequestMapping("/user/delete/{id}") public Integer insertUser(@PathVariable("id") Integer id){ return userMapper.deleteUser(id); } @RequestMapping("/user/select/{id}") public User getUser(@PathVariable("id") Integer id){ return userMapper.getUserById(id); } @RequestMapping("/user/update") public User updateUser(User user){ userMapper.updateUser(user); return user; } }
访问 localhost:8090/user/select/1,获得:数据库
{"id":1,"loginName":"akuya","userName":"阿库娅","departmentId":1}
前面咱们至关于复习了一遍以前的操做,咱们如今先作一下改变,通常来讲,controller调用的service相关的东西,所以,咱们将对mapper的操做提到servicer一层,咱们添加一个service包:express
package com.zhaoyi.aweb.service; import com.zhaoyi.aweb.mapper.UserMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.zhaoyi.aweb.bean.User; @Service public class UserService { @Autowired private UserMapper userMapper; public User getUser(Integer id){ System.out.println("查询:" + id); return userMapper.getUserById(id); } }
接下来,咱们从新写一下controller的方法,以下所示:apache
package com.zhaoyi.aweb.controller; import com.zhaoyi.aweb.bean.User; import com.zhaoyi.aweb.mapper.UserMapper; import com.zhaoyi.aweb.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class UserController { @Autowired private UserService userService; @RequestMapping("/user/select/{id}") public User getUser(@PathVariable("id") Integer id){ return userService.getUser(id); } }
这样,咱们就保证controller只和service进行交互了。咱们开始新知识了,通常使用缓存,咱们须要以下步骤:
很简单吧?
如今,咱们每访问一次select url,都会在控制台打印一次
查询:1
也就是说,当前都会调用service的getUser在数据库进行查询操做。接下来咱们为该方法提供缓存效果,即在启动类中添加注解@EnableCaching
:
package com.zhaoyi.aweb; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCaching; @EnableCaching @MapperScan(value = "com.zhaoyi.aweb.mapper") @SpringBootApplication public class AwebApplication { public static void main(String[] args) { SpringApplication.run(AwebApplication.class, args); } }
而后咱们去标注缓存注解在对应的服务方法上
@Cacheable(value = {"user"}) public User getUser(Integer id){ System.out.println("查询:" + id); return userMapper.getUserById(id); }
这一次咱们再次访问,发现除了第一次会打印查询记录以外,其余的查询都不会打印了(一个id只会进行一次查询,即第一次),显然已经作了缓存了。
仍是得先从自动配置类源码入手。
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package org.springframework.boot.autoconfigure.cache; import java.util.List; import java.util.function.Supplier; import java.util.stream.Collectors; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration; import org.springframework.boot.autoconfigure.data.jpa.EntityManagerFactoryDependsOnPostProcessor; import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration; import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cache.CacheManager; import org.springframework.cache.interceptor.CacheAspectSupport; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.ImportSelector; import org.springframework.core.type.AnnotationMetadata; import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.util.Assert; @Configuration @ConditionalOnClass({CacheManager.class}) @ConditionalOnBean({CacheAspectSupport.class}) @ConditionalOnMissingBean( value = {CacheManager.class}, name = {"cacheResolver"} ) @EnableConfigurationProperties({CacheProperties.class}) @AutoConfigureAfter({CouchbaseAutoConfiguration.class, HazelcastAutoConfiguration.class, HibernateJpaAutoConfiguration.class, RedisAutoConfiguration.class}) @Import({CacheAutoConfiguration.CacheConfigurationImportSelector.class}) public class CacheAutoConfiguration { public CacheAutoConfiguration() { } @Bean @ConditionalOnMissingBean public CacheManagerCustomizers cacheManagerCustomizers(ObjectProvider<CacheManagerCustomizer<?>> customizers) { return new CacheManagerCustomizers((List)customizers.orderedStream().collect(Collectors.toList())); } @Bean public CacheAutoConfiguration.CacheManagerValidator cacheAutoConfigurationValidator(CacheProperties cacheProperties, ObjectProvider<CacheManager> cacheManager) { return new CacheAutoConfiguration.CacheManagerValidator(cacheProperties, cacheManager); } static class CacheConfigurationImportSelector implements ImportSelector { CacheConfigurationImportSelector() { } public 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; } } static class CacheManagerValidator implements InitializingBean { private final CacheProperties cacheProperties; private final ObjectProvider<CacheManager> cacheManager; CacheManagerValidator(CacheProperties cacheProperties, ObjectProvider<CacheManager> cacheManager) { this.cacheProperties = cacheProperties; this.cacheManager = cacheManager; } public void afterPropertiesSet() { Assert.notNull(this.cacheManager.getIfAvailable(), () -> { return "No cache manager could be auto-configured, check your configuration (caching type is '" + this.cacheProperties.getType() + "')"; }); } } @Configuration @ConditionalOnClass({LocalContainerEntityManagerFactoryBean.class}) @ConditionalOnBean({AbstractEntityManagerFactoryBean.class}) protected static class CacheManagerJpaDependencyConfiguration extends EntityManagerFactoryDependsOnPostProcessor { public CacheManagerJpaDependencyConfiguration() { super(new String[]{"cacheManager"}); } } }
咱们经过打断点方式,能够查看配置相关的信息,这里就不一一列出了。其运行过程大体以下:
方法运行以前,先查询Cache
(缓存组件),按照@CacheName
指定的名字获取(``CacheManager`获取相应的缓存);第一次获取缓存时,若是没有对应的组件,会先自动建立。 总之,第一步获取了一个缓存组件。
在组件中经过咱们提供的key
,默认是方法的参数,以后,在缓存内部,根据咱们提供的key又根据某种策略生成内部的key
,默认使用SimpleKeyGenerator
生成key(若是没有参数:key = new SimpleKey(),若是有一个参数,key=参数的值,多个参数 key = SimpleKey(param))查询对应的值。
若是没有从对应的key
中查到值,就会调用目标方法(缓存标注的方法)返回的结果放到缓存(key对应的值)之中;若是有值,则直接将结果返回。
@Cacheable
标注的方法执行以前先来检查缓存中有没有这个数据,默认按照参数的值查找缓存,若是没有就运行方法结果放入缓存。之后再次调用就能够直接使用缓存中的数据。
核心:
使用CacheManager
按照名字获得Cache组件,若没有配置,默认就是ConcurrentMapCacheManager
组件;
key是使用KeyGenerator
生成的,默认使用SimpleKeyGenerator
;
CacheEnable主要用于标准方法,表示对该方法的返回结果进行缓存保存。
经过观察CacheEnable源码以下所示:
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package org.springframework.cache.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.core.annotation.AliasFor; @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface Cacheable { @AliasFor("cacheNames") String[] value() default {}; @AliasFor("value") String[] cacheNames() default {}; String key() default ""; String keyGenerator() default ""; String cacheManager() default ""; String cacheResolver() default ""; String condition() default ""; String unless() default ""; boolean sync() default false; }
从中能够查询到咱们能够定制的属性8个,其中value
和cacheNames
是相同功效的两个注解参数。咱们接下来分析几个经常使用的,cacheManager放到后面。
value
&cacheNames
缓存组件的名称,在 spring 配置文件中定义,必须指定至少一个。@Cacheable(value="mycache")
他等同于
@Cacheable(value={"cache1","cache2"}
key
缓存的 key,能够为空,若是指定要按照 SpEL 表达式编写,若是不指定,则缺省按照方法的全部参数进行组合。@Cacheable(value = {"user"}, key="#root.methodName+'[' + #id + ']'")
此方法自定义了key的值为:methodname[id]。其中id为咱们传入的参数的值。
其语法特色遵循以下取值方式: |名字| 位置 |描述 |示例| |-|-|-|-| |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表明参数的索引; #iban 、 #a0 、 #p0 |result |evaluation context| 方法执行后的返回值(仅当方法执行以后的判断有效,如‘unless’,’cache put’的表达式 ’cache evict’的表达式beforeInvocation=false) #result ||||
咱们来测试一下这个功能,首先编写配置文件
package com.zhaoyi.aweb.config; import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.lang.reflect.Method; @Configuration public class CacheConfig { @Bean public KeyGenerator keyGenerator(){ return new KeyGenerator() { @Override public Object generate(Object o, Method method, Object... objects) { return method.getName() + "[" + Arrays.asList(objects).toString() + "]"; } }; } }
咱们自定义了一个key的生成器,接下来只须要在缓存注解处标注咱们定义的生成器便可:
@Cacheable(value = {"user"}, keyGenerator="keyGenerator") public User getUser(Integer id){ System.out.println("查询:" + id); return userMapper.getUserById(id); }
keyGenerator
在孤独部分的是咱们加入容器的bean的名字,@Bean不指定name时,默认就是方法名做为类名,所以咱们此时的bean的名字就是keyGenerator
。
经过断点,咱们能够发现,当查询用户1的信息时,该方式生成的key相似为:getUser[[1]]
@Cacheable(value = {"user"}, condition = "#a0 > 1") public User getUser(Integer id){ System.out.println("查询:" + id); return userMapper.getUserById(id); }
当condition
代表只对id大于2的用户信息进行缓存。
#a0
表明第1个参数,你也能够直接#参数名
提取对应的参数值。
result
进行判断。条件为true不会缓存,fasle才缓存。例如:@Cacheable(value="testcache",unless="#result == null")
#result
表示返回结果。
是否使用异步模式,默认false,是否等待方法执行完才返回结果,另外,该配置若是生效,则@unless
注解失效。
缓存机制若是对一个常常变化的值进行操做的话,显然咱们须要一个更新机制,例如当编号为1的用户被修改了以后,也但愿经过返回结果缓存或者更新(若是对应的key已经有了的话)他的缓存信息。这个功能就是由@CachePut
来完成。
他的特色是标注方法在进行操做以后,对结果进行缓存。咱们不难想象,他的使用方式和@Cacheable
一模一样,对其指定一样的和@Cacheable
同样的key便可。
为了测试一下,咱们为controller添加一个update方法
@RequestMapping("/user/update") public User updateUser(User user){ return userService.updateUser(user); }
而后对service使用@CachePut
注解,注意指定和读取时使用的同样的key(即用户ID):
@Cacheable(value = "user", key = "#id") public User getUser(Integer id){ System.out.println("查询:" + id); return userMapper.getUserById(id); } @CachePut(value = "user", key="#user.id") public User updateUser(User user) { userMapper.updateUser(user); return userMapper.getUserById(user.getId()); }
咱们和@Cacheabl
e同样用了相同的缓存组件user,以及一致的key生成策略——用户ID,同时查询了更新后的用户信息做为返回值,确保@CachePut
将其放进缓存。
接下来咱们先查询id为1的用户信息,重复查两次,确保对当前结果进行了缓存,访问localhost:8090/user/select/1
{"id":1,"loginName":"akuya","userName":"阿库娅","departmentId":1}
如今,咱们将userName修改成“阿库娅520”,访问localhost:8090/user/update?id=1&userName=阿库娅520,确保修改为功以后,咱们再来访问localhost:8090/user/select/1:
{"id":1,"loginName":"akuya","userName":"阿库娅520","departmentId":1}
获得了咱们更新以后的结果,说明@CachePut
达到了咱们想要的需求。
为了测试@CachePut的效果,能够先去除更新@CachePut的注解,这样咱们就能够发现,即使咱们修改了用户信息,缓存的信息仍是旧用户信息,添加了@CachePut以后,结果就实时更新了。
和
@Cacheable
不同的是,@CachePut
是优先调用方法,再将结果存储在缓存中;而@Cacheable
则是先判断缓存中是否存在对应的key,不存在才调用方法。所以咱们能够推导出@Cacheable
指定key的时候是不能使用#result
这样的语法的(聪明的你应该很容易理解)。
缓存清除,例如咱们删除一个用户的时候,可使用该注解删除指定的缓存信息。
一样须要指定缓存组件,以及key,有了以前的经验,咱们应该写这样的service方法就能够了:
@CacheEvict(value = "user", key="#id") public void deleteUser(Integer id) { System.out.println("删除"); userMapper.deleteUser(id); }
咱们也指定了user
,也指定了其key的值。
相比较以前的注解,CacheEvict
还有一些特殊的注解,例如:
allEntries
默认值false,标志为true以后,会清除指定组件中的全部缓存信息,例如@CacheEvict(value = "user", allEntries = true)
该注解就会在方法执行后,将user组件中的全部缓存都清除。
beforeInvocation
默认为false,若是设置为true,那么清除缓存操做会发生在方法执行以前。这种状况比较特殊,默认状况下,咱们是方法执行后才进行清缓存操做,显然若是在方法运行过程当中出现异常,就不会清除缓存。因此,若是有特殊要求,咱们可使用该参数,让缓存一开始就清除掉,而无论你方法是否运行成功与否。若是咱们的缓存规则比较复杂,须要用到以上多个注解的特性,那么能够考虑使用@Caching
注解,查询其源码咱们就能够知道,他其实就是其余注解的组合体:
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface Caching { Cacheable[] cacheable() default {}; CachePut[] put() default {}; CacheEvict[] evict() default {}; }
为了使用@Caching
,咱们能够编写一个这样的service,经过loginName查询用户信息,而后呢,将返回结果放在几个不一样的key值之中,这样,咱们直接经过id或者其余为查询条件查询时,就能够直接从 以前的查询中,拿到结果,而无需再从数据库中查询了:
@Select("select * from user where login_name = #{loginName}") User getUserByLoginName(String loginName);
@Caching( cacheable = { @Cacheable(value = "user", key = "#loginName") }, put = { @CachePut(value="user", key = "#result.id"), @CachePut(value="user", key = "#result.userName") } ) public User getUserByLoginName(String loginName){ return userMapper.getUserByLoginName(loginName); }
咱们经过loginName查询到结果以后,还想要经过userName以及id做为缓存的key保存到内存中,这些key值都只能从返回结果中取到。咱们前面有提到,@cacheable
是没办法拿到返回结果的。所以咱们使用@CachePut
来完善了咱们的需求。
接下来经过测试就会发现,只要咱们经过loginName查询了akuya的数据,咱们再经过id为1(就是akuya)来查询akuya信息,发现这时候就直接获得结果,无需查询数据库了。由于咱们的缓存组件中,经过userService.getUserByLoginName
方法的执行,已经就id、loginName以及userName对akuya的信息进行了缓存。
仔细观察咱们以前缓存属性配置你会发现,不少属性很是的啰嗦,例如value="user"
,重复指定了N次,咱们能不能经过某个注解,直接一次性将这些配置指定了呢?
有,他就是CacheConfig
,用于抽取缓存的公共配置。如下是其源码:
/* * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.cache.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * {@code @CacheConfig} provides a mechanism for sharing common cache-related * settings at the class level. * * <p>When this annotation is present on a given class, it provides a set * of default settings for any cache operation defined in that class. * * @author Stephane Nicoll * @author Sam Brannen * @since 4.1 */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface CacheConfig { /** * Names of the default caches to consider for caching operations defined * in the annotated class. * <p>If none is set at the operation level, these are used instead of the default. * <p>May be used to determine the target cache (or caches), matching the * qualifier value or the bean names of a specific bean definition. */ String[] cacheNames() default {}; /** * The bean name of the default {@link org.springframework.cache.interceptor.KeyGenerator} to * use for the class. * <p>If none is set at the operation level, this one is used instead of the default. * <p>The key generator is mutually exclusive with the use of a custom key. When such key is * defined for the operation, the value of this key generator is ignored. */ String keyGenerator() default ""; /** * The bean name of the custom {@link org.springframework.cache.CacheManager} to use to * create a default {@link org.springframework.cache.interceptor.CacheResolver} if none * is set already. * <p>If no resolver and no cache manager are set at the operation level, and no cache * resolver is set via {@link #cacheResolver}, this one is used instead of the default. * @see org.springframework.cache.interceptor.SimpleCacheResolver */ String cacheManager() default ""; /** * The bean name of the custom {@link org.springframework.cache.interceptor.CacheResolver} to use. * <p>If no resolver and no cache manager are set at the operation level, this one is used * instead of the default. */ String cacheResolver() default ""; }
源码也加了很多注释,但其内在属性都是提取的公共配置,咱们直接在service类上指定以后,若是内部方法没有特殊指定,都会套用咱们使用该注解指定的值。例如,咱们抽取出公共value属性。
@Service @CacheConfig(cacheNames = "user") public class UserService {
这样,咱们整个service的缓存方法注解,若是都是用的user组件,就无需特殊指定,直接删除便可。经过该注解能够自动的帮咱们指定这个值。
cacheNames和value是同一个意思,不过
@CacheConfig
没对value作兼容,因此咱们这里必须写cacheNames
.
通过这些练习,或许您会发现,缓存内部存储的数据咱们能看到吗?若是能看到就行了。能够吗?固然能够。因此,咱们接下来要学习的redis,为咱们解决这个问题。