1. SpringBoot加强
1.1 健康检查
1.1.1 添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
1.1.2 健康检查分析
在浏览器中输入如下地址:http://localhost/actuator/health
假如希望查看更多actuator选项,可以在spring boot中配置文件application.properties中添加如下语句:(生产环境不加),此时在浏览器地址栏可以输入http://localhost/actuator/beans 查看所有的spring 容器中的bean信息。
management.endpoints.web.exposure.include=*
说明:当需要以一种更好的结构化方式查看bean相关信息,可以对Google浏览器安装jsonView插件或者使用Postman等工具进行资源请求然后查询bean信息,还可以直接在sts工具的Boot Dashboard中选中项目,查看其属性(show properties)。
1.2 热部署
基于SpringBoot的Web项目,修改了某个类以后,默认不会自动重新部署和加载,需要我们手动重启服务器。假如我们希望项目可以自动部署,可以添加如下依赖,进行热部署实现。
1.2.1 添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
说明:当我们修改了src/main/java目录下的java文件或修改了src/main/resources目录下的配置文件时,默认都会重启你的web服务器,但是修改了测试类或html文件不会自动重启和部署。
1.3 Lombok插件
Lombok是一个第三库,可以基于lombok中提供的API,在程序编译时自动织入一些方法。这样做的话可以简化我们对某些方法的编写,例如,省略pojo类中的set方法,get方法,toString等方法的编写。我们写的java程序可以在编译时,通过lombok自动将这些方法添加到.class文件中。
1.3.1 添加依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
1.3.2 安装lombok
1)找到下载的lombok-xxx.jar依赖所在的位置(例如:C:\Users\Administrator.m2\repository\org\projectlombok\lombok\1.18.16)
2)在当前目录下打开cmd窗口,运行如下命令
java -jar lombok-1.18.16.jar
3)选择sts所在目录
4)点击安装
5)安装成功后效果如下
6)安装成功以后可在STS工具的根目录看到一个lombok.jar文件
同时会在SpringToolSuite4.ini文件的尾部添加lombok.jar文件所在的具体路径,例如 “-javaagent:C:\SoftWare\sts-4.6.1.RELEASE\lombok.jar”
7)重启sts工具
1.3.3 Lombok常用注解
@Setter // 用于为描述的类生成setter方法,不包含final修饰属性。
@Getter // 用于为描述的类生成getter方法。
@ToString // 用于为描述的类添加toString方法。
@EqualsAndHashCode // 用于为描述的类,生成hashCode和equals方法。
@NoArgsConstructor // 用于为描述的类生成无参的构造方法。
@AllArgsConstructor // 用于为描述的类生成包含类中所有字段的构造方法。
@Data // 用于为描述的类生成setter/getter、equals、canEqual、hashCode、toString方法,如为final属性,则不会为该属性生成setter方法。
@Slf4J // 用于为描述的类添加一个日志属性对象,private static final Logger log =LoggerFactory.getLogger(Xxx.class);
2. 需求分析
在页面上呈现全部活动(Activity)信息。
在页面上添加活动信息到数据库。
在页面上修改活动信息,并且在修改界面呈现修改前的信息。
在页面上删除指定活动信息。
3. 综合实践
3.1 搭建项目初始环境
3.1.1 准备数据
创建数据库db_activity,创建表tb_activity,并导入相关数据。
drop database if exists db_activity;
create database db_activity default character set utf8;
use db_activity;
create table tb_activity(
id bigint primary key auto_increment,
title varchar(100) not null,
category varchar(100) not null,
startTime datetime not null,
endTime datetime not null,
remark text,
state tinyint,
createdTime datetime not null,
createdUser varchar(100)
)engine=InnoDB;
insert into tb_activity values (null,'ABS','cuoxiao','2020-02-02 15:00:00','2020-02-03 15:00:00','...',1,now(),'xiaoli');
insert into tb_activity values (null,'VALIDATE','cuoxiao','2020-02-02 15:00:00','2020-02-03 15:00:00','...',1,now(),'xiaoli');
insert into tb_activity values (null,'VALIDATE','cuoxiao','2020-02-02 15:00:00','2020-02-03 15:00:00','...',1,now(),'xiaoli');
insert into tb_activity values (null,'ABS','cuoxiao','2020-02-02 15:00:00','2020-02-03 15:00:00','...',1,now(),'xiaoli');
insert into tb_activity values (null,'ACCESS','cuoxiao','2020-02-02 15:00:00','2020-02-03 15:00:00','...',1,now(),'xiaoli');
insert into tb_activity values (null,'SD_ALL','cuoxiao','2020-02-02 15:00:00','2020-02-03 15:00:00','...',1,now(),'xiaoli');
insert into tb_activity values (null,'DF','cuoxiao','2020-02-02 15:00:00','2020-02-03 15:00:00','...',1,now(),'xiaoli');
insert into tb_activity values (null,'FG','cuoxiao','2020-02-02 15:00:00','2020-02-03 15:00:00','...',1,now(),'xiaoli');
insert into tb_activity values (null,'ER','cuoxiao','2020-02-02 15:00:00','2020-02-03 15:00:00','...',1,now(),'xiaoli');
insert into tb_activity values (null,'EF','cuoxiao','2020-02-02 15:00:00','2020-02-03 15:00:00','...',1,now(),'xiaoli');
insert into tb_activity values (null,'FFF','cuoxiao','2020-02-02 15:00:00','2020-02-03 15:00:00','...',1,now(),'xiaoli');
insert into tb_activity values (null,'EEE','cuoxiao','2020-02-02 15:00:00','2020-02-03 15:00:00','...',1,now(),'xiaoli');
insert into tb_activity values (null,'WWW','cuoxiao','2020-02-02 15:00:00','2020-02-03 15:00:00','...',1,now(),'xiaoli');
insert into tb_activity values (null,'AAA','cuoxiao','2020-02-02 15:00:00','2020-02-03 15:00:00','...',1,now(),'xiaoli');
insert into tb_activity values (null,'CCC','cuoxiao','2020-02-02 15:00:00','2020-02-03 15:00:00','...',1,now(),'xiaoli');
insert into tb_activity values (null,'XXXX','cuoxiao','2020-02-02 15:00:00','2020-02-03 15:00:00','...',1,now(),'xiaoli');
insert into tb_activity values (null,'WWW','cuoxiao','2020-02-02 15:00:00','2020-02-03 15:00:00','...',1,now(),'xiaoli');
insert into tb_activity values (null,'QQQQ','cuoxiao','2020-02-02 15:00:00','2020-02-03 15:00:00','...',1,now(),'xiaoli');
3.1.2 创建SpringBoot项目
1)打开Spring Starter Project界面,如图所示。
2)添加项目依赖(MySql,Jdbc API,MyBatis,Spring Web,Thymeleaf),如图所示
3)修改application.properties文件(src/main/resources/),进行资源配置,如图所示
3.1)添加数据源配置(使用内置的HikariCP连接池)
# spring datasource
spring.datasource.url=jdbc:mysql:///dbactivity?serverTimezone=GMT%2B8&charac# terEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root
3.2)添加mybatis配置
# spring mybatis
mybatis.mapper-locations=classpath:/mapper/*/*.xml
3.3)添加thymeleaf配置
# spring web
spring.thymeleaf.prefix=classpath:/templates/pages/
3.4)添加日志配置
# Spring log
logging.level.com.cy=debug
3.1.3 启动项目
3.2 活动模块API设计
3.2.1 API对象类型及业务关系设计
3.2.2 服务端实现
分别创建数据层 ActivityDao、业务层 ActivityService、ActivityServiceImpl、控制层 ActivityController、实体类 Activity
3.2.3 客户端实现
3.3 活动模块查询业务实现
3.3.1 业务时序分析
3.3.2 服务端实现
1)定义pojo对象
package com.cy.pj.activity.pojo;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Setter
@Getter
@ToString
@NoArgsConstructor //无参构造
@AllArgsConstructor //所有参数的构造函数
public class Activity {
private Long id;
private String title;
private String category;
@DateTimeFormat(pattern = "yyyy/MM/dd HH:mm")
private Date startTime;
@DateTimeFormat(pattern = "yyyy/MM/dd HH:mm")
private Date endTime;
private String remark;
private int state = 1;
private Date createdTime;
private String createdUser;
}
2)定义ActivityDao接口及方法
package com.cy.pj.activity.dao;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import com.cy.pj.activity.pojo.Activity;
@Mapper
public interface ActivityDao {
/**
* 查询所有活动信息
* @return
*/
@Select("select * from tb_activity order by createdTime desc")
List<Activity> findActivitys();
}
3)定义ActivityService接口及实现类ActivityServiceImpl
package com.cy.pj.activity.service;
import java.util.List;
import com.cy.pj.activity.pojo.Activity;
public interface ActivityService {
List<Activity> findActivitys();
}
package com.cy.pj.activity.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.cy.pj.activity.dao.ActivityDao;
import com.cy.pj.activity.pojo.Activity;
import com.cy.pj.activity.service.ActivityService;
@Service
public class ActivityServiceImpl implements ActivityService {
@Autowired
private ActivityDao activityDao;
@Override
public List<Activity> findActivitys() {
return activityDao.findActivitys();
}
}
第四步:定义ActivityController对象及url映射
package com.cy.pj.activity.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import com.cy.pj.activity.pojo.Activity;
import com.cy.pj.activity.service.ActivityService;
@Controller
@RequestMapping("/activity/")
public class ActivityController {
@Autowired
private ActivityService activityService;
@RequestMapping("doFindActivitys")
public String doFindActivitys(Model model) {
List<Activity> list = activityService.findActivitys();
model.addAttribute("list", list);
return "activity";
}
}
3.3.3 客户端实现
1)定义activity.html页面(src/main/resources/templates/pages/)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<div class="container">
<h1>The Activity Page</h1>
<div>
<table class="table">
<thead>
<tr>
<th>id</th>
<th>title</th>
<th>category</th>
<th>startTime</th>
<th>endTime</th>
<th>state</th>
<th>createdTime</th>
</tr>
</thead>
<tbody>
<tr th:each="a:${list}">
<td th:text="${a.id}"></td>
<td th:text="${a.title}"></td>
<td th:text="${a.category}"></td>
<td th:text="${#dates.format(a.startTime, 'yyyy/MM/dd HH:mm')}"></td>
<td th:text="${#dates.format(a.endTime, 'yyyy/MM/dd HH:mm')}"></td>
<td th:text="${a.state}"></td>
<td th:text="${#dates.format(a.createdTime, 'yyyy/MM/dd HH:mm')}"></td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
</html>
3.3.4 运行效果
启动程序后,在浏览器输入http://localhost:8080/activity/doFindActivitys,效果如图
3.4 活动模块添加业务实现
3.4.1 业务时序分析
3.4.2 服务端实现
1)在ActivityDao中定义insertActivity(Activity entity)方法以及SQL映射
ActivityDao :
/**
* 添加活动信息
* @return
*/
int insertActivity(Activity activity);
ActivityMapper.xml(src/main/resources/mapper/activity):
<?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">
<mapper namespace="com.cy.pj.activity.dao.ActivityDao">
<insert id="insertActivity">
insert into tb_activity
(title,category,startTime,endTime,state,remark,createdTime,createdUser)
values
(#{title},#{category},#{startTime},#{endTime},#{state},#{remark},now(),#{createdUser})
</insert>
</mapper>
2)在ActivityService接口及实现类中添加saveActivity(Activity entity)
ActivityService:
int saveActivity(Activity activity);
ActivityServiceImpl:
@Override
public int saveActivity(Activity activity) {
return activityDao.insertActivity(activity);
}
3)在ActivityController对象中添加doSaveActivity方法并定义url映射
@RequestMapping("doSaveActivity")
public String doSaveActivity(Activity activity) {
activityService.saveActivity(activity);
return "redirect:doFindActivitys";
}
3.4.3 客户端实现
1)在页面中activity.html页面中引入两个js(jquery,bootstrap)
1.1)复制bootstrap和jquery到src/main/resources/static/目录下
1.2)在 标签体中引入bootstrap.min.css
<link rel="stylesheet" href="/bootstrap/css/bootstrap.min.css">
1.3)在 标签体最后引入jquery.min.js、bootstrap.min.js
<!-- 当在页面中需要使用bootstrap插件时,需要添加如下两个js,但也要注意顺序 -->
<script type="text/javascript" src="/jquery/jquery.min.js"></script>
<script type="text/javascript" src="/bootstrap/js/bootstrap.min.js"></script>
2)基于bootcss.com官方模态框案例,在activity.html页面在 标签体中添加模态框。
<!-- Modal -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog"
aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"
aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<h4 class="modal-title" id="myModalLabel">Modal title</h4>
</div>
<div class="modal-body">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary"
onclick="doSaveActivity()">Save changes</button>
</div>
</div>
</div>
</div>
3)在
标签体中添加form表单(用户填写用户数据) <form id="atyFormId" class="form-horizontal"
action="doSaveActivity" method="post">
<input type="hidden" name="id" id="atyId">
<div class="form-group">
<label for="titleId" class="col-sm-2 control-label">title</label>
<div class="col-sm-10">
<input type="text" class="form-control" name="title"
id="titleId" placeholder="title">
</div>
</div>
<div class="form-group">
<label for="categoryId" class="col-sm-2 control-label">category</label>
<div class="col-sm-10">
<input type="text" class="form-control" name="category"
id="categoryId" placeholder="category">
</div>
</div>
<div class="form-group">
<label for="startTimeId" class="col-sm-2 control-label">startTime</label>
<div class="col-sm-10">
<input type="text" class="form-control" name="startTime"
id="startTimeId" placeholder="yyyy/MM/dd">
</div>
</div>
<div class="form-group">
<label for="endTimeId" class="col-sm-2 control-label">endTime</label>
<div class="col-sm-10">
<input type="text" class="form-control" name="endTime"
id="endTimeId" placeholder="yyyy/MM/dd">
</div>
</div>
<div class="form-group">
<label for="remarkId" class="col-sm-2 control-label">remark</label>
<div class="col-sm-10">
<textarea class="form-control" id="remarkId" name="remark"
placeholder="remark"></textarea>
</div>
</div>
</form>
在 标签体最后添加如下 标签体,提交form表单,关闭模态框
<script>
function doSaveActivity() {
// alert("save activity");
//对表单内容进行校验
//执行表单提交操作(基于js方式提交表单)
$("#atyFormId").submit();
//关闭模态框
$('#myModal').modal('hide');
}
</script>
4)在activity.html页面
标签体下方添加添加按钮,点击按钮时呈现模态框 <div>
<button type="button" class="btn btn-primary" data-toggle="modal"
onclick="doShowAddDialog()">添加活动信息</button>
</div>
在 标签体中添加如下代码
// 呈现添加模态框
function doShowAddDialog() {
$("#atyId").val('');
$("#titleId").val('');
$("#categoryId").val('');
$("#startTimeId").val('');
$("#endTimeId").val('');
$("#remarkId").text('');
// 显示模态框
$('#myModal').modal('show');
}
5)在表单中输入数据, 然后点击保存按钮,将数据传递到服务端。
3.5 活动模块修改表单数据的呈现
3.5.1 业务时序分析
3.5.2 服务端实现
第一步:在ActivityDao中定义findById方法,基于活动id查询活动信息
/**
* 基于id查询指定活动信息
* @param id
* @return
*/
@Select("select * from tb_activity where id=#{id}")
Activity findById(Integer id);
第二步:在ActivityService及实现类中添加findById方法,基于活动id查询活动信息。
ActivityService:
Activity findById(Integer id);
ActivityServiceImpl:
@Override
public Activity findById(Integer id) {
return activityDao.findById(id);
}
第三步:在ActivityController中定义doFindById方法,基于id获取活动信息将其存储在作用域对象中,并定义url映射。
@RequestMapping("doFindById")
public String doFindById(Integer id,Model model) {
Activity aty=activityService.findById(id);
model.addAttribute("aty", aty);
return "forward:doFindActivitys";
}
3.5.3 客户端实现
1)activity.html中 标签体最后注册修改按钮事件并定义事件处理函数
<td>
<button class="btn btn-warning btn-sm" th:onclick="doLoadById([[${a.id}]])">更新</button>
</td>
2)在 标签体中修改按钮事件处理函数中向服务端发起请求基于id获取活动信息。
function doLoadById(id){
location.href="doFindById?id="+id;
}
3)在activity.html页面中给 标签体添加 “th:inline” 属性,定义处理函数获取基于id查询到的活动信息
<!-- 假如要在js中获取thymeaf表达式中的数据script标签内部需要th:inline="javascript" -->
<script th:inline="javascript">
$(function(){//页面加载完成以后执行
var aty=[[${aty}]];//在js中获取thyemeaf表达式中的内容
//console.log("aty",aty); 在浏览器输出相关内容
if(aty){
doInitEditFormData(aty)
}
});
function doInitEditFormData(aty){
$("#atyId").val(aty.id);
$("#titleId").val(aty.title);
$("#categoryId").val(aty.category);
$("#startTimeId").val(aty.startTime);
$("#endTimeId").val(aty.endTime);
$("#remarkId").text(aty.remark);
//显示模态框
$('#myModal').modal('show');
}
</script>
4)将要修改的记录信息呈现在表单中并以模态框的信息进行呈现。
3.6 活动模块修改操作中表单数据的提交
3.6.1 业务时序分析
3.6.2 服务端实现
1)ActivityDao中定义基于id更新活动信息的方法 updateObject(Activity entity)
/**
* 基于id更新指定活动信息
* @param entity
* @return
*/
int updateActivity(Activity entity);
ActivityMapper.xml(src/main/resources/mapper/activity/)
<update id="updateActivity">
update tb_activity
set title=#{title},
category=#{category},
startTime=#{startTime},
endTime=#{endTime},
remark=#{remark}
where id=#{id}
</update>
2)修改ActivityServiceImpl类中saveObject方法,基于的值进行添加或修改操作
@Override
public int saveActivity(Activity entity) {
if(entity.getId()==null) {
return activityDao.insertActivity(entity);
}else {
return activityDao.updateActivity(entity);
}
}
3.7 活动模块删除操作实现
3.7.1 业务时序分析
3.7.2 服务端实现
1)ActivityDao中定义基于id删除记录的方法 deleteById(Integer id);
/**
* 基于id删除活动信息
* @param id
* @return
*/
@Delete("delete from tb_activity where id=#{id}")
int deleteById(Integer id);
2)ActivityService及实现类中定义deleteById(Integer id)方法用于执行记录删除
ActivityService:
int deleteById(Integer id);
ActivityServiceImpl:
@Override
public int deleteById(Integer id) {
return activityDao.deleteById(id);
}
3)ActivityController中定义doDeleteById(Integer id)方法用于处理删除请求
@RequestMapping("doDeleteById")
public String doDeleteById(Integer id) {
activityService.deleteById(id);
return "forward:doFindActivitys";
}
3.7.3 客户端实现
1)Activity.html 标签中注册删除按钮事件以及事件处理函数定义
<button class="btn btn-danger btn-sm" th:onclick="doDeleteById([[${a.id}]])">删除</button>
2)在 标签中的删除事件处理函数内部向服务端发请求执行删除操作
function doDeleteById(id) {
if (!confirm("确定删除吗")) return;
location.href = "doDeleteById?id=" + id;
}
3)点击删除按钮删除指定活动信息