1. 程式人生 > >Spring Boot 整合 Web 開發

Spring Boot 整合 Web 開發



這一節我們主要學習如何整合 Web 相關技術:

  • Servlet
  • Filter
  • Listener
  • 訪問靜態資源
  • 檔案上傳
  • 檔案下載

  Web三大基本元件分別是:Servlet,Listener,Filter。正常來說一旦我們用了框架,這三個基本就用不上了,Servlet 被 Controller 代替,Filter 被攔截器代替。但是可能在一些特殊的場景下不得不使用這三個基本元件時,Spring Boot 中要如何去引用呢?下面我們來一起學習一下。

  Spring Boot 集成了 Servlet 容器,當我們在 pom.xml 中增加 spring-boot-starter-web

元件依賴時,不做任何 web 相關的配置便能提供 web 服務,這還得歸功於 Spring Boot 自動配置的功能,幫我們建立了一堆預設的配置,以前在 web.xml 中的配置,現在都可以通過 Spring Bean 的方式或者註解方式進行配置,由 Spring 來進行生命週期的管理,大多數情況下,我們需要自定義這些配置,如:修改服務的啟動埠,ContextPath,Filter,Listener,Servlet,Session超時時間等等。

  Spring Boot 提供了 ServletRegistrationBeanFilterRegistrationBeanServletListenerRegistrationBean

@WebServlet@WebFilter@WebListener 三種類型分別配置應用的 Servlet,Filter,Listener。

  建立 jar 專案,編寫 pom.xml

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.springboot</groupId>
    <artifactId>springboot-hello</artifactId>
    <version>1.0-SNAPSHOT</version>

    <name>springboot-hello</name>
    <!-- FIXME change it to the project's website -->
    <url>http://www.example.com</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <!-- 引入springboot父類依賴 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
    </parent>

    <dependencies>
        <!-- springboot-web 元件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
</project>


整合Servlet


  Servlet 是 Java Servlet 的簡稱,稱為小服務程式或服務聯結器,用 Java 編寫的伺服器端程式,具有獨立於平臺和協議的特性,主要功能在於互動式地瀏覽和生成資料,生成動態Web內容。

  Java Servlet 是執行在 Web 伺服器或應用伺服器上的程式,它是作為來自 Web 瀏覽器或其他 HTTP 客戶端的請求和 HTTP 伺服器上的資料庫或應用程式之間的中間層。使用 Servlet 可以收集來自網頁表單的使用者輸入,呈現來自資料庫或者其他源的記錄,還可以動態建立網頁。

方法一


  通過註解掃描完成 Servlet 元件的註冊

  編寫FirstServlet.java

package com.springboot.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/*
    配置檔案的寫法:
    <servlet>
        <servlet-name>FirstServlet</servlet-name>
        <servlet-class>com.springboot.servlet.FirstServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>FirstServlet</servlet-name>
        <url-pattern>/first</url-pattern>
    </servlet-mapping>
 */
@WebServlet(name = "FirstServlet", urlPatterns = {"/first"})
public class FirstServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        System.out.println("FirstServlet");
    }
    
}

  啟動類App.java

package com.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

@SpringBootApplication
// 在 SpringBoot 啟動時會掃描 @WebServlet,並將該類例項化
@ServletComponentScan
public class App {

    // 專案中不需要有多個 main 方法只需要有一個入口就可以了
    public static void main(String[] args) {
        // 主函式執行 springboot 專案
        SpringApplication.run(App.class, args);
    }

}


方法二


  通過方法完成 Servlet 元件的註冊

  編寫SecondServlet.java

package com.springboot.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(name = "SecondServlet", urlPatterns = {"/second"})
public class SecondServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        System.out.println("FirstServlet");
    }

}

  啟動類App.java

package com.springboot;

import com.springboot.servlet.SecondServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class App {

    // 專案中不需要有多個 main 方法只需要有一個入口就可以了
    public static void main(String[] args) {
        // 主函式執行 springboot 專案
        SpringApplication.run(App.class, args);
    }

    // 通過方法完成 Servlet 元件的註冊
    @Bean
    public ServletRegistrationBean getServletRegistrationBean() {
        ServletRegistrationBean bean = new ServletRegistrationBean(new SecondServlet());
        bean.addUrlMappings("/second");
        return bean;
    }

}


