Spring的學習與實戰

目錄

一、Spring起步

  • Spring早已經成爲企業級開發的業界標準,尤其是Spring Boot 2.0、Spring 5發佈後,Spring的生態系統引領了技術架構發展的潮流,對於Java開發人員,深入掌握Spring全家桶的各種框架應用及必要的底層原理知識,是一件非常重要的事情。

學習路線圖

在這裏插入圖片描述

Spring的基礎知識

什麼是Spring

Spring的核心是提供了一個容器(container),通常稱爲Spring應用上下文(Spring application context),它們會創建和管理應用組件。這些組件也可以稱爲bean,會在Spring應用上下文中裝配在一起,從而形成一個完整的應用程序。
在這裏插入圖片描述

將bean裝配在一起的行爲是通過一種基於依賴注入(dependency injection,DI)的模式實現的。此時,組件不會再去創建它所依賴的組件並管理它們的生命週期,使用依賴注入的應用依賴於單獨的實體(容器)來創建和維護所有的組件,並將其注入到需要它們的bean中。通常,這是通過構造器參數和屬性訪問方法來實現的。

Spring框架核心模塊

在這裏插入圖片描述

SpringBoot

在歷史上,一般通過兩種配置方式爲Spring應用上下文提供Bean

  1. 使用一個或多個XML文件描述bean
  2. 使用@Configuration註解會告知Spring這是一個配置類

隨着Spring Boot 2.x的引入,Spring自動配置的能力已經大大加強,Spring Boot能夠基於類路徑中的條目、環境變量和其他因素合理猜測需要配置的組件並將它們裝配在一起。Java程序員儘可能多地使用Spring Boot,只有在必要的時候才使用顯式配置。

第一個Spring應用DEMO
  1. 在IntelliJ IDEA中創建新項目
    在這裏插入圖片描述
  2. 通過地址爲https://start.spring.io/初始化項目;
    在這裏插入圖片描述
  3. 指定項目通用信息:
    在這裏插入圖片描述
  4. 選擇項目Starter:
    在這裏插入圖片描述
  5. 生成的項目結構:
    在這裏插入圖片描述
  6. maven規範
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

可參見本人博客《Maven POM( Project Object Model,項目對象模型 )》《一圖說清maven常見要素》這兩篇文章。

  1. 入口類
/**
* SpringBoot應用
*/
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        // 運行應用
        SpringApplication.run(DemoApplication.class, args);
    }

}

@SpringBootApplication是一個組合註解,它組合了3個其他的註解。

  • @SpringBootConfiguration:將該類聲明爲配置類。儘管這個類目前還沒有太多的配置,但是後續我們可以按需添加基於Java的Spring框架配置。這個註解實際上是@Configuration註解的特殊形式。
  • @EnableAutoConfiguration:啓用Spring Boot的自動配置。我們隨後會介紹自動配置的更多功能。就現在來說,我們只需要知道這個註解會告訴SpringBoot自動配置它認爲我們會用到的組件。
  • @ComponentScan:啓用組件掃描。這樣我們能夠通過@Component@Controller、@Service這樣的註解聲明其他類,Spring會自動發現它們並將它們註冊爲Spring應用上下文中的組件。

在這裏插入圖片描述
8. 測試類

// SpringBoot測試
@SpringBootTest
class DemoApplicationTests {

    // 測試方法
    @Test
    void contextLoads() {
    }

}
  • 程序啓動
    在這裏插入圖片描述
編寫自己的第一個SpringMVC例子
  • 第一個Controller

index()是一個簡單的控制器方法。它帶有@GetMapping註解,表明如果針對“/”發送HTTP GET請求,那麼這個方法將會處理請求。該方法所做的只是返回String類型的index值,該控制器方法中還通過Spring自動注入IndexService服務組件,及調用服務組件方法。

/**
 * 第一個SpringMVC程序--Controller層
 *
 * @author zhuhuix
 * @date 2020-07-02
 */
@Controller
public class IndexController {
	// Spring注入服務組件
    @Autowired
    private IndexService indexService;

    @GetMapping("/")
    public String index(Model model) {
        String index = indexService.getIndex();
        model.addAttribute("index", index);
        // 返回視圖 即index.html
        return "index";
    }
}
  • 第一個Service

getIndex()是一個簡單的服務方法。該方法所做的只是返回String類型的index值,該服務組件供控制層調用。

/**
 * 第一個SpringMVC程序--Service層
 *  * @author zhuhuix
 * @date 2020-07-02
 */
@Service
public class IndexService {
    public String getIndex() {
        return "hello world!!!";
    }
}
  • 第一個View

-- 使用Thymeleaf模板引擎

### application.properties
###ThymeLeaf配置
spring:
  thymeleaf:
    #模板的模式,支持 HTML, XML TEXT JAVASCRIPT
    mode: HTML5
    #編碼 可不用配置
    encoding: UTF-8
    #內容類別,可不用配置
    content-type: text/html
    #開發配置爲false,避免修改模板還要重啓服務器
    cache: false
    #配置模板路徑,默認是templates,可以不用配置
    prefix: classpath:/templates

-- 定義index.html視圖層

<!DOCTYPE html>
<html  xmlns:th="http://www.thymeleaf.org" >
<head>
     <meta charset="UTF-8"/>
     <title>Title</title>
</head>
 <body>
 <p th:text="${index}" />
 </body>
 </html>
  • 運行測試
    在這裏插入圖片描述
嘗試使用Spring Boot DevTools

•代碼變更後應用會自動重啓;
•當面向瀏覽器的資源(如模板、JavaScript、樣式表)等發生變化時,會自動刷新瀏覽器

  • pom.xml
	<dependencies>
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
            <scope>runtime</scope>
        </dependency>
        ...
	</dependencies>
 	<build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <!--fork : devtools生效必須設置成true -->
                    <fork>true</fork>
                </configuration>
            </plugin>
        </plugins>
    </build>
  • idea設置
    -- 需勾選Build project automaticallty
    在這裏插入圖片描述
    -- ctrl+alt+shift+/ :Registry 中第一項必須打勾
    在這裏插入圖片描述

