前言

先来看一张效果图:

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

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

Redis安装与使用

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

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

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

  • 进入该目录。

  • 编译安装

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

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

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

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

1
2
3
4
进入src目录
cd /usr/local/redis-3.2.5/src
启动服务
./redis-server ../redis.conf
  • 登陆redis

    1
    ./redis-cli -a 你的密码

Spring整合Redis

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

  • 修改Spring配置文件
    加入以下内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    <!-- jedis 配置 -->
    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
    <property name="maxIdle" value="${redis.maxIdle}"/>
    <property name="maxWaitMillis" value="${redis.maxWait}"/>
    <property name="testOnBorrow" value="${redis.testOnBorrow}"/>
    </bean>
    <!-- redis服务器中心 -->
    <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
    <property name="poolConfig" ref="poolConfig"/>
    <property name="port" value="${redis.port}"/>
    <property name="hostName" value="${redis.host}"/>
    <property name="password" value="${redis.password}"/>
    <property name="timeout" value="${redis.timeout}"></property>
    </bean>
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
    <property name="connectionFactory" ref="connectionFactory"/>
    <property name="keySerializer">
    <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
    </property>
    <property name="valueSerializer">
    <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
    </property>
    </bean>
     
    <!-- cache配置 -->
    <bean id="methodCacheInterceptor" class="com.crossoverJie.intercept.MethodCacheInterceptor">
    <property name="redisUtil" ref="redisUtil"/>
    </bean>
    <bean id="redisUtil" class="com.crossoverJie.util.RedisUtil">
    <property name="redisTemplate" ref="redisTemplate"/>
    </bean>
     
    <!--配置切面拦截方法 -->
    <aop:config proxy-target-class="true">
    <!--将com.crossoverJie.service包下的所有select开头的方法加入拦截
    去掉select则加入所有方法w
    -->
    <aop:pointcut id="controllerMethodPointcut" expression="
    execution(* com.crossoverJie.service.*.select*(..))"/>
     
    <aop:pointcut id="selectMethodPointcut" expression="
    execution(* com.crossoverJie.dao..*Mapper.select*(..))"/>
     
    <aop:advisor advice-ref="methodCacheInterceptor" pointcut-ref="controllerMethodPointcut"/>
    </aop:config>
    ```
    更多的配置可以直接在源码里面查看:[https://github.com/crossoverJie/SSM/blob/master/src/main/resources/spring-mybatis.xml](https://github.com/crossoverJie/SSM/blob/master/src/main/resources/spring-mybatis.xml)。
    以上都写有注释,也都是一些简单的配置相信都能看懂。
    下面我会着重说下如何配置缓存的。
     
    # Spring切面使用缓存
    Spring的`AOP`真是是一个好东西,还不太清楚是什么的同学建议先自行`Google`下吧。
    在不使用切面的时候如果我们想给某个方法加入缓存的话肯定是在方法返回之前就要加入相应的逻辑判断,只有一个或几个倒还好,如果有几十上百个的话那GG了,而且维护起来也特别麻烦。
    > 好在Spring的AOP可以帮我们解决这个问题。
    > 这次就在我们需要加入缓存方法的切面加入这个逻辑,并且只需要一个配置即可搞定,就是上文中所提到的配置文件,如下:
     
    ```xml
    <!--配置切面拦截方法 -->
    <aop:config proxy-target-class="true">
    <!--将com.crossoverJie.service包下的所有select开头的方法加入拦截
    去掉select则加入所有方法w
    -->
    <aop:pointcut id="controllerMethodPointcut" expression="
    execution(* com.crossoverJie.service.*.select*(..))"/>
     
    <aop:pointcut id="selectMethodPointcut" expression="
    execution(* com.crossoverJie.dao..*Mapper.select*(..))"/>
     
    <aop:advisor advice-ref="methodCacheInterceptor" pointcut-ref="controllerMethodPointcut"/>
    </aop:config>

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
@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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
 
<plugins>
<!-- com.github.pagehelper为PageHelper类所在包名 -->
<plugin interceptor="com.github.pagehelper.PageHelper">
<property name="dialect" value="mysql"/>
<!-- 该参数默认为false -->
<!-- 设置为true时,会将RowBounds第一个参数offset当成pageNum页码使用 -->
<!-- 和startPage中的pageNum效果一样 -->
<property name="offsetAsPageNum" value="true"/>
<!-- 该参数默认为false -->
<!-- 设置为true时,使用RowBounds分页会进行count查询 -->
<property name="rowBoundsWithCount" value="true"/>
 
<!-- 设置为true时,如果pageSize=0或者RowBounds.limit = 0就会查询出全部的结果 -->
<!-- (相当于没有执行分页查询,但是返回结果仍然是Page类型) <property name="pageSizeZero" value="true"/> -->
 
