【实训邦@腾讯课堂】Java核心面试之Spring MVC面试解析

原标题:【实训邦@腾讯课堂】Java核心面试之Spring MVC面试解析

SpringMVC简介

SpringMVC是一种基于Spring实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,使用了MVC架构模式的思想,将web层进行职责解耦,并管理应用所需对象的生命周期,为简化日常开发,提供了很大便利。

SpringMVC提供了总开关——前端控制器(DispatcherServlet);请求处理映射器(Handler Mapping)和处理适配器(Handler Adapter),视图解析器(View Resolver)进行视图管理;动作处理器Controller接口(包含ModelAndView,以及处理请求响应对象request和response),配置灵活,支持文件上传,数据简单转化等强大功能。

Spring MVC 的工作流程是什么?

典型回答

用户发送请求到后端,后端前端控制器接收后根据请求URL到映射器查找处理器和执行链并返回,前端控制器通过适配器执行处理器,然后接收处理器返回的ModelAndView对象,前端控制器调用视图解析器解析ModelAndView,根据返回的结果去响应用户请求或渲染视图。

详细流程

首先:加载springmvc.xml配置

1. 用户向服务器发送请求,请求被Spring 前端控制器(DispatcherServlet)捕获;

2. DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用处理器映射器(HandlerMapping)获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;

3. DispatcherServlet 根据获得的处理器(Handler),选择一个合适的HandlerAdapter(处理器适配器) [适配器模式]。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(...)方法)

4. 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:

HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息

数据转换:对请求消息进行数据转换。如String转换成Integer、Double等

数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等

数据验证(如果有): 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中

5. 处理器(Handler)执行完成后,向前端控制器(DispatcherServlet)返回一个ModelAndView对象;

6. 根据返回的ModelAndView,选择一个适合的视图解析器(ViewResolver)(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet ;

7. 视图解析器(ViewResolver)结合 Model 和 View,来渲染视图(View)

8. 将渲染结果返回给客户端。

DisptacherServlet代码分析

DisptacherServlet 实现了 Servlet 接口。

下面展示了HttpServlet定义的一下方法

当然,父类 HttpServlet 只是给出了定义,直接调用父类这些方法将会报错,所以 FrameworkServlet 将它们覆盖重写了处理逻辑:

protected final voiddoGet(HttpServletRequest request, HttpServletResponse response) {

//注解 10. 具体调用的是 processRequest 方法 processRequest(request, response);

}

protected final void doPost(HttpServletRequest request,HttpServletResponse response) {

processRequest(request, response);

}

可以看到 doGet 、doPost 这些方法,底层调用的都是 processRequest 方法进行处理,关键方法是委托给子类 DispatcherServlet 的 doServie() 方法

DispatcherServlet#doService

protected voiddoService(HttpServletRequest request, HttpServletResponse response) throwsException {

logRequest(request);

// 暂存请求参数

Map attributesSnapshot = null;

// 经过前面的准备(属性、辅助变量),进入请求处理过程 doDispatch(request,response);

}

请求分发和处理逻辑的核心是在 doDispatch(request,

response) 方法中。

DispatcherServlet#doDispatch

protected voiddoDispatch(HttpServletRequest request, HttpServletResponse response) {

HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler =null;

// 注释 10. 检查是否 MultipartContent 类型

processedRequest = checkMultipart(request);

// 根据 request 信息寻找对应的

Handler mappedHandler =getHandler(processedRequest); if(mappedHandler == null) {

// 没有找到 handler,通过 response 向用户返回错误信息 noHandlerFound(processedRequest, response);

return;

}

// 根据当前的 handler 找到对应的 HandlerAdapter 适配器 HandlerAdapter ha =getHandlerAdapter(mappedHandler.getHandler());

// 如果当前 handler 支持 last-modified 头处理

String method = request.getMethod();

boolean isGet = "GET".equals(method);

if (isGet ||"HEAD".equals(method)) {

long lastModified = ha.getLastModified(request,mappedHandler.getHandler());

if (new ServletWebRequest(request,response).checkNotModified(lastModified) && isGet) {

return;

}

}

// 拦截器的 preHandler 方法的调用

if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; }

// 真正激活 handler 进行处理,并返回视图

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

if(asyncManager.isConcurrentHandlingStarted()) { return; } //视图名称转换(有可能需要加上前后缀) applyDefaultViewName(processedRequest,mv);

// 应用所有拦截器的 postHandle 方法 mappedHandler.applyPostHandle(processedRequest,response, mv);

// 处理分发的结果(如果有 mv,进行视图渲染和跳转) processDispatchResult(processedRequest,response, mappedHandler, mv, dispatchException);

}

知识拓展

1. Spring MVC的主要组件?

(1)DispatcherServlet(前端控制器):从图中我们可以看出DispatcherServlet是整个流程的核心,用来控制其他组件的执行,统一调度,降低组件之间的耦合度,方便后期扩展。

(2)Handler(处理器):直接对应着MVC中的C也就是Controller层,它的具体表现形式有很多,可以是类,也可以是方法,也可以是其他表现形式,它的类型是Object。例如我们可以将@RequestMapping的所有方法都看成是一个Handler。总的来说只要可以实际处理请求就可以是Handler。

(3)HandleMapping(处理器映射器):对于每一个请求都需要一个Handler来处理,那么我们怎么知道需要通过哪个Handler来处理呢?这就是HandleMapping的工作,它会根据request请求来找到具体处理请求的Handler。

