1 SpringBoot整合Redis的环境配置

1.1 添加依赖

   <properties>
        <java.version>1.8</java.version>
        <druid.version>1.1.9</druid.version>
        <!-- mybatis的版本 -->
        <mybatis.version>3.4.6</mybatis.version>
    </properties>

    <dependencies>

        <!-- 配置web启动器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- 配置mybatis-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.15</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.2.0</version>
        </dependency>

        <!-- 配置数据库连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>${druid.version}</version>
        </dependency>


        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <exclusions> <!--不依赖Redis的异步客户端lettuce-->
                <exclusion>
                    <groupId>io.lettuce</groupId>
                    <artifactId>lettuce-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--引入Redis的客户端驱动jedis-->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>

        <!--SpringBoot缓存支持启动器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <!--Ehcache坐标-->
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
        </dependency>

</dependencies>  
    这样我们就引入了Spring对Redis的starter,只是在默认的情况下,spring-boot-starter-data-redis(版本2.x)  
    会依赖Lettuce的Redis客户端驱动,而在一般的项目中,我们会使用Jedis,所以在代码中使用了<exclusions>  
    元素将其依赖排除了,与此同时引入了Jedis的依赖,这样就可以使用Jedis进行编程了。  

1.2 配置application.properties文件

################# 配置数据库连接参数*************************************************
spring.datasource.driverClassName =com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
###### 指定mapper文件的位置
mybatis.mapper-locations=classpath:mapper/*.xml
## 设置视图层的别名,类似于在spring项目中的mybatis-config.xml文件中配置<typeAliases>
mybatis.type-aliases-package=com.fyzn12.pojo
################## 更改springboot内置tomcat的参数**********************
# 连接池中的最大空闲连接
spring.datasource.tomcat.max-idle=10
# 连接池最大连接数(使用负值表示没有限制)
spring.datasource.tomcat.max-active=50
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.datasource.tomcat.max-wait=10000
spring.datasource.tomcat.initial-size=5

################### 设置默认的隔离级别为读已提交 ************************
spring.datasource.tomcat.default-transaction-isolation=2

################### 配置redis****************************************************

# 配置连接池属性
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
#spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=5000 
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=1000
#spring-session 使用
spring.session.store-type=none

################### 配置缓存管理  **********************************************
# 配置redis的缓存管理器
# spring.cache.type=redis配置的是缓存类型,Spring Boot会自动生成RedisCacheManager对象
spring.cache.type=redis
# spring.cache.cache-names则是配置缓存名称,多个名称可以使用逗号分隔,以便于缓存注解的引用。
spring.cache.cache-names=redisCache


################### 配置日志 **************************************************

#logging.level.root=DEBUG
#logging.level.org.springframework=DEBUG
#logging.level.org.mybatis=DEBUG

在上面配置文件中详细配置了redis的使用,这里注意“配置缓存管理”的配置

2 三个Spring注解的讲解 @Cacheable 、 @CacheEvict 和 @CachePut

2.1 @Cacheable用于查询

  1. @Cacheable作用:把方法的返回值添加到Ehcache中做缓存Value属性:指定一个Ehcache配置文件中的缓存策略,如果么有给定value,name则表示使用默认的缓存策略。

  2. 在Spring中使用xml文件配置时采用如下配置

      
    <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
    
    <diskStore path="java.io.tmpdir"/>
    
    <!--defaultCache:echcache的默认缓存策略  -->
    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            maxElementsOnDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap"/>
    </defaultCache>
    <!-- 自定义缓存策略 -->
    <cache name="users"
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            maxElementsOnDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap"/>
    </cache>
    </ehcache>
        
  3. 使用方式

    	@Override
    	//@Cacheable:对当前查询的对象做缓存处理
    	@Cacheable(value="users")
    	public Users findUserById(Integer id) {
    		return this.usersRepository.findOne(id);
    	}    
    	//其中value值为xml文件中配置的name的值
  4. 在SpringBoot中使用

    /**
     * @Param: param
     * @description:查询
     * @Return
     * @Cacheable注解的说明
     *      value:该值为在application.properties文件中配置spring.cache.cache-names=redisCache指定的值 
     *      key :为该缓存取一个名字
    */
    @Override
    @Cacheable(value = "redisCache", key = "'key_user'+#param.getParam()")
    @Transactional(isolation = Isolation.REPEATABLE_READ, propagation = Propagation.REQUIRES_NEW, rollbackFor = RuntimeException.class)
    public Object strategy(HandlerParam param) throws IOException {
        System.out.println("执行查询");
        if (param.getParam() instanceof Integer) {
            //如果是全是数字组成的查询值,则认为是按照id进行查询,否则认为按照name查询
            Users20174046 users20174046 = users20174046Mapper.selectById((Integer) param.getParam());
            return users20174046;
        } else {
            List<Users20174046> list = users20174046Mapper.selectByName((String) param.getParam());
            return list;
        }
    }
      

