redis+spring实现透明缓存

1、什么是Redis:


Redis是由意大利人Salvatore Sanfilippo(网名:antirez)开发的一款内存高速缓存数据库。Redis全称为:Remote Dictionary Server(远程数据服务),该软件使用C语言编写,Redis是一个key-value存储系统,它支持丰富的数据类型,如:string、list、set、zset(sorted set)、hash。

2、Redis特点:

Redis以内存作为数据存储介质,所以读写数据的效率极高,远远超过数据库。以设置和获取一个256字节字符串为例,它的读取速度可高达110000次/s,写速度高达81000次/s。

3、Redis缺点:

3.1. 由于是内存数据库,所以,单台机器,存储的数据量,跟机器本身的内存大小。虽然redis本身有key过期策略,但是还是需要提前预估和节约内存。如果内存增长过快,需要定期删除数据。

3.2. 如果进行完整重同步,由于需要生成rdb文件,并进行传输,会占用主机的CPU,并会消耗现网的带宽。不过redis2.8版本,已经有部分重同步的功能,但是还是有可能有完整重同步的。比如,新上线的备机。

3.3. 修改配置文件,进行重启,将硬盘中的数据加载进内存,时间比较久。在这个过程中,redis不能提供服务。

4、为什么要使用Redis:

当大量用户使用同一个查询功能,程序内部就会调用同一个查询方法,每次都会向服务器请求数据,可能会出现响应慢或者加载失败的情况,效率极低。使用redis后,只要有一个用户使用这个查询,会先通过redis的key去redis里面找,如果没找到则会向数据库去查询,当查询完成后结果会保存到redis里面,redis再返回出去。当后面的用户使用同一个查询时就会使用redis里的数据,不会再去向数据库去查询,提高了效率。

5、为什么要使用AOP来实现Redis

因为使用AOP可以在不改变原来代码的基础上增加新功能

6、具体的代码实现

使用到的jar包:

    <dependencies>
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-core</artifactId>
          <version>3.2.13.RELEASE</version>
      </dependency>
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-beans</artifactId>
          <version>3.2.13.RELEASE</version>
      </dependency>
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context-support</artifactId>
          <version>3.2.13.RELEASE</version>
      </dependency>
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>3.2.13.RELEASE</version>
      </dependency>
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-aop</artifactId>
          <version>3.2.13.RELEASE</version>
      </dependency>
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-tx</artifactId>
          <version>3.2.13.RELEASE</version>
      </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>3.2.13.RELEASE</version>
    </dependency>

      <dependency>
          <groupId>org.aspectj</groupId>
          <artifactId>aspectjweaver</artifactId>
          <version>1.8.6</version>
      </dependency>
      <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.12</version>
      </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.30</version>
    </dependency> 
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.3.0</version>
    </dependency>
    <dependency>
        <groupId>commons-dbcp</groupId>
        <artifactId>commons-dbcp</artifactId>
        <version>1.4</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>1.2.3</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    <dependency>
          <groupId>redis.clients</groupId>
          <artifactId>jedis</artifactId>
          <version>2.6.3</version>
      </dependency>
  </dependencies>

spring 配置文件:

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

    <aop:aspectj-autoproxy/> <!-- 声明切面代理 -->
    <context:annotation-config/> <!-- 向spring注册容器,容器提供相对应的注解 -->
    <context:component-scan base-package="com.ketai.liudun"/> <!-- 告诉spring要扫描的包 -->
    
    <!-- 配置Jedis -->
    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">  
        <property name="maxTotal" value="20"></property> <!-- 最大连接数 -->
        <property name="maxIdle" value="10"></property>    <!-- 最大空闲连接数 -->
        <!-- MaxWait 连接池中连接用完时,新的请求等待时间,毫秒,取值-1,表示无限等待,直到超时为止,
        也可取值9000,表示9秒后超时。超过时间会出错误信息一般把maxActive设置成可能的并发量就行了 -->
        <property name="MaxWaitMillis" value="1000"></property>
        <property name="testOnBorrow" value="true"></property>
    </bean>

    <bean id="jedisPool" class="redis.clients.jedis.JedisPool">
        <!--通过构造注入,index == 参数下标 -->
        <constructor-arg index="0" ref="jedisPoolConfig" /><!-- 加载jedisPool配置信息 -->
        <constructor-arg index="1" value="127.0.0.1" /><!-- redis 主机地址 -->
        <constructor-arg index="2" value="6379" /><!-- redis 连接端口 -->
        <!-- timeout -->
        <constructor-arg index="3" value="2000" />     <!-- 超时时间 -->
        <!-- password -->
        <constructor-arg index="4" value="IT_training_CZKT_is_Best!" /> <!-- 配置密码 -->
    </bean>

    <import resource="spring-mybatis.xml"/>
</beans> 

测试类:

package com.ketai.liudun;

import static org.junit.Assert.assertNotNull;

import org.aspectj.lang.annotation.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.ketai.liudun.entity.User;
import com.ketai.liudun.service.UserService;
import com.ketai.liudun.service.impl.UserServiceImpl;

public class TestAspectOfCache {

    private UserService userService;
    
    /**
     * 在执行查询前先获得spring配置文件
     * @throws Exception
     */
    @Before
    public void setUp() throws Exception {
        ClassPathXmlApplicationContext ac = 
                new ClassPathXmlApplicationContext();
        ac.setValidating(false);
        ac.setConfigLocation("spring-beans.xml");
        ac.refresh();
        System.out.println("Spring IoC container initialized.");
        userService = (UserService)ac.getBean("userService");
        assertNotNull(userService);
        System.out.println("userService initialized.");
    }

    @Test
    public void testFindById() {
        User u = userService.findUser(78);
        assertNotNull(u);
        System.out.println(u);
    }
    
//    @Test
//    public void testAddUser(){
//        User user = new User();
//        user.setUserName("zhangjun");
//        user.setPassword("333");
//        userService.addUser(user);
//    }

    public void setUserService(UserService userService) {
        this.userService = userService;
    }
}

redis实现类:

package com.ketai.liudun.cache;

import javax.annotation.Resource;

import org.springframework.stereotype.Component;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

@Component
public class RedisCache {

    @Resource
    private JedisPool jedisPool;
    
    public Object getFromCache(String key){
        Jedis jedis = jedisPool.getResource(); //连接到redis
        byte[] result = jedis.get(key.getBytes());//通过key来获得值,保存在byte数组里面
        
        //如果查询没有为空
        if(null == result){
            return null;
        }
        //查询到了,反序列化
        return SerializeUtil.unSerialize(result);
    }
    
    //将数据库中查询到的数据放入redis
    public void save(String redisKey, Object obj){
        //序列化
        byte[] bytes = SerializeUtil.serialize(obj);
        //存入redis
        Jedis conn = jedisPool.getResource();
        String result = conn.set(redisKey.getBytes(), bytes);
        
        if("OK".equals(result)){
            System.out.println("[RedisCache]:数据成功保存到redis cache...");
        }
    }
}

序列工具类:

package com.ketai.liudun.cache;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class SerializeUtil {

    //序列化 
    public static byte[] serialize(Object obj){
        ObjectOutputStream obi=null;
        ByteArrayOutputStream bai=null;//byte类型数字缓冲区
        try {
            bai=new ByteArrayOutputStream();
            obi=new ObjectOutputStream(bai);
            obi.writeObject(obj);
            byte[] byt=bai.toByteArray();
            return byt;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
    
    //反序列化
    public static Object unSerialize(byte[] byt){
        ObjectInputStream oii=null;
        ByteArrayInputStream bis=null;
        bis=new ByteArrayInputStream(byt);
        try {
            oii=new ObjectInputStream(bis);
            Object obj=oii.readObject();
            return obj;
        } catch (Exception e) {
            
            e.printStackTrace();
        }
        return null;
    }
}

AOP增强类:

package com.ketai.liudun.cache;

import javax.annotation.Resource;

import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;


/**
 * 该类表示一个切面,表示:
 *     “调用find*方法根据id查某个对象时,先从缓存查询,如果没有,再查数据库”
* 
*/
@Component
@Aspect
public class RedisCacheAspect {

    private static String PROJECT_NAME = "ITRIP-";
    private static String MODULE_NAME  = "AUTH-";
    
    private static final Logger log = 
            Logger.getLogger(RedisCacheAspect.class);
    
    @Resource
    private RedisCache redisCache;
    
    @Pointcut("execution(* com.ketai.liudun.service..*.find*(..))")
    public void getCachePointcut(){}
    
    @Around("getCachePointcut()")
    public Object around(ProceedingJoinPoint joinPoint){
        
        //先获取目标方法参数
        String id = null;
        Object[] args = joinPoint.getArgs();
        if (args != null && args.length > 0) {
           id = String.valueOf(args[0]);
        }
        
        //得到一个具有唯一性的缓存key
        String redisKey = getRedisKey(joinPoint, id);
        log.debug("进入环绕增强前半部分,尝试从redis查询,生成的缓存key:"+redisKey);
        //根据key获取从redis中查询到的对象
        Object data = redisCache.getFromCache(redisKey);
        //如果查询到了
        if(null != data){
            log.debug("["+redisKey+"]缓存命中:"+data);
            return data;
        }
        //没有查到,那么查询数据库
        try {
            data = joinPoint.proceed();//执行目标方法(service中的find*方法)
        } catch (Throwable e) {
            e.printStackTrace();
        }
        log.debug("没有从redis中取出数据,改为从数据库中查询的数据..."+data);
        
        //后置:将数据库中查询的数据放到redis中
        redisCache.save(redisKey, data);
        log.debug("数据已存储到redis"+data);
        //将查询到的数据返回
        return data;
    }
    private String getRedisKey(ProceedingJoinPoint joinPoint, String id) {
        return PROJECT_NAME + MODULE_NAME 
                + joinPoint.getTarget().getClass().getName() + "-" +id;
    }
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值