SpringCache與redis集成,優雅的緩存解決方案

  緩存能夠說是加速服務響應速度的一種很是有效而且簡單的方式。在緩存領域,有不少知名的框架,如EhCache 、Guava、HazelCast等。Redis做爲key-value型數據庫,因爲他的這一特性,Redis也成爲一種流行的數據緩存工具。java

  在傳統方式下對於緩存的處理代碼是很是臃腫的。redis

  例如:咱們要把一個查詢函數加入緩存功能,大體須要三步。spring

    1、在函數執行前,咱們須要先檢查緩存中是否存在數據,若是存在則返回緩存數據數據庫

    2、若是不存在,就須要在數據庫的數據查詢出來。apache

    3、最後把數據存放在緩存中,當下次調用此函數時,就能夠直接使用緩存數據,減輕了數據庫壓力。spring-mvc

  那麼實現上面的三步須要多少代碼呢?下面是一個示例:緩存

   

  上圖中的紅色部分都是模板代碼,真正與這個函數有關的代碼卻只佔了1/5,對於全部須要實現緩存功能的函數,都須要加上臃腫的模板代碼。可謂是一種極不優雅的解決方案。mvc

  那麼如何讓臃腫的代碼重回清新的當初呢?app

  AOP不就是專門解決這種模板式代碼的最佳方案嗎,幸運的是咱們不須要再本身實現切面了,SpringCache已經爲咱們提供好了切面,咱們只須要進行簡單的配置,就能夠重回當初了,像下面這樣:框架

  

  只須要加一個註解就能夠了,對於原來的代碼連改都不須要改,是否是已經躍躍欲試了?

   對於配置SpringCache只須要三步:

第一步:加入相關依賴:

<dependency>  
   <groupId>redis.clients</groupId>  
   <artifactId>jedis</artifactId>  
  <version>2.9.0</version>  
</dependency>  
<dependency>  
   <groupId>org.springframework.data</groupId>  
   <artifactId>spring-data-redis</artifactId>  
   <version>1.6.0.RELEASE</version>  
</dependency> 
<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-lang3</artifactId> 
  <version>3.3.2</version> </dependency>

第二步:配置SpringCache,Redis鏈接等信息

applicationContext-redis.xml

<?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:p="http://www.springframework.org/schema/p"    
    xmlns:context="http://www.springframework.org/schema/context"    
    xmlns:mvc="http://www.springframework.org/schema/mvc"    
    xmlns:cache="http://www.springframework.org/schema/cache"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans      
                        http://www.springframework.org/schema/beans/spring-beans-4.2.xsd      
                        http://www.springframework.org/schema/context      
                        http://www.springframework.org/schema/context/spring-context-4.2.xsd      
                        http://www.springframework.org/schema/mvc      
                        http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd  
                        http://www.springframework.org/schema/cache   
                        http://www.springframework.org/schema/cache/spring-cache-4.2.xsd">
    

  <!-- 配置文件加載 -->

  <context:property-placeholder location="classpath:*.properties"/>

<cache:annotation-driven cache-manager="cacheManager"/>
    <!-- redis鏈接池 -->
    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxIdle" value="${redis.maxIdle}" />     
         <property name="maxWaitMillis" value="${redis.maxWait}" />    
         <property name="testOnBorrow" value="${redis.testOnBorrow}" /> 
    </bean>
    <!-- 鏈接工廠 -->
    <bean id="JedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"    
       p:host-name="${redis.host}" p:port="${redis.port}" p:password="${redis.pass}" p:pool-config-ref="poolConfig"/>
    <!-- redis模板 -->
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">    
         <property name="connectionFactory" ref="JedisConnectionFactory" />    
    </bean>
     
    <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">    
         <property name="caches">    
            <set>    
                <!-- 這裏能夠配置多個redis -->  
                <bean class="com.cky.rest.utils.RedisCache">    
                     <property name="redisTemplate" ref="redisTemplate" />    
                     <property name="name" value="content"/>    
                     <!-- name對應的名稱要在類或方法的註解中使用 -->  
                </bean>  
            </set>    
         </property>    
     </bean>    
</beans>

redis.properties文件:

# Redis settings  
# server IP  
redis.host=192.168.100.55 
# server port  
redis.port=6379  
# server pass  
redis.pass=
# use dbIndex  
redis.database=0 
#max idel instance of jedis
redis.maxIdle=300  
#if wait too long ,throw JedisConnectionException
redis.maxWait=3000  
#if true,it will validate before borrow jedis instance,what you get instance is all usefull
redis.testOnBorrow=true 