(4)HandlerAdapter(处理器适配器): 上面说过Handler可以是任意的形式,只要能处理请求就OK,但是Servlet需要的处理方法的结构确实固定的,都是以request和response为参数的方法(如doservic方法)。怎么让固定的servlet处理方法调用灵活的Handler来进行处理,这就是HandlerAdaper要做的事情。通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。(DispatcherServlte会根据HandlerMapping传过来的controller与已经注册好了的HandlerAdapter一一匹配,看哪一种HandlerAdapter是支持该controller类型的,如果找到了其中一种HandlerAdapter是支持传过来的controller类型,那么该HandlerAdapter会调用自己的handle方法,handle方法运用Java的反射机制执行controller的具体方法来获得ModelAndView)

通俗的讲就是Handler是用来干活的工具,HandlerMapping则是用于需要干的活找到相应的工具,HandlerAdapter是使用工具干活的人。

(5)View(视图):View是一个接口,实现类支持不同的View类型(jsp、freemarker、pdf…)

(6)ViewResolver(视图解析器):作用:进行视图解析,根据逻辑视图名解析成真正的视图(view) 。

ViewResolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。 springmvc框架提供了很多的View视图类型,包括:jstlView、freemarkerView、pdfView等。

一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由工程师根据业务需求开发具体的页面。(View和View Resolver的原理与Handler和HandlerMapping的原理类似,View是用来展示数据的,而View Resolver是用来查找View的)

2. SpringMVC常用的注解有哪些?

@Controller

使用它标记的类就是一个SpringMVC Controller 对象,也可以使用@RestController,@RestController注解相当于@ResponseBody+ @Controller。

@RequestMapping

用于处理请求 url 映射的注解,可用于类或方法上。用于类上,则表示类中的所有响应请求的方法都是以该地址作为父路径。

@RequestMapping(value="/new",method = RequestMethod.GET)

// 简写

@GetMapping(value="/new")

// 其他请求方法

@PostMapping

@PutMapping

@DeleteMapping

@PatchMapping

@PathVariable

用于对应 RESTful 风格 url 中的参数

// url:/happy/1

@RequestMapping(value="/happy/{dayid}") findPet(@PathVariable

String dayid) // dayid == 1

@RequestParam

将请求的参数绑定到方法中的参数上

// Url:x?name=steven

@RequestParam(value = "name", required = false) String name

@ResponseBody

将返回类型直接输入到HTTP response body中。

一般在异步获取数据时使用,在使用@RequestMapping后,返回值通常解析为跳转路径,加上@ResponseBody后返回结果不会被解析为跳转路径,而是通过适当的 HttpMessageConverter 转换为指定格式后, 直接写入HTTP response body中。比如异步获取json数据,加上@ResponseBody后,会直接返回json数据。

@RequestBody

方法参数直接被绑定到http request body中

3. 如何通过SpringMVC实现跨域请求

方案一:@CrossOrigin

@RestController

@RequestMapping("/account")

public class AccountController {

@CrossOrigin

@GetMapping("/{id}")

public Accountretrieve(@PathVariable Long id) {

// ...

}

@DeleteMapping("/{id}")

public voidremove(@PathVariable Long id) {

//...

}

}

默认情况下,@CrossOrigin允许:

1,所有起源。

2,所有标题。

3,控制器方法映射到的所有HTTP方法。

参数:

allowedCredentials 默认情况下不启用,因为它建立了一个信任级别,用于公开敏感的用户特定信息(例如cookie和CSRF令牌),并且只应在适当的地方使用。

maxAge 设定为30分钟。

方案二:全局配置

@Configuration @EnableWebMvc public class WebConfig implementsWebMvcConfigurer {

@Override public void addCorsMappings(CorsRegistry registry) {

registry.addMapping("/api/**")

.allowedOrigins("https://domain2.com")

.allowedMethods("PUT", "DELETE")

.allowedHeaders("header1","header2", "header3")

.exposedHeaders("header1", "header2")

.allowCredentials(true).maxAge(3600); // Add more mappings...

}

}

默认情况下,全局配置启用以下内容:

1,所有起源。

2,所有标题。,

3,GET,HEAD和POST方法

参数:allowedCredentials 默认情况下不启用,因为它建立了一个信任级别,用于公开敏感的用户特定信息(例如cookie和CSRF令牌),并且只应在适当的地方使用。maxAge 设定为30分钟。

巩固练习

1. 在Spring MVC中,哪个类是负责处理HTTP请求和响应的?( A )

A,DispatcherServlet

B,RequestHandler

C,HttpServlet

D,SpringController

2. 下面关于Spring MVC 描述正确的是?( C )

A,DispatcherServlet在 Spring MVC 中是核心servlet , 它负责接收请求并将请求分发给适合的控制器

B,在Spring MVC 中,可以配置多个DispatcherServlet

C,全部选项

D,要使Spring MVC可用,DispatcherServlet需要在web.xml中配置

拓展学习:【实训邦·Java】 Spring Boot+Vue/前后端分离/高并发/秒杀实战

教程地址:https://ke.qq.com/course/447591?flowToken=1013959返回搜狐,查看更多

责任编辑:

声明:该文观点仅代表作者本人,搜狐号系信息发布平台,搜狐仅提供信息存储空间服务。
免费获取
今日搜狐热点
今日推荐