springboot+ehcache+redis实现性能更好的一级和二级缓存

本文介绍了一种结合Redis和Ehcache的双层缓存方案,利用Ehcache的高速内存缓存作为一级缓存,Redis作为二级缓存,详细阐述了配置与实现流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

由于目前redis是主流的缓存框架。所以我们平常在想到缓存的时候,首先想到的就是用redis作为缓存。但是redis也不是完全就没有缺点。其中一个确定就是我们用redis作为缓存服务器。缓存的内容都是存在在redis服务器上。那么我们需要查询,时候从redis中命中缓存并且取出并加载到我们的内存中使用这个过程是需要网络IO的。那么就是要有性能损耗的。而且单纯用redis作为缓存服务器则要考虑缓存穿透等极端问题。这个时候我们不妨把reids作为二级缓存。而把我们在单体应用中的ehcache作为一级缓存(ehcache是java编写的基于内存的缓存方案特点就是性能快,基于内存操作)。接下来我实现了一个基于ehcache和redis实现的二级缓存。大致的逻辑如下:

客户端---首先从一级缓存查---一级缓存命中则返回----一级缓存未命中-----到二级缓存查---二级缓存命中则返回----二级缓存未命中--则查询数据库---查询结果为空返回--查询结果不为空缓存一级和二级缓存中去。

代码如下:经过本人测试通过

1:引入一级缓存的相关依赖和配置文件:

<dependency>
   <groupId>net.sf.ehcache</groupId>
   <artifactId>ehcache</artifactId>
</dependency>
<?xml version="1.0" encoding="UTF-8"?>
<ehcache updateCheck="false" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		 xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
	<!-- 指定一个文件目录,当EhCache把数据写到硬盘上时,将把数据写到这个文件目录下 -->
	<diskStore path="java.io.tmpdir" />
	<!-- name 缓存名称 maxElementsInMemory 设置基于内存缓存可存放对象的最大数目 maxElementsOnDisk
		在磁盘上缓存的element的最大数目,默认值为0,表示不限制。 eternal 如果为true,表示对象永远不会过期 ,false 时为timeToldleSeconds和timeToLiveSeconds
		timeToldleSeconds 对象允许处于空闲的最长时间 timeToLiveSeconds 即缓存自创建日期起能够存活的最长时间 overflowToDisk
		如果内存中数据超过内存限制,是否要缓存到磁盘上 true表示存在硬盘上 memoryStoreEvictionPolicy 缓存对象清除策略 以下为三种策略
		FIFO 先进先出 LFU 一直以来最少被使用策略 LRU 最近最少被使用 -->
	<!-- 默认缓存 -->
	<defaultCache maxElementsInMemory="1000" eternal="false"
				  timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true"
				  diskSpoolBufferSizeMB="30" maxElementsOnDisk="10000000"
				  diskPersistent="true" diskExpiryThreadIntervalSeconds="120"
				  memoryStoreEvictionPolicy="LRU">
	</defaultCache>

	<!-- demo缓存 --><!-- name="userCache" 对应我们在 @CacheConfig(cacheNames={"userCache"}) !!!!! -->
	<!--Ehcache底层也是用Map集合实现的 -->
	<cache name="userCache" maxElementsInMemory="1000" eternal="false"
		   timeToIdleSeconds="10" timeToLiveSeconds="10" overflowToDisk="true"
		   diskSpoolBufferSizeMB="30" maxElementsOnDisk="10000000"
		   diskPersistent="false" diskExpiryThreadIntervalSeconds="120"
		   memoryStoreEvictionPolicy="LRU">  <!-- LRU缓存策略 -->
	</cache>
</ehcache>

2:配置缓存的类型和加载配置文件的位置:

#ehcache配置
spring.cache.ehcache.config=classpath:ehcache.xml
spring.cache.type = ehcache

3:开启缓存开关并且初始化缓存管理器

@SpringBootApplication
@EnableCaching
@MapperScan(basePackages = "com.example.demo.dao")
public class DemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}

}

package com.example.demo.config;

import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.ClassPathResource;

@Configuration
public class EhcacheConfig {