第三步,編寫Cache接口實現類

  Spring對於緩存只是提供了抽象的接口,而且經過接口來調用功能,沒有具體的實現類,因此須要咱們本身實現具體的操做。

  在上面配置中可知,每一個實現類都會注入一個redisTemplate實例,咱們就能夠經過redisTemplate來操做redis

package com.cky.rest.utils;

import java.io.Serializable;

import org.apache.commons.lang3.SerializationUtils;
import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;

public class RedisCache implements Cache {

    private RedisTemplate<String, Object> redisTemplate;
    private String name;

    @Override
    public void clear() {
        System.out.println("-------緩存清理------");
        redisTemplate.execute(new RedisCallback<String>() {
            @Override
            public String doInRedis(RedisConnection connection) throws DataAccessException {
                connection.flushDb();
                return "ok";
            }
        });
    }

    @Override
    public void evict(Object key) {
        System.out.println("-------緩存刪除------");
        final String keyf=key.toString();
        redisTemplate.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.del(keyf.getBytes());
            }
            
        });

    }

    @Override
    public ValueWrapper get(Object key) {
        System.out.println("------緩存獲取-------"+key.toString());
        final String keyf = key.toString();
        Object object = null;
        object = redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                byte[] key = keyf.getBytes();
                byte[] value = connection.get(key);
                if (value == null) {
                    System.out.println("------緩存不存在-------");
                    return null;
                }
                return SerializationUtils.deserialize(value);
            }
        });
        ValueWrapper obj=(object != null ? new SimpleValueWrapper(object) : null);
        System.out.println("------獲取到內容-------"+obj);
        return  obj;
    }

    @Override
    public void put(Object key, Object value) {
        System.out.println("-------加入緩存------");
        System.out.println("key----:"+key);
        System.out.println("key----:"+value);
        final String keyString = key.toString();
        final Object valuef = value;
        final long liveTime = 86400;
        redisTemplate.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                byte[] keyb = keyString.getBytes();
                byte[] valueb = SerializationUtils.serialize((Serializable) valuef);
                connection.set(keyb, valueb);
                if (liveTime > 0) {
                    connection.expire(keyb, liveTime);
                }
                return 1L;
            }
        });

    }
    
    @Override
    public <T> T get(Object arg0, Class<T> arg1) {
        // TODO Auto-generated method stub
        return null;
    }
    
    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public Object getNativeCache() {
        return this.redisTemplate;
    }
    
    @Override
    public ValueWrapper putIfAbsent(Object arg0, Object arg1) {
        // TODO Auto-generated method stub
        return null;
    }

    public RedisTemplate<String, Object> getRedisTemplate() {
        return redisTemplate;
    }

    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public void setName(String name) {
        this.name = name;
    }
}
View Code

 

  

在配置過程當中曾經出現過兩次錯誤:

1.Xxxx.ClassNotFoundException 最後發現是jar下載不完整,把maven本地倉庫的對應jar包文件夾刪除完重新下載就行了

2.Xxxx.MethodNotFoundException 這種狀況是版本不對,換成第一步中的版本就能夠了

SpringCache中常見註解的使用:

@Cacheable註解

  最經常使用的註解,會把被註解方法的返回值緩存。工做原理是:首先在緩存中查找,若是沒有執行方法並緩存結果,而後返回數據。此註解的緩存名必須指定,和cacheManager中的caches中的某一個Cache的name值相對應。能夠使用value或cacheNames指定。

  若是沒有指定key屬性,spring會使用默認的主鍵生成器產生主鍵。也能夠自定義主鍵,在key中能夠使用SpEL表達式。以下:

    @Cacheable(cacheNames=」content」,key=」#user.userId」)
  Public User getUser(User user){
        xxxxx
    }        

 

  能夠使用condition屬性,來給緩存添加條件,以下:

@Cacheable(cacheNames=」content」,key=」#user.userId」,condition=」#user.age<40)
Public User getUser(User user){xxxxx}

@CachePut註解

  先執行方法,而後將返回值放回緩存。能夠用做緩存的更新。

@CacheEvict註解

  該註解負責從緩存中顯式移除數據,一般緩存數據都有有效期,當過時時數據也會被移除。

  此註解多了兩個屬性:

    allEntries是否移除全部緩存條目。

    beforeInvocation:在方法調用前仍是調用後完成移除操做。true/false

相關文章
相關標籤/搜索