Spring Cache 学习笔记

mac2026-10-05  0

先来谈谈JSR107(Java Cache API)

Java Caching定义了5个核心接口,分别是CachingProvider, CacheManager, Cache, Entry 和 Expiry。

•CachingProvider定义了创建、配置、获取、管理和控制多个CacheManager。一个应用可以在运行期访问多个CachingProvider。

•CacheManager定义了创建、配置、获取、管理和控制多个唯一命名的Cache,这些Cache存在于CacheManager的上下文中。一个CacheManager仅被一个CachingProvider所拥有。

•Cache是一个类似Map的数据结构并临时存储以Key为索引的值。一个Cache仅被一个CacheManager所拥有。

•Entry是一个存储在Cache中的key-value对。

•Expiry 每一个存储在Cache中的条目有一个定义的有效期。一旦超过这个时间,条目为过期的状态。一旦过期,条目将不可访问、更新和删除。缓存有效期可以通过ExpiryPolicy设置。

Spring 缓存抽象

Spring从3.1开始定义了org.springframework.cache.Cache

和org.springframework.cache.CacheManager接口来统一不同的缓存技术;

并支持使用JCache(JSR-107)注解简化我们开发;

•Cache接口为缓存的组件规范定义,包含缓存的各种操作集合;

•Cache接口下Spring提供了各种xxxCache的实现;如RedisCache,EhCacheCache , ConcurrentMapCache等;

•每次调用需要缓存功能的方法时,Spring会检查检查指定参数的指定的目标方法是否已经被调用过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回给用户。下次调用直接从缓存中获取。

•使用Spring缓存抽象时我们需要关注以下两点;

1、确定方法需要被缓存以及他们的缓存策略

2、从缓存中读取之前缓存存储的数据

如何使用

这里我们使用ConcurrentMapCacheManager和EhCacheCacheManager为例,EhCacheCacheManager需要引入jar包

<dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> <version>2.10.4</version> </dependency> <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd"> <!-- 注意需要引入cache空间 xmlns:cache="http://www.springframework.org/schema/cache" xsi:schemaLocation="http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd" --> <!-- 创建spring的缓存管理器,这里使用ConcurrentMapCacheManager --> <bean id="concurrentMapCacheManager" class="org.springframework.cache.concurrent.ConcurrentMapCacheManager"> <property name="cacheNames"> <set> <value>default</value> <!-- 创建缓存,缓存名称为default --> <value>user</value> <!-- 创建缓存,缓存名称为user --> </set> </property> </bean> <!-- 创建一个spring的缓存管理器,这里使用ehCacheCacheManager --> <bean id="ehCacheCacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager"> <!-- 注入net.sf.ehcache.CacheManager交给spring管理 --> <property name="cacheManager"> <!-- 通过工厂创建一个net.sf.ehcache.CacheManager --> <bean class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"> <property name="configLocation" value="classpath:ehcache.xml" /> <!-- ehcache缓存配置文件 --> </bean> </property> </bean> <!-- 启用缓存注解 --> <!-- cache-manager:缓存管理器bean的id,缺省值为 cacheManager,有什么用? 如果设置了cache-manager,当使用缓存注解@Cacheable(cacheManager="concurrentMapCacheManager")时,cacheManager可以省略不写 --> <cache:annotation-driven cache-manager="concurrentMapCacheManager" /> </beans>

使用注解操作缓存 

/** * @Cacheable 注解用来将方法返回的结果放入缓存中(先检查是否有缓存,没有缓存才执行方法),有以下参数 * cacheManager:使用哪一个缓存管理器,可以为空,使用缺省值;缺省值的使用见本文章xml配置里面的说明 * cacheNames:存放到哪个缓存中,数组类型,可以写多个cacheNames={"default","dept"},也可以使用values="dept" * keyGenerator:key的生成器,可以不写,使用默认的生成器 * key:指定key的值,使用spel表达式,key="#id"(使用参数的id作为key),key="#user.id"(使用参数user对象中的id作为key)如果为空,则按生成器的策略生成key,默认生成器策略:如果只有一个参数,则使用这个参数值作为key,如果有多个,则使用所有的参数加起来的hash值作为key * condition:指定符合条件的情况下才缓存,condition = "#id>0" * unless:不缓存;当unless指定的条件为true,方法的返回值就不会被缓存;可以获取到结果进行判断unless = "#result == null or #result eq ''",返回结果为null就不缓存 * sync:是否使用异步模式,默认false方法执行完后同步进行缓存,如果为true,则异步进行缓存,异步模式不支持unless */ @Cacheable(cacheNames="default",key="#id") public String getDefault(int id) { return this.getDept(id); } @Cacheable(cacheManager="ehCacheCacheManager",cacheNames="dept") public String getDept(int id) { return "getDept" + System.currentTimeMillis(); } /** * @CachePut 注解不管缓存是否存在都会进入方法,然后把结果缓存,主要用于更新数据 * key="#employee.id" 也可以使用 key="#result.id" * */ @CachePut(cacheNames="default",key="#employee.id") public Employee updateEmp(Employee employee){ employeeMapper.updateEmp(employee); return employee; } /** * @CacheEvict:缓存清除 * key:指定要清除的数据 * allEntries = true:指定清除这个缓存中所有的数据 * beforeInvocation:缓存的清除是否在方法之前执行 ,默认false,如果出现异常缓存就不会清除,true 方法执行之前清除,无论方法是否出现异常,缓存都清除 * allEntries 是否删除该缓存下的所有数据(cacheNmae=emp的),默认false * */ @CacheEvict(value="emp",beforeInvocation = true) public void deleteEmp(Integer id){ int i = 10/0; } // @Caching 定义复杂的缓存规则 @Caching( cacheable = { @Cacheable(value="emp",key = "#lastName") }, put = { @CachePut(value="emp",key = "#result.id"), @CachePut(value="emp",key = "#result.email") }, evict = { @CacheEvict(value="emp",key="#lastName") } ) public Employee getEmpByLastName(String lastName){ return employeeMapper.getEmpByLastName(lastName); } //@CacheConfig(cacheManager="ehCacheCacheManager",cacheNames="default") //@CacheConfig 在类上使用,抽取缓存的公共配置 //public class UserCacheService {}

