欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring MVC的 架构模式

程序员文章站 2022-04-02 07:54:46
...

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/*");

 

<!-- 创建和注册 DispatcherServlet -->
<servlet>
    <servlet-name>app</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value></param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>app</servlet-name>
    <url-pattern>/app/*</url-pattern>
</servlet-mapping>

 Spring Web MVC 启动过程

Spring MVC的 架构模式


为了更好的了解  Spring Web MVC 启动过程,接下来用代码进行演示:

工程目录:

Spring MVC的 架构模式


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 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/> <!-- lookup parent from repository -->
	</parent>
	<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>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</dependency>
		<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>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</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的 架构模式就到这里结束了,重要的是理解,理解,理解!!!有问题的小伙伴,欢迎留言!!!

相关标签: Spring