希文的个人博客

白日依山尽,黄河入海流。欲穷千里目,更上一层楼。

0%

Spring Boot教程

Spring Boot教程

[TOC]

1.idea创建Spring boot项目

在IDEA中创建一个Java Web项目。

  • Spring Boot 项目(本次使用)

项目创建完成后,Maven会自动进行相关依赖jar文件的下载,首次下载需要等待的时间较长。下载完成后,右侧Maven工具栏中不再出现报错信息:

2. 导入地图服务的相关内容

在IDEA中创建一个Java Web项目。

大数据方向实训课前资料\2.地图页面\static.zip解压缩后,将static文件夹下的所有内容拷贝到项目的src/main/resource/static目录下。

nymap.html中保存了地图服务的代码,可以打开该页面,点击右侧的浏览器按钮,使用本地浏览器访问该页面。

地图服务背景知识

3. 服务器端开发

服务器端程序的作用:

  • 能够接受浏览器发来的请求,获取浏览器发来的用户行为数据
  • 将用户行为数据写入到数据库中

用例(Use Case)组件设计:

首次运行持久层测试用例前的配置(选一种配置文件方法)

​ 有一种配置是yml文件的,properties和yml配置文件使用一种就行(两种都存在就删掉一种,两种同时存在的话应该是后面的会把前面的配置覆盖掉)

1.配置文件修改

方案一 application.properties修改(也可以多文档切换配置,需要看官网文档)

1
2
3
4
5
6
7
8
9
10
11
# 应用名称
spring.application.name=nybike
#下面这些内容是为了让MyBatis映射
mybatis.mapper-locations=classpath:mappers/*xml
# 数据库驱动:
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 数据库连接地址
spring.datasource.url=jdbc:mysql://localhost:3306/nybikedb?serverTimezone=UTC
# 数据库用户名&密码:
spring.datasource.username=root
spring.datasource.password=root

方案二 .yml文件配置

​ 生产环境和开发环境自动切换,默认是开发模式,如果需要使用生产模式可以创建环境变量

如果windows中不想写环境变量idea也可以修改某个地方即可即可切换为开发环境,就不用修改环境变量了,直接做第2小结就可以了

image-20220625174925928

image-20220625175434994

1.配置环境变量

image-20220625172840322

  • 变量名

    image-20220625173128731

1
PRODUCTION
  • 变量值

image-20220625173735365

1
prod

​ **注意:windows电脑需要重启让环境变量生效,linux中用环境变量生效命令 **

2.Spring boot默认初始会先看application.yml文件

application.yml

1
2
3
4
spring:
profiles:
#自动切换默认为dev模式
active: ${PRODUCTION:dev}

application-dev.yml 开发模式配置

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
server:
port: 8080

spring:
application:
# 应用名称
name: nybike
datasource:
# 数据库驱动:
driver-class-name: com.mysql.cj.jdbc.Driver
# 数据库连接地址
url: jdbc:mysql://localhost:33068/nybikedb?serverTimezone=UTC
# 数据库用户名&密码:
username: root
password: xiwen123

dbcp2:
min-idle: 5 # 数据库连接池的最小维持连接数
initial-size: 5 # 初始化连接数
max-total: 5 # 最大连接数
max-wait-millis: 150 # 等待连接获取的最大超时时间

#下面这些内容是为了让MyBatis映射
mybatis:
#指定Mybatis的Mapper文件位置
mapper-locations: classpath:mappers/*xml
# 实体类所在的位置
type-aliases-package: cn.tedu.nybike.entity
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #用于控制台打印sql语句

application-prop.yml 生产环境配置

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
server:
port: 8081

spring:
profiles:
active: prod

application:
# 应用名称
name: nybike
datasource:
# 数据库驱动:
driver-class-name: com.mysql.cj.jdbc.Driver
# 数据库连接地址
url: jdbc:mysql://localhost:33068/nybikedb?serverTimezone=UTC
# 数据库用户名&密码:
username: root
password: xiwen123

dbcp2:
min-idle: 5 # 数据库连接池的最小维持连接数
initial-size: 5 # 初始化连接数
max-total: 5 # 最大连接数
max-wait-millis: 150 # 等待连接获取的最大超时时间

#下面这些内容是为了让MyBatis映射
mybatis:
#指定Mybatis的Mapper文件位置
mapper-locations: classpath:mappers/*xml
# 实体类所在的位置
type-aliases-package: cn.tedu.nybike.entity
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #用于控制台打印sql语句

项目中文件截图

image-20220625174512236

1.开发实体类

  1. 创建cn.tedu.nybike.entity

  2. entity包下新建OperationDO类,用于封装用户的一次操作记录

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public class OperationDO {

    private Integer id;
    private String userIp;
    private Date createdTime;
    private Integer optType;
    private Double zoomStartLevel;
    private Double zoomEndLevel;
    private Double dragStartLat;
    private Double dragStartLng;
    private Double dragEndLat;
    private Double dragEndLng;
    private String stationId;
    // 这里省略toString, get/set, 无参构造器

    }
  3. 创建cn.tedu.nybike.util

  4. unti.zip解压缩,将其中的2个文件JsonResult.javaIpUtils.java复制到util包中

2.开发持久层

  1. 创建cn.tedu.nybike.dao

  2. dao包下创建OperationDAO接口(interface)

  3. OpeartionDAO接口中声明抽象方法insertOperation(OperationDO optDO)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /**
    * 用户操作模块的持久层接口
    */
    public interface OperationDAO {

    /**
    * 向数据库中添加一行操作记录
    * @param optDO 一行操作记录
    * @return 添加成功的数据条数
    */
    int insertOperation(OperationDO optDO);

    }
  4. src/main/resources下创建mappers文件夹

  5. 将下载的OperationMapper.xml拷贝到mappers文件夹中

  6. OperationMapper.xml中添加要执行的SQL语句

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <!-- namespace:当前XML文件用于配置哪个接口中抽象方法对应的SQL语句 -->
    <mapper namespace="cn.tedu.nybiket.dao.OperationDAO">

    <!--向数据库中添加一行操作记录-->
    <!--int insertOperation(OperationDO optDO)-->
    <insert id="insertOperation" >
    insert into t_operation(
    user_ip, created_time, opt_type,
    zoom_start_level, zoom_end_level,
    drag_start_lat, drag_start_lng,
    drag_end_lat, drag_end_lng, station_id
    ) values (
    #{userIp}, now(), #{optType},
    #{zoomStartLevel}, #{zoomEndLevel},
    #{dragStartLat}, #{dragStartLng},
    #{dragEndLat}, #{dragEndLng}, #{stationId}
    )
    </insert>

    </mapper>

3.开发持久层测试用例

  1. 打开OperationDAO接口,光标点击接口中任意一行,右键 -> Generate... -> Test...,打开自动生成测试用例的对话框

  2. 对话框中的内容保持默认,勾选insertOperation..方法前的复选框,点击OK

  3. IDEA会在动在src/test/java下的对应包中生成测试类OprtionDAOTest

  4. 在该类中编写测试用例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    @SpringBootTest
    class OperationDAOTest {

    // 该注解的作用是通知Spring框架查找OperationDAO类型的对象
    // 将该对象的引用添加到dao属性中
    @Autowired(required = false)
    OperationDAO dao;

    @Test
    void insertOperation() {
    // 创建一个DO对象,封装要添加到数据库中的数据
    OperationDO optDO=new OperationDO();
    optDO.setUserIp("127.0.0.1");
    optDO.setStationId("7789");
    // 调用dao的方法,将数据写入数据库
    int row = dao.insertOperation(optDO);
    // 在控制台输出插入成功的数据条数
    System.err.println("row="+row);
    }

    }

4.在项目的主类(cn.tedu.nybike.NybikeApplication)前添加注解

1
2
3
4
//这是应该是配置的base-package范围太大,导致service层的接口也被包装,将base-package的范围缩小到dao即可
//改成@MapperScan("cn.tedu.nybike.dao")
@MapperScan("cn.tedu.nybike.dao")
public class NybikeApplication {

运行测试用例

点击测试方法前的绿色播放键,执行测试用例,如出现如下提示,表示测试用例运行成功。

5.开发业务层

在src/main/java下新建cn.tedu.nybike.service包。

在该包下新建OperationService接口,并在类中开发对应的业务层方法。

1
2
3
4
5
6
7
8
9
10
package cn.tedu.nybike.service;

import cn.tedu.nybike.entity.OperationDO;
import cn.tedu.nybike.entity.PieItem;
import java.util.List;

public interface OperationService {
//保存数据
void saveOperation(OperationDO optDO);
}

在src/main/java下新建cn.tedu.nybike.service.Impl包。

在该包下新建OperationServiceImpl类继承OperationService接口,并在类中开发对应的业务层方法。

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
package cn.tedu.nybiket.service.impl;

import cn.tedu.nybiket.dao.OperationDAO;
import cn.tedu.nybiket.entity.OperationDO;
import cn.tedu.nybiket.util.IPUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
* 用户操作模块的业务层类
*/
@Service
public class OperationServiceImpl {

@Autowired(required = false)
OperationDAO dao;

/**
* 保存一条用户操作记录
* @param optDO
*/
public void saveOperation(OperationDO optDO){
// 获取当前发请求用户的IP地址
String userIp = IPUtils.getIpAddr();
// 将IP地址添加到optDO对象中
optDO.setUserIp(userIp);
// 调用持久层方法,保存操作记录
dao.insertOperation(optDO);
}

}

6.开发业务层测试用例

光标选中OperationService类中任意位置,右键 ->Generate… -> Test… ,在里面勾选saveOpeartion前面的复选框,然后点OK,自动生成测试类。

1
2
3
4
5
6
7
8
9
10
11
12
13
@SpringBootTest
class OperationServiceTest {

@Autowired
OperationService service;

@Test
void saveOperation() {
OperationDO optDO = new OperationDO();
service.saveOperation(optDO);
}

}

7.开发控制器层

在src/main/java下新建cn.tedu.nybike.controller包。

在该包下新建OperationController类,并在类中开发对应的控制器层方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RestController
@RequestMapping("/opt")
public class OperationController {
@Autowired
OperationService service;

@RequestMapping("/save")
public JsonResult<Void> saveOperation(OperationDO optDO){
// TODO 参数验证:非空验证、格式验证、逻辑验证
// 调用业务层方法,保存optDO
service.saveOperation(optDO);
// 返回执行结果
return JsonResult.getSuccessJR();
}
}

8.测试控制器层

通过项目主类NybikeApplication的main方法启动项目。

在浏览器地址栏访问http://localhost:8080/opt/save?optType=1&stationId=1234,如果页面显示如下信息,说明访问成功。

常见问题:

  • 如果显示连接被拒绝,或无法连接:项目没有正常启动

  • 如果显示连接异常,状态码为404:请求的路径和控制器层提供的路径不对应,检查OperationController中两个@RequestMapping注解的值是否正确

  • 如果显示连接异常,状态码为500:服务器执行程序时出现异常,在IDEA的控制台中会有具体的报错信息,根据报错信息排查对应的问题

  • 如果提交了请求参数,但是查询数据库时发现对应的参数值没有被写进数据库:一般是提交参数的参数名和实体类中的属性名不一致导致的,核对参数名和属性名

  • 注:对服务器代码进行修改后,需要重启项目,新的代码才会生效

9.前端向后台发送数据(前端的代码的一部分不全主要学习springboot)

在nymap.html中添加如下代码:

  • 采集缩放行为
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
// 用于记录缩放开始级别的变量
var zoomStartLevel = 15;

map.addEventListener("zoomstart",function(){
// alert("start!"+map.getZoom())
zoomStartLevel = map.getZoom(); // 记录缩放开始级别
})

map.addEventListener("zoomend",function(){
// alert("end!"+map.getZoom())
// 获取缩放开始级别和缩放终止级别
const zoomEndLevel = map.getZoom();
// 按后台接口格式封装请求参数
const params = {
optType:1,
zoomStartLevel:zoomStartLevel,
zoomEndLevel:zoomEndLevel
}
// 向后台接口发送请求
sendOptData(params)
})

function sendOptData(params){
const url="opt/save" // 后台接口的相对路径
$.post(url, params, function(result){
console.log(result)
})
}
  • 采集拖拽行为
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
// 保存拖拽开始时地图的中心点
var dragStartPoint;
// 添加拖拽开始监听
map.addEventListener("dragstart",function (e) {
// 获取地图中心点的经纬度信息
const point = map.getCenter()
dragStartPoint = point
// 获取经纬度
// console.log(point.lat+":"+point.lng)
})
// 10分钟时间 到14:51 实现该功能
map.addEventListener("dragend",function (e) {
// 获取拖拽终止时地图的中心点
const point = map.getCenter();
// 按后台需求封装请求参数
const params = {
optType:2,
dragStartLat:dragStartPoint.lat,
dragStartLng:dragStartPoint.lng,
dragEndLat:point.lat,
dragEndLng:point.lng
}
// 将数据发送给后台
sendOptData(params)
})
  • 采集点击站点行为:在marker的点击监听中添加如下代码:
1
2
3
4
5
6
// 采集用户点击站点的数据
const params = {
optType:0,
stationId:this.sid
}
sendOptData(params)

10.开发业务层

在src/main/java下新建cn.tedu.nybike.service包。

在该包下新建OperationService类,并在类中开发对应的业务层方法。

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
package cn.tedu.nybiket.service;

import cn.tedu.nybiket.dao.OperationDAO;
import cn.tedu.nybiket.entity.OperationDO;
import cn.tedu.nybiket.util.IPUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
* 用户操作模块的业务层类
*/
@Service
public class OperationService {

@Autowired(required = false)
OperationDAO dao;

/**
* 保存一条用户操作记录
* @param optDO
*/
public void saveOperation(OperationDO optDO){
// 获取当前发请求用户的IP地址
String userIp = IPUtils.getIpAddr();
// 将IP地址添加到optDO对象中
optDO.setUserIp(userIp);
// 调用持久层方法,保存操作记录
dao.insertOperation(optDO);
}

}

11.开发业务层测试用例

光标选中OperationService类中任意位置,右键 ->Generate… -> Test… ,在里面勾选saveOpeartion前面的复选框,然后点OK,自动生成测试类。

1
2
3
4
5
6
7
8
9
10
11
12
13
@SpringBootTest
class OperationServiceTest {

@Autowired
OperationService service;

@Test
void saveOperation() {
OperationDO optDO = new OperationDO();
service.saveOperation(optDO);
}

}

12.开发控制器层

在src/main/java下新建cn.tedu.nybike.controller包。

在该包下新建OperationController类,并在类中开发对应的控制器层方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RestController
@RequestMapping("/opt")
public class OperationController {
@Autowired
OperationService service;

@RequestMapping("/save")
public JsonResult<Void> saveOperation(OperationDO optDO){
// TODO 参数验证:非空验证、格式验证、逻辑验证
// 调用业务层方法,保存optDO
service.saveOperation(optDO);
// 返回执行结果
return JsonResult.getSuccessJR();
}
}

13.测试控制器层

通过项目主类NybikeApplication的main方法启动项目。

在浏览器地址栏访问http://localhost:8080/opt/save?optType=1&stationId=1234,如果页面显示如下信息,说明访问成功。

常见问题:

