程序员

SSM(七)在JavaWeb应用中使用Redis

前言

1

由于最近换(mang)了(de)家(yi)公(bi)司接触了新的东西所以很久没有更新了。
这次谈谈Redis,关于Redis应该很多朋友就算没有用过也听过,算是这几年最流行的NoSql之一了。
Redis的应用场景非常多这里就不一一列举了,这次就以一个最简单的也最常用的 缓存数据 来举例。
先来看一张效果图:

01.gif

作用就是在每次查询接口的时候首先判断Redis中是否有缓存,有的话就读取,没有就查询数据库并保存到Redis中,下次再查询的话就会直接从缓存中读取了。
Redis中的结果:

02.gif

之后查询redis发现确实是存进来了。

Redis安装与使用

首先第一步自然是安装Redis。我是在我VPS上进行安装的,操作系统是CentOS6.5

  • 下载Redishttps://redis.io/download,我机器上安装的是3.2.5

  • 将下载下来的’reidis-3.2.5-tar.gz’上传到usr/local这个目录进行解压。

  • 进入该目录。

    03.jpg
  • 编译安装

    make
    make install
  • 修改redis.conf配置文件。

这里我只是简单的加上密码而已。

vi redis.conf
requirepass 你的密码
  • 启动Redis

启动时候要选择我们之前修改的配置文件才能使配置文件生效。

进入src目录
cd /usr/local/redis-3.2.5/src
启动服务
./redis-server ../redis.conf
  • 登陆redis
    ./redis-cli -a 你的密码

Spring整合Redis

这里我就直接开始用Spring整合毕竟在实际使用中都是和Spring一起使用的。

Spring切面使用缓存

Spring的AOP真是是一个好东西,还不太清楚是什么的同学建议先自行Google下吧。
在不使用切面的时候如果我们想给某个方法加入缓存的话肯定是在方法返回之前就要加入相应的逻辑判断,只有一个或几个倒还好,如果有几十上百个的话那GG了,而且维护起来也特别麻烦。

好在Spring的AOP可以帮我们解决这个问题。
这次就在我们需要加入缓存方法的切面加入这个逻辑,并且只需要一个配置即可搞定,就是上文中所提到的配置文件,如下:

    
    
        
        

        

        
    

这里我们使用表达式execution(* com.crossoverJie.service.*.select*(..))来拦截service中所有以select开头的方法。这样只要我们要将加入的缓存的方法以select命名开头的话每次进入方法之前都会进入我们自定义的MethodCacheInterceptor拦截器。
这里贴一下MethodCacheInterceptor中处理逻辑的核心方法:

@Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Object value = null;

        String targetName = invocation.getThis().getClass().getName();
        String methodName = invocation.getMethod().getName();
        // 不需要缓存的内容
        //if (!isAddCache(StringUtil.subStrForLastDot(targetName), methodName)) {
        if (!isAddCache(targetName, methodName)) {
            // 执行方法返回结果
            return invocation.proceed();
        }
        Object[] arguments = invocation.getArguments();
        String key = getCacheKey(targetName, methodName, arguments);
        logger.debug("redisKey: " + key);
        try {
            // 判断是否有缓存
            if (redisUtil.exists(key)) {
                return redisUtil.get(key);
            }
            // 写入缓存
            value = invocation.proceed();
            if (value != null) {
                final String tkey = key;
                final Object tvalue = value;
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        if (tkey.startsWith("com.service.impl.xxxRecordManager")) {
                            redisUtil.set(tkey, tvalue, xxxRecordManagerTime);
                        } else if (tkey.startsWith("com.service.impl.xxxSetRecordManager")) {
                            redisUtil.set(tkey, tvalue, xxxSetRecordManagerTime);
                        } else {
                            redisUtil.set(tkey, tvalue, defaultCacheExpireTime);
                        }
                    }
                }).start();
            }
        } catch (Exception e) {
            e.printStackTrace();
            if (value == null) {
                return invocation.proceed();
            }
        }
        return value;
    }
  • 先是查看了当前方法是否在我们自定义的方法中,如果不是的话就直接返回,不进入拦截器。
  • 之后利用反射获取的类名、方法名、参数生成rediskey
  • 用key在redis中查询是否已经有缓存。
  • 有缓存就直接返回缓存内容,不再继续查询数据库。
  • 如果没有缓存就查询数据库并将返回信息加入到redis中。

使用PageHelper

这次为了分页方便使用了比较流行的PageHelper来帮我们更简单的进行分页。
首先是新增一个mybatis的配置文件mybatis-config




    
        
        
        
        
        
        
        
        
        
        
        
        
        
    

    
        
        
            
            
            
            
            
            
            
            

            
            

            
            
            
            
            
            
            
            
            
        
    

接着在mybatis的配置文件中引入次配置文件:

    
        
        
        
        
        
    

接着在service方法中:

    @Override
    public PageEntity selectByPage(Integer pageNum, Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        //因为是demo,所以这里默认没有查询条件。
        List rediscontents = rediscontentMapper.selectByExample(new RediscontentExample());
        PageEntity rediscontentPageEntity = new PageEntity();
        rediscontentPageEntity.setList(rediscontents);
        int size = rediscontentMapper.selectByExample(new RediscontentExample()).size();
        rediscontentPageEntity.setCount(size);
        return rediscontentPageEntity;
    }

