Spring 的缓存抽象

https://docs.spring.io/spring/docs/5.0.13.RELEASE/spring-framework-reference/integration.html#cache

从3.1版开始,Spring Framework提供了对现有Spring应用程序透明地添加缓存的支持。 与事务支持类似,缓存抽象允许一致地使用各种缓存解决方案,而对代码的影响最小。

从Spring 4.1开始,通过JSR-107注释和更多自定义选项的支持,缓存抽象得到了显着改进。

抽象的核心是将缓存应用于Java方法,从而减少了基于缓存中可用信息的执行次数。也就是说,每次调用目标方法时,抽象都会应用缓存行为检查方法是否已经为给定的参数执行。如果有,则返回缓存的结果,而不必执行实际的方法;如果没有,则执行方法,缓存结果并返回给用户,以便在下次调用方法时返回缓存的结果。这样,对于给定的一组参数,昂贵的方法(CPU或IO绑定)只能执行一次,并且结果可以重用,而不必实际再次执行该方法。缓存逻辑被透明地应用,没有任何对调用程序的干扰。

就像Spring Framework中的其他服务一样,缓存服务是一种抽象(不是缓存实现),需要使用实际存储来存储缓存数据 - 也就是说,抽象使开发人员不必编写缓存逻辑 但不提供实际的存储。 这种抽象由org.springframework.cache.Cache和org.springframework.cache.CacheManager接口实现。

这个抽象的一些实现可以直接使用:基于JDK java.util.concurrent.ConcurrentMap的缓存(即默认的缓存是基于JVM的ConcurrentMap),Ehcache 2.x,Gemfire缓存,符合Caffeine和JSR-107的缓存(例如Ehcache 3.x)。 有关插入其他缓存存储/提供程序的更多信息,请参阅插入不同的后端缓存。

对于缓存声明,抽象提供了一组Java注释:

@Cacheable 触发数据存储于缓存

@CacheEvict 触发删除缓存条目

@CachePut 更新缓存而不会干扰方法执行

@Caching 重新组合要在方法上应用的多个缓存操作

@CacheConfig 在类级别共享一些常见的缓存相关设置

@ Cacheable

顾名思义,@ Cacheable用于划分可缓存的方法 - 即,将结果存储到缓存中的方法,以便在后续调用(具有相同的参数)时,返回缓存中的值而不必 实际执行该方法。

@CachePut

对于需要在不干扰方法执行的情况下更新缓存的情况,可以使用@CachePut批注。 也就是说,该方法将始终执行并将其结果放入缓存中(根据@CachePut选项)。 它支持与@Cacheable相同的选项

@CacheEvict

缓存抽象不仅允许缓存存储的填充,还允许驱逐。 此过程对于从缓存中删除陈旧或未使用的数据非常有用。 与@Cacheable相反,注释@CacheEvict划分了执行缓存逐出的方法,即用作从缓存中删除数据的触发器的方法。

@Caching

有些情况下需要指定相同类型的多个注释,例如@CacheEvict或@CachePut,例如因为条件或键表达式在不同的缓存之间是不同的。 @Caching允许在同一方法上使用多个嵌套的@ Cacheable,@ CachePut和@CacheEvict:

基于内存ConcurrentMap的缓存实例

@Slf4j
@Service
@CacheConfig(cacheNames = "coffee")
public class CoffeeService {
    @Autowired
    private CoffeeRepository coffeeRepository;

    /**
     * 查询结果存储于缓存中
     * @return List<Coffee>
     */
    @Cacheable
    public List<Coffee> findAllCoffee() {
        return coffeeRepository.findAll();
    }

    /**
     * 剔除缓存
     */
    @CacheEvict
    public void reloadCoffee() {
    }

    public Optional<Coffee> findOneCoffee(String name) {
        ExampleMatcher matcher = ExampleMatcher.matching()
                .withMatcher("name", exact().ignoreCase());
        Optional<Coffee> coffee = coffeeRepository.findOne(
                Example.of(Coffee.builder().name(name).build(), matcher));
        log.info("Coffee Found: {}", coffee);
        return coffee;
    }
}
@Slf4j
@EnableTransactionManagement
@SpringBootApplication
@EnableJpaRepositories
//开启缓存
@EnableCaching(proxyTargetClass = true)
public class SpringBucksApplication implements ApplicationRunner {
	@Autowired
	private CoffeeService coffeeService;

	public static void main(String[] args) {
		SpringApplication.run(SpringBucksApplication.class, args);
	}

	@Override
	public void run(ApplicationArguments args) throws Exception {
		log.info("Count: {}", coffeeService.findAllCoffee().size());
		for (int i = 0; i < 10; i++) {
			log.info("Reading from cache.");
			coffeeService.findAllCoffee();
		}
		coffeeService.reloadCoffee();
		log.info("Reading after refresh.");
		coffeeService.findAllCoffee().forEach(c -> log.info("Coffee {}", c.getName()));
	}
}

