一:目录结构

redis二级缓存和mybatis二级缓存 redis二级缓存实现_缓存

二:分而治之

redis和caffeine有各自的bean目录 自定义实现的bean(xxxxCache,Manager,Configuration,CacheResolve)等可以放在这里
redis和caffeine有各自的配置目录,分开配置自己的bean,序列化等
分而治之,回归一统:单独配置好Redis,单独配置好Caffeine,最后交给合并缓存(CaffeineRedis)进行数据操作

三:application.yml配置文件

spring:
#自定义管理缓存
  custom-manager: 
    isOpen-custom-cache: true #是否开启管理缓存
    #选择开启的缓存类型 
    #caffeine:开启Caffeine一级缓存
    #redis:开启Redis一级缓存
    #caffeine-redis:开启一二级缓存,caffeine为一级缓存,redis为二级缓存 
    cache-type: caffeine-redis 
    #cache-type: redis
  redis:
    database: 0
    host: *******
    port: 6379
    password: 123456
#    jedis:
#      pool:
#        #连接池最大连接数
#        max-active: 8
#        #连接池最大阻塞等待时间
#        max-wait: -1
#        #连接池最大空闲连接数
#        max-idle: 500
#        #连接池最小空闲连接数
#        min-idle: 0
    lettuce:
      pool:
        max-wait: -1
        max-idle: 8
        min-idle: 0
        max-active: 8
      shutdown-timeout: 0

**

四:Redis

   CustomeRedisCacheWriter类

这个类对象是RedisCache的一个属性 当操作redisCache时,redisCache会对redisCacheWriter对象操作

public class CustomeRedisCacheWriter implements RedisCacheWriter {
    private final RedisConnectionFactory connectionFactory;
    private final Duration sleepTime;
    public CustomeRedisCacheWriter(RedisConnectionFactory connectionFactory){
        this(connectionFactory, Duration.ZERO);
    }

    public CustomeRedisCacheWriter(RedisConnectionFactory connectionFactory,Duration sleepTime){
        this.connectionFactory=connectionFactory;
        this.sleepTime=sleepTime;
    }
    @Override
    public void put(String name, byte[] key, byte[] value, @Nullable Duration ttl) {
        Assert.notNull(name, "Name must not be null!");
        Assert.notNull(key, "Key must not be null!");
        Assert.notNull(value, "Value must not be null!");
        this.execute(name, (connection) -> {
            if (shouldExpireWithin(ttl)) {
                connection.set(key, value, Expiration.from(ttl.toMillis(), TimeUnit.MILLISECONDS), RedisStringCommands.SetOption.upsert());
            } else {
                connection.set(key, value);
            }

            return "OK";
        });
    }
    @Override
    public byte[] get(String name, byte[] key) {
        Assert.notNull(name, "Name must not be null!");
        Assert.notNull(key, "Key must not be null!");
        return (byte[])this.execute(name, (connection) -> {
            return connection.get(key);
        });
    }
    @Override
    public byte[] putIfAbsent(String name, byte[] key, byte[] value, @Nullable Duration ttl) {
        Assert.notNull(name, "Name must not be null!");
        Assert.notNull(key, "Key must not be null!");
        Assert.notNull(value, "Value must not be null!");
        return (byte[])this.execute(name, (connection) -> {
            if (this.isLockingCacheWriter()) {
                this.doLock(name, connection);
            }

            Object var6;
            try {
                if (!connection.setNX(key, value)) {
                    byte[] var10 = connection.get(key);
                    return var10;
                }

                if (shouldExpireWithin(ttl)) {
                    connection.pExpire(key, ttl.toMillis());
                }

                var6 = null;
            } finally {
                if (this.isLockingCacheWriter()) {
                    this.doUnlock(name, connection);
                }

            }

            return (byte[])var6;
        });
    }
    @Override
    public void remove(String name, byte[] key) {
        Assert.notNull(name, "Name must not be null!");
        Assert.notNull(key, "Key must not be null!");
        this.execute(name, (connection) -> {
            return connection.del(new byte[][]{key});
        });
    }

