Spring MVC的 架構模式

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的具體作用:(接下來用代碼演示)

工程目錄:

Spring MVC的 架構模式

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 MVC的 架構模式

為了更好的瞭解 Spring Web MVC 啟動過程,接下來用代碼進行演示:

工程目錄:

Spring 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 MVC的 架構模式

接下來分析 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


分享到:


相關文章: