1.內存緩存的使用
1.1 自定義HashMap實現
如題所說,自己實現key-value的map,使用定時器管理緩存的清空之類的, 比較麻煩,不建議使用
1.2 使用現有的內存緩存框架
spring引入緩存框架是非常容易的, 詳細使用方式可以參考Spring Boot 緩存配置
在之前做的項目中使用的是 caffenie 框架, 該框架爲 google guava 框架內緩存模塊的升級版本, 提高了性能, 增加了更多的功能, 使用方式可以參考Caffeine 緩存框架
簡單介紹一下使用方式
1.2.1 通用配置方式
在application.yml文件中增加配置
cache:
type: caffeine
caffeine:
spec: maximumSize=%d,expireAfterWrite=%ds
在SpringBoot啓動類中增加註解
@EnableCaching
@SpringBootApplication
class Application {
companion object {
@JvmStatic
fun main(args: Array<String>) {
SpringApplication.run(Application::class.java, *args)
}
}
}
在需要被緩存的方法上增加註解
/* 對方法增加緩存 */
@Override
@Cacheable
public List<ConfigAllSite> listConfigAllSite() {
return configMapper.listConfigAllSite();
}
/* 對類中的所有方法增加緩存 */
@Component
@CacheConfig(cacheNames = CacheNames.ConfigRefer)
@Cacheable
public class ConfigRepositoryImpl implements ConfigRepository {
//````
}
其中CacheConfig指定了該緩存塊的名稱,使用其他註解進行更新和清除操作時會用到
1.3 使用redis
內存緩存框架有一定的侷限性, 包括分佈式緩存以及大數據量緩存的恢復和重建等
redis很好的解決了這些問題, 而且性能也並沒有差太多, 比數據庫快許多
使用redis的方式
1.3.1 添加依賴
spring-data-redis是spring爲redis操作開發的一套框架, 對於redis的簡單使用引用這個包就十分方便了
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
在application.yml中聲明
// 主從模式, 配置host,port,password
spring:
redis:
host: xxx
password: xxx
port: xxx
// cluster模式, 配置集羣所有節點host, port
spring:
redis:
cluster:
nodes: 127.0.0.1:6379,127.0.0.1:6380...
password: xxx
port: xxx
我們可能引入一些序列化工具, 因爲spring 默認使用 jvmserialiser 來序列化對象, 存入redis的對象必須繼承 serialer 接口, 非常不方便,
spring 自帶 jackson, 我們可以使用jackson, 也可以使用 alibaba 的 fastjson
使用 jackson
@Configuration
public class RedisConfiguration {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer();
redisTemplate.setDefaultSerializer(serializer);
return redisTemplate;
}
}
使用 fastjson (文檔), 首先需要引用依賴
<!--https://github.com/alibaba/fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.38</version>
</dependency>
然後配置, 基本同上
@Configuration
public class RedisConfiguration {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
GenericFastJsonRedisSerializer serializer = new GenericFastJsonRedisSerializer();
redisTemplate.setDefaultSerializer(serializer);
return redisTemplate;
}
}
fastjson其他使用方法可以參考文檔
2 使用多個緩存時發生衝突的問題
有時可能我們的工程既需要使用redis, 又因爲對速度的需求需要使用內存緩存, 這時就需要增加配置來適配多個manager的問題.
一般情況下, 引用了 spring-data-redis 模塊後, 在不進行額外配置的情況下spring會默認使用redis的cachemanager作爲全局的cachemanager. 也就是@Cacheable相關注解也會使用redis作爲管理器, 這是與我們的需求違背的.
首先還是需要增加依賴和相關配置, 如前幾點所說, 這裏不做敘述
接下來需要增加一個新的 cachemManager
@Configuration
@EnableCaching
public class SimpleCaffeineCacheManager {
// CacheManager bean name
public static final String CACHE_MANAGER_BEAN_NAME = "caffeineCacheManager";
// 最多存儲的緩存條數
private static final int DEFAULT_MAX_SIZE = 20000;
// 默認過期時間
private static final int DEFAULT_TTL_SECONDS = 300;
@Bean
@Qualifier(CACHE_MANAGER_BEAN_NAME)
public CacheManager caffeineCacheManager() {
CaffeineCacheManager manager = new CaffeineCacheManager();
String spec = String.format("maximumSize=%d,expireAfterWrite=%ds", DEFAULT_MAX_SIZE, DEFAULT_TTL_SECONDS);
CaffeineSpec caffeineSpec = CaffeineSpec.parse(spec);
manager.setCaffeineSpec(caffeineSpec);
return manager;
}
}
然後在其他地方使用@Cacaheable相關主解時, 需要指定CacheManager
@CacheConfig(cacheNames = CacheNames.ConfigRefer, cacheManager = SimpleCaffeineCacheManager.CACHE_MANAGER_BEAN_NAME)
@Cacheable
public class ConfigRepositoryImpl implements ConfigRepository {
//...
}
這樣就解決了多個CacheManager的衝突問題
備註
今天使用時候碰到了個bug, 使用@Cacheable標註類, 在類的方法中使用mybatis的mapper的查詢結果作爲返回值, 會出現說返回值是單個無法轉換爲list的bug.
查詢了一下發現是由於key的生成策略導致的:
默認的key是通過KeyGenerator生成的,其默認策略如下:
1. 如果方法沒有參數,則使用0作爲key;
2. 如果只有一個參數的話則使用該參數作爲key;
3. 如果參數多於一個則使用所有參數的hashcode作爲key;
所以添加了一個keygenerator來避免這個問題
用 Cachemanager 繼承 CachingConfigurerSupport 類, 並實現keygenerator方法
public class SimpleCaffeineCacheManager extends CachingConfigurerSupport {
//...
@Bean
public KeyGenerator keyGenerator() {
return (target, method, objects) -> {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getSimpleName() + " :");
sb.append(method.getName() + "=-=");
for (Object object : objects) {
if (object == null) sb.append("|-|-|");
else sb.append(object.toString());
}
return sb.toString();
};
}
}