    @Override
    public void clean(String name, byte[] pattern) {
        Assert.notNull(name, "Name must not be null!");
        Assert.notNull(pattern, "Pattern must not be null!");
        this.execute(name, (connection) -> {
            boolean wasLocked = false;

            try {
                if (this.isLockingCacheWriter()) {
                    this.doLock(name, connection);
                    wasLocked = true;
                }

                byte[][] keys = (byte[][])((Set) Optional.ofNullable(connection.keys(pattern)).orElse(Collections.emptySet())).toArray(new byte[0][]);
                if (keys.length > 0) {
                    connection.del(keys);
                }
            } finally {
                if (wasLocked && this.isLockingCacheWriter()) {
                    this.doUnlock(name, connection);
                }

            }

            return "OK";
        });
    }

    void lock(String name) {
        this.execute(name, (connection) -> {
            return this.doLock(name, connection);
        });
    }

    void unlock(String name) {
        this.executeLockFree((connection) -> {
            this.doUnlock(name, connection);
        });
    }

    private Boolean doLock(String name, RedisConnection connection) {
        return connection.setNX(createCacheLockKey(name), new byte[0]);
    }

    private Long doUnlock(String name, RedisConnection connection) {
        return connection.del(new byte[][]{createCacheLockKey(name)});
    }

    boolean doCheckLock(String name, RedisConnection connection) {
        return connection.exists(createCacheLockKey(name));
    }

    private boolean isLockingCacheWriter() {
        return !this.sleepTime.isZero() && !this.sleepTime.isNegative();
    }

    private <T> T execute(String name, Function<RedisConnection, T> callback) {
        RedisConnection connection = this.connectionFactory.getConnection();
        Object var4;
        try {
            this.checkAndPotentiallyWaitUntilUnlocked(name, connection);
            var4 = callback.apply(connection);
        } finally {
            connection.close();
        }

        return (T) var4;
    }

    private void executeLockFree(Consumer<RedisConnection> callback) {
        RedisConnection connection = this.connectionFactory.getConnection();

        try {
            callback.accept(connection);
        } finally {
            connection.close();
        }

    }

    private void checkAndPotentiallyWaitUntilUnlocked(String name, RedisConnection connection) {
        if (this.isLockingCacheWriter()) {
            try {
                while(this.doCheckLock(name, connection)) {
                    Thread.sleep(this.sleepTime.toMillis());
                }

            } catch (InterruptedException var4) {
                Thread.currentThread().interrupt();
                throw new PessimisticLockingFailureException(String.format("Interrupted while waiting to unlock cache %s", name), var4);
            }
        }
    }

    private static boolean shouldExpireWithin(@Nullable Duration ttl) {
        return ttl != null && !ttl.isZero() && !ttl.isNegative();
    }

    private static byte[] createCacheLockKey(String name) {
        return (name + "~lock").getBytes(StandardCharsets.UTF_8);
    }




}

   RedisConfig类

package www.gl.com.cache.cache.redis.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
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.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import www.gl.com.cache.cache.redis.bean.CustomeRedisCacheWriter;
import java.time.Duration;

@Configuration
@ConditionalOnProperty(prefix = "spring.custom-manager",name="isOpen-custom-cache",havingValue = "true")
@ConditionalOnExpression(value = "'${spring.custom-manager.cache-type}'== 'redis'|| '${spring.custom-manager.cache-type}'=='caffeine-redis'")
@EnableConfigurationProperties(RedisProperties.class)
public class RedisConfig{
    static{
        System.out.println("RedisConfig被创建");
    }

    @Bean
    public CustomeRedisCacheWriter customeRedisCacheWriter(RedisConnectionFactory connectionFactory){
        return new CustomeRedisCacheWriter(connectionFactory);
    }

