MVC总结

1. 概述

还是之前的三个套路

1.1 是什么?

Spring提供一套视图层的处理框架,他基于Servlet实现,可以通过XML或者注解进行我们需要的配置。

他提供了拦截器,文件上传,CORS等服务。

1.2 为什么用?

原生Servlet在大型项目中需要进过多重封装,来避免代码冗余,其次由于不同接口需要的参数不同,我们需要自己在Servlet层 封装我们需要的参数,这对于开发者来说是一种重复且枯燥的工作,于是出现了视图层框架,为我们进行参数封装等功能。让开发者的注意力全部放在逻辑架构中,不需要考虑参数封装等问题。

1.3 怎么用

再聊怎么用之前,我们需要了解一下MVC的工作原理。

他基于一个DispatcherServlet类实现对各种请求的转发,即前端的所有请求都会来到这个Servlet中,然后这个类进行参数封装和请求转发,执行具体的逻辑。(第二章我们细聊)

1.3.1 XML
  • 根据上面的原理,我们需要一个DispatcherServlet来为我们提供基础的Servlet服务,我们可以通过servlet规范的web.xml文件,对该类进行初始化。并且声明该类处理所有的请求,然后通过这个类实现请求转发。
  • 另外,我们还需要一个配置文件,用来配置我们需要的相关的mvc信息。

下面来看一个完整的web.xml配置

<web-app>

    <servlet>
<servlet-name>dispatchServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatchServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping> </web-app>
1.3.2 注解

注解方式也是现在主流,SpringBoot基于JavaConfig实现了自动配置

实现方式:

Servlet3.0的时候定义了一个规范SPI规范。

SPI ,全称为 Service Provider Interface,是一种服务发现机制。它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。也就是在服务启动的时候会Servlet会自动加载该文件定义的类

我们看一眼这个文件里的内容。他内部定义了SpringServletContainerInitializer容器初始化类,也就是说在Servlet启动的时候会自动初始化这个类,这个类也是注解实现的关键。

这个类中存在一个onStartup方法,这个也是当容器初始化的时候调用的方法,这个方法有两参数

  • Set<Class<?>> webAppInitializerClasses他代表了当前我们的Spring容器中存在的web初始化类。我们自己可以通过实现WebApplicationInitializer类来自定义Servlet初始化的时候执行的方法。
  • ServletContext servletContex代表了Servlet上下文对象
org.springframework.web.SpringServletContainerInitializer

@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> webAppInitializerClasses,
ServletContext servletContext) throws ServletException {
//启动逻辑
}
}

具体看一下注解配置方式:

public class MyWebApplicationInitializer implements WebApplicationInitializer {

    @Override
public void onStartup(ServletContext servletCxt) { // Load Spring web application configuration
AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
//一个配置类,@Configuration
ac.register(AppConfig.class);
//spring的那个refresh方法
ac.refresh(); // Create and register the DispatcherServlet
DispatcherServlet servlet = new DispatcherServlet(ac);
ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);
registration.setLoadOnStartup(1);
registration.addMapping("/app/*");
}
}

通过实现WebApplicationInitializer接口,来作为MVC的配置类,在加载SpringServletContainerInitializer的时候加载这个类。


不过在具体的实现中,Spring不建议我们这样做,他建议将SpringSpringMvc分开,看个图

他在Spring之上加了一层Web环境配置。相当于在Spring的外面包装了一层Servlet

看一下此时的代码

public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    //Spring配置文件
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { RootConfig.class };
} //SpringMVC的配置文件
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { App1Config.class };
} //指定DispatcherServlet可以拦截的路径
@Override
protected String[] getServletMappings() {
return new String[] { "/app1/*" };
}
}

通过AbstractAnnotationConfigDispatcherServletInitializer

可以看到他实现了WebApplicationInitializer接口,即在Servlet初始化的时候会加载这个类。

AbstractContextLoaderInitializer类,他初始化了Spring

