首先,在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>
解释一下上述配置信息:
- 配置连接池:配置了Redis数据库连接池的最大闲置连接数、最大连接数和最长等待时间
- 配置Redis连接工厂:配置了Redis数据库的主机和端口号,同时引入了之前配置的连接池
- 配置序列化器:什么是序列化器呢?由于Redis只能提供基于字符串型的操作,而在java中是以类和对象为主,序列化器的作用是将java对象和Redis字符串相互转换,使得可以将java对象序列化为字符串存入Redis,同时也可以取出Redis序列化过的字符串转换成java对象。
- 配置RedisTemplate:redisTemplate封装了对redis的操作,要操作redis,则肯定要引入上面配置好的连接工厂和序列化器
- 配置缓存管理器:有了对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,结合代码段我们对这些缓存策略分别给出讲解:
- @Cacheable:通常在查询方法上使用,如果缓存中有值,则直接返回缓存中的值,如果没有,则去数据库中取值,取出后存入缓存。这里@Cacheable有两个属性,value和key,value表示使用的缓存管理器的名称(之前配置的),key表示要存入缓存中的key值,其中#id表示接受方法中的参数以此组成key值。
- @CachePut:通常在插入和更新方法中使用,此注解无论如何都会更新数据库,更新数据库后会同步更新到缓存。同样有value和key两个属性,value为缓存管理器的名称,key表示要存入缓存中的key值。
- @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语句的执行。