只需要使用PageHelper.startPage(pageNum, pageSize);方法就可以帮我们简单的分页了。
这里我自定义了一个分页工具类PageEntity来更方便的帮我们在之后生成JSON数据。

package com.crossoverJie.util;

import java.io.Serializable;
import java.util.List;

/**
 * 分页实体
 *
 * @param 
 */
public class PageEntity implements Serializable {
    private List list;// 分页后的数据
    private Integer count;

    public Integer getCount() {
        return count;
    }

    public void setCount(Integer count) {
        this.count = count;
    }

    public List getList() {
        return list;
    }

    public void setList(List list) {
        this.list = list;
    }
}

更多PageHelper的使用请查看一下链接:
https://github.com/pagehelper/Mybatis-PageHelper

前端联调

接下来看下控制层RedisController:

package com.crossoverJie.controller;

import com.crossoverJie.pojo.Rediscontent;
import com.crossoverJie.service.RediscontentService;
import com.crossoverJie.util.CommonUtil;
import com.crossoverJie.util.PageEntity;
import com.github.pagehelper.PageHelper;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import javax.servlet.http.HttpServletResponse;


@Controller
@RequestMapping("/redis")
public class RedisController {

    private static Logger logger = LoggerFactory.getLogger(RedisController.class);

    @Autowired
    private RediscontentService rediscontentService;


    @RequestMapping("/redis_list")
    public void club_list(HttpServletResponse response,
                          @RequestParam(value = "page", defaultValue = "0") int page,
                          @RequestParam(value = "pageSize", defaultValue = "0") int pageSize) {
        JSONObject jsonObject = new JSONObject();
        JSONObject jo = new JSONObject();
        try {
            JSONArray ja = new JSONArray();
            PageHelper.startPage(1, 10);
            PageEntity rediscontentPageEntity = rediscontentService.selectByPage(page, pageSize);
            for (Rediscontent rediscontent : rediscontentPageEntity.getList()) {
                JSONObject jo1 = new JSONObject();
                jo1.put("rediscontent", rediscontent);
                ja.add(jo1);
            }
            jo.put("redisContents", ja);
            jo.put("count", rediscontentPageEntity.getCount());
            jsonObject = CommonUtil.parseJson("1", "成功", jo);

        } catch (Exception e) {
            jsonObject = CommonUtil.parseJson("2", "操作异常", "");
            logger.error(e.getMessage(), e);
        }
        //构建返回
        CommonUtil.responseBuildJson(response, jsonObject);
    }
}

这里就不做过多解释了,就是从redis或者是service中查询出数据并返回。

前端的显示界面在https://github.com/crossoverJie/SSM/blob/master/src/main/webapp/redis/showRedis.jsp中(并不是前端,将就看)。
其中核心的redis_list.js的代码如下:

var page = 1,
    rows = 10;
$(document).ready(function () {
    initJqPaginator();
    //加载
    load_redis_list();
    $(".query_but").click(function () {//查询按钮
        page = 1;
        load_redis_list();
    });
});
//初始化分页
function initJqPaginator() {
    $.jqPaginator('#pagination', {
        totalPages: 100,
        visiblePages: 10,
        currentPage: 1,
        first: '',
        last: '',
        prev: '',
        next: '',
        page: '
  • {{page}}
  • ', onPageChange: function (num, type) { page = num; if (type == "change") { load_redis_list(); } } }); } //列表 function create_club_list(redisContens) { var phone = 0; var html = '
    ' + '
    ' + '' + '' + '' + '' + '' + '
    ' + '
    '; return html; } //加载列表 function load_redis_list() { var name = $("#name").val(); $.ajax({ type: 'POST', url: getPath() + '/redis/redis_list', async: false, data: {name: name, page: page, pageSize: rows}, datatype: 'json', success: function (data) { if (data.result == 1) { $(".product_length_number").html(data.data.count); var html = ""; var count = data.data.count; for (var i = 0; i < data.data.redisContents.length; i++) { var redisContent = data.data.redisContents[i]; html += create_club_list(redisContent.rediscontent); } $(".product_content").html(html); //这里是分页的插件 $('#pagination').jqPaginator('option', { totalPages: (Math.ceil(count / rows) < 1 ? 1 : Math.ceil(count / rows)), currentPage: page }); } else { alert(data.msg); } } }); $(".product_box:even").css("background", "#e6e6e6");//隔行变色 }

    其实就是一个简单的请求接口,并根据返回数据动态生成Dom而已。

    总结

    以上就是一个简单的redis的应用。
    redis的应用场景还非常的多,比如现在我所在做的一个项目就有用来处理短信验证码的业务场景,之后有时间可以写一个demo。

    项目地址:https://github.com/crossoverJie/SSM.git
    个人博客地址:http://crossoverjie.top
    GitHub地址:https://github.com/crossoverJie