    /*
    * ehcache 主要的管理器
    */
    @Primary
    @Bean(name = "ehCacheCacheManager")
    public EhCacheCacheManager ehCacheCacheManager(EhCacheManagerFactoryBean factoryBean){
        return new EhCacheCacheManager (factoryBean.getObject ());
    }

    /*
     * 据shared与否的设置,Spring分别通过CacheManager.create()或new CacheManager()方式来创建一个ehcache基地.
     */
    @Bean
    public EhCacheManagerFactoryBean ehCacheManagerFactoryBean(){
        EhCacheManagerFactoryBean cacheManagerFactoryBean = new EhCacheManagerFactoryBean ();
        cacheManagerFactoryBean.setConfigLocation (new ClassPathResource("/ehcache.xml"));
        cacheManagerFactoryBean.setShared (true);
        return cacheManagerFactoryBean;
    }

}

4:测试:控制层代码省略。就是接受请求跳转一下就行了

 @Override
    @Cacheable(value = CACHE_NAME ,key = "#p0")
    public User testEhcache(String name) {
       System.out.println("没有缓存则执行如下代码");
       return new User(1,"zhagnsan",10);
    }

5:添加二级缓存:

(1) 添加二级缓存redis相关的依赖和配置

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-redis</artifactId>
   <!--<version>2.1.3.RELEASE</version>-->
</dependency>
spring.redis.database = 0
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=foobared
#最大活跃连接数
spring.redis.pool.max-active=10
#最大空闲连接数
spring.redis.pool.max-idle=5
#最长等待时间。-1不等待
spring.redis.pool.max-wait=-1
#最小空闲数0
spring.redis.pool.min-idle=0
#连接超时时间
spring.redis.timeout = 300

(2)初始化缓存管理器。指定缓存序列化策略。

package com.example.demo.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.PropertySource;
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.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.lang.reflect.Method;
import java.time.Duration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

@Configuration
@PropertySource(value = "classpath:redis.properties")
public class RedisConfig {

    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private int port;

    @Value("${spring.redis.timeout}")
    private int timeout;

    @Value("${spring.redis.pool.max-wait}")
    private long maxWaitMillis;

    @Value("${spring.redis.password}")
    private String password;

    @Value("${spring.redis.pool.max-idle}")
    private int maxIdle;

    @Value("${spring.redis.pool.max-active}")
    private int maxTotal;

    @Value("${spring.redis.pool.max-wait}")
    private long maxWait;

    @Value("${spring.redis.pool.min-idle}")
    private int minIdle;

