当前位置: 首页 > news >正文

Spring MVC__HttpMessageConverter、拦截器、异常处理器、注解配置SpringMVC、SpringMVC执行流程

目录

  • 一、HttpMessageConverter
    • 1、@RequestBody
    • 2、RequestEntity
    • 3、@ResponseBody
    • 4、SpringMVC处理json
    • 5、SpringMVC处理ajax
    • 6、@RestController注解
    • 7、ResponseEntity
      • 7.1、文件下载
      • 7.2、文件上传
  • 二、拦截器
    • 1、拦截器的配置
    • 2、拦截器的三个抽象方法
    • 3、多个拦截器的执行顺序
  • 三、异常处理器
    • 1、基于配置的异常处理
    • 2、基于注解的异常处理
  • 四、注解配置SpringMVC
    • 1、创建初始化类,代替web.xml
    • 2、创建SpringConfig配置类,代替spring的配置文件
    • 3、创建WebConfig配置类,代替SpringMVC的配置文件
    • 4、测试功能
  • 五、SpringMVC执行流程
    • 1、SpringMVC常用组件
    • 2、DispatcherServlet初始化过程
    • 3、DispatcherServlet调用组件处理请求
    • 4、SpringMVC的执行流程

一、HttpMessageConverter

HttpMessageConverter ,报文信息转换器,将请求报文转换为Java对象,或将Java对象转换为响应报文
HttpMessageConverter提供了两个注解和两个类型: @RequestBody , @ResponseBody ,RequestEntity ,ResponseEntity

1、@RequestBody

@RequestBody 可以获取请求体,需要在控制器方法设置一个形参,使用 @RequestBody 进行标识,当前请求的请求体就会为当前注解所标识的形参赋值

<form th:action="@{/testRequestBody}" method="post"> 用户名:<input type="text" name="username"><br> 密码:<input type="password" name="password"><br> <input type="submit"> 
</form>
@RequestMapping("/testRequestBody") 
public String testRequestBody(@RequestBody String requestBody){System.out.println("requestBody:"+requestBody);          return "success"; 
}

输出结果:requestBody:username=admin&password=123456

2、RequestEntity

RequestEntity 封装请求报文的一种类型,需要在控制器方法的形参中设置该类型的形参,当前请求的请求报文就会赋值给该形参,可以通过getHeaders() 获取请求头信息,通过 getBody() 获取请求体信息

@RequestMapping("/testRequestEntity") 
public String testRequestEntity(RequestEntity<String> requestEntity){System.out.println("requestHeader:"+requestEntity.getHeaders());System.out.println("requestBody:"+requestEntity.getBody());             return "success"; 
}

输出结果: requestHeader:[host:“localhost:8080”, connection:“keep-alive”, content-length:“27”,
cache-control:“max-age=0”, sec-ch-ua:“” Not A;Brand";v=“99”, “Chromium”;v=“90”, “Google
Chrome”;v=“90"”, sec-ch-ua-mobile:“?0”, upgrade-insecure-requests:“1”, origin:" http://localhost:8
080 ", user-agent:“Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like
Gecko) Chrome/90.0.4430.93 Safari/537.36”] requestBody:username=admin&password=123

3、@ResponseBody

@ResponseBody 用于标识一个控制器方法,可以将该方法的返回值直接作为响应报文的响应体响应到浏览器

@RequestMapping("/testResponseBody") 
@ResponseBody 
public String testResponseBody(){ return "success"; 
}

结果:浏览器页面显示success

4、SpringMVC处理json

@ResponseBody处理json的步骤:

  • 导入jackson的依赖

    <!--导入jackson的依赖--><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.12.1</version></dependency>
    
  • 在 SpringMVC 的核心配置文件中开启 mvc 的注解驱动,此时在 HandlerAdaptor 中会自动装配一个消息转换器:MappingJackson2HttpMessageConverter ,可以将响应到浏览器的 Java 对象转换为 Json 格式的字符串

    <!--开启注解驱动--><mvc:annotation-driven />
    
  • 在处理器方法上使用 @ResponseBody 注解进行标识

  • 将 Java 对象直接作为控制器方法的返回值返回,就会自动转换为 Json 格式的字符串

    @RequestMapping("/testResponseUser") 
    @ResponseBody 
    public User testResponseUser(){ return new User(1001,"admin","123456",23,"男"); 
    }
    

    浏览器的页面中展示的结果:
    {“id”:1001,“username”:“admin”,“password”:“123456”,“age”:23,“sex”:“男”}

5、SpringMVC处理ajax

  • 请求超链接:

    <div id="app"><a th:href="@{/testAjax}" @click="testAjax">testAjax</a><br> 
    </div>
    
  • 通过vue和axios处理点击事件:

    <script type="text/javascript" th:src="@{/static/js/vue.js}"></script> 
    <script type="text/javascript" th:src="@{/static/js/axios.min.js}"></script> 
    <script type="text/javascript"> var vue = new Vue({ el:"#app", methods:{ testAjax:function (event) { axios({ method:"post", url:event.target.href, params:{ username:"admin", password:"123456" } }).then(function (response) { alert(response.data); });event.preventDefault(); } } }); 
    </script>
    
  • 控制器方法:

    @RequestMapping("/testAjax")
    @ResponseBody 
    public String testAjax(String username, String password){System.out.println("username:"+username+",password:"+password); return "hello,ajax";
    }
    

6、@RestController注解

@RestController 注解是 springMVC 提供的一个复合注解,标识在控制器的类上,就相当于为类添加了@Controller注解,并且为其中的每个方法添加了 @ResponseBody 注解

7、ResponseEntity

ResponseEntity用于控制器方法的返回值类型 ,该控制器方法的返回值就是响应到浏览器的响应报文

7.1、文件下载

使用ResponseEntity实现下载文件的功能

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>测试文件上传和下载</title>
</head>
<body>
<a th:href="@{/testDown}">下载1.jpeg</a>
<br>
<form th:action="@{/testUp}" method="post" enctype="multipart/form-data">头像:<input type="file" name="photo"/><br><input type="submit" value="上传"/>
</form>
</body>
</html>
@RequestMapping(value = "/testDown",method = RequestMethod.GET)public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException {//获取ServletContext对象ServletContext servletContext = session.getServletContext();//获取服务器中文件的真实路径String realPath = servletContext.getRealPath("/static/img/1.jpeg");//创建输入流InputStream is = new FileInputStream(realPath);//创建字节数组byte[] bytes = new byte[is.available()];//将流读到字节数组中is.read(bytes);//创建HttpHeaders对象设置响应头信息MultiValueMap<String,String> headers = new HttpHeaders();headers.add("Content-Disposition","attachment;filename=1.jpeg");//设置响应状态码HttpStatus statusCode = HttpStatus.OK;//创建ResponseEntity对象ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes,headers,statusCode);//关闭输入流is.close();return responseEntity;}

7.2、文件上传

文件上传要求 form 表单的请求方式必须为 post ,并且添加属性 enctype="multipart/form-data"SpringMVC 中将上传的文件封装到 MultipartFile 对象中,通过此对象可以获取文件相关信息
上传步骤:

  • 添加依赖:

    <!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -- >
    <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> 
    </dependency>
    
  • 在SpringMVC的配置文件中添加配置:

    <!--必须通过文件解析器的解析才能将文件转换为MultipartFile对象--><bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
    
  • 控制器方法:

    @RequestMapping(value = "/testUp",method = RequestMethod.POST)public String testUp(MultipartFile photo,HttpSession session) throws IOException {//获取上传的文件的文件名String fileName = photo.getOriginalFilename();//处理文件重名问题String hzName = fileName.substring(fileName.lastIndexOf("."));fileName = UUID.randomUUID().toString() + hzName;//获取服务器中photo目录的路径ServletContext servletContext = session.getServletContext();String photoPath = servletContext.getRealPath("photo");File file = new File(photoPath);if(!file.exists()){file.mkdir();}String finalPath = photoPath + File.separator + fileName;//实现上传功能photo.transferTo(new File(finalPath));return "success";}
    

二、拦截器

1、拦截器的配置

SpringMVC 中的拦截器用于拦截控制器方法的执行
SpringMVC 中的拦截器需要实现 HandlerInterceptor
SpringMVC 的拦截器必须在 SpringMVC 的配置文件中进行配置:

<mvc:interceptors><bean class="com.zhao.interceptor.FirstInterceptor"></bean><ref bean="firstInterceptor"></ref><!-- 以上两种配置方式都是对DispatcherServlet所处理的所有的请求进行拦截 --><mvc:interceptor><mvc:mapping path="/**"/><mvc:exclude-mapping path="/testRequestEntity"/><ref bean="firstInterceptor"></ref></mvc:interceptor><!--以上配置方式可以通过ref或bean标签设置拦截器,通过mvc:mapping设置需要拦截的请求,通过 mvc:exclude-mapping设置需要排除的请求,即不需要拦截的请求-->
</mvc:interceptor>

2、拦截器的三个抽象方法

SpringMVC 中的拦截器有三个抽象方法:

  • preHandle :控制器方法执行之前执行 preHandle() ,其 boolean 类型的返回值表示是否拦截或放行,返回true 为放行,即调用控制器方法;返回 false 表示拦截,即不调用控制器方法
  • postHandle :控制器方法执行之后执行 postHandle()
  • afterComplation :处理完视图和模型数据,渲染视图完毕之后执行afterComplation()

3、多个拦截器的执行顺序

  • 若每个拦截器的 preHandle() 都返回 true
    此时多个拦截器的执行顺序和拦截器在 SpringMVC 的配置文件的配置顺序有关:preHandle() 会按照配置的顺序执行,而 postHandle() 和 afterComplation() 会按照配置的反序执行
  • 若某个拦截器的 preHandle() 返回了 false
    preHandle() 返回 false 和它之前的拦截器的 preHandle() 都会执行, postHandle() 都不执行,返回 false的拦截器之前的拦截器的afterComplation() 会执行

三、异常处理器

1、基于配置的异常处理

SpringMVC 提供了一个处理控制器方法执行过程中所出现的异常的接口: HandlerExceptionResolver
HandlerExceptionResolver 接口的实现类有:DefaultHandlerExceptionResolver 和
SimpleMappingExceptionResolver
SpringMVC 提供了自定义的异常处理器 SimpleMappingExceptionResolver ,使用方式:

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="exceptionMappings"> <props> <!--properties的键表示处理器方法执行过程中出现的异常 properties的值表示若出现指定异常时,设置一个新的视图名称,跳转到指定页面 --> <prop key="java.lang.ArithmeticException">error</prop> </props> </property> <!--exceptionAttribute属性设置一个属性名,将出现的异常信息在请求域中进行共享 以value的值为键,以异常为值--> <property name="exceptionAttribute" value="ex"></property> </bean>

2、基于注解的异常处理

//@ControllerAdvice将当前类标识为异常处理的组件 
@ControllerAdvice 
public class ExceptionController { //@ExceptionHandler用于设置所标识方法处理的异常 @ExceptionHandler(ArithmeticException.class) //ex表示当前请求处理中出现的异常对象 public String handleArithmeticException(Exception ex, Model model){model.addAttribute("ex", ex);return "error"; } 
}

四、注解配置SpringMVC

使用配置类和注解代替web.xml和SpringMVC配置文件的功能

1、创建初始化类,代替web.xml

在 Servlet3.0 环境中,容器会在类路径中查找实现 javax.servlet.ServletContainerInitializer 接口的类,如果找到的话就用它来配置Servlet 容器。
Spring 提供了这个接口的实现,名为
SpringServletContainerInitializer ,这个类反过来又会查找实现 WebApplicationInitializer 的类并将配置的任务交给它们来完成。Spring3.2 引入了一个便利的 WebApplicationInitializer 基础实现,名为AbstractAnnotationConfigDispatcherServletInitializer,当我们的类扩展了
AbstractAnnotationConfigDispatcherServletInitializer 并将其部署到 Servlet3.0 容器的时候,容器会自动发现它,并用它来配置Servlet 上下文

//web工程的初始化类,用来代替web.xml
public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer {/*** 指定spring的配置类* @return*/@Overrideprotected Class<?>[] getRootConfigClasses() {return new Class[]{SpringConfig.class};}/*** 指定springMVC的配置类* @return*/@Overrideprotected Class<?>[] getServletConfigClasses() {return new Class[]{WebConfig.class};}/*** 指定DispatcherServlet的映射规则,即url-pattern* @return*/@Overrideprotected String[] getServletMappings() {return new String[]{"/"};}/*** 注册过滤器* @return*/@Overrideprotected Filter[] getServletFilters() {CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();characterEncodingFilter.setEncoding("UTF-8");characterEncodingFilter.setForceResponseEncoding(true);HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();return new Filter[]{characterEncodingFilter, hiddenHttpMethodFilter};}
}

2、创建SpringConfig配置类,代替spring的配置文件

@Configuration 
public class SpringConfig { 
//ssm整合之后,spring的配置信息写在此类中 
}

3、创建WebConfig配置类,代替SpringMVC的配置文件

/*** 代替SpringMVC的配置文件:* 1、扫描组件   2、视图解析器     3、view-controller    4、default-servlet-handler* 5、mvc注解驱动    6、文件上传解析器   7、异常处理      8、拦截器*/
//将当前类标识为一个配置类
@Configuration
//1、扫描组件
@ComponentScan("com.atguigu.mvc.controller")
//5、mvc注解驱动
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {//4、default-servlet-handler@Overridepublic void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {configurer.enable();}//8、拦截器@Overridepublic void addInterceptors(InterceptorRegistry registry) {TestInterceptor testInterceptor = new TestInterceptor();registry.addInterceptor(testInterceptor).addPathPatterns("/**");}//3、view-controller@Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/hello").setViewName("hello");}//6、文件上传解析器@Beanpublic MultipartResolver multipartResolver(){CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();return commonsMultipartResolver;}//7、异常处理@Overridepublic void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver();Properties prop = new Properties();prop.setProperty("java.lang.ArithmeticException", "error");exceptionResolver.setExceptionMappings(prop);exceptionResolver.setExceptionAttribute("exception");resolvers.add(exceptionResolver);}//配置生成模板解析器@Beanpublic ITemplateResolver templateResolver() {WebApplicationContext webApplicationContext = ContextLoader.getCurrentWebApplicationContext();// ServletContextTemplateResolver需要一个ServletContext作为构造参数,可通过WebApplicationContext 的方法获得ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(webApplicationContext.getServletContext());templateResolver.setPrefix("/WEB-INF/templates/");templateResolver.setSuffix(".html");templateResolver.setCharacterEncoding("UTF-8");templateResolver.setTemplateMode(TemplateMode.HTML);return templateResolver;}//生成模板引擎并为模板引擎注入模板解析器@Beanpublic SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) {SpringTemplateEngine templateEngine = new SpringTemplateEngine();templateEngine.setTemplateResolver(templateResolver);return templateEngine;}//生成视图解析器并未解析器注入模板引擎@Beanpublic ViewResolver viewResolver(SpringTemplateEngine templateEngine) {ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();viewResolver.setCharacterEncoding("UTF-8");viewResolver.setTemplateEngine(templateEngine);return viewResolver;}}

4、测试功能

@RequestMapping("/") 
public String index(){ return "index"; 
}

五、SpringMVC执行流程

1、SpringMVC常用组件

  • DispatcherServlet:前端控制器,不需要工程师开发,由框架提供
    • 作用:统一处理请求和响应,整个流程控制的中心,由它调用其它组件处理用户的请求
  • HandlerMapping : 处理器映射器 ,不需要工程师开发,由框架提供
    • 作用:根据请求的 url 、 method 等信息查找 Handler ,即控制器方法
  • Handler : 处理器 ,需要工程师开发
    • 作用:在 DispatcherServlet 的控制下 Handler 对具体的用户请求进行处理
  • HandlerAdapter : 处理器适配器 ,不需要工程师开发,由框架提供
    • 作用:通过 HandlerAdapter 对处理器(控制器方法)进行执行
  • ViewResolver : 视图解析器 ,不需要工程师开发,由框架提供
    • 作用:进行视图解析,得到相应的视图,例如:ThymeleafView、InternalResourceView、RedirectView
  • View : 视图
    • 作用:将模型数据通过页面展示给用户

2、DispatcherServlet初始化过程

DispatcherServlet 本质上是一个 Servlet ,所以天然的遵循 Servlet 的生命周期。所以宏观上是 Servlet生命周期来进行调度
在这里插入图片描述

  • 初始化WebApplicationContext
    所在类:org.springframework.web.servlet.FrameworkServlet
protected WebApplicationContext initWebApplicationContext() {WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());WebApplicationContext wac = null;if (this.webApplicationContext != null) {// A context instance was injected at construction time -> use itwac = this.webApplicationContext;if (wac instanceof ConfigurableWebApplicationContext) {ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)wac;if (!cwac.isActive()) {// The context has not yet been refreshed -> provide services such as             // setting the parent context, setting the application context id, etcif (cwac.getParent() == null) {// The context instance was injected without an explicit parent -> set // the root application context (if any; may be null) as the parentcwac.setParent(rootContext);}this.configureAndRefreshWebApplicationContext(cwac);}}}if (wac == null) {// No context instance was injected at construction time -> see if one // has been registered in the servlet context. If one exists, it is assumed // that the parent context (if any) has already been set and that the // user has performed any initialization such as setting the context idwac = this.findWebApplicationContext();}if (wac == null) {// No context instance is defined for this servlet -> create a local one // 创建WebApplicationContextwac = this.createWebApplicationContext(rootContext);}if (!this.refreshEventReceived) {// Either the context is not a ConfigurableApplicationContext with refresh // support or the context injected at construction time had already been // refreshed -> trigger initial onRefresh manually here.synchronized(this.onRefreshMonitor) {// 刷新WebApplicationContextthis.onRefresh(wac);}}if (this.publishContext) {// Publish the context as a servlet context attribute. // 将IOC容器在应用域共享String attrName = this.getServletContextAttributeName();this.getServletContext().setAttribute(attrName, wac);}return wac;}
  • 创建WebApplicationContext
    所在类:org.springframework.web.servlet.FrameworkServlet
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {Class<?> contextClass = this.getContextClass();if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {throw new ApplicationContextException("Fatal initialization error in servlet with name '" + this.getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext");} else {// 通过反射创建 IOC 容器对象ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);wac.setEnvironment(this.getEnvironment());// 设置父容器wac.setParent(parent);String configLocation = this.getContextConfigLocation();if (configLocation != null) {wac.setConfigLocation(configLocation);}this.configureAndRefreshWebApplicationContext(wac);return wac;}}
  • DispatcherServlet初始化策略
    FrameworkServlet 创建 WebApplicationContext 后,刷新容器,调用 onRefresh(wac) ,此方法在
    DispatcherServlet 中进行了重写,调用了 initStrategies(context) 方法,初始化策略,即初始化
    DispatcherServlet 的各个组件
    所在类:org.springframework.web.servlet.DispatcherServlet
protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context);initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); 
}

3、DispatcherServlet调用组件处理请求

  • processRequest()
    FrameworkServlet 重写 HttpServlet 中的 service() 和 doXxx() ,这些方法中调用了

    processRequest(request, response)
    所在类: org.springframework.web.servlet.FrameworkServlet
    protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {long startTime = System.currentTimeMillis();Throwable failureCause = null;LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();LocaleContext localeContext = this.buildLocaleContext(request);RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes);WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new FrameworkServlet.RequestBindingInterceptor());this.initContextHolders(request, localeContext, requestAttributes);try {// 执行服务,doService()是一个抽象方法,在DispatcherServlet中进行了重写this.doService(request, response);} catch (IOException | ServletException var16) {failureCause = var16;throw var16;} catch (Throwable var17) {failureCause = var17;throw new NestedServletException("Request processing failed", var17);} finally {this.resetContextHolders(request, previousLocaleContext, previousAttributes);if (requestAttributes != null) {requestAttributes.requestCompleted();}this.logResult(request, response, (Throwable)failureCause, asyncManager);this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause);}}
    
  • doService()
    所在类:org.springframework.web.servlet.DispatcherServlet

    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {this.logRequest(request);// Keep a snapshot of the request attributes in case of an include, // to be able to restore the original attributes after the include.Map<String, Object> attributesSnapshot = null;if (WebUtils.isIncludeRequest(request)) {attributesSnapshot = new HashMap();Enumeration attrNames = request.getAttributeNames();label120:while(true) {String attrName;do {if (!attrNames.hasMoreElements()) {break label120;}attrName = (String)attrNames.nextElement();} while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));attributesSnapshot.put(attrName, request.getAttribute(attrName));}}// Make framework objects available to handlers and view objects.request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource());if (this.flashMapManager != null) {FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);if (inputFlashMap != null) {request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));}request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);}RequestPath requestPath = null;if (this.parseRequestPath && !ServletRequestPathUtils.hasParsedRequestPath(request)) {requestPath = ServletRequestPathUtils.parseAndCache(request);}try {// 处理请求和响应this.doDispatch(request, response);} finally {// Restore the original attribute snapshot, in case of an include.if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {this.restoreAttributesAfterInclude(request, attributesSnapshot);}if (requestPath != null) {ServletRequestPathUtils.clearParsedRequestPath(request);}}}
    
  • doDispatch()
    所在类:org.springframework.web.servlet.DispatcherServlet

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {try {ModelAndView mv = null;Object dispatchException = null;try {processedRequest = this.checkMultipart(request);multipartRequestParsed = processedRequest != request;// Determine handler for the current request. /* mappedHandler:调用链 包含handler、interceptorList、interceptorIndex handler:浏览器发送的请求所匹配的控制器方法 interceptorList:处理控制器方法的所有拦截器集合 interceptorIndex:拦截器索引,控制拦截器afterCompletion()的执行 */mappedHandler = this.getHandler(processedRequest);if (mappedHandler == null) {this.noHandlerFound(processedRequest, response);return;}// Determine handler adapter for the current request. // 通过控制器方法创建相应的处理器适配器,调用所对应的控制器方法HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());// Process last-modified header, if supported by the handler.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;}}// 调用拦截器的preHandle()if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// Actually invoke the handler. // 由处理器适配器调用具体的控制器方法,最终获得ModelAndView对象mv = ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}this.applyDefaultViewName(processedRequest, mv);// 调用拦截器的postHandle()mappedHandler.applyPostHandle(processedRequest, response, mv);} catch (Exception var20) {dispatchException = var20;} catch (Throwable var21) {// As of 4.3, we're processing Errors thrown from handler methods as well, // making them available for @ExceptionHandler methods and other scenarios.dispatchException = new NestedServletException("Handler dispatch failed", var21);}// 后续处理:处理模型数据和渲染视图this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);} catch (Exception var22) {this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);} catch (Throwable var23) {this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));}} finally {if (asyncManager.isConcurrentHandlingStarted()) {// Instead of postHandle and afterCompletionif (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}// Clean up any resources used by a multipart request.} else if (multipartRequestParsed) {this.cleanupMultipart(processedRequest);}}}
    
  • processDispatchResult()

    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {boolean errorView = false;if (exception != null) {if (exception instanceof ModelAndViewDefiningException) {this.logger.debug("ModelAndViewDefiningException encountered", exception);mv = ((ModelAndViewDefiningException)exception).getModelAndView();} else {Object handler = mappedHandler != null ? mappedHandler.getHandler() : null;mv = this.processHandlerException(request, response, handler, exception);errorView = mv != null;}}// Did the handler return a view to render?if (mv != null && !mv.wasCleared()) {// 处理模型数据和渲染视图this.render(mv, request, response);if (errorView) {WebUtils.clearErrorRequestAttributes(request);}} else if (this.logger.isTraceEnabled()) {this.logger.trace("No view rendering, null ModelAndView returned.");}// Concurrent handling started during a forwardif (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {if (mappedHandler != null) {// Exception (if any) is already handled.. // 调用拦截器的afterCompletion()mappedHandler.triggerAfterCompletion(request, response, (Exception)null);}}}
    

4、SpringMVC的执行流程

  1. 用户向服务器发送请求,请求被 SpringMVC 前端控制器 DispatcherServlet 捕获。
  2. DispatcherServlet 对请求 URL 进行解析,得到请求资源标识符( URI ),判断请求 URI 对应的映射:
  • 不存在

    • 再判断是否配置了 mvc:default-servlet-handler

      • 如果没配置,则控制台报映射查找不到,客户端展示 404 错误

      在这里插入图片描述

      • 如果有配置,则访问目标资源(一般为静态资源,如: JS,CSS,HTML ),找不到客户端也会展示 404错误

      在这里插入图片描述

  • 存在则执行下面的流程

  1. 根据该 URI ,调用 HandlerMapping 获得该 Handler 配置的所有相关的对象(包括 Handler 对象以及Handler对象对应的拦截器),最后以 HandlerExecutionChain 执行链对象的形式返回
  2. DispatcherServlet 根据获得的 Handler ,选择一个合适的 HandlerAdapter
  3. 如果成功获得 HandlerAdapter ,此时将开始执行拦截器的 preHandler(…) 方法【正向】
  4. 提取 Request 中的模型数据,填充 Handler 入参,开始执行 Handler ( Controller) 方法,处理请求。在填充 Handler 的入参过程中,根据你的配置, Spring 将帮你做一些额外的工作:
    • HttpMessageConveter : 将请求消息(如 Json 、 xml 等数据)转换成一个对象,将对象转换为指定的响应信息
    • 数据转换:对请求消息进行数据转换。如 String 转换成 Integer 、 Double 等
    • 数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
    • 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到 BindingResult 或 Error 中
  5. Handler 执行完成后,向 DispatcherServlet 返回一个 ModelAndView 对象
  6. 此时将开始执行拦截器的 postHandle(…) 方法【逆向】
  7. 根据返回的 ModelAndView (此时会判断是否存在异常:如果存在异常,则执行
    HandlerExceptionResolver 进行异常处理)选择一个适合的 ViewResolver 进行视图解析,根据 Model和View ,来渲染视图
  8. 渲染视图完毕执行拦截器的 afterCompletion(…) 方法【逆向】
  9. 将渲染结果返回给客户端

相关文章:

Spring MVC__HttpMessageConverter、拦截器、异常处理器、注解配置SpringMVC、SpringMVC执行流程

目录 一、HttpMessageConverter1、RequestBody2、RequestEntity3、ResponseBody4、SpringMVC处理json5、SpringMVC处理ajax6、RestController注解7、ResponseEntity7.1、文件下载7.2、文件上传 二、拦截器1、拦截器的配置2、拦截器的三个抽象方法3、多个拦截器的执行顺序 三、异…...

GAMES101(19节,相机)

相机 synthesis合成成像&#xff1a;比如光栅化&#xff0c;光线追踪&#xff0c;相机是capture捕捉成像&#xff0c; 但是在合成渲染时&#xff0c;有时也会模拟捕捉成像方式&#xff08;包括一些技术 动态模糊 / 景深等&#xff09;&#xff0c;这时会有涉及很多专有名词&a…...

Django Nginx+uwsgi 安装配置

Django Nginx+uwsgi 安装配置 本文将详细介绍如何在Linux环境下安装和配置Django应用程序,使用Nginx作为Web服务器和uwsgi作为应用程序服务器。我们将覆盖以下主题: 安装Python和相关库安装和配置Django安装Nginx安装和配置uwsgi配置Nginx以使用uwsgi测试和调试1. 安装Pytho…...

oracle数据备份和导入

一、数据导出 创建目录对象&#xff1a; CREATE DIRECTORY dpump_dir AS /path/to/your/directory;授予权限&#xff1a; GRANT READ, WRITE ON DIRECTORY dpump_dir TO test_user; #导出的用户导出全库数据 expdp your_user/your_password DIRECTORYdpump_dir DUMPFILEfu…...

C++ | Leetcode C++题解之第452题用最少数量的箭引爆气球

题目&#xff1a; 题解&#xff1a; class Solution { public:int findMinArrowShots(vector<vector<int>>& points) {if (points.empty()) {return 0;}sort(points.begin(), points.end(), [](const vector<int>& u, const vector<int>&…...

react-问卷星项目(3)

项目实战 React Hooks 缓存&#xff0c;性能优化&#xff0c;提升时间效率&#xff0c;但是不要为了技术而优化&#xff0c;应该是为了业务而进行优化 内置Hooks保证基础功能&#xff0c;灵活配合实现业务功能&#xff0c;抽离公共部分&#xff0c;自定义Hooks或者第三方&am…...

69 BERT预训练_by《李沐:动手学深度学习v2》pytorch版

系列文章目录 文章目录 系列文章目录NLP里的迁移学习Bert的动机Bert架构对输入的修改五、预训练任务1、2、3、 六、1、2、3、 七、1、2、3、 八、1、2、3、 NLP里的迁移学习 之前是使用预训练好的模型来抽取词、句子的特征&#xff0c;例如 word2vec 或语言模型这种非深度学习…...

Java报错输出的信息究竟是什么?

Java报错输出的信息究竟是什么&#xff1f; 本篇会带大家了解一下java运行时报错输出的信息内容&#xff0c;简单学习一下虚拟机内存中Java虚拟机栈的工作方式以及栈帧中所存储的信息内容 异常信息 当你的程序运行报错时&#xff0c;你是否会好奇打印出来的那一大坨红色的究竟…...

解表之紫苏

** 声明&#xff1a;本文介绍的中药仅供学习使用&#xff0c;请勿擅自使用&#xff0c;否则后果自负&#xff01;&#xff01;&#xff01;因水平有限&#xff0c;如有不当之处&#xff0c;请批评指正&#xff01;&#xff01;&#xff01;&#xff01;图片来源网络&#xff0…...

JavaScript数据类型

目录 JavaScripit数据类型 原始类型&#xff08;Primitive Types&#xff09; 1 Undefined 特点 实例 2 Null 实例 3 Boolean 重点&#xff1a; 常用falsy情况&#xff1a; 思考 4 Number,BigInt 实例 特点 NaN 5 String 在JavaScript中表示字符串有三种表示方…...

市场中的新兴力量与未来发展

在当前瞬息万变的全球金融市场中&#xff0c;期货交易以其高杠杆与灵活性&#xff0c;吸引了越来越多的投资者参与其中。大粤期货作为中国期货行业的新兴力量&#xff0c;凭借其创新的交易平台、广泛的产品线及专业的风险管理服务&#xff0c;迅速在市场中崭露头角。本文将介绍…...

Golang | Leetcode Golang题解之第446题等差数列划分II-子序列

题目&#xff1a; 题解&#xff1a; func numberOfArithmeticSlices(nums []int) (ans int) {f : make([]map[int]int, len(nums))for i, x : range nums {f[i] map[int]int{}for j, y : range nums[:i] {d : x - ycnt : f[j][d]ans cntf[i][d] cnt 1}}return }...

Java 常用序列化对比

Java 中常用的序列化方式主要包括以下几种: 1. Java 原生序列化 使用方式: 使用 java.io.Serializable 接口。对象需要实现该接口,然后通过 ObjectOutputStream 和 ObjectInputStream 进行序列化和反序列化。 示例代码: import java.io.*;public class Person impleme…...

【redis学习篇1】redis基本常用命令

目录 redis存储数据的模式 常用基本命令 一、set 二、keys pattern keys 字符串当中携带问号 keys 字符串当中携带*号 keys 【^字母】 keys * 三、exists 四、del 五、expire 5.1 ttl命令 5.2key删除策略 5.2.1惰性删除 5.2.2定期删除 六、type key的数据类型…...

量子计算:颠覆未来计算的革命性技术

量子计算&#xff1a;颠覆未来计算的革命性技术 量子计算作为下一代颠覆性技术&#xff0c;正在引领计算领域的重大变革。与传统计算机基于比特的二进制运算不同&#xff0c;量子计算通过量子比特&#xff08;qubits&#xff09;在叠加态和纠缠态下实现并行计算&#xff0c;能…...

ctfshow-web入门(信息收集,持续更新中。。)

写在之前:近期打了个比赛,备受打击,入手了vip账号进修,加油! 文章目录 ctfshow-web1查看源代码ctfshow-web2burp抓包ctfshow-web3burp抓包ctfshow-web4访问robots.txtctfshow-web5dirscarch扫描PHPS文件泄露ctfshow-web6dirscarch扫描ctfshow-web7dirscarch扫描ctfshow-w…...

蓝桥杯【物联网】零基础到国奖之路:十五. 扩展模块之双路ADC

蓝桥杯【物联网】零基础到国奖之路:十五. 扩展模块之双路ADC 第一节 硬件解读第二节 CubeMX配置第三节 代码编写 第一节 硬件解读 STM32的ADC是12位&#xff0c;通过硬件过采样扩展到16位&#xff0c;模数转换器嵌入到STM32L071xx器件中。有16个外部通道和2个内部通道&#xf…...

李飞飞谈AI+3D发展:3D/4D AI将成为下一个重要前沿

人工智能(AI)的发展已经深刻改变了我们的世界,从简单的图像识别到复杂的自然语言处理,再到如今正在兴起的生成式模型。在这个过程中,李飞飞教授认为,3D/4D AI技术将是推动下一波变革的关键力量。以下根据她的观点整理了AI发展历程中的关键里程碑以及对3D/4D AI未来发展的…...

centos72009源码编译R语言

./dev/make-distribution.sh --name custom-spark --pip --r --tgz -Pconnect -Psparkr -Phive -Phive-thriftserver -Pmesos -Pyarn -Dhadoop.version3.4.0 -Pkubernetes spark3.5.3 源码版本 ./dev/make-distribution.sh --name custom-spark --pip --r --tgz -Pconnect -P…...

初识算法 · 双指针(4)

目录 前言&#xff1a; 复写零 题目解析 算法原理 算法编写 四数之和 题目解析 算法原理 算法编写 前言&#xff1a; 本文是双指针算法的最后一文&#xff0c;以复写零和四数之和作为结束&#xff0c;介绍方式同样是题目解析&#xff0c;算法原理&#xff0c;算法编写…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现

目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密

在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

汽车生产虚拟实训中的技能提升与生产优化​

在制造业蓬勃发展的大背景下&#xff0c;虚拟教学实训宛如一颗璀璨的新星&#xff0c;正发挥着不可或缺且日益凸显的关键作用&#xff0c;源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例&#xff0c;汽车生产线上各类…...

vue3 字体颜色设置的多种方式

在Vue 3中设置字体颜色可以通过多种方式实现&#xff0c;这取决于你是想在组件内部直接设置&#xff0c;还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法&#xff1a; 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...

【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表

1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

React19源码系列之 事件插件系统

事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年&#xff0c;作为行业领先的3D工业相机及视觉系统供应商&#xff0c;累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成&#xff0c;通过稳定、易用、高回报的AI3D视觉系统&#xff0c;为汽车、新能源、金属制造等行…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...