跳至主要內容

SpringMVC开发实战-3

wangdx大约 13 分钟

WEB 资源安全访问

资源安全与访问

  • WEB 项目由于采用 HTTP 开放式协议,并且所有的动态页面以及静态资源都可以在浏览器中通过指定的路径直接进行访问,这样在实际的开发中就会时刻面临着资源安全性的问题。而此时的常见做法,是将所有的 WEB 资源(例如:JSP 动态页面、静态 JS 脚本、静态 CSS 样式等)保存在 WEB-INF 目录之中,从而实现资源的安全保护
1、

@GetMapping("echo") // 映射地址
public ModelAndView echo(String msg) { // 负责跳转
    ModelAndView mav = new ModelAndView("/WEB-INF/pages/message/show.jsp");
    mav.addObject("echoMessage", "【ECHO】" + msg);
    return mav;
}

2、
<%@ page pageEncoding="UTF-8"%>
<html>
    <head><title>SpringMVC开发框架</title></head>
    <body>
        <h1>${echoMessage}</h1>
    </body>
</html>

3、
localhost/pages/message/echo?msg=www.yootk.com

4、
package com.yootk.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

@Configuration
public class ResourceViewConfig { // 资源视图的映射配置
    @Bean
    public InternalResourceViewResolver resourceViewResolver() {
        InternalResourceViewResolver resolver =
                new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/pages"); // 统一的前缀配置
        resolver.setSuffix(".jsp"); // 统一的后缀配置
        return resolver; // 返回解析器
    }
}


5、
package com.yootk.action;

import com.yootk.action.abs.AbstractAction;
import com.yootk.service.IMessageService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller // 控制层注解
@RequestMapping("/pages/message/") // 父路径
public class MessageAction extends AbstractAction {
    private static final Logger LOGGER = LoggerFactory.getLogger(MessageAction.class);
    @Autowired
    private IMessageService messageService; // 业务接口实例
    @GetMapping("echo") // 映射地址
    public ModelAndView echo(String msg) { // 负责跳转
        ModelAndView mav = new ModelAndView("/message/show");
        mav.addObject("echoMessage", "【ECHO】" + msg);
        return mav;
    }
    @GetMapping("input") // 映射地址
    public String input() {
        return "/pages/message/input.jsp";
    }
}


6、
localhost/pages/message/echo?msg=快乐的小李老师

7、
.text {
    color: #337ab7;
    font-size: 25px;
    text-decoration: none;
    cursor: point;
}

8、
console.log("【JavaScript输出】小李老师的编程训练营")

9、
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) { // 添加资源
        registry.addResourceHandler("yootk-js/**").addResourceLocations("/WEB-INF/static/js/");
        registry.addResourceHandler("yootk-css/**").addResourceLocations("/WEB-INF/static/css/");
        registry.addResourceHandler("yootk-images/**").addResourceLocations("/WEB-INF/static/images/");
    }

10、

<%@ page pageEncoding="UTF-8"%>
<html>
    <head>
        <title>SpringMVC开发框架</title>
        <link rel="stylesheet" type="text/css" href="/yootk-css/style.css">
        <script type="text/javascript" src="/yootk-js/yootk.js"></script>
    </head>
    <body>
        <h1>${echoMessage}</h1>
        <h1><a href="https://www.yootk.com" class="text">${echoMessage}</a></h1>
        <img src="/yootk-images/book.jpg" style="width:200px;">
    </body>
</html>


11·、
localhost/pages/message/echo?msg=李兴华原创图书

统一异常处理

异常统一处理

  • 一个完善的应用项目,除了要考虑其自身完善的业务处理之外,也需要进行有效的异常处理,这样才可以在出现问题后,进行错误的正确展示
1、
package com.yootk.action;

