整合SpringBoot
-
- [1、Spring Boot项目搭建与Redis整合应用](#1、Spring Boot项目搭建与Redis整合应用)
-
- [1.1、Spring Boot简介](#1.1、Spring Boot简介)
- [1.2、使用Spring Initializr新建项目](#1.2、使用Spring Initializr新建项目)
- [1.3、Spring Boot结合Redis实战](#1.3、Spring Boot结合Redis实战)
- [2、RedisTemplate API详解](#2、RedisTemplate API详解)
- [3、Spring Boot集成Spring Session](#3、Spring Boot集成Spring Session)
-
- [3.1、配置Spring Boot项目](#3.1、配置Spring Boot项目)
- 3.2、创建配置类和控制器类
- 3.3、编译和部署项目
1、Spring Boot项目搭建与Redis整合应用
1.1、Spring Boot简介
微服务是一种架构风格,是一种使用一套微小服务来开发单个应用的方法。每个服务运行在自己的进程中,通过轻量的通信机制联系,经常是基于HTTP资源的API。这些服务基于业务能力构建,能够通过自动化部署方式独立部署。微服务,简单地说就是将一个大服务(项目)划分为多个子服务(项目)。典型微服务架构如图所示。

从图可以看出微服务的好处:每个微服务组件都是简单、灵活的,能够独立部署;微服务应用不需要一个庞大的应用服务器来支撑;微服务之间是松耦合的,内部是高内聚的,每个微服务很容易按需扩展;微服务架构与语言、工具无关,可以自由选择合适的语言和工具,高效地完成业务目标。
Spring Boot是Spring的一套快速配置"脚手架",可以基于Spring Boot快速开发单个微服务。Spring Boot本身并不提供Spring的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于Spring的应用程序,如图8-2所示。也就是说,Spring Boot并不是用来替代Spring的解决方案,而是和Spring紧密结合用于提升Spring开发者体验的工具。
Spring Boot在开发过程中大量使用"约定优先配置"的思想来摆脱Spring中各类繁复纷杂的配置。采用SpringBoot可以大大地简化开发模式,同时它集成了大量常用的第三方库配置(例如Redis、MongoDB、JPA、RabbitMQ、Quartz等),Spring Boot中这些第三方库几乎可以零配置地开箱即用。大部分的Spring Boot应用都只需要非常少量的配置代码,这使开发者能够更加专注于业务逻辑。
Spring Boot不等于微服务,它只是一套开源框架,跟SSM(Spring+Spring MVC+MyBatis)差不多,只是基 于Spring Boot来开发微服务相当方便,所以这两个词一般都是成对出现的。当我们的服务越来越多时,就可以通过Spring Cloud来统一管理这些服务了。SpringCloud才算是真正的微服务架构,而Spring Boot是JavaEE一站式解决方案。使用Spring Boot可以利用微服务开发小型的Web项目。

Spring Boot官网上的文档质量很高,如果想快速理解Spring Boot,浏览官网文档是最快的途径之一。
1.2、使用Spring Initializr新建项目
本小节介绍在Windows下使用Spring Boot。SpringBoot依赖的开发环境如表所示。

本实例目录为"Redis\Chapter08\springbootDemo"。创建一个Spring Boot项目,可以使用Spring Initializr方式。这种方式很简单,通过在线网页设置项目的选项,即可自动生成Spring Boot项目。
首先在https://start.spring.io/上创建一个简单的SpringBoot项目,类型为Maven Project,开发语言为Java,Spring Boot版本为2.2.0 M6,并填写项目的基本信息Group和Artifact,如图所示。最后单击"Generatethe Project"按钮下载springbootDemo.zip压缩包。

主要以IntelliJ IDEA进行实验,把springbootDemo.zip解压缩到本地硬盘,然后使用IntelliJ IDEA导入工程,选择"File"→"Open",选择springbootDemo在本地硬盘的路径。如果是第一次配置Spring Boot,可能需要等待IntelliJ IDEA下载相应的依赖包。默认创建好的项目结构如图所示。本实例文件名为"Redis\Chapter08\springbootDemo"。

springbootDemo的项目结构很清晰,相比Java EE项目少了很多配置文件,默认生成的文件主要有4个。
SpringbootDemoApplication.java:一个带有main()方法的类,用于启动应用程序。SpringbootDemoApplicationTests.java:一个空的JUnit测试类,它加载了一个使用Spring Boot字典配置功能的Spring应用程序上下文。application.properties:一个空的properties文件,可以根据需要添加配置属性。pom.xml:Maven构建项目的说明文件。
传统基于Java的Web应用需要配置web.xml文件,将Web应用打包成JAR文件放入应用服务器(Tomcat、WebLogic等)中运行。如果基于Spring Boot开发微服务,将变得更加简单,只需要在pom.xml文件中引入spring-boot-starter-web开发依赖模块。
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
pom.xml文件中默认有两个模块。
spring-boot-starter:核心模块,包括自动配置支持、日志和YAML。spring-boot-starter-test:测试模块,包括JUnit、Hamcrest、Mockito。
本实例使用"SpringbootDemoApplication.java",SpringbootDemoApplication是Spring Boot项目的入口类,它的关键源代码如下。
java
@SpringBootApplication
public class SpringbootDemoApplication {
public static void main(String[] args)
{
SpringApplication.run(SpringbootDemoApplication.class, args);
}
}
其中,@SpringBootApplication注解是Spring Boot启动项目时需要加上的。需要说明的是,SpringbootDemoApplication是整个Spring Boot项目启动的初始点,因此如果在项目启动时需要进行某些资源初始化处理,那么最好都在该类中完成。
双击SpringbootDemoApplication,在代码区域右击"Run'SpringbootDemoApplic...'"启动Spring Boot项目,如图所示。

我们可以看到IntelliJ IDEA控制台输出Spring Boot的启动信息,如图所示。

如图所示,可以看出Tomcat服务器默认开启了8080
端口。要访问这个项目提供的服务,可以在浏览器的地址栏中输入http://localhost:8080/。我们在项目中没有手动地去配置Tomcat服务器,因为Spring Boot内置了Tomcat服务器。
如果要把项目部署到服务器上,还需要对项目进行发布。通过Maven的"mvn package"命令对整个SpringBoot项目进行编译、单元测试和打包,然后运行打包后的JAR文件。我们切换到项目所在的目录,然后执行"mvn package"命令,如图所示。

打包成功后,在工程的target目录中会生成JAR文件springbootDemo-0.0.1-SNAPSHOT.jar,如图所示。

在命令提示符窗口切换到target目录中,执行如下命令就可以启动项目,这样项目就发布成功了。启动的效果如图所示。
bash
java -jar springbootDemo-0.0.1-SNAPSHOT.jar

1.3、Spring Boot结合Redis实战
首先,建立Maven项目,在Maven项目中引入pom.xml文件。
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
Jedis封装了Spring Boot与Redis的连接工具。
其次,编写RedisTemplate对象,设置RedisConnectionFactory参数。本实例使用"RedisConfig.java",内容如下。
java
package com.dxtd.springbootDemo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String,String> redisTemplate(RedisConnectionFactory
factory){
RedisTemplate<String,String> redisTemplate = new RedisTemplate<String,
String>();
redisTemplate.setConnectionFactory(factory);
return redisTemplate;
}
}
@Configuration注解指明当前类是一个配置类,就是来代替当前的Spring配置文件。
编写控制器类RedisController。本实例使用"RedisController.java",内容如下。
java
package com.dxtd.springbootDemo.controller;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
public class RedisController {
@Resource
private RedisTemplate redisTemplate;
@RequestMapping("/redis/setAndGet")
@ResponseBody
public String setAndGetValue(String name,String value){
redisTemplate.opsForValue().set(name,value);
return (String) redisTemplate.opsForValue().get(name);
}
}
@Resource注解默认按照名称进行装配,名称可以通过name属性进行指定。如果没有指定name属性,则当注解写在字段上时,默认取字段名按照名称进行查找。在RedisController类中引入一个Bean命名为RedisTemplate。
再次,引入配置文件application.properties,在springbootDemo\src\main\resources下新建配置文件application.properties,内容如下。
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
最后,启动SpringbootDemoApplication.java,访问控制器类RedisController对外提供的服务,在浏览器中访问http://127.0.0.1:8080/redis/setAndGet?name=username\&value=xinping,访问这个请求地址后,username将作为key,xinping作为value保存到Redis中,并把这个key-value作为结果返回到前台页面,返回的结果是JSON格式的数据,如下所示。
java
{
"username": "xinping"
}
在浏览器中访问这个请求的结果如图所示。

从以上结果可以看出,username作为key,xinping作为value,已经以key-value键值对的形式存入Redis了。
2、RedisTemplate API详解
Redis有5种不同的数据类型,这5种数据类型分别为String、List、Set、Hash和Sorted Set。Spring封装了RedisTemplate对象来对5种数据类型操作,它支持所有的Redis原生的API。
redisTemplate.opsForValue():操作String。redisTemplate.opsForHash():操作Hash。redisTemplate.opsForList():操作List。redisTemplate.opsForSet():操作Set。redisTemplate.opsForZSet():操作Sorted Set。
编写RedisService类,通过注解的方式调用RedisTemplate对象来操作Redis的5种数据类型。在RedisService类中使用了@Service注解来标注业务层,RedisService类将自动注入到Spring容器中。本实例使用"RedisService.java",内容如下。
java
package com.dxtd.springbootDemo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Service;
import java.io.Serializable;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@Service
public class RedisService {
@Autowired
private RedisTemplate redisTemplate;
}
然后就可以在RedisService类里添加具体操作Redis的业务逻辑了。
2.1、写入和读取缓存
在RedisService类中封装写入Redis缓存的业务逻辑的具体内容如下,使用redisTemplate.opsForValue()操作String。
java
public boolean set(final String key, Object value) {
boolean result = false;
try {
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
operations.set(key, value);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
其中封装写入Redis缓存设置过期时间的业务逻辑的具体内容如下,需要调用redisTemplate.expire(key,expireTime,TimeUnit.SECONDS)。
java
public boolean set(final String key, Object value, Long expireTime) {
boolean result = false;
try {
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
operations.set(key, value);
redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
其中封装批量删除对应的key的业务逻辑的具体内容如下。
java
public void remove(final String... keys) {
for (String key : keys) {
remove(key);
}
}
其中封装删除对应的key的业务逻辑的具体内容如下,需要调用redisTemplate.delete(key)。
java
public void remove(final String key) {
if (exists(key)) {
redisTemplate.delete(key);
}
}
其中封装判断Redis缓存中是否有对应的key的业务逻辑的具体内容如下,需要调用redisTemplate.hasKey(key)。
java
public boolean exists(final String key) {
return redisTemplate.hasKey(key);
}
其中封装读取Redis缓存的业务逻辑的具体内容如下,使用redisTemplate.opsForValue()获得operations句柄,然后使用operations.get(key)获得Redis中对应的key的业务逻辑。
java
public Object get(final String key) {
Object result = null;
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
result = operations.get(key);
return result;
}
2.2、添加和获取散列数据
在RedisService类中封装添加散列数据的业务逻辑的具体内容如下,使用redisTemplate.opsForHash()操作Hash。
java
public void hmSet(String key, Object hashKey, Object value) {
HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
hash.put(key, hashKey, value);
}
封装获取散列数据的业务逻辑的具体内容如下,使用redisTemplate.opsForHash()获得hash句柄,然后使用hash.get(key,hashKey)获得key对应的Hash值。
java
public Object hmGet(String key, Object hashKey) {
HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
return hash.get(key, hashKey);
}
2.3、添加和获取列表数据
在RedisService类中封装添加列表数据的业务逻辑的具体内容如下,使用redisTemplate.opsForList()操作List。
java
public void lPush(String k, Object v) {
ListOperations<String, Object> list = redisTemplate.opsForList();
list.rightPush(k, v);
}
封装获取列表数据的业务逻辑的具体内容如下,使用redisTemplate.opsForList()获得list句柄,然后使用list.range(k,l,l1)获得Redis的列表数据。
java
public List<Object> lRange(String k, long l, long l1) {
ListOperations<String, Object> list = redisTemplate.opsForList();
return list.range(k, l, l1);
}
2.4、添加和获取集合数据
在RedisService类中封装添加集合数据的业务逻辑的具体内容如下,使用redisTemplate.opsForSet()操作Set。
java
public void add(String key, Object value) {
SetOperations<String, Object> set = redisTemplate.opsForSet();
set.add(key, value);
}
封装获取集合数据的业务逻辑的具体内容如下,使用redisTemplate.opsForSet()获得SetOperations<String,Object>对象,然后使用set.members(key)获得Redis的集合数据。
java
public Set<Object> setMembers(String key) {
SetOperations<String, Object> set = redisTemplate.opsForSet();
return set.members(key);
}
2.5、添加和获取有序集合数据
在RedisService类中封装添加有序集合数据的业务逻辑的具体内容如下,使用redisTemplate.opsForZSet()操作Sorted Set。
java
public void zAdd(String key, Object value, double scoure) {
ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
zset.add(key, value, scoure);
}
封装获取有序集合数据的业务逻辑的具体内容如下,使用redisTemplate.opsForZSet()获得ZSetOperations<String,Object>对象,然后使用zset.rangeByScore(key,scoure,scoure1)获得Redis的有序集合数据。
java
public Set<Object> rangeByScore(String key, double scoure, double scoure1) {
ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
redisTemplate.opsForValue();
return zset.rangeByScore(key, scoure, scoure1);
}
2.6、优化控制器
封装好RedisService类后就可以对控制器类RedisController进行优化了。
java
package com.dxtd.springbootDemo.controller;
import com.dxtd.springbootDemo.RedisService;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
@RestController
public class RedisController {
@Resource
private RedisTemplate redisTemplate;
@Resource
private RedisService service;
@RequestMapping("/redis/setAndGet")
@ResponseBody
public Map setAndGetValue(String name, String value){
System.out.println( "name="+ name + ",value=" + value );
redisTemplate.opsForValue().set(name,value);
Map result = new HashMap();
String getValue = (String) redisTemplate.opsForValue().get(name);
result.put(name ,getValue);
return result;
}
@RequestMapping("/redis/setAndGet2")
@ResponseBody
public Map setAndGetValueV2(String name,String value){
service.set(name,value);
Map result = new HashMap();
String getValue = service.get(name).toString();
result.put(name ,getValue);
return result;
}
}
启动SpringbootDemoApplication.java,使用service.get()获得Redis缓存中的数据,访问http://127.0.0.1:8080/redis/setAndGet2?name=db\&value=redis,返回消息如图所示。

3、Spring Boot集成Spring Session
传统Session的问题在于Session是由Web容器管理的,即一个Session只保存在一台服务器上,适合于单体应用。随着架构不断地向微服务分布式集群演进,传统的Session在集群环境下就不能正常工作了。例如,现在有3台Web服务器,客户端访问服务器通过负载均衡Nginx负载到某一台服务器上,用户此次的数据就保存到这台服务器的Web容器中了,如果用户下次请求被负载到其他服务器上,就获取不到之前保存的数据了。
这时候就需要整个服务器集群共享同一个Session。为了解决所有服务器共享一个Session的问题,Session就不能单独保存在自己的Web容器中,而是保存在一个公共的会话仓库(Session Repository)中,所有服务器都访问同一个会话仓库,这样所有服务器的状态都一致了。Spring Session支持的会话仓库有Redis、MongoDB、JDBC,本章使用Redis作为Spring Session的会话仓库。
Spring Session有以下优点。
- Spring Session是基于Servlet规范实现的一套Session管理框架。Spring Session主要解决了分布式场景下Session的共享问题。Spring Session最核心的类是SessionRepositoryFilter,用于包装用户的请求和响应。
- 可在程序中直接替换HttpSession,而无须修改一行代码。
- 可以很方便地与Spring Security集成,增加诸如findSessionsByUserName、rememberMe,限制同一个账号可以同时在线的Session数(例如设置成1,即可达到把前一次登录替换的效果)等。
3.1、配置Spring Boot项目
在https://start.spring.io/中创建一个Spring Boot项目SpringSessionDemo,使用IntelliJ IDEA导入项目。本实例目录为Redis\Chapter08\SpringSessionDemo,内容如下。
在Maven项目的pom.xml文件里引入必要的依赖包。
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
新建Spring Boot的配置文件application.properties,内容如下。
# Redis 配置
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
3.2、创建配置类和控制器类
创建配置类RedisHttpSessionConfiguration,本实例使用"Configuration.java",内容如下。
java
package com.dxtd.SpringSessionDemo;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
@Configuration
//maxInactiveIntervalInSeconds默认是1800s过期,这里测试修改为60s
@EnableRedisHttpSession(maxInactiveIntervalInSeconds=60)
public class RedisHttpSessionConfiguration {
}
配置类RedisHttpSessionConfiguration使用@Configuration注解表明这是一个配置类。在这个类中也添加了注解@EnableRedisHttpSession,表示开启Redis的Session管理。如果需要设置会话失效时间,可以使用@EnableRedisHttpSession(maxInactiveIntervalInSeconds=60)表示在60秒后会话失效。
创建用户类User,用户类只有两个属性username和password,保存用户登录的用户名和密码。本实例使用"User.java",内容如下。
java
package com.dxtd.SpringSessionDemo.controller;
import java.io.Serializable;
public class User implements Serializable{
private String name;
private String password;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}
创建控制器类SessionController,在这个类中定义了2个方法。login方法用于登录验证,当输入username等于"xinping",password等于"123",判断用户登录成功,在Session中保存用户信息。get方法用于从Session中获取用户信息。本实例使用"SessionController.java",内容如下。
java
package com.dxtd.SpringSessionDemo.controller;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;
@RestController
public class SessionController {
/**
* 登录系统
*
* */
@RequestMapping(value="/session/login", method = RequestMethod.GET)
@ResponseBody
public Map login(@RequestParam("username") String username,@RequestParam
("password") String password,HttpServletRequest request, HttpSession session) {
String message = "login failure";
Map<String, Object> result = new HashMap<String, Object>();
if(username != null && "xinping".equals(username) && "123".equals
(password)){
User user = new User();
user.setName(username);
user.setPassword(password);
request.getSession().setAttribute("admin", user);
message = "login success";
result.put("message" , message);
result.put("sessionId",session.getId());
}else{
result.put("message" , message);
}
return result;
}
/**
* 查询用户
*
* */
@RequestMapping(value="/session/get", method = RequestMethod.GET)
@ResponseBody
public Map get(@RequestParam("username") String username,HttpServletRequest
request, HttpSession session) {
Object value = request.getSession().getAttribute("admin");
Map<String,Object> result = new HashMap<String,Object>();
result.put("message" ,value);
result.put("sessionId",session.getId());
return result;
}
}
3.3、编译和部署项目
启动项目,运行SpringSessionDemoApplication.java,进入SpringSessionDemo项目所在目录。
bash
D:\>cd D:\quant\Redis\Chapter08\SpringSessionDemo
使用Maven的"mvn clean package"命令将SpringSessionDemo项目打包成JAR文件。
bash
D:\quant\Redis\Chapter08\SpringSessionDemo>mvn clean package
在SpringSessionDemo\target目录下获得编译好的压缩包SpringSessionDemo-0.0.1-SNAPSHOT.jar,如图所示。

使用如下命令分别以两个不同的端口(8081和8082)启动项目,用于模拟分布式应用中的两个服务。
bash
java -jar SpringSessionDemo-0.0.1-SNAPSHOT.jar --server.port=8081
java -jar SpringSessionDemo-0.0.1-SNAPSHOT.jar --server.port=8082
启动项目,如图所示。

第1步:访问http://127.0.0.1:8081/session/login?username=xinping\&password=123。
第一次访问URL地址端口为8081的Spring Boot项目时模拟用户登录,在浏览器中访问这个请求后的结果如图所示。

查看Redis中的key可以看出有一个key为"spring:session:sessions:ddf10226-8ffe-4294-ab93-7a461535f8ed",这个key包含了图8-14的页面中显示的Session ID值,说明Session已经成功保存到了Redis中,如图所示。

第2步:访问地址http://127.0.0.1:8082/session/get?username=xinping。
第二次访问URL地址端口为8082的Spring Boot项目时模拟获取登录的用户信息,在浏览器中访问这个请求后的结果如图所示。

可以看出成功获取了保存在Redis中的用户名为xinping的用户信息,并且两次请求的Session ID是相同的,实现了Session的共享。
通过Spring Boot使用Redis来实现Session的共享很方便,再配合Nginx进行负载均衡,便能实现分布式的应用了。