整合Filter


  過濾器:Filter 是 Servlet 技術中最實用的技術,Web開發人員通過 Filter 技術,對 web 伺服器管理的所有 web 資源,例如:Jsp,Servlet,圖片,HTML,CSS,JS等檔案進行攔截,從而實現一些特殊的功能。例如實現 URL 級別的許可權訪問控制、過濾敏感詞彙、壓縮響應資訊等一些高階功能。它主要用於對使用者請求進行預處理,也可以對 HttpServletResponse 進行後處理。

  使用 Filter 的完整流程:Filter 對使用者請求進行預處理,接著將請求交給 Servlet 進行處理並生成響應,最後 Filter 再對伺服器響應進行後處理。

方法一


  通過註解掃描完成 Filter 元件的註冊

  編寫FirstFilter.java

package com.springboot.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

/*
    配置檔案寫法:
    <filter>
        <filter-name>FirstFilter</filter-name>
        <filter-class>com.springboot.filter.FirstFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>FirstFilter</filter-name>
        <url-pattern>/first</url-pattern>
    </filter-mapping>
 */
@WebFilter(filterName = "FirstFilter", urlPatterns = {"/first"})
public class FirstFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain filterChain)
            throws IOException, ServletException {
        System.out.println("FirstFilter Begin");
        filterChain.doFilter(req, resp);
        System.out.println("FirstFilter End");
    }

    @Override
    public void destroy() {

    }

}

  啟動類App.java

package com.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

@SpringBootApplication
@ServletComponentScan
public class App {

    // 專案中不需要有多個 main 方法只需要有一個入口就可以了
    public static void main(String[] args) {
        // 主函式執行 springboot 專案
        SpringApplication.run(App.class, args);
    }

}


方法二


  通過方法完成 Filter 元件的註冊

  編寫SecondFilter.java

package com.springboot.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter(filterName = "SecondFilter", urlPatterns = {"/second"})
public class SecondFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain filterChain)
            throws IOException, ServletException {
        System.out.println("SecondFilter Begin");
        filterChain.doFilter(req, resp);
        System.out.println("SecondFilter End");
    }

    @Override
    public void destroy() {

    }

}

  啟動類App.java

package com.springboot;

import com.springboot.filter.SecondFilter;
import com.springboot.servlet.SecondServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class App {

    // 專案中不需要有多個 main 方法只需要有一個入口就可以了
    public static void main(String[] args) {
        // 主函式執行 springboot 專案
        SpringApplication.run(App.class, args);
    }

    // 通過方法完成 Servlet 元件的註冊
    @Bean
    public ServletRegistrationBean getServletRegistrationBean() {
        ServletRegistrationBean bean = new ServletRegistrationBean(new SecondServlet());
        bean.addUrlMappings("/second");
        return bean;
    }

    // 通過方法完成 Filter 元件的註冊
    @Bean
    public FilterRegistrationBean getFilterRegistrationBean() {
        FilterRegistrationBean bean = new FilterRegistrationBean(new SecondFilter());
        bean.addUrlPatterns("/second");
        return bean;
    }

}


整合Listener


  Servlet 的監聽器 Listener 實現了 javax.servlet.ServletContextListener 介面的伺服器端程式,它隨 web應用的啟動而啟動,只初始化一次,隨 web 應用的停止而銷燬。主要作用是: 做一些初始化的內容新增工作、設定一些基本的內容、比如一些引數或者是一些固定的物件等等。


方法一


  通過註解掃描完成 Listener 元件的註冊

  編寫FirstListener.java

package com.springboot.listener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

/*
    配置檔案寫法:
    <listener>
        <listener-class>com.springboot.listener.FirstListener</listener-class>
    </listener>
 */
@WebListener
public class FirstListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        System.out.println("FirstListener Init");
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {

    }

}

​ 啟動類App.java

package com.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

@SpringBootApplication
@ServletComponentScan
public class App {

    // 專案中不需要有多個 main 方法只需要有一個入口就可以了
    public static void main(String[] args) {
        // 主函式執行 springboot 專案
        SpringApplication.run(App.class, args);
    }

}


方法二


  通過方法完成 Listener 元件註冊

  編寫SecondListener.java

package com.springboot.listener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class SecondListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        System.out.println("SecondListener Init");
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {

    }

}

  啟動類App.java

package com.springboot;

import com.springboot.listener.SecondListener;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class App {

    // 專案中不需要有多個 main 方法只需要有一個入口就可以了
    public static void main(String[] args) {
        // 主函式執行 springboot 專案
        SpringApplication.run(App.class, args);
    }

    // 通過方法完成 Listener 元件註冊
    @Bean
    public ServletListenerRegistrationBean<SecondListener> getServletListenerRegistrationBean() {
        return new ServletListenerRegistrationBean(new SecondListener());
    }

}