<!-- 3.3.0版本可用 - 分页参数合理化,默认false禁用 -->
<!-- 启用合理化时,如果pageNum<1会查询第一页,如果pageNum>pages会查询最后一页 -->
<!-- 禁用合理化时,如果pageNum<1或pageNum>pages会返回空数据 -->
<property name="reasonable" value="true"/>
<!-- 3.5.0版本可用 - 为了支持startPage(Object params)方法 -->
<!-- 增加了一个`params`参数来配置参数映射,用于从Map或ServletRequest中取值 -->
<!-- 可以配置pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默认值 -->
<!-- 不理解该含义的前提下,不要随便复制该配置 -->
<property name="params" value="pageNum=start;pageSize=limit;"/>
</plugin>
</plugins>
</configuration>

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

1
2
3
4
5
6
7
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 自动扫描mapping.xml文件 -->
<property name="mapperLocations" value="classpath:mapping/*.xml"></property>
<!--加入PageHelper-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>

接着在service方法中:

1
2
3
4
5
6
7
8
9
10
11
@Override
public PageEntity<Rediscontent> selectByPage(Integer pageNum, Integer pageSize) {
PageHelper.startPage(pageNum, pageSize);
//因为是demo,所以这里默认没有查询条件。
List<Rediscontent> rediscontents = rediscontentMapper.selectByExample(new RediscontentExample());
PageEntity<Rediscontent> rediscontentPageEntity = new PageEntity<Rediscontent>();
rediscontentPageEntity.setList(rediscontents);
int size = rediscontentMapper.selectByExample(new RediscontentExample()).size();
rediscontentPageEntity.setCount(size);
return rediscontentPageEntity;
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.crossoverJie.util;
 
import java.io.Serializable;
import java.util.List;
 
/**
* 分页实体
*
* @param <T>
*/
public class PageEntity<T> implements Serializable {
private List<T> list;// 分页后的数据
private Integer count;
 
public Integer getCount() {
return count;
}
 
public void setCount(Integer count) {
this.count = count;
}
 
public List<T> getList() {
return list;
}
 
public void setList(List<T> list) {
this.list = list;
}
}

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

前端联调

接下来看下控制层RedisController:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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<Rediscontent> 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的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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: '<li class="prev"><a href="javascript:;">首页</a></li>',
last: '<li class="prev"><a href="javascript:;">末页</a></li>',
prev: '<li class="prev"><a href="javascript:;">上一页</a></li>',
next: '<li class="next"><a href="javascript:;">下一页</a></li>',
page: '<li class="page"><a href="javascript:;">{{page}}</a></li>',
onPageChange: function (num, type) {
page = num;
if (type == "change") {
load_redis_list();
}
}
});
}
//列表
function create_club_list(redisContens) {
var phone = 0;
var html = '<div class="product_box">'
+ '<div class="br">'
+ '<div class="product_link">'
+ '<div class="product_phc">'
+ '<img class="phc" src="" >'
+ '</div>'
+ '<span class="product_name">' + redisContens.id + '</span></div>'
+ '<div class="product_link toto">' + redisContens.content + '</div>'
+ '<div class="product_link toto">'
+ '<span>' + "" + '</span>'
+ '</div>'
+ '<div class="product_link toto">'
+ '<span>' + phone + '</span></div>'
+ '<div class="product_link toto">'
+ '<span>' + 0 + '</span></div>'
+ '<div class="product_link toto product_operation">'
+ '<span onclick="edit_club(' + 0 + ')">编辑</span>'
+ '<span onclick="edit_del(' + 0 + ')">删除</span></div></div>'
+ '</div>';
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
GitHub地址:https://github.com/crossoverJie

 原文 https://crossoverjie.top/2016/12/18/SSM7/

SSM(七)在JavaWeb应用中使用Redis的更多相关文章

  1. SSM+Maven的JavaWeb项目中的异常的可能性

    1.404 可能:1):被拦截了,即:springmvc中的controller可能不存在,可能没有被配置,可能配置出错 2):资源确实不存在 3):路径出错 2.500,程序异常,但是业务逻辑什么都 ...

  2. redis的安装使用以及在python中操作redis

    一.Redis介绍: Redis可以看作是一个key-value的存储系统,它为我们提供了丰富的数据结构,包括lists,sets,ordered sets和hashes.还包括了对这些数据结构的丰富 ...

  3. ElasticSearch搜索引擎在JavaWeb项目中的应用

    近几篇ElasticSearch系列: 1.阿里云服务器Linux系统安装配置ElasticSearch搜索引擎 2.Linux系统中ElasticSearch搜索引擎安装配置Head插件 3.Ela ...

  4. 2017.11.6 JavaWeb-----第七章 JavaWeb常用开发模式与案例

    JavaWeb-----第七章 JavaWeb常用开发模式与案例 (1)单纯的JSP页面开发模式 通过在JSP中的脚本标记,直接在JSP页面中实现各种功能.称为"单纯的JSP页面编程模式&q ...

  5. MVC的验证(模型注解和非侵入式脚本的结合使用) .Net中初探Redis .net通过代码发送邮件 Log4net (Log for .net) 使用GDI技术创建ASP.NET验证码 Razor模板引擎 (RazorEngine) .Net程序员应该掌握的正则表达式

    MVC的验证(模型注解和非侵入式脚本的结合使用)   @HtmlHrlper方式创建的标签,会自动生成一些属性,其中一些属性就是关于验证 如图示例: 模型注解 通过模型注解后,MVC的验证,包括前台客 ...

  6. 基于SSM框架的JavaWeb通用权限管理系统

    - - ->关注博主公众号[C you again],获取更多IT资源(IT技术文章,毕业设计.课程设计系统源码,经典游戏源码,HTML网页模板,PPT.简历模板,!!还可以投稿赚钱!!,点击查 ...

  7. .NET中使用Redis (二)

    很久以前写了一篇文章 .NET中使用Redis 介绍了如何安装Redis服务端,以及如何在.NET中调用Redis读取数据.本文简单介绍如何设计NoSQL数据库,以及如何使用Redis来存储对象. 和 ...

  8. .NET中使用Redis

    Redis是一个用的比较广泛的Key/Value的内存数据库,新浪微博.Github.StackOverflow 等大型应用中都用其作为缓存,Redis的官网为http://redis.io/. 最近 ...

  9. 如何在ASP.NET Core中使用Redis

    注:本文提到的代码示例下载地址> https://code.msdn.microsoft.com/How-to-use-Redis-in-ASPNET-0d826418 Redis是一个开源的内 ...

随机推荐

  1. Java连载54-两种单例模式、接口详解

    一.单例模式分为两种: (1)饿汉式单例:在类加载阶段就创建了一个对象. (2)懒汉式单例:用对对象的时候才会创建对象.(连载53中例子就是懒汉式) 饿汉式举例: package com.bjpowe ...

  2. Mybatis基本类型参数非空判断(异常:There is no getter for property...)

    先看一小段代码 <select id="queryByPhone" parameterType="java.lang.String" resultType ...

  3. jmeter 中使用正则表达式提取依赖参数

    1:登录接口 这里有一个实际的登录接口,在响应中返回了一串token,如下图 那么我们在接下来的接口-经验库列表中,就必须带入这一串token,否则响应报错,如下图所示    如何获取登录的口令呢?这 ...

  4. PalletOne调色板跨链的ETH提币实现

    实现区块链的跨链,最主要的诉求就是Token的转移,而Token的跨链转移又分为充币和提币2种操作.以PalletOne调色板来说,如果要把ETH跨链到PalletOne上来流转,就是ETH的充币操作 ...

  5. Python 调用 Hprose接口、Dubbo接口、Java方法

    #!/usr/bin/env python # -*- coding:utf-8 -*- # ************************************* # @Time : 2019/ ...

  6. .Net Core 3.0后台使用httpclient请求网络网页和图片_使用Core3.0做一个简单的代理服务器

    目标:使用.net core最新的3.0版本,借助httpclient和本机的host域名代理,实现网络请求转发和内容获取,最终显示到目标客户端! 背景:本人在core领域是个新手,对core的使用不 ...

  7. JS基础语法---创建对象---三种方式创建对象:调用系统的构造函数;自定义构造函数;字面量的方式

    创建对象三种方式: 调用系统的构造函数创建对象 自定义构造函数创建对象(结合第一种和需求通过工厂模式创建对象) 字面量的方式创建对象 第一种:调用系统的构造函数创建对象 //小苏举例子: //实例化对 ...

  8. Node.js操作Mysql的简单示例

    API的封装:封装为系统可用的工具,分为线上和线上的数据库. 使用:让API直接操作数据库,不再使用假数据. DEMO代码: const mysql = require('mysql'); // 创建 ...

  9. Hacking/Penetrating tester bookmark collection

    Blogs http://carnal0wnage.blogspot.com/ http://www.mcgrewsecurity.com/ http://www.gnucitizen.org/blo ...

  10. tomcat7控制台日志中文乱码

    windows电脑 idea启动Tomcat调试程序时,Tomcat控制台输出里,中文是乱码. 解决办法: 修改Tomcat/bin/catalina.bat文件: set JAVA_OPTS= 的内 ...