扩展

启用Spring的注释驱动的缓存管理功能,类似于Spring的<cache:*> XML命名空间中的支持。 要与@Configuration类一起使用,如下所示:

   @Configuration
   @EnableCaching
   public class AppConfig {
  
       @Bean
       public MyService myService() {
           // configure and return a class having @Cacheable methods
           return new MyService();
       }
  
       @Bean
       public CacheManager cacheManager() {
           // configure and return an implementation of Spring's CacheManager SPI
           SimpleCacheManager cacheManager = new SimpleCacheManager();
           cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("default")));
           return cacheManager;
       }
   }

基于xml的配置

  <beans>
       <cache:annotation-driven/>
       <bean id="myService" class="com.foo.MyService"/>
       <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和<cache:annotation-driven />负责注册为注释驱动的缓存管理提供支持的必要Spring组件,例如CacheInterceptor和基于代理或基于AspectJ的建议。 调用@Cacheable方法时,拦截器进入调用堆栈。


如果存在JSR-107 API和Spring的JCache实现,则还会注册管理标准高速缓存注释的必要组件。 这会创建基于代理或基于AspectJ的建议,当调用使用CacheResult,CachePut,CacheRemove或CacheRemoveAll注释的方法时,它会将拦截器编织到调用堆栈中。

必须注册CacheManager类型的bean,因为框架没有合理的默认值可用作约定。 虽然<cache:annotation-driven>元素假定一个名为“cacheManager”的bean,但@EnableCaching按类型搜索缓存管理器bean。 因此,缓存管理器bean方法的命名并不重要。

对于那些希望在@EnableCaching和要使用的确切缓存管理器bean之间建立更直接关系的人,可以实现CachingConfigurer回调接口。 请注意下面的@ Override-annotated方法:

 

   @Configuration
   @EnableCaching
   public class AppConfig extends CachingConfigurerSupport {
  
       @Bean
       public MyService myService() {
           // configure and return a class having @Cacheable methods
           return new MyService();
       }
  
       @Bean
       @Override
       public CacheManager cacheManager() {
           // configure and return an implementation of Spring's CacheManager SPI
           SimpleCacheManager cacheManager = new SimpleCacheManager();
           cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("default")));
           return cacheManager;
       }
  
       @Bean
       @Override
       public KeyGenerator keyGenerator() {
           // configure and return an implementation of Spring's KeyGenerator SPI
           return new MyKeyGenerator();
       }
   }

根据Spring的KeyGenerator SPI,这允许自定义缓存密钥生成策略。 通常,@ EnableCaching将为此配置Spring的SimpleKeyGenerator,但在实现CachingConfigurer时,必须明确提供密钥生成器。 如果不需要自定义,则从此方法返回null或新的SimpleKeyGenerator()。

基于Redis的缓存

#缓存类型。 默认情况下,根据环境自动检测
spring.cache.type=redis
spring.cache.cache-names=coffee
#缓存过期时间
spring.cache.redis.time-to-live=5000
#是否允许缓存null
spring.cache.redis.cache-null-values=false
spring.redis.host=localhost
@Slf4j
@Service
@CacheConfig(cacheNames = "coffee")
public class CoffeeService {
    @Autowired
    private CoffeeRepository coffeeRepository;

    @Cacheable
    public List<Coffee> findAllCoffee() {
        return coffeeRepository.findAll();
    }

    @CacheEvict
    public void reloadCoffee() {
    }

    public Optional<Coffee> findOneCoffee(String name) {
        ExampleMatcher matcher = ExampleMatcher.matching()
                .withMatcher("name", exact().ignoreCase());
        Optional<Coffee> coffee = coffeeRepository.findOne(
                Example.of(Coffee.builder().name(name).build(), matcher));
        log.info("Coffee Found: {}", coffee);
        return coffee;
    }
}
@Slf4j
@EnableTransactionManagement
@SpringBootApplication
@EnableJpaRepositories
@EnableCaching(proxyTargetClass = true)
public class SpringBucksApplication implements ApplicationRunner {
	@Autowired
	private CoffeeService coffeeService;

	public static void main(String[] args) {
		SpringApplication.run(SpringBucksApplication.class, args);
	}

	@Override
	public void run(ApplicationArguments args) throws Exception {
		log.info("Count: {}", coffeeService.findAllCoffee().size());
		for (int i = 0; i < 5; i++) {
			log.info("Reading from cache.");
			coffeeService.findAllCoffee();
		}
		Thread.sleep(5_000);
		log.info("Reading after refresh.");
		coffeeService.findAllCoffee().forEach(c -> log.info("Coffee {}", c.getName()));
	}
}

    

微信公众号

                          
posted @ 2020-01-13 11:31  盲目的拾荒者  阅读(382)  评论(0编辑  收藏  举报