AbstractDispatcherServletInitializer类,初始化了DispatcherServlet

AbstractAnnotationConfigDispatcherServletInitializer类,将两个类整合到一起

2. 实现原理

聊这个原理之前,先来聊聊他要干什么?

需求:请求分发;参数封装;结果返回

那如果我们自己来实现,该怎么办?(单说注解,先来看看我们怎么使用MVC)

  • 一个@Controller注解,标识当前类为控制层接口,
  • 一个RequestMapping标识这个方法的URI和请求方式等信息
  • 一个@ResponseBody标识这个方法的返回类型为JSON
  • 一个test01标识这个方法用来处理/test请求
@Controller
public class UserController { @GetMapping("/test")
@ResponseBody
public String test01(){
return "success" ; }
}

接下来,我们通过我们已有的东西,看一下我们自己去处理请求的逻辑

先来想一下我们的请求过程:

  • 前端发送一个Http请求,通过不同的uri实现不同逻辑的处理
  • 而这个uri和我们后端的定义的@RequestMapping中的value值相同
  • 即我们可以通过一个Map结构,将value作为key,将methodClass对象作为一个value存到一个MappingRegister
  • 请求来了以后,通过URI从这个Map中获取相应的Method执行,如果没有对应的Method给一个404.

2.1 Spring加载

在上面的怎么用中提到了,他通过AbstractContextLoaderInitializer来加载Spring配置文件的。

此时关于Spring的东西已经加载好了,但并未进行初始化

2.2 MVC加载

同样也是通过AbstractDispatcherServletInitializer类实现

2.2.1 DispatcherServlet

接下来我们具体看一下在这个期间,DispatcherServlet如何处理请求的

作用:分发所有的请求

类继承结构图

可以看到他继承了HttpServlet类,属于一个Servlet,而在之前我们配置了这个Servlet的拦截路径。他会将所有的请求拦截,然后做一个分发。

下面这个图各位看官应该非常熟悉:

其实DispatcherServlet处理所有请求的方式在这个图里完全都体现了。

接下来聊一下他的设计思路吧。

当一个请求来的时候,进入doDispatch方法中,然后处理这个请求,也是返回一个执行链

Spring提供了三种方式的处理器映射器来处理不同的请求。

  • BeanNameUrlHandlerMapping处理单独Bean的请求。适用于实现ControllerHttpRequestHandler接口的类
@Component("/test02")
public class HttpController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("HttpController执行");
return null;
}
}
@Component("/test01")
public class HandlerController implements HttpRequestHandler { @Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("handlerRequest");
}
}
  • RequestMappingHandlerMapping适用于方法类型的处理器映射。
@Controller
public class UserController { @GetMapping("/test")
public String test01(){
System.out.println("执行了");
return "success" ;
}
}
  • RouterFunctionMapping,MVC提供的一个处理通过函数式编程定义控制器的一个映射器处理器。需要直接添加到容器中,然后 通过路由一个地址,返回对应的数据
@Configuration
@ComponentScan("com.bywlstudio.controller")
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer { @Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp("/WEB-INF/pages/",".jsp");
} @Bean
public RouterFunction<?> routerFunctionA() {
return RouterFunctions.route()
.GET("/person/{id}", request1 -> ServerResponse.ok().body("Hello World"))
.build();
} }

聊完了处理器映射器,再来聊一下处理器适配器

不同的请求方式,需要不同的处理方式,这也是Spring为什么要提供一个适配器的原因。

  • RequestMappingHandlerAdapter用来处理所有的方法请求,即通过@Controller注解定义的
  • HandlerFunctionAdapter用来处理函数式的映射,即通过RouterFunctionMapping定义的
  • HttpRequestHandlerAdapter用来处理实现了HttpRequestHandler接口的
  • SimpleControllerHandlerAdapter用来处理实现了Controller接口的请求

通过处理器适配器拿到适合的处理器,来处理对应的请求。

在处理器执行具体的请求的过程,实际上就是调用我们的方法的过程,于是就会出现返回值

