Spring的@EnableCaching注解
https://segmentfault.com/a/1190000011069802
@EnableCaching注解是spring framework中的注解驅動的緩存管理功能。自spring版本3.1起加入了該注解。如果你使用了這個注解,那么你就不需要在XML文件中配置cache manager了。
當你在配置類(@Configuration)上使用@EnableCaching注解時,會觸發一個post processor,這會掃描每一個spring bean,查看是否已經存在注解對應的緩存。如果找到了,就會自動創建一個代理攔截方法調用,使用緩存的bean執行處理。
如果你對緩存感興趣並想了解更多,請閱讀spring caching. 本文會幫助你了解如何使用@EnableCaching注解。
接下來的例子演示了@EnableCaching的用法。在代碼中,我緩存了Book類找那個的方法。
代碼
//Book.java import org.springframework.cache.annotation.Cacheable; public class Book { @Cacheable(value = { "sampleCache" }) public String getBook(int id) { System.out.println("Method executed.."); if (id == 1) { return "Book 1"; } else { return "Book 2"; } } } //CachingConfig.java import java.util.Arrays; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.concurrent.ConcurrentMapCache; import org.springframework.cache.support.SimpleCacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @EnableCaching public class CachingConfig { @Bean public Book book() { return new Book(); } @Bean public CacheManager cacheManager() {
//1. //SimpleCacheManager cacheManager = new SimpleCacheManager(); //cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("sampleCache")));
//2.
ConcurrentMapCacheManager
cacheManager = new
ConcurrentMapCacheManager
();
return cacheManager;
} }
上面的java config和下面的xml配置文件是等效的:
<beans> <cache:annotation-driven/> <bean id="book" class="Book"/> <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager"> <property name="caches"> <set> <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean"> <property name="name" value="sampleCache"/> </bean> </set> </property> </bean> </beans>
//EnableCachingAnnotationExample.java import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class EnableCachingAnnotationExample { public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(CachingConfig.class); ctx.refresh(); Book book = ctx.getBean(Book.class); // calling getBook method first time. System.out.println(book.getBook(1)); // calling getBook method second time. This time, method will not // execute. System.out.println(book.getBook(1)); // calling getBook method third time with different value. System.out.println(book.getBook(2)); ctx.close(); } }
會得到如下的輸出
Method executed.. Book 1 Book 1 Method executed.. Book 2
________________________________________________________________________________________________________________________________________________________________________
Spring緩存注解@Cacheable,@CacheEvict,@CachePut的使用
https://www.cnblogs.com/jiangbotao233/p/10190375.html
SpringCache之 @CachePut
https://blog.csdn.net/mrliar17/article/details/88253702
大部分公司在開發中運用緩存是一件很平常的事,主要用來緩解數據庫的訪問壓力,其使用方法和原理都類似於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/