社交網站后端項目開發日記(一)


本項目目標是開發一個社區網站,擁有發帖、討論、搜索、登錄等一個正常社區擁有的功能。涉及到的版本參數為:

  • 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

本人配置如下:

image-20210621235357240

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)

當客戶端想要和服務端進行信息交互時(服務端是指最終服務器,或者是一個中間代理),過程表現為下面幾步:

  1. 打開一個TCP連接:TCP連接被用來發送一條或多條請求,以及接受響應消息。客戶端可能打開一條新的連接,或重用一個已經存在的連接,或者也可能開幾個新的TCP連接連向服務端。

  2. 發送一個HTTP報文:HTTP報文(在HTTP/2之前)是語義可讀的。在HTTP/2中,這些簡單的消息被封裝在了幀中,這使得報文不能被直接讀取,但是原理仍是相同的。

    GET / HTTP/1.1
    Host: developer.mozilla.org
    Accept-Language: fr
    
  3. 讀取服務端返回的報文信息:

    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)
    
  4. 關閉連接或者為后續請求重用連接。

當HTTP流水線啟動時,后續請求都可以不用等待第一個請求的成功響應就被發送。然而HTTP流水線已被證明很難在現有的網絡中實現,因為現有網絡中有很多老舊的軟件與現代版本的軟件共存。因此,HTTP流水線已被在有多請求下表現得更穩健的HTTP/2的幀所取代。

HTTP請求的例子:

image-20210628144125974

響應的例子:

image-20210628144336876

2.1 SpringMVC概要

三層架構:表現層,業務層和數據訪問層。

MVC:Model(模型層),View(視圖層),Controller(控制層)

image-20210628145510740

核心組件: DispatcherServlet

2.2 Thymeleaf思想

如果想給瀏覽器返回一個動態網頁,則需要一個工具支持,例如:Thymeleaf(模板引擎,生成動態的HTML)。

image-20210628170701838

該引擎需要學習的有:標准表達式,判斷與循環,模板的布局。

學習建議參考官方文檔 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的文本。

image-20210713235642108

這是通過底層對象處理請求的方式,便於理解。

更簡單的方式為:

// 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注解,實現效果為:

image-20210714001035908

控制台返回結果:

image-20210714000959995

利用這種方式,不論是返回數據還是獲取數據都很方便。

另外,通過路徑方式傳參查詢的話:(注意,路徑長度並不是無限的,並不可以傳很多參數)

// /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中代碼如下,展示效果:

image-20210714003253800

控制台:

image-20210714003310693

響應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>

實現效果,程序已經設定初始值。

image-20210714004731050

模板引擎會自動進行處理。

image-20210714004749881

另一種方式:

@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的對象使用方式不同。效果還是一致的,第二種要更加簡便一些。

image-20210714005338290

響應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。

這里只是簡單介紹了響應方式,以后的博客中會介紹具體應用及代碼。

image-20210714010900698

3. MyBatis

MyBatis 是一款優秀的持久層框架,它支持自定義 SQL、存儲過程以及高級映射。MyBatis 免除了幾乎所有的 JDBC 代碼以及設置參數和獲取結果集的工作。MyBatis 可以通過簡單的 XML 或注解來配置和映射原始類型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 對象)為數據庫中的記錄。

image-20210714013153967

mybatis文檔

mybatis-spring文檔

在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的使用,以及公布了部分示范代碼。

上手起來是非常快的,本系列的目的在於,重點講解實戰部分,理論部分將在其他博客中展開。

下篇博客將會對網站的登陸界面等如何實現進行闡述。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM