【零基础入门SpringMVC】第六期——尾声

news2025/2/27 8:26:34

一、注解配置SpringMVC

  • 采用全注解开发,替代我们的web.xmlSpringMVC的核心配置文件

  • 我们需要创建对应的配置类,继承AbstractAnnotationConfigDispatcherServletInitializer

    • 使用的Servlet版本要求在3.0以上
    • 项目启动后容器会找到配置了,基于我们的配置来初始化Servlet的上下文
  • 接下来给出模板代码及注释,演示如何使用注解进行配置
    在这里插入图片描述

准备一个新的模块,无需创建 web.xml,打包方式仍为war,导入之前的依赖,手动创建webappWEB-INF目录

  • 编写我们的核心配置类 WebInit

    package com.atguigu.mvc.config;
    
    import org.springframework.web.filter.CharacterEncodingFilter;
    import org.springframework.web.filter.HiddenHttpMethodFilter;
    import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
    
    import javax.servlet.Filter;
    
    /**
     * 代替我们的web.xml
     * @author Bonbons
     * @version 1.0
     */
    public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer {
        /**
         * 指定Spring配置类
         * @return
         */
        @Override
        protected Class<?>[] getRootConfigClasses() {
            return new Class[]{SpringConfig.class};
        }
    
        /**
         * 指定SpringMVC配置类
         * @return
         */
        @Override
        protected Class<?>[] getServletConfigClasses() {
            return new Class[]{WebConfig.class};
        }
    
        /**
         * 指定前端控制器的映射规则, url-pattern
         * @return
         */
        @Override
        protected String[] getServletMappings() {
            return new String[]{"/"};
        }
    
        /**
         * 配置我们的过滤器
         * @return
         */
        @Override
        protected Filter[] getServletFilters() {
            // 编码过滤器
            CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
            characterEncodingFilter.setEncoding("UTF-8");
            characterEncodingFilter.setForceResponseEncoding(true);
            // 隐藏过滤器,将post转换为delete和put [没有啥初始化参数,就是注册一下就能用]
            HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
            // 返回我们创建的过滤器
            return new Filter[]{characterEncodingFilter, hiddenHttpMethodFilter};
        }
    }
    
  • 编写我们 SpringMVC 的核心配置类 WebConfig

    package com.atguigu.mvc.config;
    
    import com.atguigu.mvc.interceptor.TestInterceptor;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.context.ContextLoader;
    import org.springframework.web.context.WebApplicationContext;
    import org.springframework.web.multipart.MultipartFile;
    import org.springframework.web.multipart.MultipartResolver;
    import org.springframework.web.multipart.commons.CommonsMultipartResolver;
    import org.springframework.web.servlet.HandlerExceptionResolver;
    import org.springframework.web.servlet.ViewResolver;
    import org.springframework.web.servlet.config.annotation.*;
    import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
    import org.thymeleaf.spring5.SpringTemplateEngine;
    import org.thymeleaf.spring5.view.ThymeleafViewResolver;
    import org.thymeleaf.templatemode.TemplateMode;
    import org.thymeleaf.templateresolver.ITemplateResolver;
    import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
    
    import java.util.List;
    import java.util.Properties;
    
    /**
     * @author Bonbons
     * @version 1.0
     */
    @Configuration // 声明配置类
    @ComponentScan ("com.atguigu.mvc.controller")// 扫描组件
    @EnableWebMvc // 开启注解驱动
    public class WebConfig implements WebMvcConfigurer {
        /**
         * 配置默认的Servlet,也就是处理我们的静态资源
         * @param configurer
         */
        @Override
        public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
            WebMvcConfigurer.super.configureDefaultServletHandling(configurer);
        }
    
        /**
         * 配置拦截器
         * @param registry
         */
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            // 创建我们自定义拦截器的对象
            TestInterceptor testInterceptor = new TestInterceptor();
            // 设置拦截规则
            registry.addInterceptor(testInterceptor).addPathPatterns("/**");
            WebMvcConfigurer.super.addInterceptors(registry);
        }
    
        /**
         * 配置视图控制器
         * @param registry
         */
        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            // 前面的viewController是请求路径,后面的viewName是视图名
            registry.addViewController("/hello").setViewName("hello");
            WebMvcConfigurer.super.addViewControllers(registry);
        }
    
        /**
         * 配置我们的文件上传解析器
         * @return
         */
        public MultipartResolver multipartResolver(){
            // 上面这个类型实际是一个接口,所以我们需要创建对应实现类的对象
            CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
            // 返回
            return commonsMultipartResolver;
        }
    
        @Override
        public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
    
            SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver();
            // Properties 集合存储异常和跳转页面的映射
            Properties prop = new Properties();
            // 添加
            prop.setProperty("java.lang.ArithmeticException", "error");
            // 将其添加到我们的SimpleMappingExceptionResolver自定义异常对象中
            exceptionResolver.setExceptionMappings(prop);
            // 将异常信息添加到共享域中
            exceptionResolver.setExceptionAttribute("exception");
            // 将配置添加到我们的解析器中
            resolvers.add(exceptionResolver);
            WebMvcConfigurer.super.configureHandlerExceptionResolvers(resolvers);
        }
    
        //配置生成模板解析器
        @Bean
        public 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;
        }
    
        //生成模板引擎并为模板引擎注入模板解析器
        @Bean
        public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) {
            SpringTemplateEngine templateEngine = new SpringTemplateEngine();
            templateEngine.setTemplateResolver(templateResolver);
            return templateEngine;
        }
    
        //生成视图解析器并未解析器注入模板引擎
        @Bean
        public ViewResolver viewResolver(SpringTemplateEngine templateEngine) {
            ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
            viewResolver.setCharacterEncoding("UTF-8");
            viewResolver.setTemplateEngine(templateEngine);
            return viewResolver;
        }
    }
    
  • 编写我们Spring的核心配置类 SpringConfig 【只是定义了这个类,但是没配置】

    package com.atguigu.mvc.config;
    
    import org.springframework.context.annotation.Configuration;
    
    /**
     * @author Bonbons
     * @version 1.0
     */
    @Configuration
    public class SpringConfig {
    }
    
  • 编写我们拦截器的配置类 TestInterceptor

    package com.atguigu.mvc.interceptor;
    
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * @author Bonbons
     * @version 1.0
     */
    public class TestInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("TestInterceptor-->preHandle");
            return HandlerInterceptor.super.preHandle(request, response, handler);
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
        }
    }
    
  • 编写我们的控制器 TestController

    package com.atguigu.mvc.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    /**
     * @author Bonbons
     * @version 1.0
     */
    @Controller
    public class TestController {
        @RequestMapping("/")
        public String index(){
            return "index";
        }
    }
    
  • 编写我们的首页 index

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    hello
    </body>
    </html>
    
  • 编写我们的 hello 页面,用于视图控制器 【还应该有个异常跳转页面 error

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    hello
    </body>
    </html>
    
  • 这部分内容主要看一下应该如何配置,我觉得实际开发的时候,不会采用全部用注解进行配置

在这里插入图片描述

二、SpringMVC的执行流程

$ SpringMVC常用组件

  • DispatcherServlet前端控制器,不需要工程师开发,由框架提供

    • 作用:统一处理请求和响应,整个流程控制的中心,由它调用其它组件处理用户的请求
  • HandlerMapping处理器映射器,不需要工程师开发,由框架提供

    • 作用:根据请求的urlmethod等信息查找Handler,即控制器方法
  • Handler处理器,需要工程师开发

    • 作用:在DispatcherServlet的控制下Handler对具体的用户请求进行处理
  • HandlerAdapter处理器适配器,不需要工程师开发,由框架提供

    • 作用:通过HandlerAdapter对处理器(控制器方法)进行执行
  • ViewResolver视图解析器,不需要工程师开发,由框架提供

    • 作用:进行视图解析,得到相应的视图,ThymeleafView、InternalResourceView、RedirectViewThymeleaf、转发、重定向】
  • View视图

    • 作用:将模型数据通过页面展示给用户

$ DispatcherServlet初始化过程

DispatcherServlet 本质上是一个 Servlet,所以天然的遵循 Servlet 的生命周期。所以宏观上是 Servlet 生命周期来进行调度。

在这里插入图片描述

a>初始化WebApplicationContext

所在类:org.springframework.web.servlet.FrameworkServlet

protected WebApplicationContext initWebApplicationContext() {
    WebApplicationContext rootContext =
        WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    WebApplicationContext wac = null;

    if (this.webApplicationContext != null) {
        // A context instance was injected at construction time -> use it
        wac = 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, etc
                if (cwac.getParent() == null) {
                    // The context instance was injected without an explicit parent -> set
                    // the root application context (if any; may be null) as the parent
                    cwac.setParent(rootContext);
                }
                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 id
        wac = findWebApplicationContext();
    }
    if (wac == null) {
        // No context instance is defined for this servlet -> create a local one
        // 创建WebApplicationContext
        wac = 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) {
            // 刷新WebApplicationContext
            onRefresh(wac);
        }
    }

    if (this.publishContext) {
        // Publish the context as a servlet context attribute.
        // 将IOC容器在应用域共享
        String attrName = getServletContextAttributeName();
        getServletContext().setAttribute(attrName, wac);
    }

    return wac;
}

b>创建WebApplicationContext

所在类:org.springframework.web.servlet.FrameworkServlet

protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
    Class<?> contextClass = getContextClass();
    if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
        throw new ApplicationContextException(
            "Fatal initialization error in servlet with name '" + getServletName() +
            "': custom WebApplicationContext class [" + contextClass.getName() +
            "] is not of type ConfigurableWebApplicationContext");
    }
    // 通过反射创建 IOC 容器对象
    ConfigurableWebApplicationContext wac =
        (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

    wac.setEnvironment(getEnvironment());
    // 设置父容器
    wac.setParent(parent);
    String configLocation = getContextConfigLocation();
    if (configLocation != null) {
        wac.setConfigLocation(configLocation);
    }
    configureAndRefreshWebApplicationContext(wac);

    return wac;
}

c>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);
}

$ DispatcherServlet调用组件处理请求

a>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 = buildLocaleContext(request);

    RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
    ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

    initContextHolders(request, localeContext, requestAttributes);

    try {
		// 执行服务,doService()是一个抽象方法,在DispatcherServlet中进行了重写
        doService(request, response);
    }
    catch (ServletException | IOException ex) {
        failureCause = ex;
        throw ex;
    }
    catch (Throwable ex) {
        failureCause = ex;
        throw new NestedServletException("Request processing failed", ex);
    }

    finally {
        resetContextHolders(request, previousLocaleContext, previousAttributes);
        if (requestAttributes != null) {
            requestAttributes.requestCompleted();
        }
        logResult(request, response, failureCause, asyncManager);
        publishRequestHandledEvent(request, response, startTime, failureCause);
    }
}

b>doService()

所在类:org.springframework.web.servlet.DispatcherServlet

@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
    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();
        while (attrNames.hasMoreElements()) {
            String attrName = (String) attrNames.nextElement();
            if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
                attributesSnapshot.put(attrName, request.getAttribute(attrName));
            }
        }
    }

    // Make framework objects available to handlers and view objects.
    request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
    request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
    request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
    request.setAttribute(THEME_SOURCE_ATTRIBUTE, 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 {
        // 处理请求和响应
        doDispatch(request, response);
    }
    finally {
        if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
            // Restore the original attribute snapshot, in case of an include.
            if (attributesSnapshot != null) {
                restoreAttributesAfterInclude(request, attributesSnapshot);
            }
        }
        if (requestPath != null) {
            ServletRequestPathUtils.clearParsedRequestPath(request);
        }
    }
}

c>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 {
        ModelAndView mv = null;
        Exception dispatchException = null;

        try {
            processedRequest = checkMultipart(request);
            multipartRequestParsed = (processedRequest != request);

            // Determine handler for the current request.
            /*
            	mappedHandler:调用链
                包含handler、interceptorList、interceptorIndex
            	handler:浏览器发送的请求所匹配的控制器方法
            	interceptorList:处理控制器方法的所有拦截器集合
            	interceptorIndex:拦截器索引,控制拦截器afterCompletion()的执行
            */
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null) {
                noHandlerFound(processedRequest, response);
                return;
            }

            // Determine handler adapter for the current request.
           	// 通过控制器方法创建相应的处理器适配器,调用所对应的控制器方法
            HandlerAdapter ha = 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;
            }

            applyDefaultViewName(processedRequest, mv);
            // 调用拦截器的postHandle()
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        }
        catch (Exception ex) {
            dispatchException = ex;
        }
        catch (Throwable err) {
            // 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", err);
        }
        // 后续处理:处理模型数据和渲染视图
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    catch (Exception ex) {
        triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    }
    catch (Throwable err) {
        triggerAfterCompletion(processedRequest, response, mappedHandler,
                               new NestedServletException("Handler processing failed", err));
    }
    finally {
        if (asyncManager.isConcurrentHandlingStarted()) {
            // Instead of postHandle and afterCompletion
            if (mappedHandler != null) {
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            }
        }
        else {
            // Clean up any resources used by a multipart request.
            if (multipartRequestParsed) {
                cleanupMultipart(processedRequest);
            }
        }
    }
}

d>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) {
            logger.debug("ModelAndViewDefiningException encountered", exception);
            mv = ((ModelAndViewDefiningException) exception).getModelAndView();
        }
        else {
            Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
            mv = processHandlerException(request, response, handler, exception);
            errorView = (mv != null);
        }
    }

    // Did the handler return a view to render?
    if (mv != null && !mv.wasCleared()) {
        // 处理模型数据和渲染视图
        render(mv, request, response);
        if (errorView) {
            WebUtils.clearErrorRequestAttributes(request);
        }
    }
    else {
        if (logger.isTraceEnabled()) {
            logger.trace("No view rendering, null ModelAndView returned.");
        }
    }

    if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
        // Concurrent handling started during a forward
        return;
    }

    if (mappedHandler != null) {
        // Exception (if any) is already handled..
        // 调用拦截器的afterCompletion()
        mappedHandler.triggerAfterCompletion(request, response, null);
    }
}

$ SpringMVC的执行流程

  1. 用户向服务器发送请求,请求被SpringMVC 前端控制器 DispatcherServlet捕获。

  2. DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI),判断请求URI对应的映射:

a) 不存在

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

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

在这里插入图片描述
在这里插入图片描述

iii. 如果有配置,则访问目标资源(一般为静态资源,如:JS,CSS,HTML),找不到客户端也会展示404错误
在这里插入图片描述

b) 存在则执行下面的流程

  1. 根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain执行链对象的形式返回。

  2. DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter

  3. 如果成功获得HandlerAdapter,此时将开始执行拦截器的preHandler(…)方法【正向】

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

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

b) 数据转换:对请求消息进行数据转换。如String转换成IntegerDouble

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

d) 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResultError

  1. Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象。

  2. 此时将开始执行拦截器的postHandle(…)方法【逆向】。

  3. 根据返回的ModelAndView(此时会判断是否存在异常:如果存在异常,则执行HandlerExceptionResolver进行异常处理)选择一个适合的ViewResolver进行视图解析,根据ModelView,来渲染视图。

  4. 渲染视图完毕执行拦截器的afterCompletion(…)方法【逆向】。

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

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/44785.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

台积电跪舔美国,日本却醒悟了而选择独立发展芯片产业

近期台积电大举包机10架将精英人才和设备转往美国引发争议&#xff0c;然而这个时候日本却选择了独立发展芯片产业的道路&#xff0c;摆脱美国的限制&#xff0c;显然日本清醒地认识到依赖美国不会有好结果。台积电之前还在左右摇摆&#xff0c;希望既能继续获得美国芯片的订单…

测试用例的重要性,看完这篇就够了

测试用例对于测试工作的作用&#xff1a;1、指导测试的实施测试用例主要适用于集成测试、系统测试和回归测试。在实施测试时测试用例作为测试的标准&#xff0c;测试人员一定要按照测试用例严格按用例项目和测试步骤逐一实施测试。并对测试情况记录在测试用例管理软件中&#x…

干货 | 数字经济创新创业——如何发展绿色经济

下文整理自清华大学大数据能力提升项目能力提升模块课程“Innovation & Entrepreneurship for Digital Economy”&#xff08;数字经济创新创业课程)的精彩内容。主讲嘉宾&#xff1a;Kris Singh: CEO at SRII, Palo Alto, CaliforniaVisiting Professor of Tsinghua Unive…

[附源码]计算机毕业设计springboot房屋租赁系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

【毕业设计】27-基于单片机的家庭监控及防盗报警_热释电报警_人体系统工程设计(原理图+源代码+仿真+实物照片+答辩论文)

【毕业设计】27-基于单片机的家庭监控及防盗报警/热释电报警/人体系统工程设计&#xff08;原理图源代码仿真实物照片论文&#xff09; 文章目录【毕业设计】27-基于单片机的家庭监控及防盗报警/热释电报警/人体系统工程设计&#xff08;原理图源代码仿真实物照片论文&#xff…

【Java实战】工作中规范使用Java集合

目录 一、前言 二、规范使用Java集合 1.【强制】关于 hashCode 和 equals 的处理&#xff0c;遵循如下规则&#xff1a; 2.【强制】判断所有集合内部的元素是否为空&#xff0c;使用 isEmpty() 方法&#xff0c;而不是 size() 0 的方式。 3.【强制】在使用 java.util.str…

接口自动化测试实践指导(中):接口测试场景有哪些

在第一篇文章中详细给小伙伴们讲解了接口自动化需要做哪些准备工作&#xff0c;准备工作中最后一步接口测试用例设计是非常重要的一个环节&#xff0c;用例设计的好不好&#xff0c;直接关系到我们的测试质量。那如何进行测试用例设计呢&#xff1f;这里呢我结合自身经验&#…

PYTHON 用几何布朗运动模型和蒙特卡罗MONTE CARLO随机过程模拟股票价格可视化分析耐克NKE股价时间序列数据...

