本專案目標是開發一個社群網站,擁有發帖、討論、搜尋、登入等一個正常社群擁有的功能。涉及到的版本引數為:
- JDK1.8
- Maven3.8.1(直接整合到IDEA)
- Springboot 2.5.1
- tomcat
參考網站(在使用框架過程中可能會看的開發文件):
https://mvnrepository.com/ 查詢maven依賴
https://mybatis.org/mybatis-3/zh/index.html mybatis的官方文件,配置等都有說明
專案程式碼已釋出到github https://github.com/GaoYuan-1/web-project
關於資料庫檔案,該篇部落格中已有提到,可去文中github獲取資料 MySQL基礎篇(一)
本文第一篇只介紹了基礎,在(二)中將會介紹如何實現登入介面等後續內容。最終將會把完整專案經歷釋出出來。
本系列主要介紹的是實戰內容,對於理論知識介紹較少,適合有一定基礎的人。
首先建立一個專案,可利用Spring Initializr
本人配置如下:
maven專案的
pom.xml
中初始依賴如下,後面會增加更多依賴。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
1. Spring相關知識
記錄一些基本概念,Spring的知識可能說的比較少,本篇關注於如何對網站進行實現。
Spring Bean是被例項的,組裝的及被Spring 容器管理的Java物件。
Spring 容器會自動完成@bean物件的例項化。
建立應用物件之間的協作關係的行為稱為:裝配(wiring),這就是依賴注入的本質。
在使用 Spring Initializr
之後,第一個接觸的註解為:
@SpringBootApplication
我們可以把 @SpringBootApplication
看作是 @Configuration
、@EnableAutoConfiguration
、@ComponentScan
註解的集合。
根據 SpringBoot
官網,這三個註解的作用分別是:
@EnableAutoConfiguration
:啟用 SpringBoot 的自動配置機制@ComponentScan
: 掃描被@Component (@Service,@Controller)註解的 bean,註解預設會掃描該類所在的包下所有的類。@Configuration
:允許在 Spring 上下文中註冊額外的 bean 或匯入其他配置類(表示該類是配置類)
Spring通過識別一些註解可以讓容器自動裝填bean:
- @Component :通用的註解,可標註任意類為 Spring 元件。如果一個 Bean 不知道屬於哪個層,可以使用@Component 註解標註。
- @Repository : 對應持久層即 Dao 層,主要用於資料庫相關操作。
- @Service : 對應服務層,主要涉及一些複雜的邏輯,需要用到 Dao (Data Access Object)層。
- @Controller : 對應 Spring MVC 控制層,主要使用者接受使用者請求並呼叫 Service 層返回資料給前端頁面。
關於@Scope,負責宣告Bean的作用域:
@Scope("singleton") //唯一 bean 例項,Spring 中的 bean 預設都是單例的。
@Scope("prototype") //每次請求都會建立一個新的 bean 例項。
@Scope("request") //每一次 HTTP 請求都會產生一個新的 bean,該 bean 僅在當前 HTTP request 內有效。
@Scope("session") //每一次 HTTP 請求都會產生一個新的 bean,該 bean 僅在當前 HTTP session 內有效。
關於@Autowired:
自動匯入物件到類中,被注入進的類同樣要被 Spring 容器管理。從構造器,到方法,到引數、屬性、註解,都可以加上@Autowired註解。
2. SpringMVC相關
可以先了解一些HTTP流的相關知識:(參考自MDN Web Docs)
當客戶端想要和服務端進行資訊互動時(服務端是指最終伺服器,或者是一箇中間代理),過程表現為下面幾步:
-
開啟一個TCP連線:TCP連線被用來傳送一條或多條請求,以及接受響應訊息。客戶端可能開啟一條新的連線,或重用一個已經存在的連線,或者也可能開幾個新的TCP連線連向服務端。
-
傳送一個HTTP報文:HTTP報文(在HTTP/2之前)是語義可讀的。在HTTP/2中,這些簡單的訊息被封裝在了幀中,這使得報文不能被直接讀取,但是原理仍是相同的。
GET / HTTP/1.1 Host: developer.mozilla.org Accept-Language: fr
-
讀取服務端返回的報文資訊:
HTTP/1.1 200 OK Date: Sat, 09 Oct 2010 14:28:02 GMT Server: Apache Last-Modified: Tue, 01 Dec 2009 20:18:22 GMT ETag: "51142bc1-7449-479b075b2891b" Accept-Ranges: bytes Content-Length: 29769 Content-Type: text/html <!DOCTYPE html... (here comes the 29769 bytes of the requested web page)
-
關閉連線或者為後續請求重用連線。
當HTTP流水線啟動時,後續請求都可以不用等待第一個請求的成功響應就被髮送。然而HTTP流水線已被證明很難在現有的網路中實現,因為現有網路中有很多老舊的軟體與現代版本的軟體共存。因此,HTTP流水線已被在有多請求下表現得更穩健的HTTP/2的幀所取代。
HTTP請求的例子:
響應的例子:
2.1 SpringMVC概要
三層架構:表現層,業務層和資料訪問層。
MVC:Model(模型層),View(檢視層),Controller(控制層)
核心元件: DispatcherServlet
2.2 Thymeleaf思想
如果想給瀏覽器返回一個動態網頁,則需要一個工具支援,例如:Thymeleaf(模板引擎,生成動態的HTML)。
該引擎需要學習的有:標準表示式,判斷與迴圈,模板的佈局。
學習建議參考官方文件 https://www.thymeleaf.org/index.html
2.3 SpringMVC程式碼示例
GET請求
@RequestMapping支援Servlet的request和response作為引數,以下為一個簡單示例:
@Controller
@RequestMapping("/alpha")
public class AlphaController {
@RequestMapping("/hello")
@ResponseBody
public String sayHello(){
return "Hello Spring Boot.";
}
@RequestMapping("/http")
public void http(HttpServletRequest request, HttpServletResponse response) throws IOException {
//獲取請求資料
System.out.println(request.getMethod());
System.out.println(request.getServletPath());//請求路徑
Enumeration<String> enumeration = request.getHeaderNames();//得到請求行的key
while(enumeration.hasMoreElements()) {
String name = enumeration.nextElement(); //當前值(key)
String value = request.getHeader(name);//得到value
System.out.println(name + ":" + value);
}
System.out.println(request.getParameter("code"));
// 返回響應資料
response.setContentType("text/html;charset=utf-8");//返回網頁型別的文字
PrintWriter writer = response.getWriter();
writer.write("<h1>牛客網</h1>");//這裡只進行簡單輸出
writer.close();
}
}
在專案Controller層加入程式碼,以response體返回一個html的文字。
這是通過底層物件處理請求的方式,便於理解。
更簡單的方式為:
// GET請求,用於獲取某些資料
// /students?current=1&limit=20 假設查詢學生資料,第一頁,每頁20條
@RequestMapping(path = "/students", method = RequestMethod.GET)
@ResponseBody
// public String getStudents(int current,int limit) { //直接使用Int型別,前端控制器會自動識別匹配
// System.out.println(current);
// System.out.println(limit);
// return "some students";
// }
// 也可加上註解
public String getStudents(
@RequestParam(name = "current", required = false, defaultValue = "1") int current,
@RequestParam(name = "limit", required = false, defaultValue = "1") int limit) {
System.out.println(current);
System.out.println(limit);
return "some students";
}
利用@ResponseBody註解,實現效果為:
控制檯返回結果:
利用這種方式,不論是返回資料還是獲取資料都很方便。
另外,通過路徑方式傳參查詢的話:(注意,路徑長度並不是無限的,並不可以傳很多引數)
// /student/123 查詢某個學生,直接放在路徑中
@RequestMapping(path = "/student/{id}", method = RequestMethod.GET)
@ResponseBody
public String getStudent(@PathVariable("id") int id) {
System.out.println(id);
return "a student";
}
注意以上傳參時使用的兩個註解。
POST請求
如何向瀏覽器提交資料?
在專案靜態檔案中新增html檔案,action指示路徑
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>增加學生</title>
</head>
<body>
<form method="post" action="/community/alpha/student">
<p>
姓名:<input type="text" name="name">
</p>
<p>
年齡:<input type="text" name="age">
</p>
<p>
<input type="submit" value="儲存">
</p>
</form>
</body>
</html>
//POST請求
@RequestMapping(path = "/student", method = RequestMethod.POST)
@ResponseBody
public String saveStudent(String name, int age) {
System.out.println(name);
System.out.println(age);
return "success";
}
在Controller中程式碼如下,展示效果:
控制檯:
響應HTML資料
從程式內部響應到HTML
//響應HTML資料
@RequestMapping(path = "/teacher", method = RequestMethod.GET)
public ModelAndView getTeacher() { //這個物件就是向前端控制器返回的moder和view
ModelAndView mav = new ModelAndView();
mav.addObject("name", "張三");
mav.addObject("age", 30);
mav.setViewName("/demo/view");//這個view實際上指的是view.html
return mav;
}
Controller程式碼
此外,還需要一個動態網頁,利用thymeleaf模板引擎(resource/templates)。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"> <!--宣告這不是html檔案,而是一個thymeleaf模板-->
<head>
<meta charset="UTF-8">
<title>Teacher</title>
</head>
<body>
<p th:text="${name}"></p>
<p th:text="${age}"></p>
</body>
</html>
實現效果,程式已經設定初始值。
模板引擎會自動進行處理。
另一種方式:
@RequestMapping(path = "/school", method = RequestMethod.GET)
public String getSchool(Model model) { //前端控制器會建立一個model
model.addAttribute("name","大學");
model.addAttribute("age",80);
return "/demo/view"; //這裡return的是view的路徑
}
這兩種方式是model和view的物件使用方式不同。效果還是一致的,第二種要更加簡便一些。
響應JSON資料
非同步請求可以理解為:當前網頁不重新整理,但是進行了資料互動,比如註冊賬號時,經常會檢測使用者名稱是否重複,這時候網頁並沒有重新整理,但是訪問了資料庫進行查詢。
//響應JSON資料(非同步請求)
// Java物件傳給瀏覽器:需要轉為JS物件,這時候就可以通過JSON進行轉化
// Java物件-> JSON字串 -> JS物件等等
@RequestMapping(path = "/emp", method = RequestMethod.GET)
@ResponseBody
public Map<String,Object> getEmp() { //自動會將map轉為JSON字串
Map<String,Object> emp = new HashMap<>();
emp.put("name","張三");
emp.put("age",23);
emp.put("salary",8000.00);
return emp;
}
注意,這裡並沒有返回html。
這裡只是簡單介紹了響應方式,以後的部落格中會介紹具體應用及程式碼。
3. MyBatis
MyBatis 是一款優秀的持久層框架,它支援自定義 SQL、儲存過程以及高階對映。MyBatis 免除了幾乎所有的 JDBC 程式碼以及設定引數和獲取結果集的工作。MyBatis 可以通過簡單的 XML 或註解來配置和對映原始型別、介面和 Java POJO(Plain Old Java Objects,普通老式 Java 物件)為資料庫中的記錄。
在maven專案pom.xml中新增依賴
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
在SpringBoot框架下,只需要對application.properties進行配置,增加如下:
# DataSourceProperties
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/community?characterEncoding=utf-8&useSSL=false&serverTimezone=Hongkong
spring.datasource.username=root
spring.datasource.password=Ss6215615
#設定連線池,這個比較好
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.maximum-pool-size=15
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.idle-timeout=30000
# MybatisProperties
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.nowcoder.community.entity
mybatis.configuration.useGeneratedKeys=true
#自動匹配 a_b == aB
mybatis.configuration.mapUnderscoreToCamelCase=true
# logger,設定日誌,方便在控制檯檢視資訊
logging.level.com.nowcoder.community=debug
mybatis.type-aliases-package=com.nowcoder.community.entity
這行程式碼宣告瞭SQL中記錄在Java中的物件位置,根據SQL中的屬性建立物件。
public class User {
private int id;
private String username;
private String password;
private String salt;
private String email;
private int type;
private int status;
private String activationCode;
private String headerUrl;
private Date createTime;
//等等,還有set,get,toString
建立物件之後,在專案中的dao檔案,建立UserMapper(對於User的操作)介面,Mybatis只需要通過xml檔案對操作進行宣告。
<?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.nowcoder.community.dao.UserMapper"> <!-- 這裡寫UserMapper的全限定名 -->
<sql id="selectFields">
id, username, password, salt, email, type, status, activation_code, header_url, create_time
</sql>
<sql id="insertFields">
username, password, salt, email, type, status, activation_code, header_url, create_time
</sql>
<select id="selectById" resultType="User"> <!-- 配置裡進行過設定package,否則需要寫全限定名 -->
select <include refid="selectFields"></include>
from user
where id = #{id} <!-- 這裡的意思是要引用方法的引數 -->
</select>
<select id="selectByName" resultType="User"> <!-- 配置裡進行過設定package,否則需要寫全限定名 -->
select <include refid="selectFields"></include>
from user
where username = #{username} <!-- 這裡的意思是要引用方法的引數 -->
</select>
<select id="selectByEmail" resultType="User"> <!-- 配置裡進行過設定package,否則需要寫全限定名 -->
select id, username, password, salt, email, type, status, activation_code, header_url, create_time
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>
這裡建立了增刪改的操作。
mybatis.mapper-locations=classpath:mapper/*.xml
根據已有的配置在resource資料夾中建立mapper目錄和.xml檔案。
由於.xml檔案不會自動檢測編譯,建議在test中依次測試相應功能,以免出現錯誤。
4. 總結
本專案計劃使用SpringBoot框架進行一個社交網站的開發,首先使用Spring Initializr對maven專案進行建立,之後對Spring的基本概念進行了一定闡述,IOC和AOP均沒有講到,將會在後續開放的Spring學習篇中,對SpringMVC的基本使用(GET,POST請求和響應資料)進行了程式碼示範,方便理解後續程式碼。另外,介紹了Mybatis的使用,以及公佈了部分示範程式碼。
上手起來是非常快的,本系列的目的在於,重點講解實戰部分,理論部分將在其他部落格中展開。
下篇部落格將會對網站的登陸介面等如何實現進行闡述。