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


分享到:


相關文章: