SpringBoot整合Reids(配置文件方式)
说明
在 SpringBoot2.x 之后, 原来使用的 Jedis 被替换成了 lettuce
jedis: 采用直连, 多个线程操作的话, 是不安全的, 如果想要避免不安全, 使用 jedis pool 连接池 它更像BIO
lettuce: 采用netty 实例可以多个线程中进行共享, 不存在线程不安全的情况, 可以减少线程数据 它更像NIO
maven依赖
<dependencies> <!--mybatis 依赖包--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--redis lettuce--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <!--<version>2.1.10.RELEASE</version>--> <!--<version>1.7.5.RELEASE</version>--> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <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.1.0</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>redis.clients</groupId> <artifactId>jedis</artifactId> <!--<version>3.0.1</version>--> </dependency> </dependencies>
application.yml
server: port: 8080 spring: datasource: name: test url: jdbc:mysql://47.104.248.219:3306/redis?serverTimezone=UTC username: root password: 12345678.Lhx driver-class-name: com.mysql.jdbc.Driver main: allow-bean-definition-overriding: true ### Redis 相关配置 redis: host: 47.104.248.219 port: 6379 timeout: 10000 database: 0 # lettuce: # pool: # max-active: 8 # max-wait: -1 # max-idle: 8 # min-idle: 0 # pool: # max-active: 8 # max-wait: -1 # max-idle: 8 # min-idle: 0 mybatis: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #开启MyBatis的二级缓存 cache-enabled: true mapper-locations: classpath:mapping/*Mapper.xml,classpath:mapping/extend/*Mapper.xml #扫描mapper.xml # type-aliases-package: com.lvhx.pojo
redis.properties
redis.hostName=47.104.248.219 redis.port=6379 #redis.password=613613 redis.timeout=10000 redis.maxIdle=300 redis.maxTotal=1000 redis.maxWaitMillis=1000 redis.minEvictableIdleTimeMillis=300000 redis.numTestsPerEvictionRun=1024 redis.timeBetweenEvictionRunsMillis=30000 redis.testOnBorrow=true redis.testWhileIdle=true
redis.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 说明: 在 SpringBoot2.x 之后, 原来使用的 Jedis 被替换成了 lettuce jedis: 采用直连, 多个线程操作的话, 是不安全的, 如果想要避免不安全, 使用 jedis pool 连接池 它更像BIO lettuce: 采用netty 实例可以多个线程中进行共享, 不存在线程不安全的情况, 可以减少线程数据 它更像NIO --> <!-- 1. 引入properties配置文件 --> <context:property-placeholder location="classpath:redis.properties" /> <!-- 2. redis连接池配置--> <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig"> <!--最大空闲数--> <property name="maxIdle" value="${redis.maxIdle}"/> <!--连接池的最大数据库连接数 --> <property name="maxTotal" value="${redis.maxTotal}"/> <!--最大建立连接等待时间--> <property name="maxWaitMillis" value="${redis.maxWaitMillis}"/> <!--逐出连接的最小空闲时间 默认1800000毫秒(30分钟)--> <property name="minEvictableIdleTimeMillis" value="${redis.minEvictableIdleTimeMillis}"/> <!--每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3--> <property name="numTestsPerEvictionRun" value="${redis.numTestsPerEvictionRun}"/> <!--逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1--> <property name="timeBetweenEvictionRunsMillis" value="${redis.timeBetweenEvictionRunsMillis}"/> <!--是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个--> <property name="testOnBorrow" value="${redis.testOnBorrow}"/> <!--在空闲时检查有效性, 默认false --> <property name="testWhileIdle" value="${redis.testWhileIdle}"/> </bean> <!-- 3. redis连接工厂 --> <!--<bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" destroy-method="destroy">--> <bean id="connectionFactory" class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory" destroy-method="destroy"> <!--<property name="poolConfig" ref="poolConfig"/>--> <!--IP地址 --> <property name="hostName" value="${redis.hostName}"/> <!--端口号 --> <property name="port" value="${redis.port}"/> <!--如果Redis设置有密码 --> <!--<property name="password" value="${redis.password}"/>--> <!--客户端超时时间单位是毫秒 --> <property name="timeout" value="${redis.timeout}"/> </bean> <!-- 4. redis操作模板,使用该对象可以操作redis --> <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"> <property name="connectionFactory" ref="connectionFactory"/> <!--如果不配置Serializer,那么存储的时候缺省(默认)使用String,如果用User类型存储,那么会提示错误User can't cast to String!! --> <property name="keySerializer"> <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/> </property> <!--<property name="valueSerializer">--> <!--<bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>--> <!--</property>--> <!--<property name="hashKeySerializer">--> <!--<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>--> <!--</property>--> <!--<property name="hashValueSerializer">--> <!--<bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>--> <!--</property>--> <!--开启事务 --> <property name="enableTransactionSupport" value="true"/> </bean> <!-- 5.使用中间类解决RedisCache.RedisTemplate的静态注入,从而使MyBatis实现第三方缓存 --> <bean id="redisCacheTransfer" class="com.lvhx.util.RedisCacheTransfer"> <property name="redisTemplate" ref="redisTemplate"/> </bean> </beans>
start-config.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!--<context:property-placeholder ignore-unresolvable="true" location="classpath:jdbc.properties,classpath:redis.properties" />--> <context:property-placeholder ignore-unresolvable="true" location="classpath:redis.properties" /> <import resource="redis.xml"></import> </beans>
启动类
@SpringBootApplication //此处扫描的是接口 @MapperScan("com.lvhx.dao") @ComponentScan(basePackages = {"com.lvhx.*"}) @ImportResource("classpath:/start-config.xml") @EnableCaching @EnableAutoConfiguration public class SpringbootMybatisApplication { // private final Logger logger = LoggerFactory.getLogger(SpringbootMybatisApplication.class); public static void main(String[] args) { SpringApplication.run(SpringbootMybatisApplication.class, args); System.out.println("*****服务启动成功*****"); } }
ApplicationContextHolder
@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"); } } }
MybatisRedisCache
public class MybatisRedisCache implements Cache { private static final Logger log = LoggerFactory.getLogger(MybatisRedisCache.class); private String id; private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); private static RedisTemplate redisTemplate; //private static final long EXPIRE_TIME_IN_MINUTES = 30; // redis过期时间 public MybatisRedisCache() {} public MybatisRedisCache(String id) { System.out.println("===缓存ID = "+id); this.id = id; } public static void setRedisTemplate(RedisTemplate redisTemplate) { MybatisRedisCache.redisTemplate = redisTemplate; } private RedisTemplate<Object, Object> getRedisTemplate(){ return ApplicationContextHolder.getBean("redisTemplate"); } @Override public String getId() { return id; } @Override public void putObject(Object key, Object value) { RedisTemplate redisTemplate = getRedisTemplate(); try { redisTemplate.boundHashOps(getId()).put(key, value); log.info("[结果放入到缓存中: " + key + "=" + value+" ]"); }catch (Exception e){ e.printStackTrace(); log.error("[结果放入缓存失败: " + key + "=" + value+" ]"); } } @Override public Object getObject(Object key) { RedisTemplate redisTemplate = getRedisTemplate(); try { Object value = redisTemplate.boundHashOps(getId()).get(key); log.info("[从缓存中获取了: " + key + "=" + value+" ]"); return value; } catch (Exception e) { e.printStackTrace(); log.info("[从缓存获取失败: " + key + " ]"); } return null; } @Override public Object removeObject(Object key) { RedisTemplate redisTemplate = getRedisTemplate(); Object value = redisTemplate.boundHashOps(getId()).delete(key); log.info("[从缓存删除了: " + key + "=" + value+" ]"); return value; } @Override public void clear() { RedisTemplate redisTemplate = getRedisTemplate(); redisTemplate.delete(getId()); log.info("清空缓存!!!"); } @Override public int getSize() { RedisTemplate redisTemplate = getRedisTemplate(); Long size = redisTemplate.boundHashOps(getId()).size(); return size == null ? 0 : size.intValue(); } /** * 清理所有缓存 */ public void clearAll(){ RedisTemplate redisTemplate = getRedisTemplate(); Set keys = redisTemplate.keys("*"); if (!ObjectUtils.isEmpty(keys)){ redisTemplate.delete(keys); } } @Override public ReadWriteLock getReadWriteLock() { return readWriteLock; } }
RedisCacheTransfer
public class RedisCacheTransfer { @Autowired public void setRedisTemplate(RedisTemplate redisTemplate) { MybatisRedisCache.setRedisTemplate(redisTemplate); } }
RedisConfig
@Configuration public class RedisConfig { @Autowired private LettuceConnectionFactory connectionFactory; // @Autowired // private JedisConnectionFactory connectionFactory; @Bean public RedisTemplate<String, Object> redisTemplate() { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); initDomainRedisTemplate(redisTemplate, connectionFactory); return redisTemplate; } /** * 设置数据存入 redis 的序列化方式 * * @param template * @param factory */ private void initDomainRedisTemplate(RedisTemplate<String, Object> template, LettuceConnectionFactory factory) { // 定义 key 的序列化方式为 string // 需要注意这里Key使用了 StringRedisSerializer,那么Key只能是String类型的,不能为Long,Integer,否则会报错抛异常。 StringRedisSerializer redisSerializer = new StringRedisSerializer(); template.setKeySerializer(redisSerializer); // 定义 value 的序列化方式为 json @SuppressWarnings({"rawtypes", "unchecked"}) 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); template.setValueSerializer(jackson2JsonRedisSerializer); //hash结构的key和value序列化方式 template.setHashKeySerializer(jackson2JsonRedisSerializer); template.setHashValueSerializer(jackson2JsonRedisSerializer); template.setEnableTransactionSupport(true); template.setConnectionFactory(factory); } }
OrderController
@RestController @RequestMapping("/order") public class OrderController { @Autowired private OrderService orderService; /** * 查找所有order * @return */ @RequestMapping(value = {"/findAll"}, produces = {"application/json;charset=UTF-8"}, method = RequestMethod.GET) public List<Order> findAll() { List<Order> list = orderService.findAllOreder(); return list; } /** * 测试插入数据 */ @RequestMapping(value = {"/testInsert"}) public void testInsert() { Order order = new Order(); order.setId(String.valueOf(System.currentTimeMillis())); order.setName(String.valueOf(System.currentTimeMillis())); order.setMessageId(String.valueOf(System.currentTimeMillis())); orderService.testInsert(order); } /** * 清理所有缓存 */ @RequestMapping(value = {"/clearCache"}) public void clearCache() { orderService.clearCache(); } }
OrderService
@Service public class OrderService { @Autowired private OrderMapper orderMapper; @Autowired private RedisTemplate redisTemplate; @Autowired private OrderExtednMapper orderExtednMapper; public List<Order> findAllOreder(){ List<Order> allInfo = orderExtednMapper.getAllInfo(); return allInfo; } public void testInsert(Order order){ orderMapper.insertSelective(order); } public void clearCache(){ MybatisRedisCache mybatisRedisCache = new MybatisRedisCache(); mybatisRedisCache.clearAll(); } }
OrderMapper.xml
<?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.lvhx.dao.OrderMapper" > <!-- 开启基于redis的二级缓存 --> <cache type="com.lvhx.util.MybatisRedisCache"/> </mapper>
gitee地址
git@gitee.com:lvhongxu/springboot-redis.git