Spring缓存机制整合Redis

版权声明:转载注明出处 https://blog.csdn.net/czr11616/article/details/84205599

 

首先,在Spring中使用Redis需要jedis.jar和spring-data-redis.jar

Spring整合Redis有两种方式,一种为注解,另一种为xml配置文件,根据你的Spring IoC配置形式进行选择,下面来分别进行讲解:

如果你的IoC容器是以xml文件形式配置的,则在你的IoC配置文件中加入如下代码:

<!-- 配置连接池 -->
       <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
           <property name="maxIdle" value="50"/>
           <property name="maxTotal" value="100"/>
           <property name="maxWaitMillis" value="20000"/>
       </bean>
                  
       <!-- 配置连接工厂 -->
       <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
           <property name="hostName" value="localhost"/>
           <property name="port" value="6379"/>
           <property name="poolConfig" ref="poolConfig"/>
       </bean>
    <!-- 键值序列化器 -->
       <bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
       <bean id="jdkSerializationRedisSerializer" class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"></bean>
       <!-- 配置redisTemplate -->
       <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
           <property name="connectionFactory" ref="connectionFactory"/>
           <!-- 设置默认的序列化器为字符串序列化 -->
           <property name="defaultSerializer" ref="stringRedisSerializer"/>
           <property name="keySerializer" ref="stringRedisSerializer"/>
           <property name="valueSerializer" ref="jdkSerializationRedisSerializer"/>
           <property name="hashKeySerializer" ref="stringRedisSerializer"/>
           <property name="hashValueSerializer" ref="jdkSerializationRedisSerializer"/>
       </bean>
       <!-- 使用注解驱动,其中属性cache-manager默认值为cacheManager -->
       <cache:annotation-driven cache-manager="redisCacheManager"/>
       <!-- 定义缓存管理器 -->
       <bean id="redisCacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">
           <!-- 通过构造方法注入redisTemplate -->
           <constructor-arg index="0" ref="redisTemplate"/>
           <!-- 定义默认超时时间 -->
           <property name="defaultExpiration" value="600"/>
           <!-- 缓存管理器名称 -->
           <property name="cacheNames">
               <list>
                   <value>redisCacheManager</value>
               </list>
           </property>
       </bean>

解释一下上述配置信息:

  1. 配置连接池:配置了Redis数据库连接池的最大闲置连接数、最大连接数和最长等待时间
  2. 配置Redis连接工厂:配置了Redis数据库的主机和端口号,同时引入了之前配置的连接池
  3. 配置序列化器:什么是序列化器呢?由于Redis只能提供基于字符串型的操作,而在java中是以类和对象为主,序列化器的作用是将java对象和Redis字符串相互转换,使得可以将java对象序列化为字符串存入Redis,同时也可以取出Redis序列化过的字符串转换成java对象。 
  4. 配置RedisTemplate:redisTemplate封装了对redis的操作,要操作redis,则肯定要引入上面配置好的连接工厂和序列化器
  5. 配置缓存管理器:有了对redis的操作,接下来就是要将spring缓存机制和redis进行结合(即配置缓存管理器),缓存管理器中我们注入了redisTemplate,同时设置了超时时间和管理器的名称。

如果你的IoC容器是以java形式配置的,则你需要新建一个redis的配置类RedisConfig.java(自命名): 

package com.ssm.config;

import java.util.ArrayList;
import java.util.Collection;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.EnableCaching;
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;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import redis.clients.jedis.JedisPoolConfig;

//代表这个类为一个配置类
@Configuration
//开启缓存机制
@EnableCaching
@SuppressWarnings({"rawtypes","unchecked"})
public class RedisConfig {

    @Bean(name="redisTemplate")
    public RedisTemplate initRedisTemplate() {
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        //最大空闲数
        poolConfig.setMaxIdle(50);
        //最大连接数
        poolConfig.setMaxTotal(100);
        //最大等待毫秒数
        poolConfig.setMaxWaitMillis(20000);
        //创建Jedis连接工厂
        JedisConnectionFactory connectionFactory = new JedisConnectionFactory(poolConfig);
        connectionFactory.setHostName("localhost");
        connectionFactory.setPort(6379);
        //调用后初始化方法,没有它将抛出异常
        connectionFactory.afterPropertiesSet();
        //自定义redis序列化器
        RedisSerializer jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer();
        RedisSerializer stringRedisSerializer = new StringRedisSerializer();
        //定义RedisTemplate,并设置连接工程
        RedisTemplate redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(connectionFactory);
        //设置序列化器
        redisTemplate.setDefaultSerializer(stringRedisSerializer);
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setValueSerializer(jdkSerializationRedisSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        redisTemplate.setHashValueSerializer(jdkSerializationRedisSerializer);
        return redisTemplate;
    }
    //定义缓存管理器
    @Bean(name="redisCacheManager")
    public RedisCacheManager initRedisCacheManager(@Autowired RedisTemplate redisTemplate) {
        
        RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
        //设置超时时间为10分钟
        cacheManager.setDefaultExpiration(600L);
        //设置缓存名称
        Collection<String> cacheNames = new ArrayList<String>();
        cacheNames.add("redisCacheManager");
        cacheManager.setCacheNames(cacheNames);
        return cacheManager;
        
    }
}
 

这里的 配置类和之前的xml配置形式其实是一模一样的,只不过一个是以xml,一个是以java形式,所以完全可以参照xml配置的讲解。

到这里缓存的配置就完成了,那么如何使用配置好的缓存呢?

我们先定义一个增删改查的接口RoleService.java:

package com.ssm.service;

import java.util.List;

import org.apache.ibatis.annotations.Param;

import com.ssm.pojo.Role;

public interface RoleService {

