redis与spring aop整合

redis与Spring整合做缓存

项目中要用到redis,没用过,费了很多功夫,还是做了一个勉强能可以使用的。因为项目中用mysql和mangoDB结合主从配合使用,感觉配置有点让人头大。

pom.xml

        <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>

redis数据源配置
使用的基本算是最原始的方法,因为不太了解,怕出错。

<!-- Jedis 连接池配置 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxTotal" value="2000" />
        <property name="maxIdle" value="2000" />
        <property name="minIdle" value="50" />
        <property name="maxWaitMillis" value="2000" />
        <property name="testOnBorrow" value="false" />
        <property name="testOnReturn" value="false" />
        <property name="testWhileIdle" value="false" />
    </bean>

<bean id="shardedJedisPool" class="redis.clients.jedis.ShardedJedisPool">
    <constructor-arg ref="jedisPoolConfig" />
    <constructor-arg>
        <list>
            <bean class="redis.clients.jedis.JedisShardInfo">
                <constructor-arg value="127.0.0.1" />
                <constructor-arg type="int" value="6379" />
                <constructor-arg value="redis1" />
                </bean>
            </list>
        </constructor-arg>
    </bean>
    <bean id="jedisClient" class="*.redis.JedisClientSingle" />

基于注解的aop的redis缓存
自定义注解@RedisCache

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface RedisCache {

    @SuppressWarnings("rawtypes")
    Class type();
    public int expire() default 120;      //缓存多少秒,默认无限期
    public String cacheKey() default "key"; //默认key值 为key
}

自定义注解@RedisEvict 清除数据,但是不太友好,很暴力,可以自己按照需求修改自己的

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();
}

JsonUtils 对象pojo 要实现序列化

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class JsonUtils {

    @SuppressWarnings("unused")
    private static Logger logger = LoggerFactory.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 beanType
     *            对象中的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;

    }

}

JedisClient

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);

}

JedisClientSingle

import org.springframework.beans.factory.annotation.Autowired;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPool;

public class JedisClientSingle implements JedisClient {

    @Autowired
    private ShardedJedisPool shardedJedisPool;

    @Override
    public String get(String key) {
        ShardedJedis jedis = shardedJedisPool.getResource();
//        Jedis jedis = jedisPool.getResource();
        String string = jedis.get(key);
        jedis.close();
        return string;
    }

    @Override
    public String set(String key, String value) {
        ShardedJedis jedis = shardedJedisPool.getResource();
//        Jedis jedis = jedisPool.getResource();
        String string = jedis.set(key, value);
        jedis.close();
        return string;
    }

    @Override
    public String hget(String hkey, String key) {
        ShardedJedis jedis = shardedJedisPool.getResource();
//        Jedis jedis = jedisPool.getResource();
        String string = jedis.hget(hkey, key);
        jedis.close();
        return string;
    }

    @Override
    public long hset(String hkey, String key, String value) {
        ShardedJedis jedis = shardedJedisPool.getResource();
//        Jedis jedis = jedisPool.getResource();
        Long result = jedis.hset(hkey, key, value);
        jedis.close();
        return result;
    }

    @Override
    public long incr(String key) {
        ShardedJedis jedis = shardedJedisPool.getResource();
//        Jedis jedis = jedisPool.getResource();
        Long result = jedis.incr(key);
        jedis.close();
        return result;
    }

    @Override
    public long expire(String key, int second) {
        ShardedJedis jedis = shardedJedisPool.getResource();
//        Jedis jedis = jedisPool.getResource();
        Long result = jedis.expire(key, second);
        jedis.close();
        return result;
    }

    @Override
    public long ttl(String key) {
        ShardedJedis jedis = shardedJedisPool.getResource();
//        Jedis jedis = jedisPool.getResource();
        Long result = jedis.ttl(key);
        jedis.close();
        return result;
    }

    @Override
    public long del(String key) {
        ShardedJedis jedis = shardedJedisPool.getResource();
//        Jedis jedis = jedisPool.getResource();
        Long result = jedis.del(key);
        jedis.close();
        return result;
    }

    @Override
    public long hdel(String hkey, String key) {
        ShardedJedis jedis = shardedJedisPool.getResource();
//        Jedis jedis = jedisPool.getResource();
        Long result = jedis.hdel(hkey, key);
        jedis.close();
        return result;
    }

    @Override
    public byte[] get(byte[] key) {
        ShardedJedis jedis = shardedJedisPool.getResource();
//        Jedis jedis = jedisPool.getResource();
        byte[] result = jedis.get(key);
        jedis.close();
        return result;
    }

    @Override
    public String set(byte[] key, byte[] value) {
        ShardedJedis jedis = shardedJedisPool.getResource();
//        Jedis jedis = jedisPool.getResource();
        String result = jedis.set(key, value);
        jedis.close();
        return result;
    }
}

CacheInterceptor Spring Aop
aop的处理还是很粗糙,可以自己根据实际需求修改,我觉得删除缓存那块可以优化下

<!-- 开启AOP -->
    <aop:aspectj-autoproxy proxy-target-class="true"/>*
    //proxy-target-class="true" 没记错的话使用cglib代理,不加他默认的是jdk的动态代理
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class CacheInterceptor {
    private static Logger logger = LoggerFactory.getLogger(JsonUtils.class);
    @Autowired
    private 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) {
            logger.info("缓存服务器出现问题,发邮箱,发信息..."+e);
        }
    }

    // 配置环绕方法
    @Around("@annotation(redisCache)")
    public Object doAround(ProceedingJoinPoint pjp, RedisCache redisCache)
            throws Throwable {
        //得到注解上类型
        @SuppressWarnings("rawtypes")
        Class modelType = redisCache.type();

        // 去Redis中看看有没有我们的数据 类名 + 方法名 + 参数(多个)
        String cacheKey = redisCache.cacheKey();
        if(cacheKey.equals("key")){
            cacheKey = this.getCacheKey(pjp);
        }
        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) {

            logger.info("缓存服务器出现问题,发邮箱,发信息..."+e);

        }

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

            // 后端查询数据
            result = pjp.proceed();

            try {//当取redis发生异常时,为了不影响程序正常执行,需要try..catch()...
                String json = serialize(result);
                //判断结果是否为空,不为空则返回
                if(!(json.equals("[]"))){
                    // 序列化结果放入缓存
                    jedisClient.hset(modelType.getName(), cacheKey, json);
                    int times = redisCache.expire();
                    if(times < 60){
                        times = 120;
                    }
                    if(redisCache.expire()>0) {
                        jedisClient.expire(cacheKey, times);//设置缓存时间
                    }
                }
            } catch (Exception e) {

                logger.info("缓存服务器出现问题,发邮箱,发信息..."+e);
            }

        } else {

            try{//当数据转换失败发生异常时,为了不影响程序正常执行,需要try..catch()...

                // int i =1/0;

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

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


                // 缓存命中
            } catch (Exception e) {

                //数据转换失败,到后端查询数据
                result = pjp.proceed();

                logger.info("缓存命中,但数据转换失败..."+e);
            }

        }

        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().getSimpleName();

        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();
    }
}

在service层,注入注解

    @RedisCache(type = Bim114CategoryLevel2.class)
    public List<Bim114CategoryLevel2> getlevel2List() {
        return null;
        }

开启redis,运行项目就可以了

欢迎加我QQ 971492191 讨论,一起向前

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值