import com.yootk.action.abs.AbstractAction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller // 控制层注解
@RequestMapping("/pages/message/") // 父路径
public class MessageAction extends AbstractAction {
    private static final Logger LOGGER = LoggerFactory.getLogger(MessageAction.class);
    @GetMapping("echo") // 映射地址
    public ModelAndView echo(String msg) { // 负责跳转
        if (msg.contains("yootk")) {    // 引爆点
            throw new RuntimeException("我膨胀了,你怎么样?我只会Java八股文我找不到工作,你能怎么样?");
        }
        ModelAndView mav = new ModelAndView("/message/show");
        mav.addObject("echoMessage", "【ECHO】" + msg);
        return mav;
    }
}


2、
http://localhost/pages/message/echo?msg=yootk

3、
package com.yootk.action.advice;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;

@ControllerAdvice // 控制层的切面操作
public class ErrorAdvice { // 异常处理
    @ExceptionHandler(Exception.class) // 进行异常的捕获
    public ModelAndView handle(Exception e) { // 处理所有程序中的异常
        // 现在所配置的全局跳转路径全部都是由用户开发出来的
        // 毕竟学习SpringMVC下一步就是SpringBoot(SpringBoot会有自动的默认配置)
        ModelAndView mav = new ModelAndView("/error"); // 默认的处理路径
        mav.addObject("message", e.getMessage()); // 保存异常信息
        return mav;
    }
}


4、
<%@ page pageEncoding="UTF-8"%>
<html>
    <head>
        <title>SpringMVC开发框架</title>
    </head>
    <body>
        <h1>对不起,我们出现了错误了,请您原谅!</h1>
        <h1>${message}</h1>
        <h1><a href="https://www.yootk.com" class="text">${echoMessage}</a></h1>
        <img src="/yootk-images/book.jpg" style="width:200px;">
    </body>
</html>

自定义页面未发现处理

自定义页面未发现

  • 在 WEB 应用中的每一个程序都是依靠路径进行链接的,但是在代码的开发与维护过程中,有可能会出现某些路径丢失,而产生 404 的响应状态码,在传统的开发中,可以通但是在过 web.xml 配置文件,使用“<error-page>”配置项进行错误页的路径定义基于 Bean 配置的环境下,就需要开发者通过代码定义的方式来实现此类功能
1、
localhost/pages/message/hello

2、
package com.yootk.servlet;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

public class YootkDispatcherServlet extends DispatcherServlet { // 自定义分发控制器
    public YootkDispatcherServlet(WebApplicationContext webApplicationContext) {
        super(webApplicationContext); // 调用父类构造
    }

    @Override // 根据需要进行父类方法的覆写
    protected void noHandlerFound(HttpServletRequest request,
                                  HttpServletResponse response) throws Exception {
        response.sendRedirect("/notfound"); // 进行未发现路径的配置
    }
}


3、
@Override
protected FrameworkServlet createDispatcherServlet(
        WebApplicationContext servletAppContext) { // 分发控制器的创建
    return new YootkDispatcherServlet(servletAppContext);
}


4、
package com.yootk.action;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class NotFoundAction { // 未发现
    @RequestMapping("/notfound") // 与YootkDispatcherServlet类中的路径相同
    public String notfound() {
        return "/notfound"; // 视图路径
    }
}


5、
<%@ page pageEncoding="UTF-8"%>
<html>
    <head>
        <title>SpringMVC开发框架</title>
    </head>
    <body>
        <h1>对不起,您已经脱离了地球引力,跑到外太空去了,拜拜!</h1>
        <h1><a href="https://www.yootk.com" class="text">${echoMessage}</a></h1>
        <img src="/yootk-images/book.jpg" style="width:200px;">
    </body>
</html>

6、
http://localhost/pages/message/hello

拦截器

拦截器机制

  • 在 JakartaEE 标准开发架构之中,用户请求和响应之间可以通过过滤器进行拦截处理但是在 SpringMVC 中,由于所有的控制器全部都是由 DispatcherServlet 类进行分发处理,所以此时就无法满足过滤器的使用机制,所以为了解决这样的设计问题,SpringMVC 提供了一种拦截器的机制来代替过滤器的使用