Spring起步小結

  • Spring核心框架:Spring核心框架是Spring領域中一切的基礎。它提供了核心容器和依賴注入框架。
  • Spring Boot:Spring Boot構建在Spring之上,通過簡化依賴管理、自動配置和運行時洞察,使Spring更加易用;
  • Spring MVC:我們通過SpringBoot初始化生成的框架上加入Controller,Service,View的分層,編寫了第一個Spring MVC程序,併成功運行。
  • 使用Idea開發環境,安裝Spring Boot DevTools並進行配置,提高了開發效率。

二、基於SpringMVC開發web應用

. 在上一小節中創建了第一個DEMO,本章將繼續基於SpringMVC框架構建我們的web應用,該應用需要實現用戶登記,具體實現步驟如下:

  1. 創建用戶的數據模型;
  2. 在服務層編寫用戶登記的業務邏輯;
  3. 生成爲Web瀏覽器提供用戶登記內容的控制器
  4. 在視圖層運用模板引擎展示數據及校驗表單輸入

在這裏插入圖片描述

創建數據模型

  • 創建一個用戶類,定義用戶id,用戶名稱,郵箱三個屬性;
  • 添加默認構造方法與全屬性構造方法;
  • 對用戶屬性添加對應getter,setter方法;
/**
 * 基於SpringMVC框架開發web應用--用戶類
 *
 * @author zhuhuix
 * @date 2020-07-03
 */
public class User implements Serializable {
    // 用戶id
    private Long id;
    // 用戶名
    private String name;
    // 郵箱
    private String email;

    User(){ }

    public User(Long id, String name, String email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

創建業務邏輯

  • 實現返回所有用戶數據的列表
  • 實現增加用戶的功能
/**
 * 基於SpringMVC框架開發web應用--用戶服務類
 *
 * @author zhuhuix
 * @date 2020-07-03
 */
@Service
public class UserService {
    public static ArrayList<User> users = new ArrayList<>();

    // mock數據
    public UserService() {
        users.add(new User(1L, "Mike", "[email protected]"));
        users.add(new User(2L, "Jack", "[email protected]"));
        users.add(new User(3L, "Kate", "[email protected]"));
        users.add(new User(4L, "Mary", "[email protected]"));
        users.add(new User(5L, "Rose", "[email protected]"));
    }

    // 返回所有的用戶
    public List<User> listUsers() {
        return users;
    }

    // 增加用戶
    public User saveUser(User user) {
        user.setId(users.size() + 1L);
        users.add(user);
        return user;
    }
}

創建控制器

在Spring MVC框架中,控制器是重要的參與者。它們的主要職責是處理HTTP請求傳遞給視圖以便於渲染HTML(瀏覽器展現)。

  • SpirngMVC的請求註解
註解 描述
@RequestMapping 通用的請求
@GetMapping 處理HTTP GET請示
@PostMapping 處理HTTP POST請示
@PutMapping 處理HTTP PUT請示
@DeleteMapping 處理HTTP DELETE請示
  • 用戶控制器的處理內容
    -- 處理路徑爲“/user”的HTTP GET請求,向服務層調用返回所有用戶數據列表的接口,獲取數據後傳遞給對應的視圖模板,併發送給發起請求的Web瀏覽器。
    -- 處理路徑爲“/user”的HTTP POST請求,向服務層調用增加用戶的接口,處理成功後調用路徑爲“/user”的HTTP GET請求,併發送給發起請求的Web瀏覽器。
    -- 處理路徑爲“/user/form”的HTTP GET請求,產生一個新用戶數據模型,並調用對應的視圖模板,發送給發起請求的Web瀏覽器。
/**
 * 基於SpringMVC框架開發web應用--用戶控制器
 *
 * @author zhuhuix
 * @date 2020-07-03
 */
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    // 保存用戶並返回到用戶列表頁面
    @PostMapping
    public ModelAndView saveUser(User user) {
        userService.saveUser(user);
        return new ModelAndView("redirect:/user");//重定向到list頁面
    }

    // 獲取創建用戶表單頁面
    @GetMapping("/form")
    public ModelAndView createForm(Model model) {
        model.addAttribute("user",new User());
        return new ModelAndView("register","userModel",model);
    }

    // 獲取用戶列表頁面
    @GetMapping
    public ModelAndView list(Model model) {
        model.addAttribute("userList", userService.listUsers());
        return new ModelAndView("userlist", "userModel", model);
    }
}

設計視圖模板

  • 設計一個用戶列表的視圖模板
    -- Thymeleaf提供了一個屬性“th:each”,它會迭代一個元素集合,爲集合中的每個條目渲染HTML,我們可以利用這個屬性,設計出用戶的列表視圖
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultrag.net.nz/thymeleaf/layout"
>
<head>
    <meta charset="UTF-8">
</head>
<body>
<h3>用戶列表</h3>
<div>
    <a th:href="@{/user/form}">創建用戶</a>
</div>
<table border="1">
    <thead>
    <tr>
        <td>ID</td>
        <td>Email</td>
        <td>Name</td>
    </tr>
    </thead>
    <tbody>
    <tr th:if="${userModel.userList.size()} eq 0">
        <td colspan="3">沒有用戶信息!</td>
    </tr>
    <tr th:each="user:${userModel.userList}">
        <td th:text="${user.id}"></td>
        <td th:text="${user.email}"></td>
        <td th:text="${user.name}"></td>
    </tr>
    </tbody>
</table>
</body>
</html>
  • 設計一個提交新用戶的視圖模板
    -- 用戶通過這個視圖,錄用名稱與郵箱地址,提交新用戶的信息。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultrag.net.nz/thymeleaf/layout"
>
<head>
    <meta charset="UTF-8">
</head>
<body>
<h3>登記用戶</h3>
<form action="/users" th:action="@{/user}" method="POST" th:object="${userModel.user}">
    <input type="hidden" name="id" th:value="*{id}">
    名稱:<br>
    <input type="text" name="name" th:value="*{name}">
    <br>
    郵箱:<br>
    <input type="text" name="email" th:value="*{email}">
    <input type="submit" value="提交" >
</form>
</body>
</html>

運行Web應用

在這裏插入圖片描述

