注:本文篇幅有点长,所以建议各位下载源码学习。(如需要请收藏!转载请声明来源,谢谢!)
代码下载:https://gitee.com/hong99/spring/issues/I1N1DF
背景
继上文《spring的缓存(cache)-本地》,本文实现集中式缓存(分布式);
redis是什么?可以干嘛?
Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理。它支持字符串、哈希表、列表、集合、有序集合,位图,hyperloglogs等数据类型。内置复制、Lua脚本、LRU收回、事务以及不同级别磁盘持久化功能,同时通过Redis Sentinel提供高可用,通过Redis Cluster提供自动分区。
参考文章:
基础教程:https://www.runoob.com/redis/redis-tutorial.html
官网:https://redis.io/
中文官网:https://www.redis.net.cn/
相关工具:
redis版本:Redis 3.2.100
redis客户端:RedisDesktopManager 0.9.3.817
spring 版本:4.x
安装windows redis
先查看电脑的是32或64位。此电脑->右键->属性
下载地址:https://github.com/MicrosoftArchive/redis/releases
安装参考这里:https://www.runoob.com/redis/redis-install.html
源码实现
redis服务
redis客户端
类图
spring data使用redis缓存,并且通过注解实现
spring_mybatis_plus_redis_cache/pom.xml
<?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">
<parent>
<artifactId>spring</artifactId>
<groupId>com.hong</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.hong</groupId>
<artifactId>spring_mybatis_plus_redis_cache</artifactId>
<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>
<logback.version>1.2.3</logback.version>
<over-slf4j.version>1.7.25</over-slf4j.version>
<spring.version>4.3.11.RELEASE</spring.version>
<commons-dbcp.version>1.4</commons-dbcp.version>
<slf4j.version>1.7.12</slf4j.version>
<org.mybatis>3.1.0</org.mybatis>
</properties>
<dependencies>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>${org.mybatis}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
<version>${org.mybatis}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.34</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<!--<dependency>-->
<!--<groupId>org.springframework</groupId>-->
<!--<artifactId>spring-jdbc</artifactId>-->
<!--<version>${spring.version}</version>-->
<!--</dependency>-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.70</version>
</dependency>
<!--引入连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.23</version>
</dependency>
<!--引入AOP-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<!--该包的主要作用是会去自动查找合适的日志记录框架进行记录-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!--引入日志-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.22</version>
<optional>true</optional>
</dependency>
<!-- 实现slf4j接口并整合 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.2</version>
</dependency>
<!--引入hibernate-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.2.0.Final</version>
</dependency>
<!--引入orm-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<!--引入redis-->
<!-- Redis客户端 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<!-- redis Spring 基于注解配置 -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.8.1.RELEASE</version>
</dependency>
</dependencies>
<!--静态资源导出问题-->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>UTF-8</encoding>
<!--开启编译调试信息的开关-->
<debug>true</debug>
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
redis.properties
#redis config
#redids地址
redis.hostname=localhost
#redis端口号
redis.port=6379
#超时时间
redis.timeout=2000
#默认db
redis.default.db=0
#连接池的最大数据库连接数
redis.maxTotal=600
#最大空闲数
redis.maxIdle=300
#逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1
redis.timeBetweenEvictionRunsMillis=30000
#逐出连接的最小空闲时间 默认1800000毫秒(30分钟)
redis.minEvictableIdleTimeMillis=30000
#是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个
redis.testOnBorrow=true
#最大建立连接等待时间
redis.maxWait=-1
#在空闲时检查有效性, 默认false
redis.testWhileIdle=true
#每次释放连接的最大数目,默认3
redis.numTestsPerEvictionRun=1024
com.hong.spring.utils.RedisCacheConfig
package com.hong.spring.utils;
import java.lang.reflect.Method;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
/**
* 通过spring管理redis缓存配置
*
* @author csh
*
*/
@Configuration
@EnableCaching
public class RedisCacheConfig extends CachingConfigurerSupport {
private volatile JedisConnectionFactory jedisConnectionFactory;
private volatile RedisTemplate<String, String> redisTemplate;
private volatile RedisCacheManager redisCacheManager;
public RedisCacheConfig() {
super();
}
/**
* 带参数的构造方法 初始化所有的成员变量
*
* @param jedisConnectionFactory
* @param redisTemplate
* @param redisCacheManager
*/
public RedisCacheConfig(JedisConnectionFactory jedisConnectionFactory, RedisTemplate<String, String> redisTemplate,
RedisCacheManager redisCacheManager) {
this.jedisConnectionFactory = jedisConnectionFactory;
this.redisTemplate = redisTemplate;
this.redisCacheManager = redisCacheManager;
}
public JedisConnectionFactory getJedisConnecionFactory() {
return jedisConnectionFactory;
}
public RedisTemplate<String, String> getRedisTemplate() {
return redisTemplate;
}
public RedisCacheManager getRedisCacheManager() {
return redisCacheManager;
}
@Bean
public KeyGenerator customKeyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... objects) {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append(method.getName());
for (Object obj : objects) {
sb.append(obj.toString());
}
return sb.toString();
}
};
}
}
其他的同上文一致。
com.hong.spring.service.UserServiceTest#findByIdTest
package com.hong.spring.service;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.log4j.Log4j2;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @Auther: csh
* @Date: 2020/8/24 11:17
* @Description:redis cache测试
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext-mybatis_plus_redis_cache.xml")
@Log4j2
public class UserServiceTest {
@Autowired
private IUserService userService;
/**
*
* 功能描述:查询
*
* @param:
* @return:
* @auther: csh
* @date: 2020/8/31 15:01
*/
@Test
public void findByIdTest() throws InterruptedException {
log.info("第一次查:"+ JSONObject.toJSONString(userService.findById(1)));
Thread.sleep(10000);
log.info("第二次查:"+ JSONObject.toJSONString(userService.findById(1)));
}
/**
*
* 功能描述:删除缓存
*
* @param:
* @return:
* @auther: csh
* @date: 2020/8/31 15:01
*/
@Test
public void deleteCache(){
userService.deleteCache(1);
}
}
结果:
15:05:51.235 [main] INFO com.hong.spring.service.impl.UserServiceImpl - 进入数据库查询
15:05:51.241 [main] DEBUG org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
15:05:51.247 [main] DEBUG org.mybatis.spring.SqlSessionUtils - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@571a9686] was not registered for synchronization because synchronization is not active
15:05:51.255 [main] DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource
15:05:51.256 [main] DEBUG org.mybatis.spring.transaction.SpringManagedTransaction - JDBC Connection [com.mysql.jdbc.JDBC4Connection@6aba5d30] will not be managed by Spring
15:05:51.259 [main] DEBUG com.hong.spring.dao.UserMapper.findById - ==> Preparing: SELECT * FROM user WHERE id = ?
15:05:51.279 [main] DEBUG com.hong.spring.dao.UserMapper.findById - ==> Parameters: 1(Integer)
15:05:51.293 [main] DEBUG com.hong.spring.dao.UserMapper.findById - <== Total: 1
15:05:51.295 [main] DEBUG com.alibaba.druid.pool.PreparedStatementPool - stmt enter cache
15:05:51.296 [main] DEBUG org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@571a9686]
15:05:51.296 [main] DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
15:05:51.302 [main] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Opening RedisConnection
15:05:51.311 [main] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Closing Redis Connection
15:05:51.325 [main] INFO com.hong.spring.service.UserServiceTest - 第一次查:{"code":0,"data":{"age":100,"id":1,"username":"333"},"total":0}
15:06:01.326 [main] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Opening RedisConnection
15:06:01.327 [main] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Closing Redis Connection
15:06:01.327 [main] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Opening RedisConnection
15:06:01.328 [main] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Closing Redis Connection
15:06:01.331 [main] INFO com.hong.spring.service.UserServiceTest - 第二次查:{"code":0,"data":{"age":100,"id":1,"username":"333"},"total":0}
可以发现第一次查询有数据库记录,第二次查询直接是从redis中获取。
com.hong.spring.service.UserServiceTest#deleteCache
结果
发现已清空。
参考文章:
https://blog.csdn.net/u010996565/article/details/79953462
测试多节点,请求redis缓存
注:配置两个tomcat分别是8082和8081来请求同一个接口,看是否共享了。
第一次请求:http://localhost:8081/user/findById/1
发现请求了数据库,并且放到了redis中。
:53:10.351 [http-nio-8081-exec-5] DEBUG org.springframework.web.servlet.DispatcherServlet - Last-Modified value for [/user/findById/1] is: -1
17:53:10.411 [http-nio-8081-exec-5] DEBUG org.springframework.cache.annotation.AnnotationCacheOperationSource - Adding cacheable method 'findById' with attribute: [Builder[public com.hong.spring.utils.DataResponse com.hong.spring.service.impl.UserServiceImpl.findById(int)] caches=[findById] | key='#id' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false']
17:53:10.512 [http-nio-8081-exec-5] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Opening RedisConnection
17:53:10.626 [http-nio-8081-exec-5] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Closing Redis Connection
17:53:10.628 [http-nio-8081-exec-5] INFO com.hong.spring.service.impl.UserServiceImpl - 进入数据库查询
17:53:10.642 [http-nio-8081-exec-5] DEBUG org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
17:53:10.655 [http-nio-8081-exec-5] DEBUG org.mybatis.spring.SqlSessionUtils - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4edba3a] was not registered for synchronization because synchronization is not active
17:53:10.674 [http-nio-8081-exec-5] DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource
17:53:10.675 [http-nio-8081-exec-5] DEBUG org.mybatis.spring.transaction.SpringManagedTransaction - JDBC Connection [com.mysql.jdbc.JDBC4Connection@3d88a0ae] will not be managed by Spring
17:53:10.682 [http-nio-8081-exec-5] DEBUG com.hong.spring.dao.UserMapper.findById - ==> Preparing: SELECT * FROM user WHERE id = ?
17:53:10.712 [http-nio-8081-exec-5] DEBUG com.hong.spring.dao.UserMapper.findById - ==> Parameters: 1(Integer)
17:53:10.739 [http-nio-8081-exec-5] DEBUG com.hong.spring.dao.UserMapper.findById - <== Total: 1
17:53:10.743 [http-nio-8081-exec-5] DEBUG com.alibaba.druid.pool.PreparedStatementPool - stmt enter cache
17:53:10.745 [http-nio-8081-exec-5] DEBUG org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4edba3a]
17:53:10.745 [http-nio-8081-exec-5] DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
17:53:10.756 [http-nio-8081-exec-5] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Opening RedisConnection
17:53:10.788 [http-nio-8081-exec-5] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Closing Redis Connection
17:53:10.918 [http-nio-8081-exec-5] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor - Written [com.hong.spring.utils.DataResponse@2ddadd1c] as "text/html" using [com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter@2920b619]
17:53:10.918 [http-nio-8081-exec-5] DEBUG org.springframework.web.servlet.DispatcherServlet - Null ModelAndView returned to DispatcherServlet with name 'mybatis_plus_redis_cache': assuming HandlerAdapter completed request handling
17:53:10.918 [http-nio-8081-exec-5] DEBUG org.springframework.web.servlet.DispatcherServlet - Successfully completed request
再次请求
结果:发现没有再请求数据库,直接获取了redis数据。
17:54:41.406 [http-nio-8081-exec-8] DEBUG org.springframework.web.servlet.DispatcherServlet - DispatcherServlet with name 'mybatis_plus_redis_cache' processing GET request for [/user/findById/1]
17:54:41.406 [http-nio-8081-exec-8] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Looking up handler method for path /user/findById/1
17:54:41.407 [http-nio-8081-exec-8] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Returning handler method [public com.hong.spring.utils.DataResponse<com.hong.spring.entity.User> com.hong.spring.controller.UserController.findById(java.lang.Integer)]
17:54:41.407 [http-nio-8081-exec-8] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'userController'
17:54:41.407 [http-nio-8081-exec-8] DEBUG org.springframework.web.servlet.DispatcherServlet - Last-Modified value for [/user/findById/1] is: -1
17:54:41.408 [http-nio-8081-exec-8] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Opening RedisConnection
17:54:41.410 [http-nio-8081-exec-8] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Closing Redis Connection
17:54:41.423 [http-nio-8081-exec-8] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Opening RedisConnection
17:54:41.424 [http-nio-8081-exec-8] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Closing Redis Connection
17:54:41.428 [http-nio-8081-exec-8] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor - Written [com.hong.spring.utils.DataResponse@3a9ec1] as "text/html" using [com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter@2920b619]
17:54:41.429 [http-nio-8081-exec-8] DEBUG org.springframework.web.servlet.DispatcherServlet - Null ModelAndView returned to DispatcherServlet with name 'mybatis_plus_redis_cache': assuming HandlerAdapter completed request handling
17:54:41.429 [http-nio-8081-exec-8] DEBUG org.springframework.web.servlet.DispatcherServlet - Successfully completed request
看看8082同一个地址。地址:http://localhost:8082/user/findById/1
结果:发现已实现了分布式缓存,不会再去查库了,而是直接返回redis结果。可以发现spring这个实现很nb
processing GET request for [/user/findById/1]
17:56:39.425 [http-nio-8082-exec-3] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Looking up handler method for path /user/findById/1
17:56:39.426 [http-nio-8082-exec-3] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Returning handler method [public com.hong.spring.utils.DataResponse<com.hong.spring.entity.User> com.hong.spring.controller.UserController.findById(java.lang.Integer)]
17:56:39.426 [http-nio-8082-exec-3] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'userController'
17:56:39.426 [http-nio-8082-exec-3] DEBUG org.springframework.web.servlet.DispatcherServlet - Last-Modified value for [/user/findById/1] is: -1
17:56:39.542 [http-nio-8082-exec-3] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Opening RedisConnection
17:56:39.628 [http-nio-8082-exec-3] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Closing Redis Connection
17:56:39.635 [http-nio-8082-exec-3] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Opening RedisConnection
17:56:39.636 [http-nio-8082-exec-3] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Closing Redis Connection
17:56:39.756 [http-nio-8082-exec-3] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor - Written [com.hong.spring.utils.DataResponse@6963905d] as "text/html" using [com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter@20ddf2cd]
17:56:39.756 [http-nio-8082-exec-3] DEBUG org.springframework.web.servlet.DispatcherServlet - Null ModelAndView returned to DispatcherServlet with name 'mybatis_plus_redis_cache': assuming HandlerAdapter completed request handling
17:56:39.756 [http-nio-8082-exec-3] DEBUG org.springframework.web.servlet.DispatcherServlet - Successfully completed request
再试试:http://localhost:8082/user/findById/2
结果:发现第一次请求了数据库并且缓存到了redis中,第二次后都是直接查询redis数据
第一次:
17:59:14.999 [http-nio-8082-exec-9] DEBUG org.springframework.web.servlet.DispatcherServlet - Last-Modified value for [/user/findById/2] is: -1
17:59:14.999 [http-nio-8082-exec-9] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Opening RedisConnection
17:59:15.001 [http-nio-8082-exec-9] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Closing Redis Connection
17:59:15.013 [http-nio-8082-exec-9] INFO com.hong.spring.service.impl.UserServiceImpl - 进入数据库查询
17:59:15.029 [http-nio-8082-exec-9] DEBUG org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
17:59:15.040 [http-nio-8082-exec-9] DEBUG org.mybatis.spring.SqlSessionUtils - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@60544f20] was not registered for synchronization because synchronization is not active
17:59:15.057 [http-nio-8082-exec-9] DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource
17:59:15.058 [http-nio-8082-exec-9] DEBUG org.mybatis.spring.transaction.SpringManagedTransaction - JDBC Connection [com.mysql.jdbc.JDBC4Connection@604f1509] will not be managed by Spring
17:59:15.063 [http-nio-8082-exec-9] DEBUG com.hong.spring.dao.UserMapper.findById - ==> Preparing: SELECT * FROM user WHERE id = ?
17:59:15.092 [http-nio-8082-exec-9] DEBUG com.hong.spring.dao.UserMapper.findById - ==> Parameters: 2(Integer)
17:59:15.111 [http-nio-8082-exec-9] DEBUG com.hong.spring.dao.UserMapper.findById - <== Total: 1
17:59:15.115 [http-nio-8082-exec-9] DEBUG com.alibaba.druid.pool.PreparedStatementPool - stmt enter cache
17:59:15.116 [http-nio-8082-exec-9] DEBUG org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@60544f20]
17:59:15.116 [http-nio-8082-exec-9] DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
17:59:15.119 [http-nio-8082-exec-9] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Opening RedisConnection
17:59:15.143 [http-nio-8082-exec-9] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Closing Redis Connection
17:59:15.144 [http-nio-8082-exec-9] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor - Written [com.hong.spring.utils.DataResponse@582045f0] as "text/html" using [com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter@20ddf2cd]
17:59:15.144 [http-nio-8082-exec-9] DEBUG org.springframework.web.servlet.DispatcherServlet - Null ModelAndView returned to DispatcherServlet with name 'mybatis_plus_redis_cache': assuming HandlerAdapter completed request handling
17:59:15.144 [http-nio-8082-exec-9] DEBUG org.springframework.web.servlet.DispatcherServlet - Successfully completed request
第二次:
18:01:17.664 [http-nio-8082-exec-7] DEBUG org.springframework.web.servlet.DispatcherServlet - DispatcherServlet with name 'mybatis_plus_redis_cache' processing GET request for [/user/findById/2]
18:01:17.664 [http-nio-8082-exec-7] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Looking up handler method for path /user/findById/2
18:01:17.665 [http-nio-8082-exec-7] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Returning handler method [public com.hong.spring.utils.DataResponse<com.hong.spring.entity.User> com.hong.spring.controller.UserController.findById(java.lang.Integer)]
18:01:17.665 [http-nio-8082-exec-7] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'userController'
18:01:17.666 [http-nio-8082-exec-7] DEBUG org.springframework.web.servlet.DispatcherServlet - Last-Modified value for [/user/findById/2] is: -1
18:01:17.666 [http-nio-8082-exec-7] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Opening RedisConnection
18:01:17.668 [http-nio-8082-exec-7] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Closing Redis Connection
18:01:17.669 [http-nio-8082-exec-7] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Opening RedisConnection
18:01:17.670 [http-nio-8082-exec-7] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Closing Redis Connection
18:01:17.671 [http-nio-8082-exec-7] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor - Written [com.hong.spring.utils.DataResponse@9ce8e1f] as "text/html" using [com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter@20ddf2cd]
18:01:17.671 [http-nio-8082-exec-7] DEBUG org.springframework.web.servlet.DispatcherServlet - Null ModelAndView returned to DispatcherServlet with name 'mybatis_plus_redis_cache': assuming HandlerAdapter completed request handling
18:01:17.672 [http-nio-8082-exec-7] DEBUG org.springframework.web.servlet.DispatcherServlet - Successfully completed request
然后再查询另一个81的服务:
结果:发现不管怎么查询都使用了redis缓存,同上一样。
第一次查询
18:01:49.813 [http-nio-8081-exec-9] DEBUG org.springframework.web.servlet.DispatcherServlet - DispatcherServlet with name 'mybatis_plus_redis_cache' processing GET request for [/user/findById/2]
18:01:49.813 [http-nio-8081-exec-9] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Looking up handler method for path /user/findById/2
18:01:49.813 [http-nio-8081-exec-9] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Returning handler method [public com.hong.spring.utils.DataResponse<com.hong.spring.entity.User> com.hong.spring.controller.UserController.findById(java.lang.Integer)]
18:01:49.814 [http-nio-8081-exec-9] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'userController'
18:01:49.814 [http-nio-8081-exec-9] DEBUG org.springframework.web.servlet.DispatcherServlet - Last-Modified value for [/user/findById/2] is: -1
18:01:49.814 [http-nio-8081-exec-9] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Opening RedisConnection
18:01:49.816 [http-nio-8081-exec-9] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Closing Redis Connection
18:01:49.817 [http-nio-8081-exec-9] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Opening RedisConnection
18:01:49.818 [http-nio-8081-exec-9] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Closing Redis Connection
18:01:49.820 [http-nio-8081-exec-9] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor - Written [com.hong.spring.utils.DataResponse@df11bfe] as "text/html" using [com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter@2920b619]
18:01:49.821 [http-nio-8081-exec-9] DEBUG org.springframework.web.servlet.DispatcherServlet - Null ModelAndView returned to DispatcherServlet with name 'mybatis_plus_redis_cache': assuming HandlerAdapter completed request handling
18:01:49.821 [http-nio-8081-exec-9] DEBUG org.springframework.web.servlet.DispatcherServlet - Successfully completed request
第二次查询
18:02:23.680 [http-nio-8081-exec-1] DEBUG org.springframework.web.servlet.DispatcherServlet - DispatcherServlet with name 'mybatis_plus_redis_cache' processing GET request for [/user/findById/2]
18:02:23.680 [http-nio-8081-exec-1] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Looking up handler method for path /user/findById/2
18:02:23.681 [http-nio-8081-exec-1] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Returning handler method [public com.hong.spring.utils.DataResponse<com.hong.spring.entity.User> com.hong.spring.controller.UserController.findById(java.lang.Integer)]
18:02:23.681 [http-nio-8081-exec-1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'userController'
18:02:23.681 [http-nio-8081-exec-1] DEBUG org.springframework.web.servlet.DispatcherServlet - Last-Modified value for [/user/findById/2] is: -1
18:02:23.682 [http-nio-8081-exec-1] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Opening RedisConnection
18:02:23.682 [http-nio-8081-exec-1] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Closing Redis Connection
18:02:23.683 [http-nio-8081-exec-1] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Opening RedisConnection
18:02:23.683 [http-nio-8081-exec-1] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Closing Redis Connection
18:02:23.685 [http-nio-8081-exec-1] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor - Written [com.hong.spring.utils.DataResponse@b234792] as "text/html" using [com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter@2920b619]
18:02:23.685 [http-nio-8081-exec-1] DEBUG org.springframework.web.servlet.DispatcherServlet - Null ModelAndView returned to DispatcherServlet with name 'mybatis_plus_redis_cache': assuming HandlerAdapter completed request handling
18:02:23.685 [http-nio-8081-exec-1] DEBUG org.springframework.web.servlet.DispatcherServlet - Successfully completed request
总结redis注解式
找了全网的资料发现极少涉及该通过redis注解式去实现,大部分都是手动的,当然注解式不管在单 机或者集群方式通过redis实现分布式事务非常便捷,但是涉及一些特殊操作比较麻烦。
redis手动式缓存
com.hong.spring.utils.RedisCacheManager
package com.hong.spring.utils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
public class RedisCacheManager {
private RedisTemplate<String, Object> redisTemplate;
public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
/**
* 指定缓存失效时间
*
* @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 by
* 要增加几(大于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 by
* 要减少几(小于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
* 值
* @param time
* 时间(秒)
* @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
* 值
* @param time
* 时间(秒)
* @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;
}
}
}
applicationContext-mybatis_plus_redis_cache.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" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
<!-- 配置组件扫描 -->
<context:component-scan base-package="com.hong.spring"></context:component-scan>
<!--加载配置文件-->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<!-- 内部配置 -->
<value>classpath:redis.properties</value>
<value>classpath:jdbc.properties</value>
</list>
</property>
<property name="fileEncoding" value="UTF-8"/>
</bean>
<!-- 开启注解 -->
<context:annotation-config />
<!--开启注解事务-->
<tx:annotation-driven transaction-manager="transactionManager" />
<!--放行静态资源-->
<mvc:default-servlet-handler />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/pages/" />
<!-- 后缀 -->
<property name="suffix" value=".html" />
<property name="contentType" value="text/html"/>
</bean>
<!--开启mvc注解事务-->
<!-- 定义注解驱动 -->
<mvc:annotation-driven>
<mvc:message-converters>
<!-- 设置支持中文 -->
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/plain;charset=UTF-8</value>
<value>text/html;charset=UTF-8</value>
</list>
</property>
</bean>
<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!-- 基础配置 -->
<property name="url" value="${jdbc.url}"></property>
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="username" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<!-- 关键配置 -->
<!-- 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时 -->
<property name="initialSize" value="3" />
<!-- 最小连接池数量 -->
<property name="minIdle" value="2" />
<!-- 最大连接池数量 -->
<property name="maxActive" value="15" />
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="10000" />
<!-- 性能配置 -->
<!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->
<property name="poolPreparedStatements" value="true" />
<property name="maxPoolPreparedStatementPerConnectionSize" value="20" />
<!-- 其他配置 -->
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="300000" />
<!-- 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,
执行validationQuery检测连接是否有效。-->
<property name="testWhileIdle" value="true" />
<!-- 这里建议配置为TRUE,防止取到的连接不可用 ,申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。-->
<property name="testOnBorrow" value="true" />
<!-- 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能 -->
<property name="testOnReturn" value="false" />
</bean>
<!--事务管理器-->
<!-- sqlSessionFactory -->
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<!-- 加载 MyBatis 的配置文件 -->
<property name="configLocation" value="classpath:mybatis-plus.xml"/>
<!-- 数据源 -->
<property name="dataSource" ref="dataSource"/>
<!-- 所有配置的mapper文件 -->
<property name="mapperLocations" value="classpath*:com/hong/spring/mapper/*.xml" />
</bean>
<!-- Mapper 扫描器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 扫描 包下的组件 -->
<property name="basePackage" value="com.hong.spring.dao" />
<!-- 关联mapper扫描器 与 sqlsession管理器 -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>
<!--事务配置-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- ******************** redis缓存 **********************-->
<!-- 注解一定要配置,不然不起作用 -->
<cache:annotation-driven cache-manager="cacheManager" />
<!-- 配置 JedisPoolConfig 实例 -->
<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.maxWait}"/>
<!--逐出连接的最小空闲时间 默认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>
<!-- 配置JedisConnectionFactory -->
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<!--IP地址 -->
<property name="hostName" value="${redis.hostname}"></property>
<!--端口号 -->
<property name="port" value="${redis.port}"></property>
<!--如果Redis设置有密码 -->
<!--<property name="password" value="${redis.pa}" />-->
<!--客户端超时时间单位是毫秒 -->
<property name="timeout" value="${redis.timeout}"></property>
<!--redis数据库序号,redis默认有16个库(从0-15),这里默认是0 -->
<property name="database" value="${redis.default.db}"/>
<property name="poolConfig" ref="poolConfig"/>
</bean>
<bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer" />
<!-- 配置RedisTemplate -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory" />
<property name="keySerializer" ref="stringRedisSerializer" />
<property name="hashKeySerializer" ref="stringRedisSerializer" />
<property name="valueSerializer" ref="stringRedisSerializer"/>
</bean>
<!-- spring自己的缓存管理器,这里定义了缓存位置名称 ,即注解中的value -->
<bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">
<constructor-arg name="redisOperations" ref="redisTemplate"></constructor-arg>
<property name="defaultExpiration" value="3000" />
</bean>
<!-- 配置RedisCacheConfig -->
<bean id="redisCacheConfig" class="com.hong.spring.utils.RedisCacheConfig">
<constructor-arg ref="jedisConnectionFactory" />
<constructor-arg ref="redisTemplate" />
<constructor-arg ref="cacheManager" />
</bean>
<!--自定义redis工具类,在需要缓存的地方注入此类 -->
<bean id="redisrCacheManager" class="com.hong.spring.utils.RedisCacheManager">
<property name="redisTemplate" ref="redisTemplate" />
</bean>
<!-- ******************** redis缓存 **********************-->
</beans>
com.hong.spring.service.IUserService#handlerById
/**
*
* 功能描述:手动查询id
*
* @param:
* @return:
* @auther: csh
* @date: 2020/8/31 19:16
*/
DataResponse<User> handlerById(Integer id);
com.hong.spring.service.impl.UserServiceImpl#handlerById
@Override
public DataResponse <User> handlerById(Integer id) {
if(null==id){
return DataResponse.BuildFailResponse("必传参数不能为空!");
}
User user;
String userStr = (String)redisCacheManager.get("user_" + id);
if(null==userStr || StringUtils.isEmpty(userStr)){
user = userMapper.findById(id);
if(null!=user){
redisCacheManager.set("user_"+id, JSONObject.toJSONString(user));
}
}else{
user = JSONObject.parseObject(userStr, User.class);
}
return DataResponse.BuildSuccessResponse(user);
}
com.hong.spring.controller.UserController#handlerById
@RequestMapping("handlerById/{id}")
public DataResponse<User> handlerById(@PathVariable("id")Integer id){
if(null==id){
return DataResponse.BuildFailResponse("参数不能为空!");
}
try {
return userService.handlerById(id);
}catch (Exception e){
log.error("findById->查询失败{}",e);
return DataResponse.BuildFailResponse("查询出错请重试!");
}
}
请求:http://localhost:8082/user/handlerById/2
结果:第一次查库,后面都是查redis
第一次
19:23:38.769 [http-nio-8082-exec-5] DEBUG org.springframework.web.servlet.DispatcherServlet - Last-Modified value for [/user/handlerById/2] is: -1
19:23:38.839 [http-nio-8082-exec-5] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Opening RedisConnection
19:23:38.902 [http-nio-8082-exec-5] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Closing Redis Connection
19:23:38.917 [http-nio-8082-exec-5] DEBUG org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
19:23:38.932 [http-nio-8082-exec-5] DEBUG org.mybatis.spring.SqlSessionUtils - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@27f6fd76] was not registered for synchronization because synchronization is not active
19:23:38.949 [http-nio-8082-exec-5] DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource
19:23:38.951 [http-nio-8082-exec-5] DEBUG org.mybatis.spring.transaction.SpringManagedTransaction - JDBC Connection [com.mysql.jdbc.JDBC4Connection@374ddb17] will not be managed by Spring
19:23:38.956 [http-nio-8082-exec-5] DEBUG com.hong.spring.dao.UserMapper.findById - ==> Preparing: SELECT * FROM user WHERE id = ?
19:23:38.990 [http-nio-8082-exec-5] DEBUG com.hong.spring.dao.UserMapper.findById - ==> Parameters: 2(Integer)
19:23:39.012 [http-nio-8082-exec-5] DEBUG com.hong.spring.dao.UserMapper.findById - <== Total: 1
19:23:39.015 [http-nio-8082-exec-5] DEBUG com.alibaba.druid.pool.PreparedStatementPool - stmt enter cache
19:23:39.017 [http-nio-8082-exec-5] DEBUG org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@27f6fd76]
19:23:39.017 [http-nio-8082-exec-5] DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
19:23:39.048 [http-nio-8082-exec-5] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Opening RedisConnection
19:23:39.048 [http-nio-8082-exec-5] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Closing Redis Connection
第二次
19:23:45.642 [http-nio-8082-exec-3] DEBUG org.springframework.web.servlet.DispatcherServlet - Last-Modified value for [/user/handlerById/2] is: -1
19:23:45.642 [http-nio-8082-exec-3] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Opening RedisConnection
19:23:45.643 [http-nio-8082-exec-3] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Closing Redis Connection
19:23:45.666 [http-nio-8082-exec-3] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor - Written [com.hong.spring.utils.DataResponse@71ad34fb] as "text/html" using [com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter@206ffcd3]
19:23:45.667 [http-nio-8082-exec-3] DEBUG org.springframework.web.servlet.DispatcherServlet - Null ModelAndView returned to DispatcherServlet with name 'mybatis_plus_redis_cache': assuming HandlerAdapter completed request handling
19:23:45.667 [http-nio-8082-exec-3] DEBUG org.springframework.web.servlet.DispatcherServlet - Successfully completed request
请求:81服务。http://localhost:8081/user/handlerById/2
结果:每次都从缓存中取,给力。
19:29:55.970 [http-nio-8081-exec-5] DEBUG org.springframework.web.servlet.DispatcherServlet - DispatcherServlet with name 'mybatis_plus_redis_cache' processing GET request for [/user/handlerById/2]
19:29:55.970 [http-nio-8081-exec-5] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Looking up handler method for path /user/handlerById/2
19:29:55.971 [http-nio-8081-exec-5] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Returning handler method [public com.hong.spring.utils.DataResponse<com.hong.spring.entity.User> com.hong.spring.controller.UserController.handlerById(java.lang.Integer)]
19:29:55.971 [http-nio-8081-exec-5] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'userController'
19:29:55.971 [http-nio-8081-exec-5] DEBUG org.springframework.web.servlet.DispatcherServlet - Last-Modified value for [/user/handlerById/2] is: -1
19:29:56.030 [http-nio-8081-exec-5] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Opening RedisConnection
19:29:56.086 [http-nio-8081-exec-5] DEBUG org.springframework.data.redis.core.RedisConnectionUtils - Closing Redis Connection
19:29:56.215 [http-nio-8081-exec-5] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor - Written [com.hong.spring.utils.DataResponse@5412ad15] as "text/html" using [com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter@297a0692]
19:29:56.216 [http-nio-8081-exec-5] DEBUG org.springframework.web.servlet.DispatcherServlet - Null ModelAndView returned to DispatcherServlet with name 'mybatis_plus_redis_cache': assuming HandlerAdapter completed request handling
19:29:56.216 [http-nio-8081-exec-5] DEBUG org.springframework.web.servlet.DispatcherServlet - Successfully completed request
最后
基本需要缓存方案的项目或者比较成熟的项目都会用到redis,而且用得特别多,当然也可以选择其他框架比如:Memcached,这里就不一一测试了。redis在高可用、高并发、高性能基本都杠杠的,当然也引发缓存穿透、缓存击穿、缓存雪崩、热点数据等问题。
建议下载源码学习:
代码实现:https://gitee.com/hong99/spring/issues/I1N1DF
考虑文章长度所以:缓存穿透、缓存击穿、缓存雪崩、热点数据,放到下文。