1、
package com.yootk.intercetor;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class YootkHandlerInterceptor implements HandlerInterceptor { // 拦截器
    private static final Logger LOGGER =
            LoggerFactory.getLogger(YootkHandlerInterceptor.class); // 日志输出

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) { // 是否为指定类的实例
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            LOGGER.info("【请求处理前 - preHandle()】Action对象:{}", handlerMethod.getBean());
            LOGGER.info("【请求处理前 - preHandle()】Action类:{}", handlerMethod.getBeanType());
            LOGGER.info("【请求处理前 - preHandle()】Action方法:{}", handlerMethod.getMethod());
        }
        return true; // 表示进行后续的控制层执行
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        LOGGER.info("【请求处理后 - postHandle()】跳转视图:{}", modelAndView.getViewName());
        LOGGER.info("【请求处理后 - postHandle()】传递属性:{}", modelAndView.getModel());
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        LOGGER.info("控制层请求处理完毕。");
    }
}


2、
    @Override
    public void addInterceptors(InterceptorRegistry registry) { // 追加拦截器
        registry.addInterceptor(new YootkHandlerInterceptor()) // 定义拦截器
                .addPathPatterns("/pages/**"); // 拦截路径匹配
    }

3、
localhost/pages/message/echo?msg=yootk.com

WebApplicationContextUtils

WebApplicationContextUtils 工具类

  • SpringMVC 的整体的核心处理都是在 DispatcherServlet 之中进行的,而 DispatcherServlet 包含在 SpringWEB 容器之中,所以该类相关的处理结构都可以基于 Spring 容器提供的 Bean 管理机制以及 AOP 代理机制实现所需的代码编写然而在一个 WEB 项目中还可能会包含有过滤器、监听器或者其他功能的 Servet,这些程序结构并不工作在 SpringWEB 容器之中,如果要想获取 Spring 上下文的支持,那么就可以依靠 WebApplicationContextUtils 工具类来实现,该类在使用时,只需要传入一 ServletContext 对象实例,即可返回所需的 WebApplicationContext 上下文实例

过滤器使用 SpringWEB 容器

  • 为便于读者理解 WebApplicationContextUtils 类的使用下面将通过一个过滤器的方式来实现 SpringWEB 容器的获取,实现类结构如图所示,由于 Filter 在 init()方法中才会提供有 FilterConfig 接口实例,所以 Bean 的获取就需要在初始化的方法中实现,为简化处理本次将在过滤器中获取 IMessageService 接口实例
1、
public static WebApplicationContext findWebApplicationContext(ServletContext sc) {
   WebApplicationContext wac = getWebApplicationContext(sc);
   if (wac == null) {
      Enumeration<String> attrNames = sc.getAttributeNames();
      while (attrNames.hasMoreElements()) {
         String attrName = attrNames.nextElement();
         Object attrValue = sc.getAttribute(attrName);
         if (attrValue instanceof WebApplicationContext) {
            if (wac != null) {
               throw new IllegalStateException("No unique WebApplicationContext found: more than one " + "DispatcherServlet registered with publishContext=true?");
            }
            wac = (WebApplicationContext) attrValue;
         }
      }
   }
   return wac;
}
public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
   return getWebApplicationContext(sc,
            WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
}
public static WebApplicationContext getWebApplicationContext(ServletContext sc,
            String attrName) {
   Assert.notNull(sc, "ServletContext must not be null");
   Object attr = sc.getAttribute(attrName);
   if (attr == null) {
      return null;
   }
   if (attr instanceof RuntimeException) {
      throw (RuntimeException) attr;
   }
   if (attr instanceof Error) {
      throw (Error) attr;
   }
   if (attr instanceof Exception) {
      throw new IllegalStateException((Exception) attr);
   }
   if (!(attr instanceof WebApplicationContext)) {
      throw new IllegalStateException("Context attribute is not of type WebApplicationContext: " + attr);
   }
   return (WebApplicationContext) attr;
}
String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE =
WebApplicationContext.class.getName() + ".ROOT";


