后端基础学习(二)
SpringBoot的学习主要是设计模式的练习和参数注入的熟练度提升,本身没有多大的难度,主要是比较杂,需要持续投入时间。
相比于看教程,最高效的方法是快速搭建一个项目,用到啥学啥,在此记录一下基本的框架。
SpringBoot 工程结构
我采用的是如下的工程结构,和其它的一些结构可能不尽相同。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
35
36
37
38java
+-com
+- nowcoder
+- community
+- Application.java
|
+- config
| +- AlphaConfig.java
+- controller
| +- AlphaController.java
| +- HomeController.java
+- dao
| +- AlphaDao.java
| +- UserMapper.java
| +- AlphaDaoMyBatisImpl.java
+- entity
| +- DiscussPost.java
| +- Page.java
| +- User.java
+- service
| +- AlphaService.java
| +- DiscusspPostService.java
| +- UserService.java
resource
| +- Application properties
| +- mapper
| +- discusspost-mapper.xml
| +- user-mapper.xml
| +- templates
| +- demo
| +- mail
| +- site
| +- index.html
| +- static
| +- html
| +- css
| +- img
| +- js
最关键的是controller
, service
和dao
Controller
:业务控制层;
就是控制业务层Service
的,它的作用主要是架起了外界与业务层沟通的桥梁,移动端,前端在调用接口访问相关业务时,都会通过Controller
,由Controller
去调相关的业务层代码并把数据返回给移动端和前端;Service
:业务层/服务层;
业务层,所有的内部的业务逻辑都会放在这里处理,比如用户的增删改查,或者发送个验证码或邮件,或者做一个抽奖活动等等等等,都会在Service中
进行,当然,业务离肯定是离不开数据的支持,因此Dao
层是必不可少的;Dao
:数据库持久化层;
数据持久化层,就是和数据库打交道的,而实现持久化层的框架又有很多,而常用的有两种:JPA和MyBatis,JPA是SpringBoot官方的,前身就是著名的三大框架之一的Hibernate,好处是不用手写SQL(当然它也支持手写,如果必要的话),国外用的比较多,而MyBatis则在国内比较流行,原因是它的灵活性非常高,但是需要手写SQL语句。- 项目复杂程度一般,追求稳定,迭代速率低的可以用JPA;
- 项目较复杂,需求变更频繁,迭代速度快的可以用MyBatis;
Entity
:实体层—>数据库在项目中的类
主要用于定义与数据库对象应的属性,提供get/set方法,tostring方法,有参无参构造函数。
为什么必须要写Service
层,我直接用Controller
层操作Dao
层,省去Service
层,岂不是更简单?
Controller
的作用,只是一个桥梁,中间者。是不允许直接操作数据库的!另外,Service
对以后的分布式部署有极大的作用,它就像一个服务员,哪桌客人需要点菜了,就喊一声服务员!对应的,外界需要完成什么样的业务,就通过Controller
去调用不同的Service
,需要记住的是,Controller
只是一个中间者或者转发者,不应该在Controller
里暴露Service
的业务逻辑,而应该直接转发Service
的业务处理结果!
一般的,一个Controller
对应一个Service
,一个Service
对应一个Dao
,一个Dao
对应一个数据库表,当然根据项目或业务复杂程度,一个Controller
可以调用多个Service
,而一个Service
也可以调用多个Dao
,但是Controller
层不允许互调,Service
层也不允许互调,意思就是A Controller
不能直接调用B Controller
,A Service
也不能直接去调用B Service
,遵循高内聚低耦合原则。
Controller 业务控制层
1 | //这里要写上controller注解 |
Service 业务层/服务层
1 | //Service层要添加Service注解 |
dao 数据库持久化层
1 |
|
entity 实体层
实体层比较简单,就是定义变量,并设置Get
, Set
, ToString
方法。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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96public class Page {
// 当前页码
private int current = 1;
// 显示上限
private int limit = 10;
// 数据总数(用于计算总页数)
private int rows;
// 查询路径(用于复用分页链接)
private String path;
public int getCurrent() {
return current;
}
public void setCurrent(int current) {
if (current >= 1) {
this.current = current;
}
}
public int getLimit() {
return limit;
}
public void setLimit(int limit) {
if (limit >= 1 && limit <= 100) {
this.limit = limit;
}
}
public int getRows() {
return rows;
}
public void setRows(int rows) {
if (rows >= 0) {
this.rows = rows;
}
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
/**
* 获取当前页的起始行
*
* @return
*/
public int getOffset() {
// current * limit - limit
return (current - 1) * limit;
}
/**
* 获取总页数
*
* @return
*/
public int getTotal() {
// rows / limit [+1]
if (rows % limit == 0) {
return rows / limit;
} else {
return rows / limit + 1;
}
}
/**
* 获取起始页码
*
* @return
*/
public int getFrom() {
int from = current - 2;
return from < 1 ? 1 : from;
}
/**
* 获取结束页码
*
* @return
*/
public int getTo() {
int to = current + 2;
int total = getTotal();
return to > total ? total : to;
}
}
mapper dao层对数据库的映射
mapper
文件主要是添加对数据库的操作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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<mapper namespace="com.nowcoder.community.dao.UserMapper">
<!--前面是对mybatis的文件说明,namespace应该写对应dao层的全界名-->
<!--一个mapper文件对应一个dao层文件-->
<sql id="insertFields"> <!--定义系列的变量域,便于日后维护-->
username, password, salt, email, type, status, activation_code, header_url, create_time
</sql>
<sql id="selectFields">
id, username, password, salt, email, type, status, activation_code, header_url, create_time
</sql>
<select id="selectById" resultType="User">
select <include refid="selectFields"></include>
from user
where id = #{id}
</select>
<select id="selectByName" resultType="User">
select <include refid="selectFields"></include>
from user
where username = #{username}
</select>
<select id="selectByEmail" resultType="User">
select <include refid="selectFields"></include>
from user
where email = #{email}
</select>
<insert id="insertUser" parameterType="User" keyProperty="id">
insert into user (<include refid="insertFields"></include>)
values(#{username}, #{password}, #{salt}, #{email}, #{type}, #{status}, #{activationCode}, #{headerUrl}, #{createTime})
</insert>
<update id="updateStatus">
update user set status = #{status} where id = #{id}
</update>
<update id="updateHeader">
update user set header_url = #{headerUrl} where id = #{id}
</update>
<update id="updatePassword">
update user set password = #{password} where id = #{id}
</update>
</mapper>
Thymeleaf
Thymeleaf
是一个动态的html模版, 它能够通过实体类的值实时改变html的响应。上文的controller
层的最终返回是index.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
29
30
31
32
<html lang="en" xmlns:th="http://www.thymeleaf.org"><!--这里要声明是thymeleaf模版-->
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="icon" href="https://static.nowcoder.com/images/logo_87_87.png"/>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" crossorigin="anonymous">
<link rel="stylesheet" th:href="@{/css/global.css}" />
<title>首页</title>
</head>
<body>
<!-- 分页 -->
<nav class="mt-5" th:if="${page.rows>0}">
<ul class="pagination justify-content-center">
<li class="page-item"><!--这里是动态的变量,用th来声明-->
<a class="page-link" th:href="@{${page.path}(current=1)}">首页</a>
</li>
<li th:class="|page-item ${page.current==1?'disabled':''}|">
<a class="page-link" th:href="@{${page.path}(current=${page.current-1})}">上一页</a></li>
<li th:class="|page-item ${i==page.current?'active':''}|" th:each="i:${#numbers.sequence(page.from,page.to)}">
<a class="page-link" href="#" th:text="${i}">1</a>
</li>
<li th:class="|page-item ${page.current==page.total?'disabled':''}|">
<a class="page-link" th:href="@{${page.path}(current=${page.current+1})}">下一页</a>
</li>
<li class="page-item">
<a class="page-link" th:href="@{${page.path}(current=${page.total})}">末页</a>
</li>
</ul>
</nav>
</body>
</html>
Application properties 配置
用来配置服务器容器的一些属性,如
server.port=8080
配置端口号server.servlet.context-path=/community
配置上下文环境spring.thymeleaf.cache=false
配置thymeleaf模版缓存
更多的配置信息可以直接访问Spring官网的Reference