  • 如果显示连接被拒绝,或无法连接:项目没有正常启动

  • 如果显示连接异常,状态码为404:请求的路径和控制器层提供的路径不对应,检查OperationController中两个@RequestMapping注解的值是否正确

  • 如果显示连接异常,状态码为500:服务器执行程序时出现异常,在IDEA的控制台中会有具体的报错信息,根据报错信息排查对应的问题

  • 如果提交了请求参数,但是查询数据库时发现对应的参数值没有被写进数据库:一般是提交参数的参数名和实体类中的属性名不一致导致的,核对参数名和属性名

  • 注:对服务器代码进行修改后,需要重启项目,新的代码才会生效

前端向后台发送数据

在nymap.html中添加如下代码:

  • 采集缩放行为
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
// 用于记录缩放开始级别的变量
var zoomStartLevel = 15;

map.addEventListener("zoomstart",function(){
// alert("start!"+map.getZoom())
zoomStartLevel = map.getZoom(); // 记录缩放开始级别
})

map.addEventListener("zoomend",function(){
// alert("end!"+map.getZoom())
// 获取缩放开始级别和缩放终止级别
const zoomEndLevel = map.getZoom();
// 按后台接口格式封装请求参数
const params = {
optType:1,
zoomStartLevel:zoomStartLevel,
zoomEndLevel:zoomEndLevel
}
// 向后台接口发送请求
sendOptData(params)
})

function sendOptData(params){
const url="opt/save" // 后台接口的相对路径
$.post(url, params, function(result){
console.log(result)
})
}
  • 采集拖拽行为
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
// 保存拖拽开始时地图的中心点
var dragStartPoint;
// 添加拖拽开始监听
map.addEventListener("dragstart",function (e) {
// 获取地图中心点的经纬度信息
const point = map.getCenter()
dragStartPoint = point
// 获取经纬度
// console.log(point.lat+":"+point.lng)
})
// 10分钟时间 到14:51 实现该功能
map.addEventListener("dragend",function (e) {
// 获取拖拽终止时地图的中心点
const point = map.getCenter();
// 按后台需求封装请求参数
const params = {
optType:2,
dragStartLat:dragStartPoint.lat,
dragStartLng:dragStartPoint.lng,
dragEndLat:point.lat,
dragEndLng:point.lng
}
// 将数据发送给后台
sendOptData(params)
})
  • 采集点击站点行为:在marker的点击监听中添加如下代码:
1
2
3
4
5
6
// 采集用户点击站点的数据
const params = {
optType:0,
stationId:this.sid
}
sendOptData(params)

11.学习可视化插件echarts有自己学习文档

12.数据可视化

从服务器动态获取图表的数据

浏览器与服务器在进行数据交互时,目前比较流行的格式有两种:

