@EnableCaching注解是spring framework中的注解驱动的缓存管理功能。自spring版本3.1起加入了该注解。如果你使用了这个注解,那么你就不需要在XML文件中配置cache manager了。
当你在配置类(@Configuration)上使用@EnableCaching注解时,会触发一个post processor,这会扫描每一个spring bean,查看是否已经存在注解对应的缓存。如果找到了,就会自动创建一个代理拦截方法调用,使用缓存的bean执行处理。
大部分公司在开发中运用缓存是一件很平常的事,主要用来缓解数据库的访问压力,其使用方法和原理都类似于Spring对事务管理的支持。在我所开发中,一般都将这三个注解运用到方法上,那么其表现为:当我们调用这个缓存方法时会把该方法参数和返回结果作为一个键值对存放在缓存中,等到下次利用同样的参数来调用该方法时将不再执行该方法,而是直接从缓存中获取结果进行返回,这样不仅减少了数据的返回时间,还极大的缓解了数据库的访问压力,这也是使用缓存的主要目的。
一、注解@Cacheable的基本使用
你需要知道的基本知识点:
1.注解@Cacheable是数据缓存的注解,将此注解放在方法上表示此方法有缓存功能,放在类上表示此类的所有方法都有缓存功能;
方法上的缓存:
1
2
3
4
5
|
@Cacheable (value = "employee" )
public Person findEmployee(String firstName, String surname, int age) {
return new Person(firstName, surname, age);
}
|
类上的缓存:
1
2
3
4
5
6
7
8
9
|
@Cacheable (value = "employee" )
public class EmployeeDAO {
public Person findEmployee(String firstName, String surname, int age) {
return new Person(firstName, surname, age);
}
public Person findAnotherEmployee(String firstName, String surname, int age) {
return new Person(firstName, surname, age);
}
}
|
2.注解的参数
1
2
3
4
5
|
@Cacheable (value = "employee" , key = "#surname" , condition = "#age < 25" )
public Person findEmployeeBySurname(String firstName, String surname, int age) {
return new Person(firstName, surname, age);
}
|
在这个缓存注解中,value参数必不可少,表示的是此缓存的存储空间名,就像Mapper文件里的namespace一样,是为了区分不同的缓存数据,其它参数可有可无;
key参数是取值的标识符,是你取缓存的入口,就像Map集合的key,靠key去取缓存值,默认是方法的所有参数进行组合,这里可以使用SpEL语言,像上面图例中,我就是将参数surname作为key,但是这样单纯的靠一个参数还是不保险,因为可能会遇到相同surname的情况,这样,key就重复了,所以要想办法保证key唯一;
condition参数表示条件,满足条件的才会缓存,当然,这个方法都会执行,像上面图例中就表示年龄小于25的数据都会被缓存;
二、注解@CacheEvict的基本使用
你需要知道的基本知识点:
1.这个注解的作用是清除缓存;
2.参数
value:表示要被清除的缓存的存储空间名;
key:缓存的key,默认为空;
condition:清除缓存的条件,同上,支持SpEL语言;
allEntries:true表示清除value中的全部缓存,默认为false
beforeInvocation:是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存
//清除掉指定key的缓存
@CacheEvict(value="andCache",key="#user.userId + \'findById\'")
public void modifyUserRole(SystemUser user) {
System.out.println("hello andCache delete"+user.getUserId());
}
//清除掉全部缓存
@CacheEvict(value="andCache",allEntries=true)
public final void setReservedUsers(String[] reservedUsers) {
System.out.println("hello andCache deleteall");
}
三、注解@CachePut的基本使用
你需要知道的知识点:
1.此注解是针对方法的,也是缓存数据的注解,基本功能与@Cacheable一样,不过不同的是此注解无论如何都会执行实际方法,而@Cacheable一旦检测到有缓存,则不执行实际方法。
2.参数
value:缓存的名称,在 spring 配置文件中定义,必须指定至少一个;
key:缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合;
condition:缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存;
3.一般使用点
更新缓存,一般将此注解放在更新方法上,这样就能实时更新缓存数据,需要注意的是key和value的取值,因为是依靠这两个参数确定到某个缓存更新。
好了,此次三个注解已经介绍完毕,在以后的基本使用中应该也没啥问题,我们将在下一章介绍缓存注解与框架的配置,以及缓存数据的存储方面的知识!
@Configuration
public class RedisCacheConfig {
@Bean
public KeyGenerator simpleKeyGenerator() {
return (o, method, objects) -> {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(o.getClass().getSimpleName());
stringBuilder.append(".");
stringBuilder.append(method.getName());
stringBuilder.append("[");
for (Object obj : objects) {
stringBuilder.append(obj.toString());
}
stringBuilder.append("]");
return stringBuilder.toString();
};
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
return new RedisCacheManager(
RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory),
this.getRedisCacheConfigurationWithTtl(600), // 默认策略,未配置的 key 会使用这个
this.getRedisCacheConfigurationMap() // 指定 key 策略
);
}
private Map<String, RedisCacheConfiguration> getRedisCacheConfigurationMap() {
Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
redisCacheConfigurationMap.put("UserInfoList", this.getRedisCacheConfigurationWithTtl(3000));
redisCacheConfigurationMap.put("UserInfoListAnother", this.getRedisCacheConfigurationWithTtl(18000));
return redisCacheConfigurationMap;
}
private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(Integer seconds) {
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
redisCacheConfiguration = redisCacheConfiguration.serializeValuesWith(
RedisSerializationContext
.SerializationPair
.fromSerializer(jackson2JsonRedisSerializer)
).entryTtl(Duration.ofSeconds(seconds));
return redisCacheConfiguration;
}
}
==================================================================================================================================================================
SpringBoot配置RedisTemplate和RedisCacheManager
https://blog.csdn.net/eric520zenobia/article/details/103286011/
展开
import com.dxy.cache.pojo.Dept;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.*;
import java.net.UnknownHostException;
@Configuration
public class MyRedisConfig {
/**
* 往容器中添加RedisTemplate对象,设置序列化方式
* @param redisConnectionFactory
* @return
* @throws UnknownHostException
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<String, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(valueSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(valueSerializer());
template.afterPropertiesSet();
return template;
}
/**
* 往容器中添加RedisCacheManager容器,并设置序列化方式
* @param redisConnectionFactory
* @return
*/
@Bean
public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer()));
redisCacheConfiguration.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);
}
// private final CacheProperties cacheProperties;
//
// MyRedisConfig(CacheProperties cacheProperties) {
// this.cacheProperties = cacheProperties;
// }
/**
* 往容器中添加org.springframework.data.redis.cache.RedisCacheConfiguration 对象
* 目的是为了向默认的RedisCacheManager中设置属性,当然包括序列化
* 如果仅仅是为了设置序列化方式可以和上面的配置二选一
* 在RedisCacheManager内部使用org.springframework.data.redis.cache.RedisCacheConfiguration去保存相关配置信息
*/
// @Bean
// public org.springframework.data.redis.cache.RedisCacheConfiguration determineConfiguration() {
// CacheProperties.Redis redisProperties = this.cacheProperties.getRedis();
// org.springframework.data.redis.cache.RedisCacheConfiguration config = org.springframework.data.redis.cache.RedisCacheConfiguration
// .defaultCacheConfig();
// config = config.serializeValuesWith(RedisSerializationContext.SerializationPair
// .fromSerializer(valueSerializer()));
// if (redisProperties.getTimeToLive() != null) {
// config = config.entryTtl(redisProperties.getTimeToLive());
// }
// if (redisProperties.getKeyPrefix() != null) {
// config = config.prefixKeysWith(redisProperties.getKeyPrefix());
// }if (!redisProperties.isCacheNullValues()) {
// config = config.disableCachingNullValues();
// }
// if (!redisProperties.isUseKeyPrefix()) {
// config = config.disableKeyPrefix();
// }
// return config;
// }
/**
* 使用Jackson序列化器
* @return
*/
private RedisSerializer<Object> valueSerializer() {
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
/**
* 这一句必须要,作用是序列化时将对象全类名一起保存下来
* 设置之后的序列化结果如下:
* [
* "com.dxy.cache.pojo.Dept",
* {
* "pid": 1,
* "code": "11",
* "name": "财务部1"
* }
* ]
*
* 不设置的话,序列化结果如下,将无法反序列化
*
* {
* "pid": 1,
* "code": "11",
* "name": "财务部1"
* }
*/
// objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
//因为上面那句代码已经被标记成作废,因此用下面这个方法代替,仅仅测试了一下,不知道是否完全正确
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(objectMapper);
return serializer;
}
}
import com.dxy.cache.mapper.DeptMapper;
import com.dxy.cache.pojo.Dept;
import com.dxy.cache.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
@Service("deptService")
public class DeptServiceImpl implements DeptService {
@Autowired
private DeptMapper deptMapper;
/**
* @Cacheable 包含的属性
* cacheNames/value:缓存的名字
* key:支持SpEL表达式,#id=#a0=#p0=#root.args[0] 都是取出第一个参数的意思
* #result :可以取出返回值
* keyGenerator:key生成器,和key属性二选一
* cacheManager:缓存管理器,获取缓存的
* condition:条件,满足条件时才缓存,如#id>0
* unless:除非,当表达式为true时不缓存 ,如:#result == null
* sync:是否使用异步模式
*
* 缓存原理:
* 1、自动配置 CacheAutoConfiguration
* 2、所有的缓存配置类
* org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration
* org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration
* org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration
* org.springframework.boot.autoconfigure.cache.HazelcastCacheConfiguration
* org.springframework.boot.autoconfigure.cache.InfinispanCacheConfiguration
* org.springframework.boot.autoconfigure.cache.CouchbaseCacheConfiguration
* org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration
* org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration
* org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration
* org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration
* 3、默认情况下,上面的配置类那个生效
* 4、给容器中创建了一个CacheManager:ConcurrentMapCacheManager
* 5、上述CacheManager可以创建和获取ConcurrentMapCache类型的缓存组件,它的作用是将数据存到ConcurrentMap中
*
* 运行流程:
* 1、方法运行之前,去查询Cache(缓存组件),通过配置的cacheNames去查询,第一次会先创建该组件
* 2、去Cache中查询缓存,通过key,默认key是方法参数,使用SimpleKeyGenerator生成key
* SimpleKeyGenerator生成key的策略:
* 如果没有参数:key = new SimpleKey()
* 如果有一个参数:key = 参数的值
* 如果有多个参数:key = new SimpleKey(多个参数)
* 3、如果没有查询到缓存则调用目标方法
* 4、将目标方法的返回值保存到缓存中
*
*
* @param id
* @return
*/
@Cacheable(cacheNames="dept",key="#p0")
@Override
public Dept getDeptById(Long id) {
System.out.println("发起数据库请求");
return deptMapper.getDeptById(id);
}
@Override
public int addDept(Dept dept) {
return deptMapper.addDept(dept);
}
@Override
/**
* 更新缓存,在方法之后执行
*/
@CachePut(cacheNames="dept",key="#p0.pid")
public Dept updateDeptById(Dept dept) {
deptMapper.updateDeptById(dept);
return dept;
}
@Override
/**
* 删除缓存
* 默认在方法执行之后进行缓存删除
* 属性:
* allEntries=true 时表示删除cacheNames标识的缓存下的所有缓存,默认是false
* beforeInvocation=true 时表示在目标方法执行之前删除缓存,默认false
*/
@CacheEvict(cacheNames = "dept",key = "#p0")
public int delDept(Long id) {
return deptMapper.delDept(id);
}
@Override
/**
* 组合@Cacheable、@CachePut、@CacheEvict的一个全面的注解
*/
@Caching(
cacheable = {
@Cacheable(cacheNames = "dept",key="#code")
},
put = {
@CachePut(cacheNames = "dept",key="#result.pid"),
@CachePut(cacheNames = "dept",key="#result.name"),
}
// ,
// evict = {
// @CacheEvict(cacheNames = "dept",key="#code")
// }
)
public Dept getDeptByCode(String code) {
return deptMapper.getDeptByCode(code);
}
}
—————————————————————————————————————————————————————————————————————————————————————————————————
原文链接:https://blog.csdn.net/eric520zenobia/article/details/103286011/