    @Bean
    public RedisCacheConfiguration redisCacheConfiguration(){
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
        return setSerializer(redisCacheConfiguration);
    }
    @Bean
    public RedisCacheManager redisCacheManager(CustomeRedisCacheWriter customeRedisCacheWriter, RedisCacheConfiguration redisCacheConfiguration) {
        System.out.println("redisCacheManager被创建");
        redisCacheConfiguration = redisCacheConfiguration
                .entryTtl(Duration.ofSeconds(1000))//有效期
                .disableCachingNullValues();//不缓存空值
        RedisCacheManager redisCacheManager = new RedisCacheManager(customeRedisCacheWriter,redisCacheConfiguration);
        return  redisCacheManager;
    }

    private RedisCacheConfiguration setSerializer(RedisCacheConfiguration redisCacheConfiguration) {
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        StringRedisSerializer stringRedisSerializer =new StringRedisSerializer();
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        redisCacheConfiguration = redisCacheConfiguration.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(stringRedisSerializer));
        redisCacheConfiguration = redisCacheConfiguration.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer));
        return redisCacheConfiguration;
    }

}

五:Caffeine

没用到

package www.gl.com.cache.cache.caffeine.bean;

import java.time.Duration;

public class CustomCaffeineCacheConfiguration{

    private final Duration ttl;
    private final Integer maximumSize;
    private final Long expireAfterAccess;

    public CustomCaffeineCacheConfiguration(Duration ttl, Integer maximumSize, Long expireAfterAccess){
        this.ttl=ttl;
        this.maximumSize=maximumSize;
        this.expireAfterAccess=expireAfterAccess;
    }

    public static CustomCaffeineCacheConfiguration  createDefaultCustomCacheConfiguration(){

        return new CustomCaffeineCacheConfiguration(Duration.ZERO,500,600L);
    }

}

  CaffeineConfig类

package www.gl.com.cache.cache.caffeine.config;

import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnProperty(prefix = "spring.custom-manager",name="isOpen-custom-cache",havingValue = "true")
@ConditionalOnExpression(value = "'${spring.custom-manager.cache-type}'== 'caffeine'|| '${spring.custom-manager.cache-type}'=='caffeine-redis'")
public class CaffeineConfig {
    @Bean
    public CaffeineCacheManager caffeineCacheManager(){
        System.out.println("caffeineCacheManager被创建");
        return new CaffeineCacheManager();
    }
}

六:CaffeineRedis

  CustomCachingConfigurer类

package www.gl.com.cache.cache.CaffeineRedis.bean;

import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurer;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.CacheResolver;
import org.springframework.cache.interceptor.KeyGenerator;

public class CustomCachingConfigurer implements CachingConfigurer {

    private CacheManager cacheManager;

    private CacheResolver cacheResolver;

    private CacheErrorHandler cacheErrorHandler;

    private KeyGenerator keyGenerator;

    @Override
    public CacheManager cacheManager() {
        return this.cacheManager;
    }

    @Override
    public CacheResolver cacheResolver() {
        return this.cacheResolver;
    }

    @Override
    public KeyGenerator keyGenerator() {
        return this.keyGenerator;
    }

    @Override
    public CacheErrorHandler errorHandler() {
        return this.cacheErrorHandler;
    }

    public CacheManager getCacheManager() {
        return cacheManager;
    }

    public void setCacheManager(CacheManager cacheManager) {
        this.cacheManager = cacheManager;
    }

    public CacheResolver getCacheResolver() {
        return cacheResolver;
    }

    public void setCacheResolver(CacheResolver cacheResolver) {
        this.cacheResolver = cacheResolver;
    }

    public CacheErrorHandler getCacheErrorHandler() {
        return cacheErrorHandler;
    }

    public void setCacheErrorHandler(CacheErrorHandler cacheErrorHandler) {
        this.cacheErrorHandler = cacheErrorHandler;
    }

    public KeyGenerator getKeyGenerator() {
        return keyGenerator;
    }

    public void setKeyGenerator(KeyGenerator keyGenerator) {
        this.keyGenerator = keyGenerator;
    }
}

  CustomCaffeineRedisCacheResolver类

package www.gl.com.cache.cache.CaffeineRedis.bean;

import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
import org.springframework.cache.interceptor.CacheResolver;
import org.springframework.cache.interceptor.SimpleCacheResolver;

import java.util.Collection;