2、
package com.yootk.service;

public interface IMessageService {
    public String echo(String msg); // 消息处理
}


3、
package com.yootk.service.impl;

import com.yootk.service.IMessageService;
import org.springframework.stereotype.Service;

@Service // Bean注册
public class MessageServiceImpl implements IMessageService {
    @Override
    public String echo(String msg) {
        return "【ECHO】" + msg;
    }
}


4、
package com.yootk.filter;

import com.yootk.service.IMessageService;
import jakarta.servlet.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.support.WebApplicationContextUtils;

import java.io.IOException;

public class YootkMessageFilter implements Filter {
    private static final Logger LOGGER = LoggerFactory.getLogger(YootkMessageFilter.class);
    // @Autowired // 写上此注解无效,因为不在Spring容器之中
    private IMessageService messageService; // 接口定义

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.messageService = WebApplicationContextUtils.findWebApplicationContext(
                filterConfig.getServletContext()).getBean(IMessageService.class);
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        LOGGER.info("【消息过滤】{}", this.messageService.echo("沐言科技:www.yootk.com"));
        chain.doFilter(request, response); // 请求转发
    }
}


5、
    @Override
    protected Filter[] getServletFilters() { // 配置过滤器
        CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
        encodingFilter.setEncoding("UTF-8"); // 编码设置
        encodingFilter.setForceEncoding(true); // 强制编码
        YootkMessageFilter messageFilter = new YootkMessageFilter(); // 自定义过滤器
        return new Filter[] {encodingFilter, messageFilter};
    }

6、
http://localhost/pages/message/echo?msg=www.yootk.com

DispatcherServlet 继承结构

DispatcherServlet 类

  • Servlet 程序类在定义时需要明确的继承 HttpServlet 父类,随后根据父类中的定义进行初始化(init()方法)以及服务处理(doService()方法)的调用.SpringMVC 之中所提供的 DispatcherServlet 类也是按照此种开发要求进行设置的,但是考虑到其内置结构与 Spring 容器之间的关联,所以并没有直接继承 HttpServlet 父类,而是提供了一系列的抽象类继承结构,以便于核心对象的配置

Servlet 初始化与服务

  • WEB 容器在启动时会默认调用 HttpServlet 类中的 init() 方法进行初始化处理,SpringMVC 将该初始化方法的具体操作实现,定义在 HttpServletBean 子类中。通过图所示的初始化流程,可以清楚的发现,最终实现容器具体初始化的实现类是由 FrameworkServlet 类提供的 initWebApplicationContext()方法完成的,而在该方法处理完成后也会像 Spring 上下文初始化那样进行刷新方法的调用。
1、
public class YootkDispatcherServlet extends DispatcherServlet { }
public class DispatcherServlet extends FrameworkServlet {}
public abstract class FrameworkServlet extends HttpServletBean
implements ApplicationContextAware{}
public abstract class HttpServletBean extends HttpServlet
implements EnvironmentCapable, EnvironmentAware {}
public abstract class HttpServlet extends GenericServlet {}
public abstract class GenericServlet implements Servlet, ServletConfig, java.io.Serializable {}
public interface Servlet {}


2、
@Nullable
private ConfigurableEnvironment environment;
private final Set<String> requiredProperties = new HashSet<>(4);
@Override
public final void init() throws ServletException {
   // Set bean properties from init parameters.
   PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); // 通过初始化参数配置Servlet的属性
   if (!pvs.isEmpty()) {
      try {
         BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
         ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
         bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));// 需要配置XML文件的路径
         initBeanWrapper(bw);
         bw.setPropertyValues(pvs, true);
      }
      catch (BeansException ex) {}
   }
   // Let subclasses do whatever initialization they like.
   initServletBean();  // 留给子类处理的
}
protected void initServletBean() throws ServletException {}