  • 到目前爲止,我們已經開發了User用戶模型、UserService用戶服務類、UserController控制器、userlist用戶列表視圖模板、register用戶登記視圖模板,接下來我們可以嘗試運行一下。打開瀏覽器並訪問http://localhost:8080/user。

在這裏插入圖片描述

  • 點擊頁面上的創建用戶,登記新用戶,並提交
    在這裏插入圖片描述
    在這裏插入圖片描述
    該web應用一切運行正常。

表單校驗

雖然我們已經實現了用戶列表與登記新用戶,但視圖層還存在漏洞,比如用戶名稱爲空的時候不能保存,郵箱輸入格式要符合規則,所以程序要對錶單輸入的內容進行校驗。

  • 引入Validation API與Hibernate的Validation API 依賴
  		<dependency>
            <groupId>org.hibernate.validator</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>6.0.13.Final</version>
        </dependency>

        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>2.0.1.Final</version>
        </dependency>
  • 在要被校驗的類上聲明校驗規則:即在User類上聲明校驗規則。
public class User implements Serializable {
    // 用戶id
    @NotNull
    private Long id;
    // 用戶名
    @NotBlank(message = "用戶名稱不能爲空")
    private String name;
    // 郵箱
    @Pattern(message ="郵箱格式不符", regexp = "^[A-Za-z0-9\\u4e00-\\u9fa5]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$")
    private String email;
    ...
 }
  • 在控制器方法中聲明要進行校驗:即在UserController類的saveUser上增加用戶數據的校驗規則。
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    // 保存用戶並返回到用戶列表頁面
    @PostMapping
    public ModelAndView saveUser(@Valid User user, Errors errors,Model model) {
        if (errors.hasErrors()){
            model.addAttribute("user",user);
            if (errors.getFieldError("name")!=null) {
                model.addAttribute("nameError", errors.getFieldError("name").getDefaultMessage());
            }
            if (errors.getFieldError("email")!=null) {
                model.addAttribute("emailError", errors.getFieldError("email").getDefaultMessage());
            }
            return new ModelAndView("register","userModel",model);
        }
        userService.saveUser(user);
        //重定向到list頁面
        return new ModelAndView("redirect:/user");
    }
   ...
 }
  • 修改register表單視圖以展現校驗錯誤。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
</head>
<body>
<h3>登記用戶</h3>
<form action="/users" th:action="@{/user}" method="POST" th:object="${userModel.user}">
    <input type="hidden" name="id" th:value="*{id}">
    名稱:<br>
    <input type="text" name="name" th:value="*{name}" >
    <br>
    郵箱:<br>
    <input type="text" name="email" th:value="*{email}">
    <br>
    <input type="submit" value="提交" >
    <div style="color:red" th:text="${nameError}"></div>
    <div style="color:red" th:text="${emailError}"></div>
</form>
</body>
</html>
展現校驗錯誤

在這裏插入圖片描述

基於SpringMVC開發web應用小結

  • Spring提供了一個強大Spring MVC的Web框架。
  • Spring MVC是基於註解的,通過像@RequestMapping、@GetMapping和@PostMapping這樣的註解來啓用請求處理方法的聲明。
  • 請求處理方法返回一個Thymeleaf模板,同時會帶有模型數據。
  • Spring MVC支持表單校驗。

三、實現數據持久化

. 在上一小節中基於SpringMVC框架構建了我們的web應用,並在視圖層運用模板引擎展示數據及校驗表單輸入,本章將使用JdbcTemplate及Spring Data實現數據持久化的操作。

數據庫

  • 一說到數據的持久化,首選方案就是關係型數據庫,本文將使用互聯網領域最常用mysql數據庫。

MySQL 最流行的關係型數據庫管理系統,MySQL所使用的 SQL 語言是用於訪問數據庫的最常用標準化語言,MySQL由於性能高、成本低、可靠性好,已經成爲最流行的開源數據庫,因此被廣泛地應用在互聯網領域中。
在這裏插入圖片描述

建立用戶信息登記表
  • 根據用戶信息模型類,設計用戶信息登錄表
DROP DATABASE IF EXISTS user_info;

CREATE DATABASE user_info
	DEFAULT CHARACTER SET utf8
	DEFAULT COLLATE utf8_general_ci;

use user_info;

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `email` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

在這裏插入圖片描述

Web應用項目集成mysql
  • 增加依賴
 <!--Mysql依賴包-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
            <scope>runtime</scope>
        </dependency>
 <!-- 數據庫連接池:druid數據源驅動 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
  • Spring配置
#配置數據源
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.url=jdbc:mysql://user_info?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true

使用JdbcTemplate實現數據持久化

Spring對JDBC的支持要歸功於JdbcTemplate類。JdbcTemplate提供了一種特殊的方式,通過這種方式,開發人員在對關係型數據庫執行SQL操作的時候能夠避免使用JDBC時常見的繁文縟節和樣板式代碼。

  • 增加依賴
 <!-- JDBC -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
  • 修改UserService業務邏輯
/**
 * 基於SpringMVC框架開發web應用--用戶服務類
 *
 * @author zhuhuix
 * @date 2020-07-03
 * @date 2020-07-04 增加通過jdbcTemplate處理數據
 */
@Service
public class UserService {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    // 返回所有的用戶
    public List<User> listUsers() {
        return jdbcTemplate.query("select id,name,email from user;",
                new Object[]{}, new BeanPropertyRowMapper<>(User.class));
    }

    // 增加用戶
    public int saveUser(User user) {
        return jdbcTemplate.update("insert into  user(name,email) values(?,?);"
                , user.getName(), user.getEmail());
    }

}