通常对于返回值我们有两种方法:

  • @ResponseBody直接返回JSON数据。
  • 或者返回一个视图,该视图会被视图解析器解析。

对于返回值解析,MVC提供了一个接口用于处理所有的返回值,这里我们仅仅谈上面的两种

  • ModelAndViewMethodReturnValueHandler用于处理返回视图模型的请求
  • RequestResponseBodyMethodProcessor用于处理返回JSON

在我们拿到方法返回值以后,会调用this.returnValueHandlers.handleReturnValue返回值解析器的这个方法,用于对视图模型的返回和JSON数据的回显(直接回显到网页,此时返回的视图对象为null

对于视图对象,通过视图解析器直接解析,进行数据模型渲染,然后回显给前端。

2.2.2 MappingRegistry

这个类存放了method的映射信息。

class MappingRegistry {

   private final Map<T, MappingRegistration<T>> registry = new HashMap<>();

   private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();

   private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();

   private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();

   private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();

   private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

MVC会从这个类中获取方法和URL的引用。相当于Spring MVC的容器。

3. 面试题

3.1 什么是MVC?什么是MVVM?

答:MVC是一个架构模式,它有三个核心

  • 视图(View)。用户界面
  • 模型(Model)。业务数据
  • 控制器(Controller)。接收用户输入,控制模型和视图进行数据交互

MVVM也是一种架构模式,它也是三个核心

  • 模型(Model)。后端数据
  • 视图模型(ViewModel)。它完成了数据和视图的绑定
  • 视图(View)。用户界面

它的核心思想是:通过ViewModel将数据和视图绑定,用数据操作视图,常见框架为Vue

3.2 Spring Mvc执行流程

  • 用户发送请求至DispatcherServlet
  • DispatcherServelt收到请求以后调用HandlerMapping,找到请求处理器映射器(三选一
  • 通过处理器映射器对应URI的处理器执行链(包含了拦截器,和处理器对象)
  • 调用处理器适配器,找到可以处理该执行链的处理器(四选一)
  • 处理器具体执行,返回ModelAndView对象
    • 如果存在@ResponseBody注解,直接进行数据回显
  • 将返回的ModelAndView对象传给ViewResove视图解析器解析,返回视图
  • DispatcherServletView进行渲染视图
  • 响应用户

更多原创文章请关注公众号@MakerStack ,转载请联系作者授权

面试都要问的Spring MVC的更多相关文章

  1. 【Java面试】说说你对Spring MVC的理解

    一个工作了7年的粉丝,他说在面试之前,Spring这块的内容准备得很充分. 而且各种面试题也刷了,结果在面试的时候,面试官问:"说说你对Spring MVC的理解". 这个问题一下 ...

  2. Java面试中常问的Spring方面问题(涵盖七大方向共55道题,含答案)

    1.一般问题 1.1. 不同版本的 Spring Framework 有哪些主要功能? VersionFeatureSpring 2.5发布于 2007 年.这是第一个支持注解的版本.Spring 3 ...

  3. 面试都在问的微服务、服务治理、RPC、下一代微服务框架... 一文带你彻底搞懂!

    文章每周持续更新,「三连」让更多人看到是对我最大的肯定.可以微信搜索公众号「 后端技术学堂 」第一时间阅读(一般比博客早更新一到两篇) 单体式应用程序 与微服务相对的另一个概念是传统的单体式应用程序( ...

  4. 面试都在问的「微服务」「RPC」「服务治理」「下一代微服务」一文带你彻底搞懂!

    ❝ 文章每周持续更新,各位的「三连」是对我最大的肯定.可以微信搜索公众号「 后端技术学堂 」第一时间阅读(一般比博客早更新一到两篇) ❞ 单体式应用程序 与微服务相对的另一个概念是传统的「单体式应用程 ...

  5. C#基础原理拾遗——面试都爱问的委托和事件(纠正)

    这篇博客是我昨天写的,文中的观点有些问题,后经过网友留言和个人学习发现错误,原文还是保留,更改补在后面,不怕贻笑大方,唯恐误人子弟.不知道还能不能放在首页,让被误导的同学再被反误导一次. 一.原文 几 ...

  6. 从一个简单案例上手Spring MVC,同时分析Spring MVC面试问题

    很多公司都会用Spring MVC,而且初级程序员在面试时,一定会被问到这方面的问题,所以这里我们来通过一个简单的案例来分析Spring MVC,事实上,我们在培训中就用这个举例,很多零基础的程序员能 ...

  7. Spring MVC简单原理

    Spring MVC原理 针对有Java Web基础.Spring基础和Spring MVC使用经验者. 前言 目前基于Java的web后端,Spring生态应该是比较常见了.虽然现在流行前后端分离, ...

  8. 搭建Spring4+Spring MVC web工程的最佳实践

    Spring是个非常非常非常优秀的java框架,主要是用它的IOC容器帮我们依赖注入和管理一些程序中的Bean组件,实现低耦合关联,最终提高系统可扩展性和可维护性,用它来辅助我们构建web工程将会感觉 ...

  9. 如何搭建Spring MVC 框架---Hello World

    传送门 现在的Web框架基本都采用了MVC(model-view-Controller)设计模式,其中,Servlet和Filter都可以充当控制器.Spring MVC采用一个Servlet作为控制 ...

随机推荐

  1. SDN实验 3: Mininet 实验——测量路径的损耗率

    验 3:Mininet 实验--测量路径的损耗率 一.实验目的 在实验 2 的基础上进一步熟悉 Mininet 自定义拓扑脚本,以及与损耗率相关的设定:初步了解 Mininet 安装时自带的 POX ...

  2. pytest文档33-Hooks函数获取用例执行结果(pytest_runtest_makereport)

    前言 pytest提供的很多钩子(Hooks)方法方便我们对测试用例框架进行二次开发,可以根据自己的需求进行改造. 先学习下pytest_runtest_makereport这个钩子方法,可以更清晰的 ...

  3. 【C语言编程学习笔记】利用462字节代码实现雅虎logo ACSII 动画!

    ACSII 动画演示:   不过本文介绍的是另一个作品:c 代码实现雅虎 logo ACSII 动图. 运行后,你将会看到:   它是一个 20fps.抗锯齿的 Yahoo! logo ASCII 动 ...

  4. 【转载】动态规划—各种 DP 优化

    原博客地址 关于氵博客:其实主要是防止我找不到这篇文了

  5. centos8平台redis5日志按天分割

    一,创建日志的备份目录 [root@yjweb crontab]# mkdir /data/logs/redislogsbackup 说明:刘宏缔的架构森林是一个专注架构的博客,地址:https:// ...

  6. Mosquitto服务器的日志分析

    启动Mosquitto后,我们可以看到Mosquitto的启动日志: 1515307521: mosquitto version 1.4.12 (build date 2017-06-01 13:03 ...

  7. Luogu-2480 古代猪文

    我们首先来概括一下题意,其实就是给定 \(n,g\),求: \[g^{\sum_{k\nmid n} C_n^{\frac{n}{k}}}\operatorname{mod} 999911659 \] ...

  8. Python包安装及使用指南

    这里长期更新一些Python第三方包的安装教程,以及使用教程... Pygame 安装教程: Windows: 首先,查看已安装的Python版本:访问https://www.lfd.uci.edu/ ...

  9. MAP;MLE

    判别学习算法:直接对问题进行求解,比如二分类问题,都是在空间中寻找一条直线从而把类别的样例分开,对于新的样例只要判断在直线的那一侧就可. ==>这种直接求解的方法称为判别学习方法 生成学习算法: ...

  10. 操作安装docker

    在本地建造起vue-cli服务 参考项目:https : //gitee.com/QiHanXiBei/myvue 在本地建造起一个django项目架构,通过/ hello能够打印出helloworl ...