Spring Cache 学习笔记

先来谈谈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 缓存抽象

Spring3.1开始定义了org.springframework.cache.Cache

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

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

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

Cache接口下Spring提供了各种xxxCache的实现;如RedisCacheEhCacheCache , 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();
	}
发布了64 篇原创文章 · 获赞 0 · 访问量 3216

猜你喜欢

转载自blog.csdn.net/q42368773/article/details/102874507