再次運行Web應用

  • 我們只修改了UserService用戶服務類;控制器、視圖模板都未做任何改變,接下來我們可以再次嘗試運行一下。打開瀏覽器並訪問http://localhost:8080/user。
    在這裏插入圖片描述

  • 輸入用戶信息並提交
    在這裏插入圖片描述
    在這裏插入圖片描述

  • 查看數據庫用戶信息表
    在這裏插入圖片描述

實現數據持久化小結

相對於普通的JDBC,Spring的JdbcTemplate能夠極大地簡化關係型數據庫的使用。但是,你會發現使用JPA會更加簡單。接下來我們會繼續通過Spring Data框架讓數據持久化變得更簡單。

四、使用Spring Data實現數據持久化

在上篇文章中,我們使用mysql數據庫與JdbcTemplate簡單實現了數據持久化操作,並對web程序進行了測試,本篇文章將繼續通過Spring Data框架讓數據持久化變得更簡單。

Spring Data

  • Spring Data是一個用於簡化數據庫訪問,並支持雲服務的開源框架。其主要目標是使得數據庫的訪問變得方便快捷,常用的子項目有:

Spring Data JDBC -數據訪問組件對JDBC的支持。

Spring Data JPA:-基於關係型數據庫進行JPA持久化。

Spring Data MongoDB - 持久化到Mongo文檔數據庫。

Spring Data Redis-持久化到Redis key-value內存數據庫。

Spring Data REST:通過Spring Data數據訪問組件導出爲RESTful資源。

Spring Data Cassandra:持久化到Cassandra數據庫。

四、使用Spring Data JPA持久化數據

  • 本文會基於原JDBC的實現替換爲使用SpringData JPA的repository
添加JPA starter依賴
<!--pom.xml-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
修改實體類,添加JPA映射註解
  • 首先我們給原來的user表增加兩個時間字段
ALTER TABLE user ADD COLUMN create_time DATETIME;
ALTER TABLE user ADD COLUMN update_time DATETIME;
  • 修改user.class
    -- 給user類添加@Entity註解,聲明爲JPA實體
    -- 給id字段添加@Id註解將其指定爲數據庫中唯一標識該實體的屬性
    -- 給id字段添加@GeneratedValue註解,依賴數據庫自動生成ID值
    -- 給其它字段添加@Column註解,並聲明對應user表中的字段名稱
    -- 增加createTime成員,添加@CreationTimestamp註解,使用該註解可以讓Hibernate在插入數據時對註解的屬性對應的日期類型創建默認值
    -- 增加updateTime成員,添加@UpdateTimestamp註解,使用該註解可以讓Hibernate在更新數據時對註解的屬性對應的日期類型進行更新
/**
 * 基於SpringMVC框架開發web應用--用戶類
 *
 * @author zhuhuix
 * @date 2020-07-03
 * @date 2020-07-07 添加JPA映射註解
 */
@Entity
public class User implements Serializable {
    // 用戶id
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    // 用戶名
    @NotBlank(message = "用戶名稱不能爲空")
    @Column(name="name")
    private String name;
    // 郵箱
    @Column(name="email")
    @Pattern(message ="郵箱格式不符", regexp = "^[A-Za-z0-9\\u4e00-\\u9fa5]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$")
    private String email;

    // 創建時間
    @Column(name = "create_time")
    @CreationTimestamp
    private Timestamp createTime;

    // 更新時間
    @Column(name = "update_time")
    @UpdateTimestamp
    private Timestamp updateTime;

    public User(Long id, String name, String email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

聲明JPA repository接口
  • 藉助Spring Data JPA,我們可以通過繼承CrudRepository接口,快速定義應用的數據層。CrudRepository定義並實現了很多用於CRUD(創建、讀取、更新、刪除)操作的方法,我們根本就不用編寫實現類!當應用啓動的時候,Spring DataJPA會在運行期自動生成實現類。這意味着,我們現在就可以在服務層直接設用repository的增刪改查操作了。
/**
 * 基於SpringMVC框架開發web應用--數據操作層
 *
 * @author zhuhuix
 * @date 2020-07-07
 */
public interface UserRepository extends CrudRepository<User,Long> {

}
服務層repository
  • 接下來我們在UserService層直接調用UserRepository中由Spring Data JPA提供的各種方法,實現查詢及增加用戶信息。
/**
 * 基於SpringMVC框架開發web應用--用戶服務類
 *
 * @author zhuhuix
 * @date 2020-07-03
 * @date 2020-07-04 增加通過jdbcTemplate處理數據
 * @date 2020-07-07 將jdbcTemplate處理數據程序改爲Spring Data JPA的處理方式
 */
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;


    // 返回所有的用戶
    public List<User> listUsers() {
       return (List<User>) userRepository.findAll();
    }

    // 增加用戶
    public User saveUser(User user) {
        return userRepository.save(user);
    }

}

再次運行Web應用

  • 我們增加了數據層並修改了UserService服務層;控制器、視圖模板都未做任何改變,接下來我們可以再次嘗試運行一下。打開瀏覽器並訪問http://localhost:8080/user。
    在這裏插入圖片描述

  • 輸入用戶信息並提交
    在這裏插入圖片描述
    在這裏插入圖片描述

  • 查看數據庫用戶信息表
    在這裏插入圖片描述

  • 測試結論:通過將JdbcTemplate替換成Spring Data JPA,同樣實現了用戶信息的查詢與增加,而且通過JPA CrudRepository接口,我們沒有書寫一行SQL語句,也實現了數據的增加與查詢。

自定義JPA repository

  • 除了CrudRepository提供的基本CRUD操作之外,還需要通過用戶名稱或郵箱地址查找用戶信息,那我們需要添加如下的方法聲明到UserRepository中:
public interface UserRepository extends CrudRepository<User,Long> {

     // 自定義添加通過用戶名稱查找用戶信息
    List<User> findByName(String name);
   
}

按照Spring Data的規範的規定,查詢方法以find | read | get開頭(比如 find、findBy、read、readBy、get、getBy),涉及查詢條件時,條件的屬性用條件關鍵字連接,要注意的是:條件屬性以首字母大寫。框架在進行方法名解析時,會先把方法名多餘的前綴截取掉,然後對剩下部分進行解析。
直接在接口中定義查詢方法,如果是符合規範的,可以不用寫實現,即不用寫SQL。

服務層增加查找接口
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
   ...
    // 根據名稱查找用戶
    public List<User> searchUser(String name){
        return userRepository.findByName(name);
    }
}
控制器增加用戶查找功能
/**
 * 基於SpringMVC框架開發web應用--用戶控制器
 *
 * @author zhuhuix
 * @date 2020-07-03
 * @date 2020-07-07 增加用戶查找
 */
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;
   ...
    // 查找輸入頁面
    @GetMapping("/index")
    public ModelAndView index(Model model) {
        model.addAttribute("user", new User());
        return new ModelAndView("index", "userModel", model);
    }
    
    // 查找提交併跳轉用戶列表
    @PostMapping("/search")
    public ModelAndView search(@ModelAttribute User user, Model model) {
        model.addAttribute("userList", userService.searchUser(user.getName()));
        return new ModelAndView("userlist", "userModel", model);
    }
}

視圖層增加用戶查找頁面
<!-- index.html-->
<!DOCTYPE html>
<html  xmlns:th="http://www.thymeleaf.org" >
<head>
     <meta charset="UTF-8"/>
     <title>Title</title>
</head>
 <body>

     <h3>查找用戶</h3>
     <form action="/users" th:action="@{/user/search}" method="POST" th:object="${userModel.user}">
         名稱:<br>
         <input type="text" name="name" th:value="*{name}" >
         <br>
         <input type="submit" value="查詢" >
     </form>

 </body>
 </html>
查找功能測試

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

使用Spring Data實現數據持久化小結

  • Spring Data JPA能夠極大地簡化JPA持久化,我們只需編寫repository接口即可;
  • Spirng Data 對於實體類可以通過各種註解進行數據的管理:比如@Id ,@Column,@CreationTimestamp等等
  • 只需要符合JPA的接口命名規則,我們可以自定義JPA repository各種對於數據的操作方法:比如findBy... ,findAllBy...,findBy..And..等。

五、使用Spring Security安全框架保護web應用

  • 在前四篇文章中已經實現了一個非常簡單的用戶郵箱登記的web應用,並將數據保存到mysql數據庫中。但這個web應用涉及的數據展示、查詢及操作都是開放的,如果部署在互聯網上,可以訪問應用的人員都可隨意增加及查詢數據,這顯然是不符合安全要求的。
    作爲軟件開發人員,我們必須採取措施來保護應用程序中的信息。

啓用Spring Security

Spring Security是一個功能強大且高度可定製的身份驗證和訪問控制框架。它是用於保護基於Spring的應用程序的標準。

本文將通過Spring Security配置實現web應用的如下功能:

  • 實現頁面的WEB安全保護
  • 實現管理員的帳戶註冊
  • 實現登錄驗證的完整流程
Spring Security的基本登錄認證
  • 在pom.xml文件中添加依賴
<!-- Spring Security-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security </artifactId>
        </dependency>

通過將security starter添加到項目的構建文件中,我們得到了如下的安全特性:

  • 所有的HTTP請求路徑都需要認證;
  • 不需要特定的角色和權限;
  • 沒有登錄頁面;
  • 認證過程是通過HTTP basic認證對話框實現的;
  • 系統只有一個用戶,用戶名爲user。

我們試着啓動一下應用,並訪問用戶列表頁面,發現程序竟然跳轉到了一個login登錄頁面,並要求輸入用戶和密碼。
在這裏插入圖片描述
這是一個HTTP basic認證對話框,提示進行認證。要想通過這個認證,需要一個用戶名和密碼。用戶名爲user,而密碼則是隨機生成的,可以在日誌監控界面上查到:
在這裏插入圖片描述
我們試着在登錄界面上輸入用戶名user與圖中的密碼,進行認證登錄:
在這裏插入圖片描述
在這裏插入圖片描述
程序順利通過了密碼認證,並進入到用戶列表界面。但顯然以上的方式是不符合需求的,我們需要的是:

  • 通過自定義的登錄頁面來提示管理員用戶進行認證;
  • 提供一個註冊頁面,新管理員用戶能夠註冊;
  • 對不同的請求路徑,執行不同的安全規則。比如註冊頁面不需要進行認證,其他頁面需要進行認證。

使用Spring Security實現自定義用戶認證

增加管理員實體類
-- ----------------------------
-- Table structure for admin
-- ----------------------------
DROP TABLE IF EXISTS `admin`;
CREATE TABLE `admin`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `user_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `user_password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;
/**
 * 基於SpringMVC框架開發web應用--管理員用戶類,用於登錄認證
 *
 * @author zhuhuix
 * @date 2020-07-08
 */
@Entity
public class Admin implements UserDetails {

    // 管理員id
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name="user_name")
    private String userName;

    @Column(name="user_password")
    private String userPassword;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"));
    }

    @Override
    public String getPassword() {
        return userPassword;
    }

    @Override
    public String getUsername() {
        return userName;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public void setUserPassword(String userPassword) {
        this.userPassword = userPassword;
    }
}

  • 管理員類實現了Spring Security的UserDetails接口。通過實現UserDetails接口,我們能夠提供更多信息給框架,比如用戶都被授予了哪些權限以及用戶的賬號是否可用。
  • getAuthorities()方法返回用戶被授予權限的一個集合。各種is…Expired()方法要返回一個boolean值,表明用戶的賬號是否可用或過期。
使用Spring Data JPA定義管理員的持久化接口
/**
 * 基於SpringMVC框架開發web應用--管理員數據操作層
 *
 * @author zhuhuix
 * @date 2020-07-08
 */
public interface AdminRepository extends CrudRepository<Admin,Long> {

    // 根據用戶名查找管理員
    Admin findByUserName(String username);
}
創建獲取管理員詳情信息的服務層
/**
 * 基於SpringMVC框架開發web應用--管理員服務層
 *
 * @author zhuhuix
 * @date 2020-07-08
 */
@Service
public class AdminService implements UserDetailsService {
    private final Logger logger = LoggerFactory.getLogger(Logger.class);
    @Autowired
    private AdminRepository adminRepository;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        Admin admin = adminRepository.findByUserName(s);
        if (admin == null) {
            logger.error("管理員" + s + "未找到");
            throw new UsernameNotFoundException("User " + s + "not found");
        }
        return admin;
    }

 	// 保存管理員
    public Admin save(Admin admin){
       return adminRepository.save(admin);
    }
}

配置Spring Security
/**
 * Spring Security配置類
 *
 * @author zhuhuix
 * @date 2020-07-08
 */

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private AdminService adminService;
    
    @Override
    protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {

        authenticationManagerBuilder
                .userDetailsService(adminService)
                .passwordEncoder(new BCryptPasswordEncoder());
    }

}
測試自定義的登錄驗證方法

以上通過建立管理員信息表,及通過JPA定義數據處理層,編寫獲取管理員信息的服務實現,最後配置Spring Security Web安全類,實現了自定義的登錄驗證方法,下面具體來測試一下:
在這裏插入圖片描述
在這裏插入圖片描述
web應用程序已經實現了自定義的用戶登錄驗證。

實現管理員的註冊

  • 以上雖然完成了管理員的登錄驗證,但沒有涉及管理員的添加,以下需要完成管理員註冊流程。
管理員註冊控制器
/**
 * 基於SpringMVC框架開發web應用--管理員註冊控制器
 *
 * @author zhuhuix
 * @date 2020-07-08
 */
@RestController
@RequestMapping("/admin")
public class AdminController {

    @Autowired
    private AdminService adminService;

   // 管理註冊頁
    @GetMapping
    public ModelAndView registerForm(Model model) {
        model.addAttribute("admin", new Admin());
        return new ModelAndView("registration", "adminModel", model);
    }

     // 提交註冊信息
    @PostMapping("/register")
    public ModelAndView save(Admin admin) {
        BCryptPasswordEncoder bCryptPasswordEncoder =new BCryptPasswordEncoder();
        admin.setUserPassword(bCryptPasswordEncoder.encode(admin.getPassword()));
        if (adminService.save(admin) != null) {
            return new ModelAndView("redirect:/login ");
        } else {
            return null;
        }
    }
}

管理員註冊頁面
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
</head>
<body>
<h3>管理員註冊</h3>
<form action="/admin" th:action="@{/admin/register}" method="POST" th:object="${adminModel.admin}">
    名稱:<br>
    <input type="text" name="userName" th:value="*{userName}" >
    <br>
    輸入密碼:<br>
    <input type="password" name="userPassword" th:value="*{userPassword}">
    <br>
    <input type="submit" value="註冊" >
</form>
</body>
</html>
配置保護Web請求的安全性規則
  • 以上完成了管理員註冊的業務邏輯、控制層、與頁面視圖,但如果我們直接訪問管理員註冊頁面的時候,web應用程序仍然會跳轉到login頁面,要求輸入用戶名和密碼,這顯然不符合需求,我們只需要對需要認證的頁面進行登錄驗證保護,對於註冊頁面是開放的,所以我們需要配置保護web請求的安全性規則:
/**
 * Spring Security配置類
 *
 * @author zhuhuix
 * @date 2020-07-08
 */

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private AdminService adminService;

    // 自定義用戶驗證
    @Override
    protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
        authenticationManagerBuilder
                .userDetailsService(adminService)
                .passwordEncoder(new BCryptPasswordEncoder());
    }

    // 保護web請求的安全性規則

     @Override
    protected void configure(HttpSecurity http) throws Exception{
        http.authorizeRequests()
                .antMatchers("/user/**").access("hasRole('ROLE_USER')")
                .antMatchers("/admin/**").permitAll()
                .and().formLogin().defaultSuccessUrl("/user")
                .and().httpBasic();
    }   
}

完整測試

  • 訪問管理員註冊頁面進行註冊
    在這裏插入圖片描述
    在這裏插入圖片描述
  • 使用註冊的管理員帳戶進行登錄驗證
    在這裏插入圖片描述
    在這裏插入圖片描述

使用Spring Security安全框架保護web應用小結

  • Spring Security的自動配置是實現基本安全性功能的好辦法,但是大多數的應用都需要自定義安全規則,這樣才能滿足特定的安全需求。
  • 認證用戶詳情信息可以通過自定義用戶存儲機制進行管理,它的後端可以是關係型數據庫。
  • 注意:以上實現功能有不完全處:如管理員註冊時,需在頁面進行密碼的兩次確認,服務層需判斷帳戶名是否存在等,請大家借鑑使用。

六、Spring的配置屬性

  • 本小節將瞭解Spring的配置屬性及完成自定義的配置

Spring的環境抽象(Environment)

Spring的環境抽象是各種配置屬性的一站式服務。它抽取了原始的屬性,這樣需要這些屬性的bean就可以從Spring本身中獲取了。Spring環境會拉取多個屬性源,包括:

  • JVM系統屬性;
  • 操作系統環境變量;
  • 命令行參數;
  • 應用屬性配置文件。

在這裏插入圖片描述

通過應用屬性配置文件完成Spring的環境配置

  • 我們回顧下原web應用中的application.properties文件:
###數據源配置
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
spring.datasource.url=jdbc:log4jdbc:mysql://localhost:3306/user_info?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true