    /**
     * 配置缓存管理器
     * @param redisConnectionFactory
     * @return
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        // 生成一个默认配置,通过config对象即可对缓存进行自定义配置
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        // 设置缓存的默认过期时间,也是使用Duration设置
        config = config.entryTtl(Duration.ofMinutes(1))
                // 设置 key为string序列化
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                // 设置value为json序列化
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new Jackson2JsonRedisSerializer(Object.class)))
                // 不缓存空值
                .disableCachingNullValues();

        // 设置一个初始化的缓存空间set集合
        Set<String> cacheNames = new HashSet<>();
        cacheNames.add("timeGroup");
        cacheNames.add("user");

        // 对每个缓存空间应用不同的配置
        Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
        configMap.put("timeGroup", config);
        configMap.put("user", config.entryTtl(Duration.ofSeconds(120)));

        // 使用自定义的缓存配置初始化一个cacheManager
        RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory)
                // 一定要先调用该方法设置初始化的缓存名,再初始化相关的配置
                .initialCacheNames(cacheNames)
                .withInitialCacheConfigurations(configMap)
                .build();
        return cacheManager;
    }

    //实例化redisTemplate。指定采用json作为序列化策略
    @Bean
    public RedisTemplate<String,String> redisTemplate(RedisConnectionFactory redisConnectionFactory){
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(redisConnectionFactory);
        Jackson2JsonRedisSerializer 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);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的value序列化方式采用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }

    //指定redis key的生成策略 类名称+方法名称+参数
    @Bean
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append(method.getName());
                for (Object obj : params) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }



    //配置jedisPool,访问redis服务客户端用的是jedis
    @Bean
    public JedisPool JedisPool() {
        JedisPoolConfig poolCofig = new JedisPoolConfig();
        poolCofig.setMaxIdle(maxIdle);//最大空闲连接数
        poolCofig.setMaxTotal(maxTotal);//最大连接数
        poolCofig.setMaxWaitMillis(maxWait);//最大等待时间毫秒
        JedisPool jedisPool = new JedisPool(poolCofig, host, port, timeout,password);
        return jedisPool;
    }

}

3:定义缓存操作工具类:

(1)操作ehcache

package com.example.demo.service;


import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.stereotype.Component;

@Component
public class EhcachServiceComponet {

    @Autowired
    private EhCacheCacheManager cacheCacheManager;

    //获取缓存
    public Object get(String cacheName,String key){
        Cache cache = cacheCacheManager.getCacheManager().getCache(cacheName);
        Element element = cache.get(key);
        return element == null ? null : element.getObjectValue();
    }

    //放入缓存
    public void put(String cacheName,String key,Object value){
        Cache cache = cacheCacheManager.getCacheManager().getCache(cacheName);
        Element element = new Element(key,value);
        cache.put(element);
    }

    //清空缓存
    public void evict(String cacheName,String key){
        Cache cache = cacheCacheManager.getCacheManager().getCache(cacheName);
        cache.remove(key);
    }

}

(2)定义操作redis类

package com.example.demo.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Component
public class RedisServiceComponet {

@Autowired
private RedisTemplate<String, Object> redisTemplate;
// =============================common============================

/**

 * 指定缓存失效时间

 * @param key 键

 * @param time 时间(秒)

 * @return

 */

public boolean expire(String key, long time) {

    try {

        if (time > 0) {

            redisTemplate.expire(key, time, TimeUnit.SECONDS);

        }

        return true;

    } catch (Exception e) {

        e.printStackTrace();

        return false;

    }

}


/**

 * 根据key 获取过期时间

 * @param key 键 不能为null

 * @return 时间(秒) 返回0代表为永久有效

 */

public long getExpire(String key) {

    return redisTemplate.getExpire(key, TimeUnit.SECONDS);

}


/**

 * 判断key是否存在

 * @param key 键

 * @return true 存在 false不存在

 */

public boolean hasKey(String key) {

    try {

        return redisTemplate.hasKey(key);

    } catch (Exception e) {

        e.printStackTrace();

        return false;

    }

}


/**

 * 删除缓存

 * @param key 可以传一个值 或多个

 */

@SuppressWarnings("unchecked")

public void del(String... key) {

    if (key != null && key.length > 0) {

        if (key.length == 1) {

            redisTemplate.delete(key[0]);

        } else {

            redisTemplate.delete(CollectionUtils.arrayToList(key));

        }

    }

}


// ============================String=============================

/**

 * 普通缓存获取

 * @param key 键

 * @return 值

 */

public Object get(String key) {

    return key == null ? null : redisTemplate.opsForValue().get(key);

}


/**

 * 普通缓存放入

 * @param key 键

 * @param value 值

 * @return true成功 false失败

 */

public boolean set(String key, Object value) {

    try {

        redisTemplate.opsForValue().set(key, value);

        return true;

    } catch (Exception e) {

        e.printStackTrace();

        return false;

    }


}


/**

 * 普通缓存放入并设置时间

 * @param key 键

 * @param value 值

 * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期

 * @return true成功 false 失败

 */

public boolean set(String key, Object value, long time) {

    try {

        if (time > 0) {

            redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);

        } else {

            set(key, value);

        }

        return true;

    } catch (Exception e) {

        e.printStackTrace();

        return false;

    }

}


/**

 * 递增

 * @param key 键

 * @param delta 要增加几(大于0)

 * @return

 */

public long incr(String key, long delta) {

    if (delta < 0) {

        throw new RuntimeException("递增因子必须大于0");

    }

    return redisTemplate.opsForValue().increment(key, delta);

}


/**

 * 递减

 * @param key 键

 * @param delta 要减少几(小于0)

 * @return

 */

public long decr(String key, long delta) {

    if (delta < 0) {

        throw new RuntimeException("递减因子必须大于0");

    }

    return redisTemplate.opsForValue().increment(key, -delta);

}


// ================================Map=================================