public class CustomCaffeineRedisCacheResolver extends SimpleCacheResolver implements CacheResolver  {
    public CustomCaffeineRedisCacheResolver(CacheManager cacheManager) {
        super(cacheManager);
    }
}

  CustomCaffeineRedisCacheContainer类

package www.gl.com.cache.cache.CaffeineRedis.bean;


import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.cache.Cache;

public class CustomCaffeineRedisCacheContainer {

    private  volatile static Map<String, Cache> cacheContainer=new ConcurrentHashMap<>();

    public Map<String, Cache> getCacheContainer() {
        return cacheContainer;
    }
}

  CustomKeyGenerator类 作用:设置缓存key(自己的定义的,需要另行设置)

package www.gl.com.cache.cache.CaffeineRedis.bean;


import org.springframework.cache.interceptor.KeyGenerator;

import java.lang.reflect.Method;
import org.springframework.util.ObjectUtils;
import www.gl.com.cache.model.common.UserModel;

public class CustomKeyGenerator implements KeyGenerator {

    //Object 调用方法的对象
    //Method 调用的方法
    //objects 参数集合
    
    @Override
    public Object generate(Object o, Method method, Object... objects) {
        String key ="";
        if(!ObjectUtils.isEmpty(objects)){
            if(objects[0].getClass().isAssignableFrom(UserModel.class)){
                UserModel userModel = (UserModel)objects[0];
                key = userModel.getUserName();
            }
        }else{
            throw new IllegalArgumentException("key设置失败");
        }
        return key;
    }
}

  CustomCaffeineRedisCache类

package www.gl.com.cache.cache.CaffeineRedis.bean;

import org.apache.ibatis.cache.CacheException;
import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;
import org.springframework.util.ObjectUtils;

import java.util.concurrent.Callable;

public class CustomCaffeineRedisCache implements Cache {
    private final String name;
    private final static Object lock = new Object();
    private final Cache caffeineCache;
    private final Cache redisCache;
    private volatile Object  value;