訪問靜態資源


static目錄


  在我們開發web應用的時候,需要引用大量的js、css、圖片等靜態資源。

  Spring Boot 預設提供靜態資源目錄位置需要置於 classpath 下,目錄名需符合如下規則:

   /static

   /public

   /resources

   /META-INF/resources

  比如:

   啟動專案後,位於 static 下的資源直接訪問,路徑裡無需新增 /static

   如果 /static下還有資料夾需要新增對應路徑訪問

  訪問:http://localhost:8080/spring-boot.svg

  demo.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>資源訪問</title>
</head>
<body>
    <p>資源訪問</p>
    <img src="images/java.jpg">
</body>
</html>

  訪問:http://localhost:8080/demo.html


ServletContext目錄


  在 src/main/webapp 下,目錄名稱必須是 webapp

  demo.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>資源訪問</title>
</head>
<body>
    <p>資源訪問</p>
    <img src="../images/spring-boot.svg">
</body>
</html>

  訪問:http://localhost:8080/html/demo.html


檔案上傳


  Spring MVC 通過 MultipartResolver(多部件解析器)物件實現對檔案上傳的支援。

  MultipartResolver 是一個介面物件,需要通過它的實現類 CommonsMultipartResolver 來完成檔案的上傳工作。在 Spring Boot 中又是如何來完成的,我們一起學習一下。


  FileController.java

package com.springboot.controller;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/file")
public class FileController {

    /**
     * 檔案上傳
     *
     * @param fileName
     * @param request
     * @return
     */
    @PostMapping("/upload")
    public Map<String, Object> fileUpload(MultipartFile fileName,
                                          HttpServletRequest request) {
        try {
            // 獲取檔名
            System.out.println("檔名:" + fileName.getOriginalFilename());
            // 將檔案儲存至當前專案 src/main/upload 資料夾
            String baseDir = System.getProperty("user.dir") + "/src/main/upload/";
            fileName.transferTo(new File(baseDir + fileName.getOriginalFilename()));
        } catch (IOException e) {
            e.printStackTrace();
        }

        Map<String, Object> map = new HashMap<>();
        map.put("message", "success");
        return map;
    }

}

  resources/static/fileUpload.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>檔案上傳</title>
</head>
<body>
    <form action="file/upload" method="post" enctype="multipart/form-data">
        檔案上傳:<input type="file" name="fileName"/>
        <input type="submit" value="上傳"/>
    </form>
</body>
</html>

  啟動類App.java

package com.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class App {

    // 專案中不需要有多個 main 方法只需要有一個入口就可以了
    public static void main(String[] args) {
        // 主函式執行 springboot 專案
        SpringApplication.run(App.class, args);
    }

}



  演示效果:


設定上傳檔案大小的預設值


  Spring 限制了檔案上傳的大小值,預設是10MB,需要通過配置檔案設定,

  在 resources 根目錄下新增一個 Spring Boot 的配置檔案 application.properties。

  resources/application.properties

# 設定單個上傳檔案的大小
spring.http.multipart.max-file-size=200MB
# 設定一次請求上傳檔案的總容量
spring.http.multipart.max-request-size=200MB


檔案下載


  本文基於簡單的功能實現,編寫 Get 請求方法,將剛才上傳的檔案進行下載。

  FileController.java

package com.springboot.controller;

import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/file")
public class FileController {

    /**
     * 檔案上傳
     *
     * @param fileName
     * @param request
     * @return
     */
    @PostMapping("/upload")
    public Map<String, Object> fileUpload(MultipartFile fileName,
                                          HttpServletRequest request) {
        ...
    }

    /**
     * 檔案下載
     *
     * @param fileName
     * @param request
     * @param response
     */
    @GetMapping("/download")
    public void fileDownload(String fileName,
                             HttpServletRequest request,
                             HttpServletResponse response) {
        // 獲取檔案儲存路徑
        String baseDir = System.getProperty("user.dir") + "/src/main/upload/";
        // 讀取檔案,宣告輸出流
        try (InputStream is = new FileInputStream(new File(baseDir, fileName));
             OutputStream os = response.getOutputStream()) {
            // 設定響應資料型別
            response.setContentType("application/x-download");
            response.addHeader("Content-Disposition", "attchment;filename=" + fileName);
            // 複製寫出檔案
            IOUtils.copy(is, os);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}



  演示效果:




  ✍️本章節到這裡就結束了,喜歡的話就點贊