/**

 * HashGet

 * @param key 键 不能为null

 * @param item 项 不能为null

 * @return 值

 */

public Object hget(String key, String item) {

    return redisTemplate.opsForHash().get(key, item);

}


/**

 * 获取hashKey对应的所有键值

 * @param key 键

 * @return 对应的多个键值

 */

public Map<Object, Object> hmget(String key) {

    return redisTemplate.opsForHash().entries(key);

}


/**

 * HashSet

 * @param key 键

 * @param map 对应多个键值

 * @return true 成功 false 失败

 */

public boolean hmset(String key, Map<String, Object> map) {

    try {

        redisTemplate.opsForHash().putAll(key, map);

        return true;

    } catch (Exception e) {

        e.printStackTrace();

        return false;

    }

}


/**

 * HashSet 并设置时间

 * @param key 键

 * @param map 对应多个键值

 * @param time 时间(秒)

 * @return true成功 false失败

 */

public boolean hmset(String key, Map<String, Object> map, long time) {

    try {

        redisTemplate.opsForHash().putAll(key, map);

        if (time > 0) {

            expire(key, time);

        }

        return true;

    } catch (Exception e) {

        e.printStackTrace();

        return false;

    }

}


/**

 * 向一张hash表中放入数据,如果不存在将创建

 * @param key 键

 * @param item 项

 * @param value 值

 * @return true 成功 false失败

 */

public boolean hset(String key, String item, Object value) {

    try {

        redisTemplate.opsForHash().put(key, item, value);

        return true;

    } catch (Exception e) {

        e.printStackTrace();

        return false;

    }

}


/**

 * 向一张hash表中放入数据,如果不存在将创建

 * @param key 键

 * @param item 项

 * @param value 值

 * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间

 * @return true 成功 false失败

 */

public boolean hset(String key, String item, Object value, long time) {

    try {

        redisTemplate.opsForHash().put(key, item, value);

        if (time > 0) {

            expire(key, time);

        }

        return true;

    } catch (Exception e) {

        e.printStackTrace();

        return false;

    }

}


/**

 * 删除hash表中的值

 * @param key 键 不能为null

 * @param item 项 可以使多个 不能为null

 */

public void hdel(String key, Object... item) {

    redisTemplate.opsForHash().delete(key, item);

}


/**

 * 判断hash表中是否有该项的值

 * @param key 键 不能为null

 * @param item 项 不能为null

 * @return true 存在 false不存在

 */

public boolean hHasKey(String key, String item) {

    return redisTemplate.opsForHash().hasKey(key, item);

}


/**

 * hash递增 如果不存在,就会创建一个 并把新增后的值返回

 * @param key 键

 * @param item 项

 * @param by 要增加几(大于0)

 * @return

 */

public double hincr(String key, String item, double by) {

    return redisTemplate.opsForHash().increment(key, item, by);

}


/**

 * hash递减

 * @param key 键

 * @param item 项

 * @param by 要减少记(小于0)

 * @return

 */

public double hdecr(String key, String item, double by) {

    return redisTemplate.opsForHash().increment(key, item, -by);

}


// ============================set=============================

/**

 * 根据key获取Set中的所有值

 * @param key 键

 * @return

 */

public Set<Object> sGet(String key) {

    try {

        return redisTemplate.opsForSet().members(key);

    } catch (Exception e) {

        e.printStackTrace();

        return null;

    }

}


/**

 * 根据value从一个set中查询,是否存在

 * @param key 键

 * @param value 值

 * @return true 存在 false不存在

 */

public boolean sHasKey(String key, Object value) {

    try {

        return redisTemplate.opsForSet().isMember(key, value);

    } catch (Exception e) {

        e.printStackTrace();

        return false;

    }

}


/**

 * 将数据放入set缓存

 * @param key 键

 * @param values 值 可以是多个

 * @return 成功个数

 */

public long sSet(String key, Object... values) {

    try {

        return redisTemplate.opsForSet().add(key, values);

    } catch (Exception e) {

        e.printStackTrace();

        return 0;

    }

}


/**

 * 将set数据放入缓存

 * @param key 键

 * @param time 时间(秒)

 * @param values 值 可以是多个

 * @return 成功个数

 */