    public CustomCaffeineRedisCache(String name,Cache caffeineCache,Cache redisCache){
        this.name = name;
        this.caffeineCache = caffeineCache;
        this.redisCache = redisCache;
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public Object getNativeCache() {
        return this;
    }

    @Override
    public ValueWrapper get(Object key) {
        if(!ObjectUtils.isEmpty(caffeineCache)&&!ObjectUtils.isEmpty(redisCache)){
            //一级缓存获取值
            value = caffeineCache.get(key);
            //如果一级缓存没有该值,降低redis访问压力
            if(ObjectUtils.isEmpty(value)){
                synchronized (lock){
                    value = caffeineCache.get(key);
                    if(ObjectUtils.isEmpty(value)){
                        value = redisCache.get(key);
                        caffeineCache.put(key,value);
                    }
                }
            }
        }else if(!ObjectUtils.isEmpty(caffeineCache)){
                value = caffeineCache.get(key);

        }else if(!ObjectUtils.isEmpty(redisCache)){
            value = redisCache.get(key);
        }else{
            throw new CacheException("Cache is  null");
        }
        return (ValueWrapper) value;
    }

    @Override
    public <T> T get(Object key, Class<T> aClass) {
        if(!ObjectUtils.isEmpty(caffeineCache)&&!ObjectUtils.isEmpty(redisCache)){
            //一级缓存获取值
            value = caffeineCache.get(key,aClass);
            //如果一级缓存没有该值,降低redis访问压力
            if(ObjectUtils.isEmpty(value)){
                synchronized (lock){
                    value = caffeineCache.get(key);
                    if(ObjectUtils.isEmpty(value)){
                        value = redisCache.get(key,aClass);
                        caffeineCache.put(key,value);
                    }
                }
            }
        }else{
            if(!ObjectUtils.isEmpty(caffeineCache)){
                value = caffeineCache.get(key);
            }else{
                value = redisCache.get(key);
            }
        }
        return (T)value;
    }

    @Override
    public synchronized  <T> T get(Object key, Callable<T> valueLoader) {
        if(!ObjectUtils.isEmpty(caffeineCache)&&!ObjectUtils.isEmpty(redisCache)){
            //一级缓存获取值
            value = caffeineCache.get(key);
            //如果一级缓存没有该值,降低redis访问压力
            if(ObjectUtils.isEmpty(value)){
                value = redisCache.get(key,valueLoader);
                redisCache.put(key,value);
                caffeineCache.put(key,value);
                return (T) value;
            }else{
                return (T) ((ValueWrapper)value).get();
            }
        }else{
            if(!ObjectUtils.isEmpty(caffeineCache)){
                value = caffeineCache.get(key,valueLoader);
                caffeineCache.put(key,value);
                return (T) value;
            }else{
                value = redisCache.get(key,valueLoader);
                redisCache.put(key,value);
                return (T)value;
            }
        }
    }

    @Override
    public void put(Object key, Object value) {
        if(!ObjectUtils.isEmpty(caffeineCache)&&!ObjectUtils.isEmpty(redisCache)){
            caffeineCache.put(key,value);
            redisCache.put(key,value);
        }else if(!ObjectUtils.isEmpty(caffeineCache)){
            caffeineCache.put(key,value);
        }else if(!ObjectUtils.isEmpty(redisCache)){
            redisCache.put(key,value);
        }

    }

    @Override
    public ValueWrapper putIfAbsent(Object key, Object value) {
        caffeineCache.put(key,value);
        redisCache.put(key,value);
        return new SimpleValueWrapper(value);
    }

    @Override
    public void evict(Object key) {
        if(!ObjectUtils.isEmpty(caffeineCache)&&!ObjectUtils.isEmpty(redisCache)){
            //先删除二级缓存
            redisCache.evict(key);
            caffeineCache.evict(key);
        }else if(!ObjectUtils.isEmpty(caffeineCache)){
            caffeineCache.evict(key);
        }else if(!ObjectUtils.isEmpty(redisCache)){
            redisCache.evict(key);
        }
    }

    @Override
    public boolean evictIfPresent(Object key) {
        if(!ObjectUtils.isEmpty(caffeineCache)&&!ObjectUtils.isEmpty(redisCache)){
            redisCache.evictIfPresent(key);
            return caffeineCache.evictIfPresent(key);
        }else if(!ObjectUtils.isEmpty(caffeineCache)){
            return caffeineCache.evictIfPresent(key);
        }else if(!ObjectUtils.isEmpty(redisCache)){
            return redisCache.evictIfPresent(key);
        }else {
           throw new CacheException("无可用缓存");
        }
    }

    @Override
    public void clear() {
        if(!ObjectUtils.isEmpty(caffeineCache)&&!ObjectUtils.isEmpty(redisCache)){
            redisCache.clear();
            caffeineCache.clear();
        }else if(!ObjectUtils.isEmpty(caffeineCache)){
            caffeineCache.clear();
        }else if(!ObjectUtils.isEmpty(redisCache)){
            redisCache.clear();
        }
    }

    @Override
    public boolean invalidate() {
        if(!ObjectUtils.isEmpty(caffeineCache)&&!ObjectUtils.isEmpty(redisCache)){
            redisCache.invalidate();
            return  caffeineCache.invalidate();
        }else if(!ObjectUtils.isEmpty(caffeineCache)){
            return  caffeineCache.invalidate();
        }else if(!ObjectUtils.isEmpty(redisCache)){
            return redisCache.invalidate();
        }

        return false;
    }
}

  CustomCaffeineRedisManager类

package www.gl.com.cache.cache.CaffeineRedis.bean;

import java.util.stream.Collectors;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;

import java.util.Collection;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.util.ObjectUtils;

public class CustomCaffeineRedisManager implements CacheManager {
    private final CaffeineCacheManager caffeineCacheManager;
    private final RedisCacheManager redisCacheManager;
    private final static CustomCaffeineRedisCacheContainer customCaffeineRedisCacheContainer=new CustomCaffeineRedisCacheContainer();
    public CustomCaffeineRedisManager(CaffeineCacheManager caffeineCacheManager,RedisCacheManager redisCacheManager){
        this.caffeineCacheManager = caffeineCacheManager;
        this.redisCacheManager = redisCacheManager;
    }

