1. 程式人生 > >Spring快取機制整合Redis

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>

解釋一下上述配置資訊:

  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語句的執行。