public long sSetAndTime(String key, long time, Object... values) {

    try {

        Long count = redisTemplate.opsForSet().add(key, values);

        if (time > 0)

            expire(key, time);

        return count;

    } catch (Exception e) {

        e.printStackTrace();

        return 0;

    }

}


/**

 * 获取set缓存的长度

 * @param key 键

 * @return

 */

public long sGetSetSize(String key) {

    try {

        return redisTemplate.opsForSet().size(key);

    } catch (Exception e) {

        e.printStackTrace();

        return 0;

    }

}


/**

 * 移除值为value的

 * @param key 键

 * @param values 值 可以是多个

 * @return 移除的个数

 */

public long setRemove(String key, Object... values) {

    try {

        Long count = redisTemplate.opsForSet().remove(key, values);

        return count;

    } catch (Exception e) {

        e.printStackTrace();

        return 0;

    }

}

// ===============================list=================================


/**

 * 获取list缓存的内容

 * @param key 键

 * @param start 开始

 * @param end 结束 0 到 -1代表所有值

 * @return

 */

public List<Object> lGet(String key, long start, long end) {

    try {

        return redisTemplate.opsForList().range(key, start, end);

    } catch (Exception e) {

        e.printStackTrace();

        return null;

    }

}


/**

 * 获取list缓存的长度

 * @param key 键

 * @return

 */

public long lGetListSize(String key) {

    try {

        return redisTemplate.opsForList().size(key);

    } catch (Exception e) {

        e.printStackTrace();

        return 0;

    }

}


/**

 * 通过索引 获取list中的值

 * @param key 键

 * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推

 * @return

 */

public Object lGetIndex(String key, long index) {

    try {

        return redisTemplate.opsForList().index(key, index);

    } catch (Exception e) {

        e.printStackTrace();

        return null;

    }

}


/**

 * 将list放入缓存

 * @param key 键

 * @param value 值

 * @return

 */

public boolean lSet(String key, Object value) {

    try {

        redisTemplate.opsForList().rightPush(key, value);

        return true;

    } catch (Exception e) {

        e.printStackTrace();

        return false;

    }

}


/**

 * 将list放入缓存

 * @param key 键

 * @param value 值

 * @param time 时间(秒)

 * @return

 */

public boolean lSet(String key, Object value, long time) {

    try {

        redisTemplate.opsForList().rightPush(key, value);

        if (time > 0)

            expire(key, time);

        return true;

    } catch (Exception e) {

        e.printStackTrace();

        return false;

    }

}


/**

 * 将list放入缓存

 * @param key 键

 * @param value 值

 * @return

 */

public boolean lSet(String key, List<Object> value) {

    try {

        redisTemplate.opsForList().rightPushAll(key, value);

        return true;

    } catch (Exception e) {

        e.printStackTrace();

        return false;

    }

}


/**

 * 将list放入缓存

 *

 * @param key 键

 * @param value 值

 * @param time 时间(秒)

 * @return

 */

public boolean lSet(String key, List<Object> value, long time) {

    try {

        redisTemplate.opsForList().rightPushAll(key, value);

        if (time > 0)

            expire(key, time);

        return true;

    } catch (Exception e) {

        e.printStackTrace();

        return false;

    }

}


/**

 * 根据索引修改list中的某条数据

 * @param key 键

 * @param index 索引

 * @param value 值

 * @return

 */

public boolean lUpdateIndex(String key, long index, Object value) {

    try {

        redisTemplate.opsForList().set(key, index, value);

        return true;

    } catch (Exception e) {

        e.printStackTrace();

        return false;

    }

}


/**

 * 移除N个值为value

 * @param key 键

 * @param count 移除多少个

 * @param value 值

 * @return 移除的个数

 */

public long lRemove(String key, long count, Object value) {

    try {

        Long remove = redisTemplate.opsForList().remove(key, count, value);

        return remove;

    } catch (Exception e) {

        e.printStackTrace();

        return 0;

    }

}


}

最后添加业务层操作代码如下:

package com.example.demo.service.imple;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.example.demo.dao.UserDao;
import com.example.demo.entity.User;
import com.example.demo.service.EhcachServiceComponet;
import com.example.demo.service.RedisServiceComponet;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

