spring整合Redis
1.Spring使用原生redisTemplate(数据一致性要求不高)
2.Spring基于注解整合Redis实现内容缓存(要求一致性高)
统一配置项目pom.xml文件(在整合项目dao层)
1.添加redis依赖版本
<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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.wwj</groupId>
<artifactId>small</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>small-dao</artifactId>
<properties>
<!--jar包版本 -->
<mybatis.version>3.4.5</mybatis.version>
<mybatis-spring.version>1.3.1</mybatis-spring.version>
<pagehelper.version>4.1.4</pagehelper.version>
<mysql-connector.version>5.1.41</mysql-connector.version>
<c3p0>0.9.5.3</c3p0>
<spring-redis>1.6.0.RELEASE</spring-redis>
<jredis-version>2.7.3</jredis-version>
<!--编译级别 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- mybatis核心包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!-- mybatis集成spring包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>${mybatis-spring.version}</version>
</dependency>
<!-- Mysql数据库链接jar包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector.version}</version>
<scope>runtime</scope>
</dependency>
<!-- mybatis分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>${pagehelper.version}</version>
</dependency>
<!-- c3p0 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>${c3p0}</version>
</dependency>
<!--Spring redis 缓存 -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>${spring-redis}</version>
</dependency>
<!--redis 客户端 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>${jredis-version}</version>
</dependency>
</dependencies>
</project>
2.添加redis配置文件
# Redis settings
redis.host=144.202.3.120
redis.port=6379
redis.pass=redis
redis.dbIndex=0
redis.expiration=3000
redis.maxIdle=300
redis.maxActive=600
redis.maxWait=1000
# check data 有效性
redis.testOnBorrow=true
3.在web层添加utils
package com.wwj.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;
/**
* 重写的generate()方法为数据存入缓存的无参的方法指定存入缓存中的数据的key
* @author Yun
*
*/
@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 keyGenerator() {
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());
if(objects.length != 0){
sb.append("_");
for (Object obj : objects) {
sb.append(obj.toString());
}
}
return sb.toString();
}
};
}
}
4.在容器中配置redis实例
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 自动扫描 -->
<context:component-scan base-package="com.wwj"/>
<!-- 引入配置文件 -->
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:jdbc.properties</value>
<value>classpath:redis.properties</value>
</list>
</property>
</bean>
<!-- 配置c3p0数据源 -->
<bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource">
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- spring和MyBatis整合 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!--mybatis分页插件-->
<property name="configLocation" value="classpath:spring/mybatis-config.xml"></property>
<!-- 自动扫描mapping.xml文件 -->
<property name="mapperLocations" value="classpath:Mapper/*.xml"></property>
</bean>
<!-- DAO接口所在包名,Spring会自动查找其下的类 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.wwj.mapper"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
<!-- 事务管理 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--支持事务注解的(@Transactional)-->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- redis config start -->
<!-- 配置JedisPoolConfig实例 -->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.maxIdle}" />
<property name="maxTotal" value="${redis.maxActive}" />
<property name="maxWaitMillis" value="${redis.maxWait}" />
<property name="testOnBorrow" value="${redis.testOnBorrow}" />
</bean>
<!-- 配置JedisConnectionFactory -->
<bean id="jedisConnectionFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="${redis.host}" />
<property name="port" value="${redis.port}" />
<!-- <property name="password" value="${redis.pass}" /> -->
<property name="database" value="${redis.dbIndex}" />
<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="hashKeySerializer" ref="stringRedisSerializer"/>
<property name="keySerializer" ref="stringRedisSerializer"/>
</bean>
<!-- 配置RedisCacheManager -->
<bean id="redisCacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">
<constructor-arg name="redisOperations" ref="redisTemplate" />
<property name="defaultExpiration" value="${redis.expiration}" />
<!-- 可选配置缓存区间
<property name="cacheNames">
<list>
<value>xxx</value>
</list>
</property> -->
</bean>
<!-- 配置RedisCacheConfig -->
<bean id="redisCacheConfig" class="com.wwj.utils.RedisCacheConfig">
<constructor-arg ref="jedisConnectionFactory"/>
<constructor-arg ref="redisTemplate"/>
<constructor-arg ref="redisCacheManager"/>
</bean>
</beans>
注解含义
@Cacheable:表明在进入方法之前,Spring会先去缓存服务器中查找对应key的缓存值,如果找到缓存值,那么Spring将不会再调用方法,而是将缓存值独处,返回给调用者;如果没有找到缓存值,那么Spring就会执行你的方法,将最后的结果通过key保存到缓存服务器中。
@CachePut:表明Spring会将该方法返回的值缓存到缓存服务器中,这里需要注意的是,Spring不会事先去缓存服务器中查找,而是直接执行方法,然后缓存。换句话说,该方法始终会被Spring所调用。
@CacheEvict:表示执行方法后从缓存服务器移除对应key的值;
加深理解
@Cacheable(value=“xxx” key=“zzz”)注解:标注该方法查询的结果进入缓存,再次访问时直接读取缓存中的数据
1.对于有参数的方法,指定value(缓存区间)和key(缓存的key);
对于无参数的方法,只需指定value,存到数据库中数据的key通过重写的generate()方法生成。
2.调用该注解标识的方法时,会根据value和key去redis缓存中查找数据,如果查找不到,则去数据库中查找,然后将查找到的数据存放入redis缓存中;
3.向redis中填充的数据分为两部分:
1).用来记录xxx缓存区间中的缓存数据的key的xxx~keys(zset类型)
2).缓存的数据,key:数据的key;value:序列化后的从数据库中得到的数据
4.第一次执行@Cacheable注解标识的方法,会在redis中新增上面两条数据
5.非第一次执行@Cacheable注解标识的方法,若未从redis中查找到数据,则执行从数据库中查询
* @CacheEvict()注解:移除指定缓存区间的一个或者多个缓存对象
* @param value + key 或者 value + allEntries=true
* 1.value + key 移除value缓存区间内的键为key的数据
* 2.value + allEntries=true 移除value缓存区间内的所有数据
代码操作示例(数据一致性不高)
1.建一张用户表模拟数据以及封装的resultmap对应的vo对象(需要被序列化)
2.构建服务层以及dao层和controller层
package com.wwj.controller;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.wwj.service.RedisService;
@Controller
public class RedisController {
@Autowired
private RedisService redisService;
@Resource
private RedisTemplate redisTemplate;
@RequestMapping("/selectRedis1")
@ResponseBody
public String selectRedis1(){
String personCount = null;
personCount= (String) redisTemplate.opsForValue().get("person_count");
if(personCount == null){
//redis缓存中无数据,从数据库中查询,并放入redis缓存中,设置生存时间为1小时
personCount = Integer.toString(redisService.getPersonCount());
redisTemplate.opsForValue().set("person_count", personCount, 1, TimeUnit.HOURS);
} else {
System.out.println("从redis拿取数据");
personCount= (String) redisTemplate.opsForValue().get("person_count");
}
return personCount;
}
}
代码操作示例(数据一致性高)以及增加或者删除修改数据后清空缓存
package com.wwj.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import com.wwj.mapper.PersonMapper;
import com.wwj.model.Person;
import com.wwj.service.RedisService;
@Service("redisService")
public class RedisServiceImpl implements RedisService{
@Autowired
private PersonMapper personMapper;
@Override
public Integer getPersonCount() {
// TODO Auto-generated method stub
return personMapper.selectCountOfPerson();
}
@Cacheable(value="getPersons")
@Override
public List<Person> getPersons() {
// TODO Auto-generated method stub
return personMapper.selectPersons();
}
@Cacheable(value="getPersonById",key="'getPersonById_'+#id")
@Override
public Person getPersonById(Integer id) {
// TODO Auto-generated method stub
return personMapper.selectPersonById(id);
}
@CacheEvict(value="getPersons",allEntries=true)
@Override
public int savePerson(String name) {
// TODO Auto-generated method stub
return personMapper.insertPerson(name);
}
}