#thymelea模板配置
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML5
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.content-type=text/html
spring.thymeleaf.cache=false
spring.resources.chain.strategy.content.enabled=true
spring.resources.chain.strategy.content.paths=/**
  • Spring Boot引入了具有層次關係的yml格式的配置文件:
spring:
  port:8080

顯然具有層次關係的配置文件更易於理解與書寫,接下來我們將使用application.yml取代application.properties完成各種屬性配置。

配置數據源
###數據源配置
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    username: root
    password: root
    driver-class-name: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
    url: jdbc:log4jdbc:mysql://localhost:3306/user_info?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true

配置模板引擎
#thymelea模板配置
  thymeleaf:
    prefix: classpath:/templates/
    suffix: .html
    mode: HTML5
    encoding: UTF-8
    content-type: text/html
    cache: false
配置日誌
  • 默認情況下,Spring Boot通過Logback配置日誌,日誌會以INFO級別寫入到控制檯中,我們希望重新配置顯示日誌的格式;
  • 同時我們希望通過監控sql日誌輸出到控制檯,並將輸出的信息進行篩選打印;
  • 首先需引入log4jdbc依賴
		 <dependency>
            <groupId>org.bgee.log4jdbc-log4j2</groupId>
            <artifactId>log4jdbc-log4j2-jdbc4.1</artifactId>
            <version>1.16</version>
        </dependency>
  • 其次生成配置文件log4jdbc.log4j2.properties:
log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator
log4jdbc.auto.load.popular.drivers=false
log4jdbc.drivers=com.mysql.cj.jdbc.Driver
  • 最後創建一個logback.xml文件完成配置:
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds" debug="false">
    <contextName>web demo</contextName>
    <property name="log.charset" value="utf-8" />
    <property name="log.pattern" value="%black(%contextName-) %red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}) - %gray(%msg%n)" />

    <!--輸出到控制檯-->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${log.pattern}</pattern>
            <charset>${log.charset}</charset>
        </encoder>
    </appender>

    <!--普通日誌輸出到控制檯-->
    <root level="info">
        <appender-ref ref="console" />
    </root>

    <!--監控sql日誌輸出 -->
    <logger name="jdbc.sqlonly" level="INFO" additivity="false">
        <appender-ref ref="console" />
    </logger>

    <logger name="jdbc.resultset" level="ERROR" additivity="false">
        <appender-ref ref="console" />
    </logger>

    <!--  如想看到表格數據,將OFF改爲INFO  -->
    <logger name="jdbc.resultsettable" level="OFF" additivity="false">
        <appender-ref ref="console" />
    </logger>

    <logger name="jdbc.connection" level="OFF" additivity="false">
        <appender-ref ref="console" />
    </logger>

    <logger name="jdbc.sqltiming" level="OFF" additivity="false">
        <appender-ref ref="console" />
    </logger>

    <logger name="jdbc.audit" level="OFF" additivity="false">
        <appender-ref ref="console" />
    </logger>
</configuration>
  • 輸出格式如下:
    在這裏插入圖片描述

創建自定義的配置屬性

爲了支持配置屬性的注入,Spring Boot提供了@ConfigurationProperties註解。將它放到Spring 應用上下文的 bean之後,它就會爲該bean中那些能夠根據Spring環境注入值的屬性賦值。

  • 我們希望給管理員註冊時,添加一個應用的默認密碼,假設用戶註冊時,不輸入密碼,web應用就以這個默認密碼進行註冊,並寫入數據庫。
在application.yml中加入配置
#管理員默認密碼
admin:
  password: 123456
編寫配置類
  • 使用 @ConfigurationProperties 配置模塊
  • 過添加 @Component 註解讓 Component Scan 掃描到
/**
 * 基於SpringMVC框架開發web應用--管理員用戶默認密鑰
 *
 * @author zhuhuix
 * @date 2020-07-09
 */
@Component
@ConfigurationProperties(prefix = "admin")
public class DefaultPasswordProperties {
    private String password;

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}
使用自定義的配置屬性
/**
 * 基於SpringMVC框架開發web應用--管理員註冊控制器
 *  * @author zhuhuix
 * @date 2020-07-08
 * @date 2020-07-09 註冊密碼爲空時使用自定義的默認密碼屬性
 */
@RestController
@RequestMapping("/admin")
public class AdminController {
    @Autowired
    private AdminService adminService;
    @Autowired
    private DefaultPasswordProperties defaultPasswordProperties;

    // 管理註冊頁
    @GetMapping
    public ModelAndView registerForm(Model model) {
        model.addAttribute("admin", new Admin());
        return new ModelAndView("registration", "adminModel", model);
    }

    // 提交註冊信息
    @PostMapping("/register")
    public ModelAndView save(Admin admin) {
        // 如果註冊密碼爲空,則賦值默認密碼
        if (StringUtils.isEmpty(admin.getPassword())) {
            admin.setUserPassword(defaultPasswordProperties.getPassword());
        }
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        admin.setUserPassword(bCryptPasswordEncoder.encode(admin.getPassword()));
        if (adminService.save(admin) != null) {
            return new ModelAndView("redirect:/login ");
        } else {
            return null;
        }
    }
}
屬性配置測試
  • 註冊admin2管理員,密碼保持爲空,並提交
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
  • 用admin2管理員,密碼爲123456進行登錄
    在這裏插入圖片描述
  • 登錄成功
    在這裏插入圖片描述

Spring的配置屬性小結

  • Spring的配置屬性可以通過命令行參數、環境變量、JVM系統屬性、屬性文件或YAML文件等方式進行設置。
  • Spring的配置屬性可以用來覆蓋自動配置相關的設置,包括指定數據源URL和日誌級別。
  • Spring的配置屬性可以添加@ConfigurationProperties註解,這樣就能夠從多個屬性源中選取一個來注入它的值。

七、Spring集成REST API服務

本節將進入到新的單元:Spring與應用的集成,今天先實現集成REST API服務。

REST API服務

微服務架構,前後端分離目前已成爲互聯網項目開發的業界標準,其核心思想就是前端(APP、小程序、H5頁面等)通過調用後端的API接口,提交及返回JSON數據進行交互。

  • 移動設備、平板電腦、智能設備已經非常常見,許多應用程序採用了一種通用的設計,那就是將用戶界面推到更接近客戶端的地方,而讓服務器公開API,通過這種API,各種客戶端都能與後端功能進行交互。
  • REST API(REpresentation State Transfer Application Programming Interface):通過URL定位資源,用HTTP動詞(GET,POST,DELETE,PUSH等)描述操作,與後端服務進行交互。