@Service
public class UserServiceImpl implements UserService {

    public static final String CACHE_NAME = "userCache";

    @Autowired
    private UserDao userDao;

    @Autowired
    private EhcachServiceComponet ehcachServiceComponet;

    @Autowired
    private RedisServiceComponet redisServiceComponet;

    @Autowired
    @Qualifier("redisTemplate")
    private RedisTemplate redisTemplate;

    @Override
    public User getUser(String name) {
        //首先从本地ehcach中取数据
        User user  = (User)ehcachServiceComponet.get(CACHE_NAME,name);
        if(!StringUtils.isEmpty(user)){
            //如果存在,则直接返回,如果不存在则从redis中取数据
            System.out.println("我是从ehcache中查出来的: ======= >>>" + name);
            return user;
        }
        //如果redis有则直接返回,如果redis 没有则查询数据
        String cacheStr = redisServiceComponet.get(name)==null ? null : JSON.toJSONString(redisServiceComponet.get(name));
        //如果数据库查询不为空则,则讲返回结果缓存到本地和redis缓存中
        if(!StringUtils.isEmpty(cacheStr)){
            //reids不为空从reids中拿
            JSONObject object = JSON.parseObject(cacheStr);
            user = JSONObject.toJavaObject(object, User.class);
            System.out.println("我是从redis中查出来的: ======= >>>" + name);
            return user;
        }
        //redis也为空则从数据库中获取
        User dbInfo = userDao.getUser(name);
        if(dbInfo != null){
            //缓存到redis中
            redisServiceComponet.set(name,dbInfo,20);
//            redisTemplate.opsForValue().set(name, JSONObject.toJSONString(dbInfo),10);
            System.out.println("我是从mysql中查出来的: ======= >>>" + name);
            //缓存到本地ehcache中
//                    productEcacheService.saveLocalCache(dbInfo);
            ehcachServiceComponet.put(CACHE_NAME,name,dbInfo);
            return dbInfo;
        }else{
            return null;
        }
    }

    @Override
    @Cacheable(value = CACHE_NAME ,key = "#p0")
    public User testEhcache(String name) {
       System.out.println("没有缓存则执行如下代码");
       return new User(1,"zhagnsan",10);
    }
}

dao层:

package com.example.demo.dao;

import com.example.demo.entity.User;

public interface UserDao {

