转载

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的具体作用:(接下来用代码演示)

工程目录:


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

正文到此结束
本文目录