Spring快取機制整合Redis
首先,在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語句的執行。