2.2 @CachePut 用于插入和修改

  1. 用于插入时用法

     
    /**
     * @Param: param
     * @description:插入
     * @Return
     * @CachePut注解的说明
     *      value:该值为在application.properties文件中配置spring.cache.cache-names=redisCache指定的值
     *      key :为该缓存取一个名字
     */
    @Override
    @CachePut(value = "redisCache",key = "'redis_user'+#result.id")
    @Transactional(isolation = Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRES_NEW,rollbackFor = RuntimeException.class)
    public Object strategy(HandlerParam param)  {
        int count = users20174046Mapper.insertUsers((Users20174046) param.getParam());
        return null;
    }    
      
    /*
    
    在插入数据库前,对应的用户是没有id的,而这个id值会在插入数据库后由MyBatis的机制回填,  
    所以我们希望使用返回结果,这样使用#result就代表返回的结果对象了,它是一个User对象,  
    所以#result.id是取出它的属性id,这样就可以引用这个由数据库生成的id了。	
    */
      
  2. 用于修改时的用法

      
    /**
     * @Param: param
     * @description:修改
     * @Return
     * @CachePut注解的说明
     *      value:该值为在application.properties文件中配置spring.cache.cache-names=redisCache指定的值
     *      key :为该缓存取一个名字
     *      condition:设置修改条件
     */
    @Override
    @CachePut(value = "redisCache",key = "'key_user'+#param.getParam().getId()",condition = "#result != null")
    @Transactional(isolation = Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRES_NEW,rollbackFor = RuntimeException.class)
    public Object strategy(HandlerParam param) throws IOException {
            //因为这里我写了两个接口,因此可以通过参数的类型判断调用哪个接口
            if (param.getParam() instanceof Map){
                //获取要修改类型的参数
                Map<Object,Object> map = (Map)param.getParam();
                users20174046Mapper.updateUsers(map);
                System.out.println("调用的是动态sql的接口");
            }else if (param.getParam() instanceof Users20174046){
                users20174046Mapper.updateByUsers((Users20174046)param.getParam());
                System.out.println("调用的map接口");
            }
            System.out.println("执行修改完成");
            return null;
    }    
      
    /*
    从代码中可以看到方法,可能返回null。如果为null,则不需要缓存数据,所以在注解@CachePut中加入了condition  
    配置工页,它也是一个SpringEL表达式,这个表达式要求返回Boolean类型值,如果为仕ue,则使用缓存操作,否则就不使用。  
    这里的表达式为#result!=’null’,意味着如果返回null,则方法结束后不再操作缓存。同样地,@Cacheable和  
    @CacheEvict也具备这个配置项。
    */
      

2.3 @CacheEvict 清除缓存

该注解用于方法体上会清除与该方法体有关的缓存

3 在启动类上开启缓存

/**
 * @author ZhangRongjun
 * @EnableCaching :开启缓存
 */
@SpringBootApplication
@MapperScan(basePackages = "com.fyzn12.mapper",annotationClass = Repository.class)
@EnableCaching
public class SpringbootDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootDemoApplication.class, args);
    }

}
 

4 自调用使注解失效

  
//获取id,取参数id缓存用户
    @Override
    @Transactional
    @Cacheable(value="redisCache",key ="'redis_user_'+#id")
    public User getUser (Long id) {
        return userDao.getUser(id);
    }
    //更新数据后,更新缓存,如果condition配置项使结果返回为null,不缓存
    Override
    @Transactional
    @CachePut(value="redisCache" , condition="#result == null ",key =="'redis_user_'+#id")
    public User updateUserName (Long id, String userName) {
        //此处调用getUser方法,该方法缓存注解失效,
        // 所以这里还会执行SQL,将查询到数据库最新数据
        User user = this.getUser(id);
        if (user == null) {
            return null;
        }
        user.setUserName(userName);
        userDao.updateUser(user);
        return user; //命中率低,所以不采用缓存机制
    }  
为什么会这样呢?其实在数据库事务中已经探讨过其原理,只要回顾一下就清楚了,那是因为Spring的缓存机制也是  
基于SpringAOP的原理,而在Spring中AOP是通过动态代理技术来实现的,这里的updateUserName方法调用getUser    
方法是类内部的自调用,并不存在代理对象的调用,这样便不会出现AOP,也就不会使用到标注在getUser上的缓存注解  
去获取缓存的值了,这是需要注意的地方。要克服这个问题,可以参考《数据库的事务》一文中关于数据库事务那样用两个  
服务(Service)类相互调用,或者直接从SpringIoC容器中获取代理对象来操作,这样就能成功克服自调用的问题了。