因爲最近換(mang)了(de)家(yi)公(bi)司接觸了新的東西因此好久沒有更新了。
此次談談Redis,關於Redis
應該不少朋友就算沒有用過也聽過,算是這幾年最流行的NoSql
之一了。Redis
的應用場景很是多這裏就不一一列舉了,此次就以一個最簡單的也最經常使用的 緩存數據 來舉例。
先來看一張效果圖:javascript
Redis
中是否有緩存,有的話就讀取,沒有就查詢數據庫並保存到
Redis
中,下次再查詢的話就會直接從緩存中讀取了。
Redis
中的結果:
首先第一步天然是安裝Redis
。我是在我VPS
上進行安裝的,操做系統是CentOS6.5
。php
下載Redisredis.io/download,我機器上安裝的是3.2.5
css
將下載下來的'reidis-3.2.5-tar.gz'上傳到usr/local
這個目錄進行解壓。html
進入該目錄。
前端
編譯安裝java
make
make install複製代碼
修改redis.conf
配置文件。mysql
這裏我只是簡單的加上密碼而已。linux
vi redis.conf
requirepass 你的密碼複製代碼
啓動時候要選擇咱們以前修改的配置文件才能使配置文件生效。git
進入src目錄
cd /usr/local/redis-3.2.5/src
啓動服務
./redis-server ../redis.conf複製代碼
./redis-cli -a 你的密碼複製代碼
這裏我就直接開始用Spring整合畢竟在實際使用中都是和Spring
一塊兒使用的。github
修改Spring
配置文件
加入如下內容:
<!-- 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>複製代碼
更多的配置能夠直接在源碼裏面查看:github.com/crossoverJi…。
以上都寫有註釋,也都是一些簡單的配置相信都能看懂。
下面我會着重說下如何配置緩存的。
Spring的AOP
真是是一個好東西,還不太清楚是什麼的同窗建議先自行Google
下吧。
在不使用切面的時候若是咱們想給某個方法加入緩存的話確定是在方法返回以前就要加入相應的邏輯判斷,只有一個或幾個倒還好,若是有幾十上百個的話那GG了,並且維護起來也特別麻煩。
好在Spring的AOP能夠幫咱們解決這個問題。
此次就在咱們須要加入緩存方法的切面加入這個邏輯,而且只須要一個配置便可搞定,就是上文中所提到的配置文件,以下:
<!--配置切面攔截方法 -->
<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
中處理邏輯的核心方法:
@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;
}複製代碼
redis
的key
。此次爲了分頁方便使用了比較流行的PageHelper
來幫咱們更簡單的進行分頁。
首先是新增一個mybatis的配置文件mybatis-config
:
<?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的配置文件中引入次配置文件:
<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方法中:
@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
數據。
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
的使用請查看一下連接:
github.com/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<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中查詢出數據並返回。
前端的顯示界面在github.com/crossoverJi…中(並非前端,將就看)。
其中核心的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: '<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。
項目地址:github.com/crossoverJi…
我的博客地址:crossoverjie.top。
GitHub地址:github.com/crossoverJi…。