    @Override
    public Cache getCache(String name) {
        Cache cache = this.customCaffeineRedisCacheContainer.getCacheContainer().get(name);
        if (cache == null) {
            synchronized (this.customCaffeineRedisCacheContainer) {
                cache = this.customCaffeineRedisCacheContainer.getCacheContainer().get(name);
                if (cache == null) {
                    cache = createCache(name);
                    this.customCaffeineRedisCacheContainer.getCacheContainer().put(name,cache);
                }
            }
        }
        return cache;
    }

    public Cache createCache(String name){
        if(!ObjectUtils.isEmpty(caffeineCacheManager)&&!ObjectUtils.isEmpty(redisCacheManager)){
            return new CustomCaffeineRedisCache(name,caffeineCacheManager.getCache(name),redisCacheManager.getCache(name));
        } else if(ObjectUtils.isEmpty(caffeineCacheManager)){
            return new CustomCaffeineRedisCache(name,null,redisCacheManager.getCache(name));
        }else if(ObjectUtils.isEmpty(redisCacheManager)){
            return new CustomCaffeineRedisCache(name,caffeineCacheManager.getCache(name),null);
        }else{
            return new CustomCaffeineRedisCache(name,null,null);
        }
    }

    @Override
    public Collection<String> getCacheNames() {
        return customCaffeineRedisCacheContainer
            .getCacheContainer()
            .entrySet()
            .stream()
            .collect(Collectors.mapping(item->item.getKey(),Collectors.toList()));
    }
}

  CaffeineRedisConfig类

package www.gl.com.cache.cache.CaffeineRedis.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import www.gl.com.cache.cache.CaffeineRedis.bean.CustomCachingConfigurer;
import www.gl.com.cache.cache.CaffeineRedis.bean.CustomCaffeineRedisCacheResolver;
import www.gl.com.cache.cache.CaffeineRedis.bean.CustomCaffeineRedisManager;
import www.gl.com.cache.cache.CaffeineRedis.bean.CustomKeyGenerator;

@Configuration
@ConditionalOnProperty(prefix = "spring.custom-manager",name="isOpen-custom-cache",havingValue = "true")
@EnableCaching
public class CaffeineRedisConfig{
    @Bean
    public CustomKeyGenerator customKeyGenerator() {
        return new CustomKeyGenerator();
    }

    //选择使用redisCacheManager还是CaffeineCacheManager
    @Bean
    public CustomCaffeineRedisCacheResolver customCaffeineRedisCacheResolver(@Qualifier(value = "customCaffeineRedisManager") CustomCaffeineRedisManager cacheManager){
      System.out.println("打印manager");
        return new CustomCaffeineRedisCacheResolver(cacheManager);
    }

//  @Bean
//  public CustomCaffeineRedisCacheResolver customCaffeineRedisCacheResolver(@Qualifier(value = "redisCacheManager") RedisCacheManager cacheManager){
//    System.out.println("打印manager");
//    return new CustomCaffeineRedisCacheResolver(cacheManager);
//  }

    @Bean("customCaffeineRedisManager")

    public CustomCaffeineRedisManager customCaffeineRedisManager(   @Autowired(
        required = false
    )RedisCacheManager redisCacheManager,   @Autowired(
        required = false
    ) CaffeineCacheManager caffeineCacheManager){
      System.out.println("创建manager");
       return new CustomCaffeineRedisManager(caffeineCacheManager,redisCacheManager);
    }

