1. 程式人生 > >【Maven+SSM】超詳細Spring+SpringMVC+Redis整合

【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。程式碼提取地在本文最開頭。