使用自定义的KeyGenerator

@Component public class MyKeyGenerator implements org.springframework.cache.interceptor.KeyGenerator { //实现KeyGenerator @Override public Object generate(Object target, Method method, Object... params) { //输出:com.fawa.cache.CacheServicegetDefault[1] //target: com.fawa.cache.CacheService 引用注解的类 //method:getDefaul 引用注解的类的方法 //params:[1] 引用注解的类的方法的参数 System.out.println(target.getClass().getName() + method.getName() + Arrays.asList(params)); return target.getClass().getName() + method.getName() + Arrays.asList(params); } } //keyGenerator="myKeyGenerator" myKeyGenerator为bean的id,myKeyGenerator必须交给spring管理 @Cacheable(cacheNames="default",keyGenerator="myKeyGenerator") public String getDefault(int id) { return "getDefault" + System.currentTimeMillis(); }

Cache Key SpEL表达式可使用的变量

除了可以使用参数中值作为变量外,还可以使用以下值

使用编码的方式操作缓存 

@Controller @RequestMapping("/test/") public class TestController { //注入缓存接口,当有多个实现类的时候,需要指定cacheManager的实现类 @Autowired @Qualifier("ehCacheCacheManager") //该bean已在xml中定义 private CacheManager cacheManager; //org.springframework.cache.CacheManager //或者直接注入缓存实现类 /*@Autowired private EhCacheCacheManager cacheManager;*/ @RequestMapping("test1") @ResponseBody public String test1(){ Collection<String> cacheNames = cacheManager.getCacheNames(); //获取所有缓存组件的名字 Cache cache = cacheManager.getCache("user"); //根据cacheName获取缓存组件 cache.put(1, System.currentTimeMillis()); //添加缓存 Long value = cache.get(1,Long.class); //获取缓存 cache.evict(1); //根据key清除缓存 cache.clear(); //清空缓存 return "success"; } }

使用RedisCacheManager

<!--2.x 以上版本需要jdk1.8 --> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>1.8.13.RELEASE</version> </dependency> <!-- 连接池配置 --> <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="maxTotal" value="100" /> <property name="maxIdle" value="50" /> <property name="minIdle" value="10" /> <!-- 其他属性到源码中查看,这里我们使用默认值 --> </bean> <!-- 使用工厂创建连接 --> <bean id="jedisConnFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"> <property name="poolConfig" ref="jedisPoolConfig" /> <property name="hostName" value="192.168.1.3" /> <property name="port" value="6379" /> <!-- <property name="password" value="123456" /> --> <!-- 其他属性到源码中查看,这里我们使用默认值 --> </bean> <!-- 使用RedisTemplate,key value 的存储采用序列化,可以配置采用哪种序列化接口,这里我们使用默认配置--> <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"> <property name="connectionFactory" ref="jedisConnFactory" /> <!-- 其他属性到源码中查看 --> </bean> <!-- 使用StringRedisTemplate,key value 的存储全部使用string类型--> <bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate"> <property name="connectionFactory" ref="jedisConnFactory" /> </bean> <!-- 创建缓存管理器,这里使用RedisCacheManager --> <bean id="redisCacheManager" class="org.springframework.data.redis.cache.RedisCacheManager"> <constructor-arg name="redisOperations" ref="stringRedisTemplate" /> <!-- 注入redisOperation接口的实现类,redisTemplate或stringRedisTemplate --> </bean> //如果使用的是stringRedisTemplate,key value 都必须是stirng类型 @Cacheable(cacheManager="redisCacheManager",cacheNames="role") public String getRole(String id) { return "getRole" + System.currentTimeMillis(); }

与SpringBoot整合

<!-- 添加依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> </dependency> //启用缓存 @SpringBootApplication @EnableCaching public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } //在properties或yaml中设置ehcache.xml路径 //ehcache.xml内容见ehcache.xml学习笔记 spring.cache.ehcache.config=classpath:ehcache.xml

 

最新回复(0)