  • 针对简单数据:key1=value1&key2=value2&key3=value3....
  • 针对复杂数据:JSON

用例组件设计:

开发实体类cn.tedu.nybike.entity.PieItem:

1
2
3
4
5
6
public class PieItem {

private String name;
private Double value;
// 笔记这里省略无参构造器,带参构造器,get/set方法,toString方法
}

声明实体类cn.tedu.nybike.entity.OptTypeCountDO

1
2
3
4
5
6
public class OptTypeCountDO {

private Integer optType; // 0点击,1缩放 2拖拽
private Integer count; // 操作的数量
// 笔记这里省略无参构造器,get/set方法,toString方法
}

cn.tedu.nybike.dao.OperationDAO中添加新的抽象方法:

1
2
3
4
5
/**
* 查询各类操作的总数量
* @return
*/
List<OptTypeCountDO> listOptTypeCount();

src/main/resources/mappers/OperationMapper.xml中添加绑定的SQL语句:

1
2
3
4
5
6
7
8
<!--查询各类操作的总数量-->
<!--List<OptTypeCountDO> listOptTypeCount()-->
<select id="listOptTypeCount" resultType="cn.tedu.nybiket.entity.OptTypeCountDO" >
select opt_type as optType, count(*) as count
from t_operation
where opt_type is not null
group by opt_type
</select>

OperationDAOTest中添加测试方法,对持久层方法进行测试:

1
2
3
4
5
@Test
void listOptTypeCount() {
List<OptTypeCountDO> list = dao.listOptTypeCount();
list.forEach(item-> System.err.println(item));
}

OperationService中添加新的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public List<PieItem> findOptTypeCount(){
// 调用持久层方法,查询数据
List<OptTypeCountDO> list = dao.listOptTypeCount();
// 对数据进行转换
List<PieItem> result = new ArrayList<>();
for(OptTypeCountDO opt:list){
PieItem item = new PieItem();
// 将数值型的optType转换为对应的字符串描述
switch (opt.getOptType()){
case 0: item.setName("点击");
break;
case 1: item.setName("缩放");
break;
case 2: item.setName("拖拽");
break;
}
// 添加value
item.setValue(opt.getCount()*1.0);
// 将item添加到result中
result.add(item);
}
// 返回转换的结果
return result;
}

修改OperationController中的方法,通过调用业务层方法获取数据:

1
2
3
4
5
6
7
8
9
10
11
12
@RequestMapping("/findOptTypeCount")
public JsonResult<List<PieItem>> findOptTypeCount(){
// 创建模拟数据
// List<PieItem> list=new ArrayList<>();
// list.add(new PieItem("点击", 8.0));
// list.add(new PieItem("缩放", 15.0));
// list.add(new PieItem("拖拽", 13.0));
// 调用业务层方法,查询真实数据
List<PieItem> list=service.findOptTypeCount();
// 返回封装了数据的JR对象
return JsonResult.getSuccessJR(list);
}

通过NybikeApplication启动项目,通过浏览器访问http://localhost:8080/demo/opt_type_count.html,查看饼图数据是否更新成数据库中的数据: