原创文章,转载请标注出处:《SpringBoot基础系列-SpringCache使用》html
SpringCache自己是一个缓存体系的抽象实现,并无具体的缓存能力,要使用SpringCache还须要配合具体的缓存实现来完成。java
虽然如此,可是SpringCache是全部Spring支持的缓存结构的基础,并且全部的缓存的使用最后都要归结于SpringCache,那么一来,要想使用SpringCache,仍是要仔细研究一下的。spring
SpringCache缓存功能的实现是依靠下面的这几个注解完成的。编程
@CacheConfig:定义公共设置,位于class之上数组
@EnableCaching @Configuration public class CacheConfig { @Bean public CacheManager cacheManager() { SimpleCacheManager cacheManager = new SimpleCacheManager(); cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("default"))); return cacheManager; } }
注意:在SpringBoot中使用SpringCache能够由自动配置功能来完成CacheManager的注册,SpringBoot会自动发现项目中拥有的缓存系统,而注册对应的缓存管理器,固然咱们也能够手动指定。缓存
使用该注解和以下XML配置具备同样的效果:app
<beans> <cache:annotation-driven/> <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager> <property name="caches"> <set> <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean> <property name="name" value="default"/> </bean> </set> </property> </bean> </beans>
下面来看看@EnableCaching的源码:less
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(CachingConfigurationSelector.class) public @interface EnableCaching { // 用于设置使用哪一种代理方式,默认为基于接口的JDK动态代理(false), // 设置为true,则使用基于继承的CGLIB动态代理 boolean proxyTargetClass() default false; // 用于设置切面织入方式(设置面向切面编程的实现方式), // 默认为使用动态代理的方式织入,固然也能够设置为ASPECTJ的方式来实现AOP AdviceMode mode() default AdviceMode.PROXY; // 用于设置在一个切点存在多个通知的时候各个通知的执行顺序,默认为最低优先级, // 其中数字却大优先级越低,这里默认为最低优先级,int LOWEST_PRECEDENCE = // Integer.MAX_VALUE;,倒是整数的最大值 int order() default Ordered.LOWEST_PRECEDENCE; } public enum AdviceMode { PROXY, ASPECTJ } public interface Ordered { int HIGHEST_PRECEDENCE = Integer.MIN_VALUE; int LOWEST_PRECEDENCE = Integer.MAX_VALUE; int getOrder(); }
由上面的源码能够看出,缓存功能是依靠AOP来实现的。ide
该注解用于标注于方法之上用于标识该方法的返回结果须要被缓存起来,标注于类之上标识该类中全部方法均须要将结果缓存起来。函数
该注解标注的方法每次被调用前都会触发缓存校验,校验指定参数的缓存是否已存在(已发生过相同参数的调用),若存在,直接返回缓存结果,不然执行方法内容,最后将方法执行结果保存到缓存中。
@Service @Log4j2 public class AnimalService { @Autowired private AnimalRepository animalRepository; //... // @Cacheable("animalById") @Cacheable(value = "animalById", key = "#id") public ResponseEntity<Animal> getAnimalById(final int id){ return ResponseEntity.ok(animalRepository.selectById(id)); } //... }
上面的实例中两个@Cacheable配置效果实际上是同样的,其中value指定的缓存的名称,它和另外一个方法cacheName效果同样,通常来讲这个缓存名称必需要有,由于这个是区别于其余方法的缓存的惟一方法。
这里咱们介绍一下缓存的简单结构,在缓存中,每一个这样的缓存名称的名下都会存在着多个缓存条目,这些缓存条目对应在使用不一样的参数调用当前方法时生成的缓存,全部一个缓存名称并非一个缓存,而是一系列缓存。
另外一个key用于指定当前方法的缓存保存时的键的组合方式,默认的状况下使用全部的参数组合而成,这样能够有效区分不一样参数的缓存。固然咱们也能够手动指定,指定的方法是使用SPEL表达式。
这里我么来简单看看其源码,了解下其余几个方法的做用:
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface Cacheable { // 用于指定缓存名称,与cacheNames()方法效果一致 @AliasFor("cacheNames") String[] value() default {}; // 用于指定缓存名称,与value()方法效果一致 @AliasFor("value") String[] cacheNames() default {}; // 用于使用SPEL手动指定缓存键的组合方式,默认状况使用全部的参数来组合成键,除非自定义了keyGenerator。 // 使用SPEL表达式能够根据上下文环境来获取到指定的数据: // #root.method:用于获取当前方法的Method实例 // #root.target:用于获取当前方法的target实例 // #root.caches:用于获取当前方法关联的缓存 // #root.methodName:用于获取当前方法的名称 // #root.targetClass:用于获取目标类类型 // #root.args[1]:获取当前方法的第二个参数,等同于:#p1和#a1和#argumentName String key() default ""; // 自定义键生成器,定义了该方法以后,上面的key方法自动失效,这个键生成器是: // org.springframework.cache.interceptor.KeyGenerator,这是一个函数式接口, // 只有一个generate方法,咱们能够经过自定义的逻辑来实现自定义的key生成策略。 String keyGenerator() default ""; // 用于设置自定义的cacheManager(缓存管理器),能够自动生成一个cacheResolver // (缓存解析器),这一下面的cacheResolver()方法设置互斥 String cacheManager() default ""; // 用于设置一个自定义的缓存解析器 String cacheResolver() default ""; // 用于设置执行缓存的条件,若是条件不知足,方法返回的结果就不会被缓存,默认无条件所有缓存。 // 一样使用SPEL来定义条件,可使用的获取方式同key方法。 String condition() default ""; // 这个用于禁止缓存功能,若是设置的条件知足,就不执行缓存结果,与上面的condition不一样之处在于, // 该方法执行在当前方法调用结束,结果出来以后,所以,它除了可使用上面condition所能使用的SPEL // 表达式以外,还可使用#result来获取方法的执行结果,亦便可以根据结果的不一样来决定是否缓存。 String unless() default ""; // 设置是否对多个针对同一key执行缓存加载的操做的线程进行同步,默认不一样步。这个功能须要明确肯定所 // 使用的缓存工具支持该功能,不然不要滥用。 boolean sync() default false; }
如何自定义一个KeyGenerator呢?
public class AnimalKeyGenerator implements KeyGenerator { @Override public Object generate(Object target, Method method, Object... params) { StringBuilder sb = new StringBuilder("animal-"); sb.append(target.getClass().getSimpleName()).append("-").append(method.getName()).append("-"); for (Object o : params) { String s = o.toString(); sb.append(s).append("-"); } return sb.deleteCharAt(sb.lastIndexOf("-")).toString(); } }
该注解用于更新缓存,不管结果是否已经缓存,都会在方法执行结束插入缓存,至关于更新缓存。通常用于更新方法之上。
@Service @Log4j2 public class AnimalService { @Autowired private AnimalRepository animalRepository; //... @CachePut(value = "animalById", key = "#animal.id") public ResponseEntity<Animal> updateAnimal(final Animal animal){ Wrapper<Animal> animalWrapper = new UpdateWrapper<>(); ((UpdateWrapper<Animal>) animalWrapper).eq("id",animal.getId()); animalRepository.update(animal, animalWrapper); return ResponseEntity.ok(this.getAnimalById(animal.getId())); } //... }
这里指定更新缓存,value一样仍是缓存名称,这里更新的是上面查询操做的同一缓存,并且key设置为id也与上面的key设置对应。
如此设置以后,每次执行update方法时都会直接执行方法内容,而后将返回的结果保存到缓存中,若是存在相同的key,直接替换缓存内容执行缓存更新。
下面来看看源码:
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface CachePut { // 同上 @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 ""; }
只有一点要注意:这里的设置必定要和执行缓存保存的方法的@Cacheable的设置一致,不然没法准确更新。
该注解主要用于删除缓存操做。
@Service @Log4j2 public class AnimalService { @Autowired private AnimalRepository animalRepository; //... @CacheEvict(value = "animalById", key = "#id") public ResponseEntity<Integer> deleteAnimalById(final int id){ return ResponseEntity.ok(animalRepository.deleteById(id)); } //... }
简单明了,看看源码:
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface CacheEvict { // 同上 @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 ""; // 这个设置用于指定当前缓存名称名下的全部缓存是否所有删除,默认false。 boolean allEntries() default false; // 这个用于指定删除缓存的操做是否在方法调用以前完成,默认为false,表示先调用方法,在执行缓存删除。 boolean beforeInvocation() default false; }
这个注解用于组个多个缓存操做,包括针对不用缓存名称的相同操做等,源码:
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface Caching { // 用于指定多个缓存设置操做 Cacheable[] cacheable() default {}; // 用于指定多个缓存更新操做 CachePut[] put() default {}; // 用于指定多个缓存失效操做 CacheEvict[] evict() default {}; }
简单用法:
@Service @Log4j2 public class AnimalService { @Autowired private AnimalRepository animalRepository; //... @Caching( evict = { @CacheEvict(value = "animalById", key = "#id"), @CacheEvict(value = "animals", allEntries = true, beforeInvocation = true) } ) public ResponseEntity<Integer> deleteAnimalById(final int id){ return ResponseEntity.ok(animalRepository.deleteById(id)); } @Cacheable("animals") public ResponseEntity<Page<Animal>> getAnimalPage(final Animal animal, final int pageId, final int pageSize){ Page<Animal> page = new Page<>(); page.setCurrent(pageId); page.setSize(pageSize); return ResponseEntity.ok((Page<Animal>) animalRepository.selectPage(page,packWrapper(animal, WrapperType.QUERY))); } //... }
该注解标注于类之上,用于进行一些公共的缓存相关配置。源码为:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface CacheConfig { // 设置统一的缓存名,适用于整个类中的方法所有是针对同一缓存名操做的状况 String[] cacheNames() default {}; // 设置统一个键生成器,免去了每一个缓存设置中单独设置 String keyGenerator() default ""; // 设置统一个自定义缓存管理器 String cacheManager() default ""; // 设置统一个自定义缓存解析器 String cacheResolver() default ""; }