Spring集成REST API服務

  • 在前幾篇文章中我們用了模板引擎開發了多頁應用(MultiPage Application,MPA),我們將在原有基礎上按以下步驟實現集成API服務:

  • 創建用戶管理的Restful Api(UserRestfulApi.java)的接口,實現增加、刪除、修改、查找用戶信息的API交互服務;

  • 集成Swagger2,對上述API接口進行測試。

用Spring MVC實現用戶管理Restful Api
  • Controller
/**
 * 基於SpringMVC框架開發web應用--用戶restful api
 * 增加、刪除、修改、查找用戶信息的API交互服務
 *
 * @author zhuhuix
 * @date 2020-07-10
 */
@RestController
@RequestMapping("/user/api")
public class UserRestfulApi {
    @Autowired
    private UserService userService;

    // 增加用戶信息
    @PostMapping
    public ResponseEntity<User> addUser(User user) {
        return ResponseEntity.ok(userService.saveUser(user));
    }

    // 根據id刪除用戶
    @DeleteMapping("/{id}")
    public ResponseEntity deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
        return ResponseEntity.ok(HttpStatus.OK);
    }

     // 根據id修改用戶
    @PutMapping("/{id}")
    public ResponseEntity<User> updateUser(@RequestBody User user) {
        return ResponseEntity.ok(userService.saveUser(user));
    }

    // 根據id查找用戶
    @GetMapping("/{id}")
    public ResponseEntity<User> findUser(@PathVariable Long id) {
        return ResponseEntity.ok(userService.findUser(id));
    }
}
  • Service
/**
 * 基於SpringMVC框架開發web應用--用戶服務類
 *
 * @author zhuhuix
 * @date 2020-07-03
 * @date 2020-07-04 增加通過jdbcTemplate處理數據
 * @date 2020-07-07 將jdbcTemplate處理數據程序改爲Spring Data JPA的處理方式
 * @date 2020-07-10 增加
 */
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;


    // 返回所有的用戶
    public List<User> listUsers() {
       return (List<User>) userRepository.findAll();
    }

    // 保存用戶
    public User saveUser(User user) {
        return userRepository.save(user);
    }

    // 刪除用戶
    public void deleteUser(Long id){
        userRepository.deleteById(id);
    }

    // 查找用戶
    public User findUser(Long id){
       return userRepository.findById(id).get();
    }

    // 根據名稱查找用戶
    public List<User> searchUser(String name){
        return userRepository.findByName(name);
    }

}
  • Repository
/**
 * 基於SpringMVC框架開發web應用--數據操作層
 *
 * @author zhuhuix
 * @date 2020-07-07
 */
public interface UserRepository extends CrudRepository<User,Long> {

    // 自定義添加通過用戶名稱查找用戶信息
    List<User> findByName(String name);

}
Spring 集成Swagger2

Swagger2 作爲一個規範和完整的框架,可以用於生成、描述、調用和可視化 RESTful 風格的 Web 服務:
1、 接口文檔在線自動生成,文檔隨接口變動實時更新,節省維護成本
2、 支持在線接口測試,不依賴第三方工具

  • 添加maven依賴
 <!-- RESTful APIs swagger2 -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
            <exclusions>
                <exclusion>
                    <groupId>io.swagger</groupId>
                    <artifactId>swagger-annotations</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>io.swagger</groupId>
                    <artifactId>swagger-models</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-annotations</artifactId>
            <version>1.5.21</version>
        </dependency>
        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-models</artifactId>
            <version>1.5.21</version>
        </dependency>
  • 創建 Swagger2配置類
/**
 * Swagger2配置類
 *
 * @author zhuhuix
 * @date 2020-07-10
 */
@Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Bean
    @SuppressWarnings("all")
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .enable(true)
                .apiInfo(apiInfo())
                .select()
                .paths(Predicates.not(PathSelectors.regex("/error.*")))
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("Restful Api接口測試")
                .version("1.0")
                .build();
    }
}

在這裏插入圖片描述

API接口進行測試
  • 在測試之前,爲防止post、put、delete請求出現 403 Forbidden ,需要禁用跨域請求的安全驗證
/**
 * Spring Security配置類
 *
 * @author zhuhuix
 * @date 2020-07-08
 * @date 2020-07-10 禁用跨域請求的安全驗證
 */

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private AdminService adminService;

    // 自定義用戶驗證
    @Override
    protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
        authenticationManagerBuilder
                .userDetailsService(adminService)
                .passwordEncoder(new BCryptPasswordEncoder());
    }

    // 保護web請求的安全性規則

    @Override
    protected void configure(HttpSecurity http) throws Exception{
        http.authorizeRequests()
                .antMatchers("/user/**").access("hasRole('ROLE_USER')")
                .antMatchers("/admin/**").permitAll()
                .and().formLogin().defaultSuccessUrl("/user")
                .and().httpBasic()
                // 禁用 CSRF
                .and().csrf().disable();
    }
}
增加用戶(HTTP POST請求)

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

查找用戶(HTTP GET請求)

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

修改用戶(HTTP PUT請求)

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

刪除用戶(HTTP DELETE請求)

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

Spring集成REST API服務小結

至此我們完成了Spring集成restful api服務,並通過集成Swagger2,簡單直觀地對http的各種請求進行了完整地測試,下面做個總結:

  • Rest端點可以通過Spring MVC來創建,這裏的控制器與面向瀏覽器的控制器遵循相同的編程模型。
  • @RestController註解簡化了REST控制器,使用它的話,處理器方法中就不需要添加@ResponseBody註解了。
  • Restful Api一般會添加JWT認證機制進行安全驗證,具體可參見《SpringBoot整合SpringSecurity實現JWT認證》。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章