利用Spring的aop整合redis缓存

来源:互联网 发布:三菱plc编程线制作 编辑:程序博客网 时间:2024/05/17 06:19

最近在做一个项目的时候,需要用到spring的aop切面方法整合redis缓存,从而提高数据库的效率,这个开始对于我来说是个挑战,开始对这个不是太了解,不过了解了redis之后,发现这是一个非常好的方法,可以利用redis作为数据库的缓存,从而提高数据库的执行效率,现在就利用aop思想来整合一下redis缓存,实现一下redis在具体web开发中的应用。

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

  1. <!-- Redis客户端 -->  
  2.         <dependency>  
  3.             <groupId>redis.clients</groupId>  
  4.             <artifactId>jedis</artifactId>  
  5.             <version>2.8.0</version>  
  6.         </dependency>  
  7.   
  8.         <!-- Jackson Json处理工具包 -->  
  9.         <dependency>  
  10.             <groupId>com.fasterxml.jackson.core</groupId>  
  11.             <artifactId>jackson-databind</artifactId>  
  12.             <version>2.7.3</version>  
  13.         </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); 

  1. @RedisEvict(type = User.class)// 表示该方法需要执行清除缓存逻辑  
  2. public void updateUserByPhone(String nickName,String phone) {  
  3.     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’,可以私信我啊!!!!!





0 0