3、
@Nullable
private WebApplicationContext webApplicationContext;
protected final void initServletBean() throws ServletException {
   getServletContext().log("Initializing Spring " + getClass().getSimpleName() +
                " '" + getServletName() + "'");
   long startTime = System.currentTimeMillis();
   try {
      this.webApplicationContext = initWebApplicationContext();  // 获取WEB应用上下文
      initFrameworkServlet();// 初始化Servlet环境
   } catch (ServletException | RuntimeException ex) {
      logger.error("Context initialization failed", ex);
      throw ex;
   }
}
protected void initFrameworkServlet() throws ServletException {} // 此方法暂未实现
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 cwac && !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
      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) {
         onRefresh(wac);
      }
   }
   if (this.publishContext) {
      // Publish the context as a servlet context attribute.
      String attrName = getServletContextAttributeName();
      getServletContext().setAttribute(attrName, wac);
   }
   return wac;
}
protected void configureAndRefreshWebApplicationContext(
ConfigurableWebApplicationContext wac) {
   if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
      // The application context id is still set to its original default value
      // -> assign a more useful id based on available information
      if (this.contextId != null) {
         wac.setId(this.contextId); // 设置上下文的ID
      } else { // Generate default id...
         wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
               ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
      }
   }
   wac.setServletContext(getServletContext());// Web应用上下文配置ServletContext
   wac.setServletConfig(getServletConfig());// ServletConfig配置
   wac.setNamespace(getNamespace());// 命名空间的配置
   wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
   // The wac environment's #initPropertySources will be called in any case when the context
   // is refreshed; do it eagerly here to ensure servlet property sources are in place for
   // use in any post-processing or initialization that occurs below prior to #refresh
   ConfigurableEnvironment env = wac.getEnvironment();
   if (env instanceof ConfigurableWebEnvironment cwe) {
      cwe.initPropertySources(getServletContext(), getServletConfig());
   }
   postProcessWebApplicationContext(wac);
   applyInitializers(wac);
   wac.refresh();
}


4、
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 previousRequestPath = null;
   if (this.parseRequestPath) {
      previousRequestPath = (RequestPath) request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE);
      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 (this.parseRequestPath) {
         ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request);
      }
   }
}
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 = 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 = HttpMethod.GET.matches(method);
         if (isGet || HttpMethod.HEAD.matches(method)) {
            long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
            if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
               return;
            }
         }

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

         // Actually invoke the handler.
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

         if (asyncManager.isConcurrentHandlingStarted()) {
            return;
         }

         applyDefaultViewName(processedRequest, mv);
         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);
         }
      }
   }
}


5、
protected void service(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
   HttpMethod httpMethod = HttpMethod.valueOf(request.getMethod());
   if (HttpMethod.PATCH.equals(httpMethod)) {
      processRequest(request, response);
   } else {
      super.service(request, response);
   }
}

初始化 WEB 应用上下文

HandlerMapping 映射配置

环境初始化方法

  • 在 SpringMVC 之中考虑到了代码设计的灵活性,所有控制层的处理方法全部都使用了@RequestMapping 及其相关的注解进行了路径的配置,而在代码配置时,开发者只需要配置 action 程序类的扫描包即可实现配置路径的注册,而配置路径的解析处理则是由 DispatcherServlet 类中的 onRefresh()方法触发的

HandlerAdapter 控制层适配

HandlerAdapter 处理

  • 在使用 DispatcherServlet.initStrategies()进行初始化配置时,其内部会调用 DispatcherServlet,initHandlerAdapters()初始化项目之中的 HandlerAdapter 接囗实例,该接口的主要作用是,对控制层中方法的调用与返回结果进行统一处理

doService()请求分发

DispatcherServlet 服务处理

  • 在 SpringMVC 的程序开发中,所有的请求会统一发送到 DispatcherServet 类中,由于在实际的开发中不同的用户存在有不同的请求模式,所以 DispatcherServlet 直接覆写了 GenericServlet 父类中的 doService()方法,以实现所有不同式的请求接收。而在接收完成后,则会根据不同的访问路径进行请求的分发处理

doDispatch()请求处理

demo


上次编辑于: