利用Spring的aop整合redis緩存

最近在做一個項目的時候,需要用到spring的aop切面方法整合redis緩存,從而提高數據庫的效率,這個開始對於我來說是個挑戰,開始對這個不是太瞭解,不過了解了redis之後,發現這是一個非常好的方法,可以利用redis作爲數據庫的緩存,從而提高數據庫的執行效率,現在就利用aop思想來整合一下redis緩存,實現一下redis在具體web開發中的應用。

第一步,考入redis需要的jar包,靠操作json的jar包

<!-- Redis客戶端 -->  
        <dependency>  
            <groupId>redis.clients</groupId>  
            <artifactId>jedis</artifactId>  
            <version>2.8.0</version>  
        </dependency>  
  
        <!-- Jackson Json處理工具包 -->  
        <dependency>  
            <groupId>com.fasterxml.jackson.core</groupId>  
            <artifactId>jackson-databind</artifactId>  
            <version>2.7.3</version>  
        </dependency>

 

第二步,在spring的配置文件裏添加spring識別切面的配置

<!-- 開啓切面代理 使得spring認識 @Aspect -->

  <aop:aspectj-autoproxy/>

第三步,然後定義兩個標註在Service實現方法上的註解,用於傳遞類型參數:

package com.fupin.redis.Service;

import java.lang.annotation.Documented;  
import java.lang.annotation.ElementType;  
import java.lang.annotation.Retention;  
import java.lang.annotation.RetentionPolicy;  
import java.lang.annotation.Target;  
 
@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.METHOD)  
@Documented  
public @interface RedisCache {  
      
    @SuppressWarnings("rawtypes")
    Class type();  
    public int expire() default 0;      //緩存多少秒,默認無限期    
    public String cacheKey() ;
}


package com.fupin.redis.Service;

import java.lang.annotation.ElementType;  
import java.lang.annotation.Retention;  
import java.lang.annotation.RetentionPolicy;  
import java.lang.annotation.Target;  
 
@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.METHOD)  
public @interface RedisEvict {  
    @SuppressWarnings("rawtypes")
    Class type();  
}  


註解使用方式如下:

@RedisCache(type = User.class, expire = 200000000) 

public List<User> findUsersByGroupId(Integer group_id) {  

    return groupDao.findUsersByGroupId(group_id); 

    }  

    @RedisEvict(type = User.class)// 表示該方法需要執行清除緩存邏輯  
    public void updateUserByPhone(String nickName,String phone) {  
        UserDao.updateUserByPhone(nickName, phone);  
    }  


第四步:創建JsonUtils類,實現json與類對象的互相轉化

package com.fupin.redis.Service;

import java.io.IOException;  
import java.util.HashMap;  
import java.util.List;  
import java.util.Map;  
import java.util.Map.Entry;  
 
import org.apache.log4j.Logger;  
 
import com.fasterxml.jackson.core.type.TypeReference;  
import com.fasterxml.jackson.databind.JavaType;  
import com.fasterxml.jackson.databind.ObjectMapper;  
 
 
 
public class JsonUtils {  
 
    @SuppressWarnings("unused")
    private static Logger logger = Logger.getLogger(JsonUtils.class);  
 
    // 定義jackson對象  
    private static final ObjectMapper MAPPER = new ObjectMapper();  
 