原文链接&#xff1a;http://tecdat.cn/?p27099 金融资产/证券已使用多种技术进行建模。该项目的主要目标是使用几何布朗运动模型和蒙特卡罗模拟来模拟股票价格。该模型基于受乘性噪声影响的随机&#xff08;与确定性相反&#xff09;变量&#xff08;点击文末“阅读原文”获取…

【 医学影像| 数据预处理】

影像读取及预处理&#xff1a;预处理后的数据集建议保存在本地&#xff0c;可以减少训练时的部分资源消耗。里面提到了归一化的 对分割的一些理解&#xff1a;基于深度学习来做医学图像处理&#xff0c;主要的工作集中在了数据预处理部分&#xff1a;深入理解医学图像的格式和特…

GLAD:体全息

概述 自从伽伯1948年提出全息术后&#xff0c;光学全息术已经被广泛用于三维光学成像领域。体全息成像技术是采用体全息光栅作为成像元件对物体进行三维成像的技术。 1990年,由Barbastathis和Brady提出体全息成像技术&#xff0c;采用体全息光栅作为选择成像元件&#xf…

【微信小程序高频面试题——精选一】

微信小程序高频面试题小程序中如何进行接口请求&#xff1f;会不会跨域&#xff0c;为什么小程序的常用命令有哪些你认为微信小程序的优点是什么&#xff0c;缺点是什么微信小程序中的js和浏览器中的js以及node中的js的区别微信小程序中的数据渲染浏览器中有什么不同小程序中如…

全国所有地级市环境污染、企业、公路、固定资产、外商投资-最新面板数据

一、1990&#xff0d;2019年地级市面板数据 1、数据来源&#xff1a;中国城市统计年鉴、WIND数据库 2、时间跨度&#xff1a;2000-2019 3、区域范围&#xff1a;所有地级市 4、指标说明&#xff1a; 该份部分数据指标如下&#xff1a; 主营业务税金及附加(万元) 发明专利…

android-CHECK_xxx分析

android-CHECK_xxx 在android源码中有不少类似这样的用法&#xff0c;上图中就是检查获得的hal版本是否大于等于版本1_3&#xff0c;满足继续往下走&#xff0c;不满足则assert&#xff0c;并报错。 接下来就展开看看CHECK_xx家族&#xff1a; 用法 类型用法含义CHECK_EQ(val…

【SpringCloud】07 流量管理sentinel

sentinel Sentinel 是面向分布式服务架构的高可用流量防护组件&#xff0c;主要以流量为切入点&#xff0c;从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。 1. 微服务中的服务雪崩 服务雪崩效应是一种因“服务提供者的不可…

Springboot系列(二十二):如何纯文本转成.csv格式文件?|超级详细,建议收藏

一、前言&#x1f525; 不知道大家有咩有遇到这么个需求&#xff0c;给你一长串文本&#xff0c;要求你能导成excel格式展示数据&#xff0c;一时间我陷入了沉思&#xff0c;如果要常规转excel&#xff0c;最明显的一点就是固定表头名&#xff0c;然而并不是&#xff0c;这表头…

[附源码]计算机毕业设计springboot冬奥资讯系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

【DL with Pytorch】第 2 章 : 神经网络的构建块

&#x1f50e;大家好&#xff0c;我是Sonhhxg_柒&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f4dd;个人主页&#xff0d;Sonhhxg_柒的博客_CSDN博客 &#x1f4c3; &#x1f381;欢迎各位→点赞…

第一个Shader Graph

上篇我们用ShaderLab来实现了第一个Shader,但对于初学者也太复杂了,那有没有简单的方式来实现shader的操作呢? 现在我们来分享下ShaderGraph,可视化编程,如图所示 ShaderGraph介绍 ShaderGraph是2018年推出的,可以看下官网出的例子https://github.com/UnityTechnologi…

[Linux] 进程程序替换之实现一个简单的shell

进程程序替换替换原理替换函数实现一个简单的shell主要过程实现代码替换原理 用fork创建子进程后执行的是和父进程相同的程序&#xff0c;若要执行不同的代码分支&#xff0c;子进程往往要调用一种exec函数以执行另一个程序&#xff1b;当进程调用一种exec函数时&#xff0c;该…

信息论与编码:随参信道特性

文章目录随参信道数学模型的建立随参信道对信号传输的影响平坦性衰落及频率选择性衰落1.平坦性衰落Rayleigh 分布Rice 分布2.频率选择性衰落多径随参信道的时延扩展与相干带宽随参信道的多径时延特性多径信道的频域特性移动信道的多普勒扩展及相干时间1.多普勒扩展2.信道的相干…