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);
}
}