    /**  
     * 將對象轉換成json字符串。  
     *   
     * @param data  
     * @return  
     * @throws IOException  
     */  
    public static String objectToJson(Object data) {  
        try {  
            String string = MAPPER.writeValueAsString(data);  
            return string;  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
        return null;  
    }  
 
    /**  
     * 將json結果集轉化爲對象  
     *   
     * @param jsonData  
     *            json數據  
     * @param clazz  
     *            對象中的object類型  
     * @return  
     */  
    public static <T> T jsonToPojo(String jsonData, Class<T> beanType) {  
        try {  
            T t = MAPPER.readValue(jsonData, beanType);  
            return t;  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
        return null;  
    }  
 
    /**  
     * 將json數據轉換成pojo對象list  
     *   
     * @param jsonData  
     * @param beanType  
     * @return  
     */  
    public static <T> List<T> jsonToList(String jsonData, Class<T> beanType) {  
        JavaType javaType = MAPPER.getTypeFactory().constructParametricType(  
                List.class, beanType);  
        try {  
            List<T> list = MAPPER.readValue(jsonData, javaType);  
            return list;  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
 
        return null;  
    }  
 
    /**  
     * json string convert to map with javaBean  
     */  
    public static <T> Map<String, T> jsonTomap(String jsonStr, Class<T> clazz)  
            throws Exception {  
        Map<String, Map<String, Object>> map = MAPPER.readValue(jsonStr,  
                new TypeReference<Map<String, T>>() {  
                });  
        Map<String, T> result = new HashMap<String, T>();  
        for (Entry<String, Map<String, Object>> entry : map.entrySet()) {  
            result.put(entry.getKey(), mapTopojo(entry.getValue(), clazz));  
        }  
        return result;  
    }  
 
    /**  
     * json string convert to map  
     */  
    @SuppressWarnings("unchecked")
    public static <T> Map<String, Object> jsonTomap(String jsonStr) {  
        try {  
            return MAPPER.readValue(jsonStr, Map.class);  
        } catch (Exception e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
        return null;  
    }  
 
    /**  
     * map convert to javaBean  
     */  
    @SuppressWarnings("rawtypes")
    public static <T> T mapTopojo(Map map, Class<T> clazz) {  
 
        try {  
            return MAPPER.convertValue(map, clazz);  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
        return null;  
 
    }  
 
}  



第五部:創建aop切面方法,實現業務邏輯

package com.fupin.redis.Service;

import java.util.List;  

import org.aspectj.lang.JoinPoint;  
import org.aspectj.lang.ProceedingJoinPoint;  
import org.aspectj.lang.annotation.Around;  
import org.aspectj.lang.annotation.Aspect;  
import org.aspectj.lang.annotation.Before;  
import org.aspectj.lang.reflect.MethodSignature;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.stereotype.Component;  

import com.fupin.redis.Service.RedisEvict;  
import com.fupin.redis.Service.JedisClient;  
import com.fupin.redis.Service.JsonUtils;  

@Aspect  
@Component  
public class CacheInterceptor {  

    @Autowired  
    JedisClient jedisClient;  


    //前置由於數據庫數據變更  清理redis緩存  
    @Before("@annotation(redisEvict)")  
    public void doBefore (JoinPoint jp,RedisEvict redisEvict){  
        try{  

            String modelName = redisEvict.type().getName();  
            // 清除對應緩存  
            jedisClient.del(modelName);    

        }catch (Exception e) {  

            e.printStackTrace();  
            System.out.println("緩存服務器出現問題,發郵箱,發信息...");       
        }  
    }  

    // 配置環繞方法  
    @Around("@annotation(redisCache)")  
    public Object doAround(ProceedingJoinPoint pjp, RedisCache redisCache)  
            throws Throwable {  
        //得到註解上類型  
        @SuppressWarnings("rawtypes")
        Class modelType = redisCache.type();  
        //System.out.println(modelType.getName());  

        // 去Redis中看看有沒有我們的數據 包名+ 類名 + 方法名 + 參數(多個)  
        String cacheKey = redisCache.cacheKey();  
        System.out.println(cacheKey);  

        String value = null;  

        try {//當取redis發生異常時,爲了不影響程序正常執行,需要try..catch()...  

            //檢查redis中是否有緩存  
            value = jedisClient.hget(modelType.getName(),cacheKey);  
            System.out.println(jedisClient.hget(modelType.getName(),cacheKey));

        } catch (Exception e) {  

            e.printStackTrace();  
            System.out.println("緩存服務器出現問題,發郵箱,發信息...");  

        }  

        // result是方法的最終返回結果  
        Object result = null;  
        if (null == value) {  
            // 緩存未命中  
            System.out.println("緩存未命中");  

            // 後端查詢數據    
            result = pjp.proceed();  

            try {//當取redis發生異常時,爲了不影響程序正常執行,需要try..catch()...  

                // 序列化結果放入緩存  
                String json = serialize(result);  
                jedisClient.hset(modelType.getName(), cacheKey, json);  

                if(redisCache.expire()>0) {   
                    jedisClient.expire(cacheKey, redisCache.expire());//設置緩存時間  
                }  

            } catch (Exception e) {  

                e.printStackTrace();  
                System.out.println("緩存服務器出現問題,發郵箱,發信息...");  
            }  

        } else {  

            try{//當數據轉換失敗發生異常時,爲了不影響程序正常執行,需要try..catch()...  

                // int i =1/0;  

                // 得到被代理方法的返回值類型  
                @SuppressWarnings("rawtypes")
                Class returnType = ((MethodSignature) pjp.getSignature()).getReturnType();  

                //把json反序列化  
                result = deserialize(value, returnType, modelType);  


                // 緩存命中  
                System.out.println("緩存命中");  
            } catch (Exception e) {  

                //數據轉換失敗,到後端查詢數據    
                result = pjp.proceed();  

                e.printStackTrace();  
                System.out.println("緩存命中,但數據轉換失敗...");  
            }  

        }  

        return result;  
    }  


    protected String serialize(Object target) {  
        return JsonUtils.objectToJson(target);  
    }  

    @SuppressWarnings({ "unchecked", "rawtypes" })
    protected Object deserialize(String jsonString, Class clazz, Class modelType) {  
        // 序列化結果應該是List對象  
        if (clazz.isAssignableFrom(List.class)) {  
            return JsonUtils.jsonToList(jsonString, modelType);  
        }  

        // 序列化結果是普通對象  
        return JsonUtils.jsonToPojo(jsonString, clazz);  
    }  


    // 包名+ 類名 + 方法名 + 參數(多個) 生成Key  
    public String getCacheKey(ProceedingJoinPoint pjp) {  
        StringBuffer key = new StringBuffer();  
        // 包名+ 類名 cn.core.serice.product.ProductServiceImpl.productList  
        String packageName = pjp.getTarget().getClass().getName();  

        key.append(packageName);  
        // 方法名  
        String methodName = pjp.getSignature().getName();  
        key.append(".").append(methodName);  

        // 參數(多個)  
        Object[] args = pjp.getArgs();  

        for (Object arg : args) {  
            // 參數  
            key.append(".").append(arg.toString());  
        }  

        return key.toString();  
    }  
}  


第六步:創建JedisClient方法,定義一下redis需要用到的方法

package com.fupin.redis.Service;

public interface JedisClient {  
    String get(String key);  
 
    byte[] get(byte[] key);  
 
    String set(String key, String value);  
 
    String set(byte[] key, byte[] value);  
 
    String hget(String hkey, String key);  
 
    long hset(String hkey, String key, String value);  
 
    long incr(String key);  
 
    long expire(String key, int second);  
 
    long ttl(String key);  
 
    long del(String key);  
 
    long hdel(String hkey, String key);  
      
}  



第七步:創建擴展類,實現這些方法

package com.fupin.redis.Service;

import org.springframework.beans.factory.annotation.Autowired;  

import redis.clients.jedis.Jedis;  
import redis.clients.jedis.JedisPool;  
 
import com.fupin.redis.Service.JedisClient;  

public class JedisClientSingle implements JedisClient{  
 
    @Autowired  
    private JedisPool jedisPool;   
      
    @Override  
    public String get(String key) {  
        Jedis jedis = jedisPool.getResource();  
        String string = jedis.get(key);  
        jedis.close();  
        return string;  
    }  
 
    @Override  
    public String set(String key, String value) {  
        Jedis jedis = jedisPool.getResource();  
          
        String string = jedis.set(key, value);  
        jedis.close();  
        return string;  
    }  
 
    @Override  
    public String hget(String hkey, String key) {  
        Jedis jedis = jedisPool.getResource();  
        String string = jedis.hget(hkey, key);  
        jedis.close();  
        return string;  
    }  
 
    @Override  
    public long hset(String hkey, String key, String value) {  
        Jedis jedis = jedisPool.getResource();  
        Long result = jedis.hset(hkey, key, value);  
        jedis.close();  
        return result;  
    }  
 
    @Override  
    public long incr(String key) {  
        Jedis jedis = jedisPool.getResource();  
        Long result = jedis.incr(key);  
        jedis.close();  
        return result;  
    }  
 
    @Override  
    public long expire(String key, int second) {  
        Jedis jedis = jedisPool.getResource();  
        Long result = jedis.expire(key, second);  
        jedis.close();  
        return result;  
    }  
 
    @Override  
    public long ttl(String key) {  
        Jedis jedis = jedisPool.getResource();  
        Long result = jedis.ttl(key);  
        jedis.close();  
        return result;  
    }  
 
    @Override  
    public long del(String key) {  
        Jedis jedis = jedisPool.getResource();  
        Long result = jedis.del(key);  
        jedis.close();  
        return result;  
    }  
 
    @Override  
    public long hdel(String hkey, String key) {  
        Jedis jedis = jedisPool.getResource();  
        Long result = jedis.hdel(hkey, key);  
        jedis.close();  
        return result;  
    }  
 
    @Override  
    public byte[] get(byte[] key) {  
        Jedis jedis = jedisPool.getResource();  
        byte[] result = jedis.get(key);  
        jedis.close();  
        return result;  
    }  
 
    @Override  
    public String set(byte[] key, byte[] value) {  
        Jedis jedis = jedisPool.getResource();  
        String result = jedis.set(key, value);  
        jedis.close();  
        return result;  
    }  
}  




第八步。創建JedisClientCluster類

package com.fupin.redis.Service;

import org.springframework.beans.factory.annotation.Autowired;

import redis.clients.jedis.JedisCluster;

public class JedisClientCluster implements JedisClient {  
    
    @Autowired  
    private JedisCluster jedisCluster;  
      
    @Override  
    public String get(String key) {  
        return jedisCluster.get(key);  
    }  
 
    @Override  
    public String set(String key, String value) {  
        return jedisCluster.set(key, value);  
    }  
 
    @Override  
    public String hget(String hkey, String key) {  
        return jedisCluster.hget(hkey, key);  
    }  
 
    @Override  
    public long hset(String hkey, String key, String value) {  
        return jedisCluster.hset(hkey, key, value);  
    }  
 
    @Override  
    public long incr(String key) {  
        return jedisCluster.incr(key);  
    }  
 
    @Override  
    public long expire(String key, int second) {  
        return jedisCluster.expire(key, second);  
    }  
 
    @Override  
    public long ttl(String key) {  
        return jedisCluster.ttl(key);  
    }  
 
    @Override  
    public long del(String key) {  
          
        return jedisCluster.del(key);  
    }  
 
    @Override  
    public long hdel(String hkey, String key) {  
          
        return jedisCluster.hdel(hkey, key);  
    }

    @Override
    public byte[] get(byte[] key) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public String set(byte[] key, byte[] value) {
        // TODO Auto-generated method stub
        return null;
    }  
 
    
 
}  


第九步:在配置文件裏配置redis連接,並同時注入JedisClientSingle

 <bean id="redisClient" class="redis.clients.jedis.JedisPool">  
        <constructor-arg name="host" value="127.0.0.1"></constructor-arg>  
        <constructor-arg name="port" value="6379"></constructor-arg>  
    </bean>  
    <bean id="jedisClient" class="com.fupin.redis.Service.JedisClientSingle" >  
</bean>


以上就完成了所有的配置,大家注意,在配置配置文件的時候,不要忘了掃描包,具體還有什麼不明白的,可以關注我的微博‘莫失XGYL’,可以私信我啊!!!!!





發佈了48 篇原創文章 · 獲贊 50 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章