专栏首页技术趋势spring的缓存(cache)-分布式缓存

spring的缓存(cache)-分布式缓存

注:本文篇幅有点长,所以建议各位下载源码学习。(如需要请收藏!转载请声明来源,谢谢!)

代码下载: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

考虑文章长度所以:缓存穿透、缓存击穿、缓存雪崩、热点数据,放到下文。

本文分享自微信公众号 - 技术趋势(jishuqs),作者:逍遥壮士

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-09-02

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 业务自增id彻底解决es深翻页的问题

    由于原来mysql库里面的数据量从2万增加到40万,并且mysql库缩容导致,每小时其他项目级小伙伴来调xinyun库的这张表导致cpu每次开销都在20%以上,...

    逍遥壮士
  • spring使用e-mail

    常用的电子邮件协议有SMTP、POP3、IMAP4,它们都隶属于TCP/IP协议簇,默认状态下,分别通过TCP端口25、110和143建立连接。

    逍遥壮士
  • 设计模式-业务代表模式

    一般在企业里面各个部门都有一个负责人,如果有什么事直接接这个负责人就可以了,而这个负责起到一个对外暴露的作用,有什么事直接找负责人。

    逍遥壮士
  • 2 秒杀系统模拟基础实现,使用Redis实现

    天涯泪小武
  • Spring Data Redis

    Java客户端(上)章节中我们使用了redis的Java客户端的第三方开源框架——Jedis,但目前Java应用已经被Spring(Spring Boot)统治...

    用户1148394
  • Spring 整合 SpringDataRedis

    Demo_Null
  • 架构设计之Spring-Session分布式集群会话管理

    前言 通常在web开发中,会话管理是很重要的一部分,用于存储与用户相关的一些数据。对于JAVA开发者来说,项目中的session一般由Tomcat或者jetty...

    小柒2012
  • Redis之Spring实现发布订阅 原

    注:Redis版本是4.0;Spring版本4.3.11;Redis client版本2.9.0。

    克虏伯
  • MIT、港中文团队暴力解剖GAN,一笔成画不是梦

    自从GAN诞生以来,一次一次创造着奇迹:填充纹理、变脸易容。但这一次,这款名叫GANpaint的神器,简单几笔就能成画。

    新智元
  • LeCun 提出基于能量的生成对抗网络,ICLR-17 重新审视 GAN

    【新智元导读】LeCun对对抗生成网络(GAN)的盛赞大家都很熟悉了。在这篇新的论文中,LeCun等人将两类无监督学习方法——GAN和自编码器结合在一起,并从替...

    新智元

扫码关注云+社区

领取腾讯云代金券