MVC 架構模式
在 UI 相關的開發領域,通過控制實現模型與視圖解耦
M 模型:實體、業務邏輯
V 視圖:用戶接口(Web、桌面、移動端)
C 控制器:Servlet、Action、Controller
MVC 架構模式相關的:MVVM(移動端、Vue、React)
問題域(通用的,與語言無關,都需要面對的)
MVC / MVVM : 模型與視圖的解耦
ORM - OOP :語言 & RDBMS 之間的映射
IoC / DI : 依賴注入
Java EE 技術規範中 Web 相關技術
Servlet : 通過繼承 HttpServlet定義大量的 Servlet
JSP/JSTL
Filter:對特定路徑的請求執行前置或後置的過濾(加入功能)
Listener: 對應用程序(全局)、用戶會話、HTTP 請求的生命週期或特定時刻進行監聽,註冊回調函數
啟動順序:
Filter 早於 Servlet
關於 Filter 和Listener的具體作用:(接下來用代碼演示)
工程目錄:
AuthFilter.java
package com.newer.mvc.web.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
/**
* Servlet Filter implementation class AuthListener
*/
@WebFilter(value = {"/admin/*","/api/*","/other"})
public class AuthFilter implements Filter {
/**
* Default constructor.
*/
public AuthFilter() {
System.out.println("AuthListener 創建");
}
/**
* @see Filter#destroy()
*/
public void destroy() {
// TODO Auto-generated method stub
}
/**
* @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
*/
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req=(HttpServletRequest) request;
System.out.println("鑑權"+req.getRequestURI());
chain.doFilter(request, response);
}
/**
* @see Filter#init(FilterConfig)
*/
public void init(FilterConfig fConfig) throws ServletException {
// TODO Auto-generated method stub
}
}
EncodingFilter.java
package com.newer.mvc.web.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 過濾器
*/
@WebFilter("/*")
public class EncodingFilter implements Filter {
/**
* Default constructor.
*/
public EncodingFilter() {
System.out.println("創建EncodingFilter");
}
/**
* @see Filter#destroy()
*/
public void destroy() {
// TODO Auto-generated method stub
}
/**
* @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
*/
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
//執行前
//應用場景:編碼設置,鑑權,圖片加水印。。。
HttpServletRequest req=(HttpServletRequest) request;
HttpServletResponse res=(HttpServletResponse) response;
req.setCharacterEncoding("UTF-8");
res.setCharacterEncoding("UTF-8");
String path=req.getRequestURI();
System.out.println("EncodingFilter doFilter:"+path);
if(path.equals("/mvc/admin")) {
//鑑權
System.out.println("需要鑑權");
}
//執行後續的正常流程
chain.doFilter(request, response);
//執行後
//如果是圖片
if(res.getContentType().equals("image/*")){
System.out.println("加水印");
}
}
/**
* @see Filter#init(FilterConfig)
*/
public void init(FilterConfig fConfig) throws ServletException {
System.out.println("EncodingFilter 初始化。。。。。。");
}
}
AppContextListener.java
package com.newer.mvc.web.listener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
/**
* 監聽器
* 不是你去調用的
* 該監聽器關注的事件發生時,回調特定的方法
*
* @author Admin
*
*/
@WebListener
public class AppContextListener implements ServletContextListener {
/**
* Default constructor.
*/
public AppContextListener() {
System.out.println("ServletContextListener 創建");
}
/**
* @see ServletContextListener#contextDestroyed(ServletContextEvent)
*/
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("ServletContextListener 準備銷燬。。。");
System.out.println("ServletContextListener 釋放資源,如關閉數據庫的連接池,");
System.out.println("ServletContextListener 銷燬完畢。。。");
}
/**
* @see ServletContextListener#contextInitialized(ServletContextEvent)
*/
public void contextInitialized(ServletContextEvent sce) {
System.out.println("ServletContextListener 初始化。。。");
System.out.println("ServletContextListener 加載資源,如創建數據庫的連接池,裝配依賴的資源");
}
}
RequestListener.java
package com.newer.mvc.web.listener;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpServletRequest;
/**
*監聽一個Http請求的生命週期
*
*/
@WebListener
public class RequestListener implements ServletRequestListener {
/**
* Default constructor.
*/
public RequestListener() {
System.out.println("RequestListener");
}
/**
* @see ServletRequestListener#requestDestroyed(ServletRequestEvent)
*/
public void requestDestroyed(ServletRequestEvent sre) {
HttpServletRequest req=(HttpServletRequest) sre.getServletRequest();
System.out.printf("RequestListener:銷燬為%s使用過的數據庫連接\\n",req.getRequestURI());
}
/**
* @see ServletRequestListener#requestInitialized(ServletRequestEvent)
*/
public void requestInitialized(ServletRequestEvent sre) {
HttpServletRequest req=(HttpServletRequest) sre.getServletRequest();
System.out.printf("RequestListener:為%s從數據庫連接池 創建一個數據庫連接\\n",req.getRequestURI());
}
}
程序運行後,控制檯輸出為:
4月 17, 2020 5:25:56 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: Server.服務器版本: Apache Tomcat/9.0.34
4月 17, 2020 5:25:56 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: 服務器構建: Apr 3 2020 12:02:52 UTC
4月 17, 2020 5:25:56 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: 服務器版本號(:9.0.34.0
4月 17, 2020 5:25:56 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: OS Name: Windows 10
4月 17, 2020 5:25:56 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: OS.版本: 10.0
4月 17, 2020 5:25:56 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: 架構: amd64
4月 17, 2020 5:25:56 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: Java 環境變量: C:\\Program Files\\Java\\jdk-14
4月 17, 2020 5:25:56 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: JVM 版本: 14+36-1461
4月 17, 2020 5:25:56 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: JVM.供應商: Oracle Corporation
4月 17, 2020 5:25:56 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: CATALINA_BASE:[D:\\springbootproject\\.metadata\\.plugins\\org.eclipse.wst.server.core\\tmp0]
4月 17, 2020 5:25:56 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: CATALINA_HOME: D:\\Spring Boot\\apache-tomcat-9.0.34
4月 17, 2020 5:25:56 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: 命令行參數:[-Dcatalina.base=D:\\springbootproject\\.metadata\\.plugins\\org.eclipse.wst.server.core\\tmp0]
4月 17, 2020 5:25:56 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: 命令行參數:[-Dcatalina.home=D:\\Spring Boot\\apache-tomcat-9.0.34]
4月 17, 2020 5:25:56 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: 命令行參數:[-Dwtp.deploy=D:\\springbootproject\\.metadata\\.plugins\\org.eclipse.wst.server.core\\tmp0\\wtpwebapps]
4月 17, 2020 5:25:56 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: 命令行參數:[-Dfile.encoding=UTF-8]
4月 17, 2020 5:25:56 下午 org.apache.catalina.core.AprLifecycleListener lifecycleEvent
信息: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [C:\\Program Files\\Java\\jdk-14\\bin;C:\\WINDOWS\\Sun\\Java\\bin;C:\\WINDOWS\\system32;C:\\WINDOWS;C:/Program Files/Java/jdk-14/bin/server;C:/Program Files/Java/jdk-14/bin;C:\\Program Files\\Java\\jdk-14\\bin;C:\\Program Files\\Java\\jdk1.8.0_131\\bin;C:\\WINDOWS\\system32;C:\\WINDOWS;C:\\WINDOWS\\System32\\Wbem;C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\;D:\\mysql\\mysql-8.0.18-winx64\\bin;C:\\WINDOWS\\System32\\OpenSSH\\;C:\\Users\\Admin;C:\\Program Files\\MySQL\\MySQL Server 6.0\\bin;C:\\Program Files\\nodejs\\;C:\\Users\\Admin\\AppData\\Local\\Microsoft\\WindowsApps;;C:\\Users\\Admin\\AppData\\Roaming\\npm;D:\\Spring Boot\\sts-4.5.1.RELEASE\\sts-4.5.1.RELEASE;;.]
4月 17, 2020 5:25:57 下午 org.apache.coyote.AbstractProtocol init
信息: 初始化協議處理器 ["http-nio-8080"]
4月 17, 2020 5:25:57 下午 org.apache.catalina.startup.Catalina load
信息: 服務器在[1,382]毫秒內初始化
4月 17, 2020 5:25:57 下午 org.apache.catalina.core.StandardService startInternal
信息: Starting service [Catalina]
4月 17, 2020 5:25:57 下午 org.apache.catalina.core.StandardEngine startInternal
信息: 正在啟動 Servlet 引擎:[Apache Tomcat/9.0.34]
ServletContextListener 創建
RequestListener
ServletContextListener 初始化。。。
ServletContextListener 加載資源,如創建數據庫的連接池,裝配依賴的資源
AuthListener 創建
創建EncodingFilter
EncodingFilter 初始化。。。。。。
DispatcherServlet
4月 17, 2020 5:25:58 下午 org.apache.coyote.AbstractProtocol start
信息: 開始協議處理句柄["http-nio-8080"]
4月 17, 2020 5:25:58 下午 org.apache.catalina.startup.Catalina start
信息: Server startup in [899] milliseconds
RequestListener:為/mvc/從數據庫連接池 創建一個數據庫連接
EncodingFilter doFilter:/mvc/
GET:/mvc/
RequestListener:銷燬為/mvc/使用過的數據庫連接
RequestListener:為/mvc/staff從數據庫連接池 創建一個數據庫連接
EncodingFilter doFilter:/mvc/staff
staff
GET:/mvc/staff
RequestListener:銷燬為/mvc/staff使用過的數據庫連接
RequestListener:為/mvc/dept從數據庫連接池 創建一個數據庫連接
EncodingFilter doFilter:/mvc/dept
dept
GET:/mvc/dept
RequestListener:銷燬為/mvc/dept使用過的數據庫連接
RequestListener:為/mvc/other從數據庫連接池 創建一個數據庫連接
鑑權/mvc/other
EncodingFilter doFilter:/mvc/other
GET:/mvc/other
RequestListener:銷燬為/mvc/other使用過的數據庫連接
以上就是 Filter 和Listener的具體作用。
Spring MVC
DispatcherServlet(分發器): 攔截所有請求
HandlerMapping:映射請求 url 到控制器中的特定方法名
ViewResolver:解析控制器返回的視圖名
ViewResolver:根據視圖類型具體視圖分發
DispatcherServlet: 返回特定視圖
Handler:處理一個 HTTP 請求(HTTP 方法加 URL)的控制器中的特定方法
HandlerInterceptor:把 HTTP 請求頭中數據攔截封裝成了控制器方法中的請求參數、路徑參數、或是對象
兩種創建和註冊DispatcherServlet的方式:
// 創建和註冊 DispatcherServlet
DispatcherServlet servlet = new DispatcherServlet(ac);
ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);
// 預實例化和初始化
registration.setLoadOnStartup(1);
// 路徑映射:接收 `*` 所有請求
registration.addMapping("/app/*");
<servlet>
<servlet-name>app/<servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet/<servlet-class>
<init-param>
<param-name>contextConfigLocation/<param-name>
<param-value>
<load-on-startup>1/<load-on-startup>
<servlet-mapping>
<servlet-name>app/<servlet-name>
<url-pattern>/app/*/<url-pattern>
Spring Web MVC 啟動過程
為了更好的瞭解 Spring Web MVC 啟動過程,接下來用代碼進行演示:
工程目錄:
pom.xml(工程所需的依賴)
<project>
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.2.5.RELEASE/<version>
<relativepath>
<groupid>com.newer/<groupid>
<artifactid>smvc/<artifactid>
<version>0.1/<version>
<name>smvc/<name>
<description>Demo project for Spring Boot/<description>
<properties>
<java.version>11/<java.version>
<dependencies>
<dependency>
<groupid>org.springframework.boot/<groupid>
<artifactid>spring-boot-starter-web/<artifactid>
<dependency>
<groupid>org.springframework.boot/<groupid>
<artifactid>spring-boot-devtools/<artifactid>
<scope>runtime/<scope>
<optional>true/<optional>
<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>
<build>
<plugins>
<plugin>
<groupid>org.springframework.boot/<groupid>
<artifactid>spring-boot-maven-plugin/<artifactid>
/<project>
application.properties(配置)
logging.level.web=debug
spring.http.log-request-details=true
SmvcApplication.java(這個工程會自動生成)
package com.newer.smvc;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SmvcApplication {
public static void main(String[] args) {
SpringApplication.run(SmvcApplication.class, args);
}
}
HomeController.java
package com.newer.smvc;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class HomeController {
@GetMapping("/")
public String home() {
//視圖名
return "index.html";
}
@ResponseBody
@GetMapping("/hello")
public String hello() {
//數據或資源
return "hello";
}
}
工程運行,控制檯輸出:
接下來分析 Spring Web MVC 啟動過程
1.在 ContextLoaderListener監聽器中初始化 WebApplicationContext
2. 初始化 characterEncodingFilter 過濾器
3. 初始化 dispatcherServlet 前端控制器
4. 初始化線程池
5. 初始化 RequestMappingHandlerAdapter處理器適配器
6. 初始化 RequestMappingHandlerMapping處理器映射
DispatcherServlet : GET "/", parameters={}
RequestMappingHandlerMapping : Mapped to com.newer.smvc.HomeController#home()
控制器方法返回 `@ResponseBody` 則不進行 `ViewResolver` 視圖的解析
o.s.web.servlet.DispatcherServlet : GET "/", parameters={}
s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.newer.smvc.HomeController#home()
o.s.w.s.v.ContentNegotiatingViewResolver : Selected 'text/html' given [text/html, application/xhtml+xml, image/webp, image/apng, application/xml;q=0.9, application/signed-exchange;v=b3;q=0.9, */*;q=0.8]
o.s.w.servlet.view.InternalResourceView : View name 'index.html', model {}
o.s.w.servlet.view.InternalResourceView : Forwarding to [index.html]
o.s.web.servlet.DispatcherServlet : "FORWARD" dispatch for GET "/index.html", parameters={}
o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler ["classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/", "/"]
o.s.web.servlet.DispatcherServlet : Exiting from "FORWARD" dispatch, status 200
o.s.web.servlet.DispatcherServlet : Completed 200 OK
關於Spring MVC的 架構模式就到這裡結束了,重要的是理解,理解,理解!!!有問題的小夥伴,歡迎留言!!!
————————————————
資料鏈接:
原文鏈接:https://blog.csdn.net/weixin_44364444/article/details/105584699
閱讀更多 肥貓三千問 的文章