    User getUser(String name);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.dao.UserDao">
    <select id="getUser" resultType="user" parameterType="java.lang.String">
        select * from user where name = #{name}
    </select>

</mapper>

其它相关的数据库,json,等相关的依赖如下:

<?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>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.5.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>demo</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>2.0.1</version>
		</dependency>
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.8</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context-support</artifactId>
			<version>5.1.6.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>net.sf.ehcache</groupId>
			<artifactId>ehcache</artifactId>
		</dependency>
		<dependency>
			<groupId>redis.clients</groupId>
			<artifactId>jedis</artifactId>
			<version>2.9.0</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
			<!--<version>2.1.3.RELEASE</version>-->
		</dependency>

		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>2.9.8</version>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-core</artifactId>
			<version>2.9.8</version>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-annotations</artifactId>
			<version>2.9.8</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

下一步将把redis做成集群。以适应更加高可用,高性能分布式缓存的方案。学海无涯。自律前行。

<think>嗯,用户问的是在Spring Boot 3.3.7中替换EhCache的方案。首先,我需要回忆一下Spring Boot支持的缓存方案,尤其是现在EhCache是否还适用,或者有没有更好的替代品。 根据之前的引用,用户提到了Spring Boot 3的更新,比如Swagger改用springdoc-openapi,Redis配置路径的变化。这可能意味着在Spring Boot 3.x中,一些旧有的库可能不再被推荐使用,或者需要新的配置方式。EhCache作为传统的缓存方案,可能在Spring Boot 3.3.7中仍然可用,但用户可能想寻找其他替代方案,比如更轻量级或者性能更好的库。 首先,我应该考虑Spring Boot官方推荐的缓存解决方案。Caffeine是一个高性能的Java缓存库,被广泛认可,而且Spring Boot原生支持,配置起来应该比较方便。另外,Redis作为分布式缓存也是一个常见选择,特别是当应用需要跨多个实例共享缓存时。用户之前提到过Spring Boot 3集成Redis的配置变化,所以可能需要提醒他们注意新的配置路径,比如spring.data.redis而不是spring.redis。 然后,HazelcastInfinispan这两个内存数据网格,它们不仅提供缓存功能,还支持分布式计算,适合需要分布式缓存的场景。不过对于简单的应用来说,可能有点复杂。用户是否需要分布式缓存呢?可能需要根据他们的具体应用场景来决定。 还有,检查EhCache的最新版本是否兼容Spring Boot 3.3.7。EhCache 3.x版本应该支持,但用户可能因为某些原因想换掉,比如性能、特性或者维护性。这时候需要确认EhCache在Spring Boot 3中的配置是否有变化,或者是否有已知的问题。 另外,用户可能关心如何迁移现有的EhCache配置到新的缓存库。比如,从EhCache切换到Caffeine,可能需要调整依赖配置文件,但Spring的缓存抽象层应该可以简化这个过程,因为只需更换底层的实现,而业务代码中的缓存注解(如@Cacheable)可能不需要改动。 需要提醒用户,无论选择哪种方案,都要注意依赖的兼容性。比如,Caffeine需要添加对应的starter,而Redis则需要配置连接信息。同时,配置文件的路径变化,比如从spring.redis改为spring.data.redis,这点在之前的引用中有提到,需要特别指出。 可能还需要比较各个方案的优缺点,比如本地缓存与分布式缓存的区别,内存使用情况,性能指标等。例如,Caffeine在本地缓存中性能优异,适合单实例应用,而Redis适合分布式环境,但会带来网络开销。 最后,生成相关问题的时候,应该围绕用户可能进一步询问的点,比如具体如何配置Caffeine,或者如何选择适合自己场景的缓存方案,以及性能调优的方法等。</think>在 Spring Boot 3.3.7 中替代 EhCache 的解决方案可通过以下步骤实现: ### 一、推荐替代方案 1. **Caffeine** - **高性能本地缓存**:基于 Java 8 设计,支持异步加载自动过期策略,性能优于 EhCache 3.x - **Spring Boot 原生支持**:直接通过 `spring-boot-starter-cache` 集成 - 配置示例: ```yaml spring: cache: type: caffeine caffeine: spec: maximumSize=500,expireAfterWrite=10m ``` 2. **Redis 分布式缓存** - **跨实例共享缓存**:适用于微服务架构,支持高可用性持久化[^2] - 依赖配置: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> ``` - 新版配置层级调整为 `spring.data.redis`[^2] 3. **Hazelcast** - **内存数据网格**:支持分布式缓存计算,提供 CP/AP 模式切换 - 集成步骤: ```java @Bean public HazelcastInstance hazelcastInstance() { return Hazelcast.newHazelcastInstance(); } ``` 4. **Infinispan** - **开源分布式缓存**:兼容 JCache 标准,支持事务查询功能 - Maven 依赖: ```xml <dependency> <groupId>org.infinispan</groupId> <artifactId>infinispan-spring-boot3-starter</artifactId> <version>15.0.0</version> </dependency> ``` ### 二、迁移注意事项 1. **代码层兼容性** - 保持 `@Cacheable` / `@CacheEvict` 注解无需修改 - 仅需更换底层实现配置文件 2. **性能对比** | 方案 | 吞吐量 (ops/ms) | 内存占用 | 适用场景 | |------------|----------------|---------|------------------| | Caffeine | 1500+ | 低 | 单机高频读操作 | | Redis | 200-500 | 中 | 分布式环境 | | Hazelcast | 800-1200 | 高 | 数据网格计算 | 3. **依赖冲突检查** 移除 EhCache 相关依赖: ```xml <!-- 删除原有依赖 --> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> </dependency> ``` ### 三、操作验证示例(以 Caffeine 为例) 1. 启用缓存注解: ```java @SpringBootApplication @EnableCaching public class Application { /*...*/ } ``` 2. 业务层使用: ```java @Service public class ProductService { @Cacheable(value = "products", key = "#id") public Product getProductById(Long id) { // DB查询逻辑 } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值