SpringBoot中mybatis整合redis做二级缓存(含源码)
1.添加依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <version>2.1.3.RELEASE</version> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.1</version> </dependency>
2.添加redis配置
spring:
redis:
host: 127.0.0.1
port: 6379
database: 1
jedis:
pool:
max-idle: 8
min-idle: 1
max-active: 8
max-wait: -1
3.添加ApplicationContextHolder类(这里不能通过@Autowire的方式引用redisTemplate,因为RedisCache并不是Spring容器里的bean。所以我们需要手动地去调用容器的getBean方法来拿到这个bean,那么这样,我们就需要引入ApplicationContextHolder这个类。)
package com.llltony.springboot.utils; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; @Component public class ApplicationContextHolder implements ApplicationContextAware { private static ApplicationContext applicationContext; /** * 实现ApplicationContextAware接口的context注入函数, 将其存入静态变量. */ public void setApplicationContext(ApplicationContext applicationContext) { ApplicationContextHolder.applicationContext = applicationContext; // NOSONAR } /** * 取得存储在静态变量中的ApplicationContext. */ public static ApplicationContext getApplicationContext() { checkApplicationContext(); return applicationContext; } /** * 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型. */ @SuppressWarnings("unchecked") public static <T> T getBean(String name) { checkApplicationContext(); return (T) applicationContext.getBean(name); } /** * 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型. */ @SuppressWarnings("unchecked") public static <T> T getBean(Class<T> clazz) { checkApplicationContext(); return (T) applicationContext.getBeansOfType(clazz); } /** * 清除applicationContext静态变量. */ public static void cleanApplicationContext() { applicationContext = null; } private static void checkApplicationContext() { if (applicationContext == null) { throw new IllegalStateException("applicaitonContext未注入,请在applicationContext.xml中定义SpringContextHolder"); } } }
4.添加序列化工具类
package com.llltony.springboot.utils; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class SerializeUtils { //private static final Logger logger = Logger.getLogger(SerializeUtils.class); private static void close(ObjectOutputStream objectOutputStream, ByteArrayOutputStream byteArrayOutputStream) { try { if (byteArrayOutputStream != null) { byteArrayOutputStream.close(); } if (objectOutputStream != null) { objectOutputStream.close(); } } catch (Exception e) { e.printStackTrace(); //logger.error("关闭IO资源异常[" + e.getMessage() + "]", e); } } private static void close(ObjectInputStream objectInputStream, ByteArrayInputStream byteArrayInputStream) { try { if (objectInputStream != null) { objectInputStream.close(); } if (byteArrayInputStream != null) { byteArrayInputStream.close(); } } catch (Exception e) { e.printStackTrace(); //logger.error("关闭IO资源异常[" + e.getMessage() + "]", e); } } public static byte[] serialize(Object object) { ObjectOutputStream objectOutputStream = null; ByteArrayOutputStream byteArrayOutputStream = null; try { byteArrayOutputStream = new ByteArrayOutputStream(); objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeObject(object); byte[] bytes = byteArrayOutputStream.toByteArray(); return bytes; } catch (Exception e) { e.printStackTrace(); //logger.error("序列化对象异常[" + e.getMessage() + "]", e); } finally { close(objectOutputStream, byteArrayOutputStream); } return null; } @SuppressWarnings("unchecked") public static <T> T unserialize(byte[] bytes) { if (bytes == null) return null; ByteArrayInputStream byteArrayInputStream = null; ObjectInputStream objectInputStream = null; try { byteArrayInputStream = new ByteArrayInputStream(bytes); objectInputStream = new ObjectInputStream(byteArrayInputStream); return (T) objectInputStream.readObject(); } catch (Exception e) { e.printStackTrace(); } finally { close(objectInputStream, byteArrayInputStream); } return null; } }
5.添加RedisCache类
package com.llltony.springboot.utils; import org.apache.ibatis.cache.Cache; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class RedisCache implements Cache { private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); private final String id; // cache instance id private RedisTemplate redisTemplate; private static final long EXPIRE_TIME_IN_MINUTES = 30; // redis过期时间 public RedisCache(String id) { if (id == null) { throw new IllegalArgumentException("Cache instances require an ID"); } this.id = id; } @Override public String getId() { return id; } /** * Put query result to redis * * @param key * @param value */ @Override @SuppressWarnings("unchecked") public void putObject(Object key, Object value) { RedisTemplate redisTemplate = getRedisTemplate(); ValueOperations opsForValue = redisTemplate.opsForValue(); opsForValue.set(key, value, EXPIRE_TIME_IN_MINUTES, TimeUnit.MINUTES); System.out.println("放入结果到缓存"); } /** * Get cached query result from redis * * @param key * @return */ @Override public Object getObject(Object key) { RedisTemplate redisTemplate = getRedisTemplate(); ValueOperations opsForValue = redisTemplate.opsForValue(); System.out.println("获取结果从缓存"); return opsForValue.get(key); } /** * Remove cached query result from redis * * @param key * @return */ @Override @SuppressWarnings("unchecked") public Object removeObject(Object key) { RedisTemplate redisTemplate = getRedisTemplate(); redisTemplate.delete(key); System.out.println("从缓存删除了"); return null; } /** * Clears this cache instance */ @Override public void clear() { RedisTemplate redisTemplate = getRedisTemplate(); redisTemplate.execute((RedisCallback) connection -> { connection.flushDb(); return null; }); System.out.println("清空缓存"); } @Override public int getSize() { return 0; } @Override public ReadWriteLock getReadWriteLock() { return readWriteLock; } private RedisTemplate getRedisTemplate() { if (redisTemplate == null) { redisTemplate = ApplicationContextHolder.getBean("redisTemplate"); } return redisTemplate; } }
6.mybatis-config.xml中开启二级缓存
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <setting name="mapUnderscoreToCamelCase" value="true"/> <!-- 打开延迟加载 --> <setting name="lazyLoadingEnabled" value="true"/> <!-- 将积极加载改为消极加载即按需要加载 --> <setting name="aggressiveLazyLoading" value="false"/> <!-- 开启二级缓存(默认也是开启的)--> <setting name="cacheEnabled" value="true"/> </settings> </configuration>
7.在mapper中开启缓存
<cache type="com.llltony.springboot.utils.RedisCache" />
8.实际效果
7.资源
源码:https://github.com/CodingPandaLLL/tsl.git
源码压缩包地址:https://codeload.github.com/CodingPandaLLL/tsl/zip/refs/tags/1.0.1
注意:bean必须序列化否则存不到redis中报错