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.xml
中的配置,現在都可以通過 Spring Bean 的方式或者註解方式進行配置,由 Spring 來進行生命週期的管理,大多數情況下,我們需要自定義這些配置,如:修改服務的啟動埠,ContextPath,Filter,Listener,Servlet,Session超時時間等等。 Spring Boot 提供了 ServletRegistrationBean
,FilterRegistrationBean
,ServletListenerRegistrationBean
@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();
}
}
}
演示效果:
✍️本章節到這裡就結束了,喜歡的話就點贊