spring - springmvc - @EnableCaching

@EnableCaching
@EnableCaching注释在应用程序中启用注释驱动的缓存管理功能,并允许我们在应用程序中使用@Cacheable和@CacheEvict注释。
具有类似功能的 XML 等效项是cache:*命名空间:

@Configuration
@EnableCaching
public class CacheConfig {
 
    @Bean
    public CacheManager cacheManager() {
        SimpleCacheManager cacheManager = new SimpleCacheManager();
        cacheManager.setCaches(
          Arrays.asList(new ConcurrentMapCache("default")));
        return cacheManager;
    }
}

该注释还有以下选项:
mode — 指示应如何应用缓存建议
order — 指示在特定连接点应用时执行缓存顾问的顺序
proxyTargetClass — 指示是否要创建基于子类 (CGLIB) 的代理,而不是基于标准 Java 接口的代理
此配置再次可以通过实现CachingConfigurerSupport类的@Configuration类进行自定义:

@Configuration
@EnableCaching
public class CacheConfig extends CachingConfigurerSupport {

    @Bean
    @Override
    public CacheManager cacheManager() {
        SimpleCacheManager cacheManager = new SimpleCacheManager();
        cacheManager.setCaches(
          Arrays.asList(new ConcurrentMapCache("default")));
        return cacheManager;
    }

    @Bean
    @Override
    public KeyGenerator keyGenerator() {
        return new MyKeyGenerator();
    }
}

当然,我们也可以通过 XML 配置启用缓存管理:

<beans>
    <cache:annotation-driven />

    <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
        <property name="caches">
            <set>
                <bean 
                  class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" 
                  name="addresses"/>
            </set>
        </property>
    </bean>
</beans>

当然,我们也可以通过 XML 配置启用缓存管理:

@Component
public class SimpleCacheCustomizer 
  implements CacheManagerCustomizer<ConcurrentMapCacheManager> {

    @Override
    public void customize(ConcurrentMapCacheManager cacheManager) {
        cacheManager.setCacheNames(asList("users", "transactions"));
    }
}

CacheAutoConfiguration 自动配置会 选取这些定制器,并在完成初始化之前将它们应用到当前的CacheManager 。
为方法启用缓存行为的最简单方法是使用@Cacheable对其进行划分,并使用将存储结果的缓存名称对其进行参数化:
getAddress ()调用将在实际调用该方法之前首先检查缓存地址,然后缓存结果。
虽然在大多数情况下一个缓存就足够了,但 Spring 框架还支持将多个缓存作为参数传递:

@Cacheable("addresses")
public String getAddress(Customer customer) {...}

@Cacheable({"addresses", "directory"})
public String getAddress(Customer customer) {...}

现在,将所有方法设置为@Cacheable会出现什么问题?
问题是尺寸。我们不想用不经常需要的值填充缓存。缓存可能会变得非常大、非常快,我们可能会保留大量陈旧或未使用的数据。
我们可以使用@CacheEvict注解来指示删除一个或多个/所有值,以便可以将新值再次加载到缓存中:

@CacheEvict(value="addresses", allEntries=true)
public String getAddress(Customer customer) {...}

这里我们使用附加参数allEntries结合要清空的缓存;这将清除缓存地址中的所有条目并为新数据做好准备。
虽然@CacheEvict通过删除陈旧和未使用的条目来减少在大型缓存中查找条目的开销,但我们希望避免从缓存中逐出太多数据。
相反,每当我们更改条目时,我们都会有选择地更新它们。
通过@CachePut注解,我们可以在不干扰方法执行的情况下更新缓存的内容。也就是说,该方法将始终被执行并缓存结果:

@CachePut(value="addresses")
public String getAddress(Customer customer) {...}

@Cacheable和@CachePut之间的区别在于,@Cacheable将跳过运行该方法,而@CachePut将实际运行该方法,然后将其结果放入缓存中。

如果我们想使用多个相同类型的注解来缓存一个方法怎么办?我们来看一个错误的例子:

@CacheEvict("addresses")
@CacheEvict(value="directory", key=customer.name)
public String getAddress(Customer customer) {...}

上面的代码将无法编译,因为 Java 不允许为给定方法声明相同类型的多个注释。
上述问题的解决方法是:`

@Caching(evict = { 
  @CacheEvict("addresses"), 
  @CacheEvict(value="directory", key="#customer.name") })
public String getAddress(Customer customer) {...}

如上面的代码片段所示,我们可以使用@Caching将多个缓存注解分组,并使用它来实现我们自己定制的缓存逻辑。
使用@CacheConfig注释,我们可以将一些缓存配置简化到类级别的单个位置,这样我们就不必多次声明:

@CacheConfig(cacheNames={"addresses"})
public class CustomerDataService {

    @Cacheable
    public String getAddress(Customer customer) {...}
}

有时,缓存可能并不适用于所有情况下的方法。
重用@CachePut注释中的示例,这将每次执行该方法并缓存结果:

@CachePut(value="addresses")
public String getAddress(Customer customer) {...}

果我们想要更多地控制注释何时处于活动状态,我们可以使用条件参数参数化@CachePut,该条件参数采用 SpEL 表达式,并确保根据评估该表达式来缓存结果:

@CachePut(value="addresses", condition="#customer.name=='Tom'")
public String getAddress(Customer customer) {...}

我们还可以根据方法的输出而不是通过except参数的输入来控制缓存:

@CachePut(value="addresses", unless="#result.length()<64")
public String getAddress(Customer customer) {...}`

上述注释将缓存地址,除非它们短于 64 个字符。
重要的是要知道条件和除非参数可以与所有缓存注释结合使用。
这种条件缓存对于管理大型结果非常有效。它对于根据输入参数自定义行为也很有用,而不是对所有操作强制执行通用行为。
基于 XML 的声明式缓存
如果我们无法访问应用程序的源代码,或者想要从外部注入缓存行为,我们还可以使用基于 XML 的声明性缓存。
这是我们的 XML 配置:

<!-- the service that you wish to make cacheable -->
<bean id="customerDataService" 
  class="com.your.app.namespace.service.CustomerDataService"/>

<bean id="cacheManager" 
  class="org.springframework.cache.support.SimpleCacheManager"> 
    <property name="caches"> 
        <set> 
            <bean 
              class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" 
              name="directory"/> 
            <bean 
              class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" 
              name="addresses"/> 
        </set> 
    </property> 
</bean>
<!-- define caching behavior -->
<cache:advice id="cachingBehavior" cache-manager="cacheManager">
    <cache:caching cache="addresses">
        <cache:cacheable method="getAddress" key="#customer.name"/>
    </cache:caching>
</cache:advice>

<!-- apply the behavior to all the implementations of CustomerDataService interface->
<aop:config>
    <aop:advisor advice-ref="cachingBehavior"
      pointcut="execution(* com.your.app.namespace.service.CustomerDataService.*(..))"/>
</aop:config>

这是等效的 Java 配置

@Configuration
@EnableCaching
public class CachingConfig {

    @Bean
    public CacheManager cacheManager() {
        SimpleCacheManager cacheManager = new SimpleCacheManager();
        cacheManager.setCaches(Arrays.asList(
          new ConcurrentMapCache("directory"), 
          new ConcurrentMapCache("addresses")));
        return cacheManager;
    }
}

这是我们的CustomerDataService:

@Component
public class CustomerDataService {
 
    @Cacheable(value = "addresses", key = "#customer.name")
    public String getAddress(Customer customer) {
        return customer.getAddress();
    }
}
posted @ 2024-03-07 20:48  dkpp  阅读(58)  评论(0编辑  收藏  举报