【Maven+SSM】超詳細Spring+SpringMVC+Redis整合
前言:
文章背景,最近專案中做了一個上傳圖片的功能,由於是流式上傳,所以閘道器層沒有對使用者許可權做過濾。需要自己手動做使用者許可權校驗。但是,如果每次上傳圖片都進行資料庫查詢會造成資料庫壓力大。因此,看前人程式碼中,用到了redis快取讀取。學習記錄之。
在專案中學習程式設計就是這樣,當用到哪不會沒見過的時候再學,就會覺得這個東西很有用,而且以後也能在這樣的場景運用起來。
正文:
本文很基礎,從零開始接入redis。redis是什麼?只需記住是一個高效能的key-value資料庫。程式碼提取點
一、redis安裝:
mac通過brew命令安裝redis:
brew install redis
安裝完後,提示中會告訴安裝位置在哪,例如我的mac預設安裝在:/usr/local/bin/ ,ls命令檢視如下
VBoxAutostart bsondump mongoimport pcre-config redis-server VBoxBalloonCtrl dump.rdb mongoperf pcregrep vbox-img VBoxDTrace flow mongoreplay pcretest vboxwebsrv VBoxHeadless install_compass mongorestore react-native watchman VBoxManage mongo mongos redis-benchmark watchman-make VBoxVRDP mongod mongostat redis-check-aof watchman-wait VirtualBox mongodump mongotop redis-check-rdb apktool mongoexport node redis-cli brew mongofiles npm redis-sentinel
1、啟動redis服務端(類似mongodb):
1.1、不含配置檔案的啟動方式
sudo redis-server
1.2、含配置檔案的啟動方式(推薦採用此種方式,因為第一種方式啟動後,通過spring連線,我本地提示需要使用者名稱密碼的錯誤)
sudo redis-server /usr/local/etc/redis.conf
啟動成功會出現如下:
_._ _.-``__ ''-._ _.-`` `. `_. ''-._ Redis 4.0.2 (00000000/0) 64 bit .-`` .-```. ```\/ _.,_ ''-._ ( ' , .-` | `, ) Running in standalone mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 6379 | `-._ `._ / _.-' | PID: 13264 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | http://redis.io `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-' 13264:M 27 Feb 14:44:43.447 # Server initialized 13264:M 27 Feb 14:44:43.448 * DB loaded from disk: 0.000 seconds 13264:M 27 Feb 14:44:43.448 * Ready to accept connections
注意一:從上面可以知道redis的配置路徑是:/usr/local/etc/redis.conf ,由於一般都會採用使用者名稱密碼連線redis服務比較安全。需要修改配置檔案的requirepass為自己的密碼(此處我的密碼為xxxx1111)如下:(原本的這行程式碼為被註釋掉的,去掉#號即可。大約在預設生成的500行位置)。修改後需要重啟redis服務。
################################## SECURITY ###################################
# Require clients to issue AUTH <PASSWORD> before processing any other
# commands. This might be useful in environments in which you do not trust
# others with access to the host running redis-server.
#
# This should stay commented out for backward compatibility and because most
# people do not need auth (e.g. they run their own servers).
#
# Warning: since Redis is pretty fast an outside user can try up to
# 150k passwords per second against a good box. This means that you should
# use a very strong password otherwise it will be very easy to break.
#
requirepass xxxx1111
2、啟動redis客戶端(類似mongodb):
2.1、另起一個控制檯,同樣進入/usr/local/bin/目錄下,輸入
sudo ./redis-cli
提示輸入當前mac機器密碼,成功連線後如下:
Password:
127.0.0.1:6379>
2.2、測試一下之前的密碼是否已經生效:發現許可權被拒。原因是我未通過密碼連線redis服務端。
127.0.0.1:6379> keys *
(error) NOAUTH Authentication required.
2.3、通過密碼連線redis的方式有兩種:
其中一種是直接通過這種方式建立連線:redis-cli -h 127.0.0.1 -p 6379 -a xxxx1111
第二種可以基於2.1的登陸方式,通過: auth xxxx1111密碼連線服務。
成功後通過如下命令測試可見不再被拒絕:
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> keys *
(empty list or set)
二、SpringMVC中接入Redis
本文基於上篇文章的專案進行修改。
1、Maven引入依賴:
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.7.2.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.1</version>
</dependency>
2、在spring.xml檔案中引入redis-context.xml
<!-- 引入同文件夾下的redis屬性配置檔案 -->
<import resource="redis-context.xml"/>
不在一個資料夾下的可以考慮使用全路徑:
<!--ssm 整合redis -->
<import resource="classpath:conf/redis-context.xml"/>
3、redis-context.xml如下:
<?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:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<!-- scanner redis properties -->
<context:property-placeholder location="classpath:conf/redis.properties" ignore-unresolvable="true"/>
<!--(1)如果你有多個數據源需要通過<context:property-placeholder管理,且不願意放在一個配置檔案裡,那麼一定要加上ignore-unresolvable=“true"-->
<!--(2)注意新版的(具體從哪個版本開始不清楚,有興趣可以查一下)JedisPoolConfig的property name,不是maxActive而是maxTotal,而且沒有maxWait屬性,建議看一下Jedis原始碼。-->
<!-- redis連線池 -->
<bean id="jedisConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxTotal" value="${redis.maxActive}"></property>
<property name="maxIdle" value="${redis.maxIdle}"></property>
<property name="maxWaitMillis" value="${redis.maxWait}"></property>
<property name="testOnBorrow" value="${redis.testOnBorrow}"></property>
</bean>
<!-- redis連線工廠 -->
<bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="${redis.host}"></property>
<property name="port" value="${redis.port}"></property>
<property name="password" value="${redis.pass}"></property>
<property name="poolConfig" ref="jedisConfig"></property>
</bean>
<!-- redis操作模板,這裡採用儘量面向物件的模板 -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
<property name="connectionFactory" ref="connectionFactory"/>
<!-- 如果不配置Serializer,那麼儲存的時候只能使用String,如果用物件型別儲存,那麼會提示錯誤 can't cast to String!!!-->
<property name="keySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
</property>
<property name="valueSerializer">
<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
</property>
<!--開啟事務-->
<property name="enableTransactionSupport" value="true"/>
</bean>
</beans>
注意二:此處需要注意這個地方。
<context:property-placeholder location="classpath:conf/redis.properties" ignore-unresolvable="true"/>
因為我在mysql的時候在spring.xml中已經使用了property-placeholder,而且當時使用的時候程式碼如下:
<!-- 引入配置檔案 -->
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:conf/jdbc.property" />
</bean>
通過此種方式引入後,因為多個placeholder衝突。提示錯誤Could not resolve placeholder,參考文章後,新增一行程式碼(倒數第二行),效果和ignore-unresolvable="true"等價,這樣子就可以使用多個placeholder了。當然,如果寫在同一個地方用同一種寫法就更優雅了。<!-- 引入配置檔案 -->
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:conf/jdbc.property" />
<property name="ignoreUnresolvablePlaceholders" value="true" />
</bean>
上面的注意二中redis.properties全路徑被引入到redis-context.xml中了。
4、redis.properties如下:
# Redis settings
#redis.host=192.168.20.101
#redis.port=6380
#redis.pass=foobared
redis.host=127.0.0.1
redis.port=6379
redis.pass=haibo1118
redis.maxIdle=300
redis.maxActive=600
redis.maxWait=1000
redis.testOnBorrow=true
此處名字和redis-context.xml中16-28行程式碼一一對應如下。
<!-- redis連線池 -->
<bean id="jedisConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxTotal" value="${redis.maxActive}"></property>
<property name="maxIdle" value="${redis.maxIdle}"></property>
<property name="maxWaitMillis" value="${redis.maxWait}"></property>
<property name="testOnBorrow" value="${redis.testOnBorrow}"></property>
</bean>
<!-- redis連線工廠 -->
<bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="${redis.host}"></property>
<property name="port" value="${redis.port}"></property>
<property name="password" value="${redis.pass}"></property>
<property name="poolConfig" ref="jedisConfig"></property>
</bean>
三、SpringMVC中Redis的運用
本文是通過RedisTemplate的方式操作redis存取。因為只是一個演示,為了簡潔明瞭,只寫了一個新增使用者資訊,一個獲取使用者資訊兩個操作。
適用場景:
第一次查詢redis的使用者,會從上一篇文章的mongo資料庫中查詢使用者資訊資料,並將使用者資訊存到redis資料庫中。
再次查詢的時候,直接從redis中獲取使用者資訊,不再查詢資料庫。提高效能。
1、先看controller程式碼如下:
先看這個程式碼,整體思路清晰明瞭。當然寫程式碼的時候,肯定不是按照這個順序來,而是按照dao,daoimpl,service,serviceimpl,controller的順序。
// 查詢
@RequestMapping(value = "r.do")
public String viewAll11() {
String name = null;
User users = null;
//嘗試通過id從redis中獲取使用者資訊
users = redisService.get("5a800f9e462f7d2c3add42d6");
//如果從redis中獲取的使用者資訊不為空,直接讀取redis獲取的使用者資訊,否則查詢資料庫
if (users != null) {
System.out.println("沒有執行了資料庫查詢操作---------------");
//直接讀取redis獲取的使用者資訊
name = users.getName();
} else {
System.out.println("執行了資料庫查詢操作---------------");
//查詢資料庫
users = mongoService.findById("5a800f9e462f7d2c3add42d6");
//向redis資料庫從插入使用者資訊
redisService.add(users);
//讀取資料庫獲取的使用者資訊
name = users.getName().toString();
}
System.out.println("-------" + "-------" + name);
return "successlogin";
}
2、dao層程式碼:
package dao;
import model.User;
public interface MemberDao {
User get(String keyId);
boolean add(User member);
}
3、dao層的實現:
RedisGeneratorDao
注入redisTemplate模版物件,通過它操作redis存取。package dao;
import java.io.Serializable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
public abstract class RedisGeneratorDao<K extends Serializable, V extends Serializable> {
@Autowired
protected RedisTemplate<K,V> redisTemplate ;
/**
* 設定redisTemplate
* @param redisTemplate the redisTemplate to set
*/
public void setRedisTemplate(RedisTemplate<K, V> redisTemplate) {
this.redisTemplate = redisTemplate;
}
/**
* 獲取 RedisSerializer
* <br>------------------------------<br>
*/
protected RedisSerializer<String> getRedisSerializer() {
return redisTemplate.getStringSerializer();
}
}
MemberDaoImpl
get取使用者資訊,add存使用者資訊
package dao;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Repository;
import model.User;
@Repository(value = "memberDao2")
public class MemberDaoImpl extends RedisGeneratorDao implements MemberDao {
@Override
public User get(String keyId) {
// TODO Auto-generated method stub
ValueOperations<String, String> stringOperations = redisTemplate.opsForValue();
String name = stringOperations.get(keyId);
if (name != null) {
return new User(keyId, name, 0);
} else {
return null;
}
}
@Override
public boolean add(User member) {
// TODO Auto-generated method stub
ValueOperations<String, String> stringOperations = redisTemplate.opsForValue();
stringOperations.set(member.getId(), member.getName());
System.out.println("member.getId():" + member.getId());
return true;
}
}
4、service層:
package service;
import model.User;
public interface RedisService {
User get(String keyId);
boolean add(User member);
}
5、service實現層:
package service;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import dao.MemberDao;
import model.User;
@Service
public class RedisServiceImpl implements RedisService {
@Resource(name="memberDao2")
MemberDao memberDao;
@Override
public User get(String keyId) {
// TODO Auto-generated method stub
return memberDao.get(keyId);
}
@Override
public boolean add(User member) {
// TODO Auto-generated method stub
return memberDao.add(member);
}
}
本文over。程式碼提取地在本文最開頭。