主頁 > 後端開發 > Spring的學習與實戰

Spring的學習與實戰

2020-10-02 02:01:05 後端開發

目錄
  • 一、Spring起步
    • 學習路線圖
    • Spring的基礎知識
      • 什么是Spring
      • Spring框架核心模塊
      • SpringBoot
      • 第一個Spring應用DEMO
      • 撰寫自己的第一個SpringMVC例子
      • 嘗試使用Spring Boot DevTools
    • Spring起步小結
  • 二、基于SpringMVC開發web應用
    • 創建資料模型
    • 創建業務邏輯
    • 創建控制器
    • 設計視圖模板
    • 運行Web應用
    • 表單校驗
      • 展現校驗錯誤
    • 基于SpringMVC開發web應用小結
  • 三、實作資料持久化
    • 資料庫
      • 建立用戶資訊登記表
      • Web應用專案集成mysql
    • 使用JdbcTemplate實作資料持久化
    • 再次運行Web應用
    • 實作資料持久化小結
    • 四、使用Spring Data實作資料持久化
    • Spring Data
  • 四、使用Spring Data JPA持久化資料
    • 添加JPA starter依賴
    • 修改物體類,添加JPA映射注解
    • 宣告JPA repository介面
    • 服務層repository
    • 再次運行Web應用
    • 自定義JPA repository
      • 服務層增加查找介面
      • 控制器增加用戶查找功能
      • 視圖層增加用戶查找頁面
      • 查找功能測驗
    • 使用Spring Data實作資料持久化小結
  • 五、使用Spring Security安全框架保護web應用
    • 啟用Spring Security
      • Spring Security的基本登錄認證
    • 使用Spring Security實作自定義用戶認證
      • 增加管理員物體類
      • 使用Spring Data JPA定義管理員的持久化介面
      • 創建獲取管理員詳情資訊的服務層
      • 配置Spring Security
      • 測驗自定義的登錄驗證方法
    • 實作管理員的注冊
      • 管理員注冊控制器
      • 管理員注冊頁面
      • 配置保護Web請求的安全性規則
    • 完整測驗
    • 使用Spring Security安全框架保護web應用小結
  • 六、Spring的配置屬性
    • Spring的環境抽象(Environment)
    • 通過應用屬性組態檔完成Spring的環境配置
      • 配置資料源
      • 配置模板引擎
      • 配置日志
    • 創建自定義的配置屬性
      • 在application.yml中加入配置
      • 撰寫配置類
      • 使用自定義的配置屬性
      • 屬性配置測驗
    • Spring的配置屬性小結
  • 七、Spring集成REST API服務
    • REST API服務
    • Spring集成REST API服務
      • 用Spring MVC實作用戶管理Restful Api
      • Spring 集成Swagger2
      • API介面進行測驗
        • 增加用戶(HTTP POST請求)
        • 查找用戶(HTTP GET請求)
        • 修改用戶(HTTP PUT請求)
        • 洗掉用戶(HTTP DELETE請求)
    • Spring集成REST API服務小結

一、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="https://www.cnblogs.com/zhuhuix/p/@{/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="https://www.cnblogs.com/zhuhuix/p/*{id}">
    名稱:<br>
    <input type="text" name="name" th:value="https://www.cnblogs.com/zhuhuix/p/*{name}">
    <br>
    郵箱:<br>
    <input type="text" name="email" th:value="https://www.cnblogs.com/zhuhuix/p/*{email}">
    <input type="submit" value="https://www.cnblogs.com/zhuhuix/p/提交" >
</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="https://www.cnblogs.com/zhuhuix/p/*{id}">
    名稱:<br>
    <input type="text" name="name" th:value="https://www.cnblogs.com/zhuhuix/p/*{name}" >
    <br>
    郵箱:<br>
    <input type="text" name="email" th:value="https://www.cnblogs.com/zhuhuix/p/*{email}">
    <br>
    <input type="submit" value="https://www.cnblogs.com/zhuhuix/p/提交" >
    <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="https://www.cnblogs.com/zhuhuix/p/*{name}" >
         <br>
         <input type="submit" value="https://www.cnblogs.com/zhuhuix/p/查詢" >
     </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="https://www.cnblogs.com/zhuhuix/p/*{userName}" >
    <br>
    輸入密碼:<br>
    <input type="password" name="userPassword" th:value="https://www.cnblogs.com/zhuhuix/p/*{userPassword}">
    <br>
    <input type="submit" value="https://www.cnblogs.com/zhuhuix/p/注冊" >
</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="https://www.cnblogs.com/zhuhuix/p/utf-8" />
    <property name="log.pattern" value="https://www.cnblogs.com/zhuhuix/p/%black(%contextName-) %red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}) - %gray(%msg%n)" />

    <!--輸出到控制臺-->
    <appender name="console" >
        <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認證》,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/146915.html

標籤:Java

上一篇:【HDFS篇11】HA高可用

下一篇:Python3.x基礎教程3上

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more