    @Bean
    public CustomCachingConfigurer customCachingConfigurer(CustomCaffeineRedisCacheResolver customCaffeineRedisCacheResolver,
                                                           CustomKeyGenerator customKeyGenerator){
      CustomCachingConfigurer customCachingConfigurer =  new CustomCachingConfigurer();
      //设置默认的CacheManager
      customCachingConfigurer.setCacheManager(new CaffeineCacheManager());
      customCachingConfigurer.setCacheResolver(customCaffeineRedisCacheResolver);
      customCachingConfigurer.setKeyGenerator(customKeyGenerator);
      return customCachingConfigurer;
    }



}

七:测试

MyRedisService类

package www.gl.com.cache.service.redis;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.cache.annotation.*;
import org.springframework.stereotype.Service;
import www.gl.com.cache.model.common.UserModel;

@Service
@CacheConfig(cacheNames = "user")
public class MyRedisService {
  public UserModel getUserModelByName(String name){
    return new HashMap<Integer,String>(){
      {
        put(1,"zhangsan");
        put(2,"lisi");
        put(3,"wanger");
      }
    }.entrySet()
        .stream()
        .map(entry->{
          UserModel userModel = new UserModel();
          userModel.setUserId(entry.getKey());
          userModel.setUserName(entry.getValue());
          return userModel;
        }).collect(Collectors.toList())
        .stream()
        .filter(item->{ return name.equals(item.getUserName()); })
        .findFirst()
        .get();
  };

    //@Cacheable(key = "#root.caches[0].name")
    @Cacheable
    public UserModel get(UserModel userModel){
     return getUserModelByName(userModel.getUserName());
    }

    @CacheEvict
    public void delete(UserModel userModel){

    }
    @CachePut
    public UserModel update(UserModel userModel){
        return userModel;
    }

}

Test类

package www.gl.com.cache;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;
import www.gl.com.cache.model.common.UserModel;
import www.gl.com.cache.service.redis.MyRedisService;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * Unit test for simple App.
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class AppTest {

    @Resource
    private MyRedisService myredisService;

    /**
     * Rigorous Test :-)
     */
    @Test
    public void shouldAnswerWithTrue() throws InterruptedException {

        myredisService.get("zhangsan");
        UserModel userModel =new UserModel();
        userModel.setUserName("zhangsan");
        userModel.setUserId(2);
        myredisService.update(userModel);
        System.out.println(myredisService.get("zhangsan"));

    }


}

测试结果:

redis二级缓存和mybatis二级缓存 redis二级缓存实现_redis_02

八:依赖

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>www.gl.com</groupId>
  <artifactId>myredis02</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>myredis02</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

  <dependencies>
    <!-- https://mvnrepository.com/artifact/junit/junit -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
      <version>2.3.1.RELEASE</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
      <version>2.3.1.RELEASE</version>
    </dependency>

    <!--test-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <version>2.3.1.RELEASE</version>
      <scope>test</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-thymeleaf -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-thymeleaf</artifactId>
      <version>2.3.1.RELEASE</version>
    </dependency>
    <!-- Druid 数据连接池依赖 -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.13</version>
    </dependency>


    <!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>2.1.3</version>
    </dependency>

    <!--    mysql-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <scope>runtime</scope>
      <version>8.0.11</version>
    </dependency>

    <!-- fast json -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.44</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.2.7.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
      <version>2.3.1.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-pool2 -->
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-pool2</artifactId>
      <version>2.8.0</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-autoconfigure</artifactId>
      <version>2.3.1.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-autoconfigure</artifactId>
      <version>2.3.1.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.7.RELEASE</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.github.ben-manes.caffeine/caffeine -->
    <dependency>
      <groupId>com.github.ben-manes.caffeine</groupId>
      <artifactId>caffeine</artifactId>
      <version>2.8.5</version>
    </dependency>


  </dependencies>
  <profiles>
    <!-- 开发环境 -->
    <profile>
      <id>dev</id>
      <properties>
        <spring.profiles.active>dev</spring.profiles.active>
      </properties>
      <!-- 设置当前环境为默认环境 -->
      <activation>
        <activeByDefault>true</activeByDefault>
      </activation>
    </profile>
    <!-- 测试环境 -->
    <profile>
      <id>test</id>
      <properties>
        <spring.profiles.active>test</spring.profiles.active>
      </properties>
    </profile>
    <!-- 生产环境 -->
    <profile>
      <id>prod</id>
      <properties>
        <spring.profiles.active>prod</spring.profiles.active>
      </properties>
    </profile>
  </profiles>
</project>