    public Role getRole(Long id);
    public int deleteRole(Long id);
    public Role insert(Role role);
    public Role updateRole(Role role);
    public List<Role> findRoles(@Param("roleName") String roleName,@Param("note") String note);

}
 

 

下面是接口的实现类RoleServiceImpl.java:

package com.ssm.service.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.ssm.dao.RoleDao;
import com.ssm.pojo.Role;
import com.ssm.service.RoleService;

@Service
public class RoleServiceImpl implements RoleService {

    @Autowired
    private RoleDao roleDao = null;
    /*
     * 使用@Cacheable定义缓存策略
     * 当缓存中有值,则返回缓存数据,否则访问方法得到数据
     * 通过value引用缓存管理器,通过key定义键
     * @param id 角色编号
     * @return 角色
     */
    @Override
    @Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRED)
    @Cacheable(value="redisCacheManager",key="'redis_role'+#id")
    public Role getRole(Long id) {
        return roleDao.getRole(id);
    }
    /*
     * 使用@CachePut则表示无论如何都会执行该方法,最后将方法的返回值再保存到缓存中
     * 使用在插入数据的地方,则表示保存到数据库后,会同期插入Redis缓存中
     * @param role角色对象
     * @return 角色对象
     */
    @Override
    @Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRED)
    @CachePut(value="redisCacheManager",key="'redis_role'+#result.id")
    public Role insert(Role role) {
        roleDao.insertRole(role);
        return role;
    }
    /*
     * 使用@CachePut,表示更新数据库的同时,也会同步更新缓存
     * @param role 角色对象
     * @return 角色对象
     */
    @Override
    @Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRED)
    @CachePut(value="redisCacheManager",key="'redis_role'+#result.id")
    public Role updateRole(Role role) {
        roleDao.updateRole(role);
        return role;
    }
    /*
     * 使用@CacheEvict删除缓存对应的key
     * @param id 角色编号
     * @return 返回删除的记录数
     */
    @Override
    @Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRED)
    @CacheEvict(value="redisCacheManager",key="'redis_role'+#id")
    public int deleteRole(Long id) {
        return roleDao.deleteRole(id);
    }

    @Override
    public List<Role> findRoles(String roleName, String note) {
        return roleDao.findRoles(roleName, note);
    }

}
 

 

 在实现类中我们分别对增删改查定义了不同的缓存策略,分别为:@Cacheable、@CachePut、@CacheEvict,结合代码段我们对这些缓存策略分别给出讲解:

  1. @Cacheable:通常在查询方法上使用,如果缓存中有值,则直接返回缓存中的值,如果没有,则去数据库中取值,取出后存入缓存。这里@Cacheable有两个属性,value和key,value表示使用的缓存管理器的名称(之前配置的),key表示要存入缓存中的key值,其中#id表示接受方法中的参数以此组成key值。
  2. @CachePut:通常在插入和更新方法中使用,此注解无论如何都会更新数据库,更新数据库后会同步更新到缓存。同样有value和key两个属性,value为缓存管理器的名称,key表示要存入缓存中的key值。
  3. @CacheEvict:通常在删除方法中使用,value为缓存管理器的名称,key为缓存中要删除的数据的key值。

实现类写好了,下面给出测试:

在测试之前,为了能够更好的看到效果,我们使用log4j进行监控执行过程,在src下创建文件log4j.properties并写入配置属性,其中其配置属性如下:

log4j.rootLogger=DEBUG,stdout
log4j.logger.org.springframework=DEBUG
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p %d %C: %m%n

首先获取ioc容器,如果你的ioc是以xml文件形式配置的,采用这种方式获取(参数为你的xml文件名带路径):

ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); 

如果你的ioc容器是以java形式配置的,采用这种(参数为所有你的ioc相关的配置类):

ApplicationContext ctx = new AnnotationConfigApplicationContext(RootConfig.class,RedisConfig.class) 

 下面是测试代码:

//获取IoC容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        //从容器中获取service实现类
        RoleService roleService = ctx.getBean(RoleService.class);
        //创建实体对象
        Role role = new Role();
        role.setRoleName("程序员");
        role.setNote("人生苦短,我恨编程!");
        //插入到数据库
        roleService.insert(role);
        //从数据库中获取刚刚插入的对象
        Role getRole = roleService.getRole(7l);
        //打印
        System.out.println(getRole.getRoleName()+":"+getRole.getNote());

运行上述代码段,在插入实体对象到数据库的时候,同时会将对象同步更新到缓存中,当从数据库中再取出插入的对象时,首先会检查缓存中是否存在该对象,若存在,直接返回,不会再去数据库中拿。 所以观察log4j在控制台打印出的日志你会发现只有一条sql语句的出现,因为在取出数据的时候是直接从缓存中拿的,并没有经过数据库,也就不会有sql语句的执行。

猜你喜欢

转载自blog.csdn.net/czr11616/article/details/84205599