那些年背过的题:Spring MVC设计与实现

518 阅读10分钟

Spring MVC(Model-View-Controller)是一个用于构建基于Web应用程序的框架,它是Spring框架的一部分。以下是Spring MVC的设计与实现的基本概述:

Spring MVC采用经典的MVC架构:

  1. Model(模型):  负责应用程序的数据逻辑,通常对应数据库中的表。
  2. View(视图):  展示数据的界面部分,通常是HTML或JSP页面。
  3. Controller(控制器):  处理用户请求,并将模型数据传递给视图。

基本组件

  1. DispatcherServlet:  核心控制器,负责将请求分发到相应的处理器。
  2. HandlerMapping:  根据请求URL找到对应的处理器(Controller)。
  3. Controller:  具体处理请求的类,通常使用@Controller注解。
  4. ViewResolver:  解析逻辑视图名并映射到具体的视图实现,如JSP、Thymeleaf等。

Spring MVC执行流程的详细步骤

1. 用户发送请求

用户通过浏览器发送一个HTTP请求到服务器。这通常是通过点击链接、提交表单等操作触发的。

2. DispatcherServlet接收请求

所有的请求首先都会到达DispatcherServlet。它是前端控制器(Front Controller),负责整个请求处理流程的协调。

3. HandlerMapping映射请求

DispatcherServlet通过HandlerMapping确定哪个控制器(Controller)将处理该请求。HandlerMapping根据请求URL找到相应的处理器。

4. 调用处理器方法

找到合适的处理器后,DispatcherServlet调用该处理器,处理请求。处理器通常是一个带有@Controller注解的类,具体的方法使用@RequestMapping注解来匹配请求路径。

5. 处理请求逻辑

控制器方法处理业务逻辑,可能需要与服务层或数据库进行交互,并准备数据模型返回给视图。

6. 返回ModelAndView

控制器方法返回一个ModelAndView对象,它包含了视图名和模型数据。

7. ViewResolver解析视图

DispatcherServlet使用ViewResolver将逻辑视图名解析为实际的视图实现(如JSP页面)。

8. 渲染视图

DispatcherServlet将模型数据传递给视图对象,视图对象进行渲染,将最终生成的HTML返回给客户端。

9. 响应返回给客户端

生成的HTML作为HTTP响应被发送回客户端,用户在浏览器中看到处理后的结果。

流程图

以下是流程的简化图示:

用户请求
   ↓
DispatcherServlet(前端控制器)
   ↓
HandlerMapping(确定处理器)
   ↓
Controller(控制器方法处理请求)
   ↓
Service/DAO(业务逻辑和数据访问)
   ↓
ModelAndView(返回模型和视图)
   ↓
ViewResolver(解析视图)
   ↓
View(渲染视图)
   ↓
响应返回给客户端

源码分析

DispatcherServlet源码分析

DispatcherServlet 是 Spring MVC 框架的核心组件,负责将 HTTP 请求分发给适当的处理程序,并协调各个组件以完成请求的处理。以下是对 DispatcherServlet 源码的详细分析和解释。

DispatcherServlet 类的定义与初始化

Spring MVC 中的 DispatcherServlet 继承自 FrameworkServlet,并实现了 HttpServlet 接口中的主要方法,是一个标准的 Servlet。

public class DispatcherServlet extends FrameworkServlet {
    //...
}

1. 初始化策略(initStrategies)

FrameworkServletinitServletBean() 方法中会调用 DispatcherServletinitStrategies() 方法来初始化各种策略对象。这些策略对象包括多部分解析器、区域解析器、主题解析器、处理器映射、处理器适配器、视图解析器等。

@Override
protected void initStrategies(ApplicationContext context) {
    initMultipartResolver(context); //初始化文件上传解析器
    initLocaleResolver(context); //初始化区域解析器
    initThemeResolver(context); //初始化主题解析器
    initHandlerMappings(context); //初始化处理器映射,将 URL 映射到具体的处理器(控制器)上。
    initHandlerAdapters(context); //初始化处理器适配器,用于将请求传递给合适的处理器执行。
    initHandlerExceptionResolvers(context); //初始化异常处理器,用于处理处理器执行过程中抛出的异常。
    initRequestToViewNameTranslator(context);  //初始化默认视图名称转换器 
    initViewResolvers(context);   //初始化视图解析器
    initFlashMapManager(context);  //初始化闪存映射管理器
}

每个策略对象的初始化方法都从 Spring 应用上下文中获取相应的 Bean,如果没有配置则使用默认策略。例如:

private void initHandlerMappings(ApplicationContext context) {
    this.handlerMappings = null;
    if (this.detectAllHandlerMappings) {
        // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
        Map<String, HandlerMapping> matchingBeans =
                BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
        if (!matchingBeans.isEmpty()) {
            this.handlerMappings = new ArrayList<>(matchingBeans.values());
            // We keep HandlerMappings in sorted order.
            AnnotationAwareOrderComparator.sort(this.handlerMappings);
        }
    } else {
        try {
            HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
            this.handlerMappings = Collections.singletonList(hm);
        } catch (NoSuchBeanDefinitionException ex) {
            // Ignore, we'll add a default HandlerMapping later.
        }
    }

    // Ensure we have at least one HandlerMapping, by registering
    // a default HandlerMapping if no other mappings are found.
    if (this.handlerMappings == null) {
        this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
        if (logger.isDebugEnabled()) {
            logger.debug("No HandlerMappings found in servlet '" + getServletName() +
                    "': using default");
        }
    }
}

请求处理流程

DispatcherServlet 接收到一个 HTTP 请求时,会执行 doDispatch 方法进行请求处理。以下是该方法的简化版本及其关键步骤分析:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    ModelAndView mv = null;
    Exception dispatchException = null;

    try {
        // 1. 检查是否为文件上传请求,并包装请求对象
        processedRequest = checkMultipart(request);

        // 2. 获取处理请求的处理器
        mappedHandler = getHandler(processedRequest);
        if (mappedHandler == null) {
            noHandlerFound(processedRequest, response);
            return;
        }

        // 3. 根据处理器类型获取对应的适配器
        HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

        // 4. 实际调用处理器方法处理请求,返回ModelAndView对象
        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

        // 5. 处理请求结果
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    catch (Exception ex) {
        dispatchException = ex;
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    catch (Throwable err) {
        dispatchException = new NestedServletException("Handler dispatch failed", err);
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
}

2. 获取处理器(getHandler)

getHandler 方法遍历已注册的 HandlerMapping,根据请求 URL 找到相应的处理器(控制器)。

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    for (HandlerMapping hm : this.handlerMappings) {
        HandlerExecutionChain handler = hm.getHandler(request);
        if (handler != null) {
            return handler;
        }
    }
    return null;
}

3. 调用处理器适配器(getHandlerAdapter)

getHandlerAdapter 方法返回可以处理该处理器的适配器。Spring MVC 内置了多种处理器适配器,如 SimpleControllerHandlerAdapterRequestMappingHandlerAdapter 等。

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    for (HandlerAdapter ha : this.handlerAdapters) {
        if (ha.supports(handler)) {
            return ha;
        }
    }
    throw new ServletException("No adapter for handler [" + handler +
                "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

4. 处理请求并返回模型和视图(HandlerAdapter.handle)

处理器适配器调用实际的处理器方法,并返回一个 ModelAndView 对象,其中包含了视图名和模型数据。

public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    // ... 具体实现由不同的HandlerAdapter定义
}

例如,对于 RequestMappingHandlerAdapter,其 handle 方法可能会查找并调用带有 @RequestMapping 注解的方法。

5. 处理结果(processDispatchResult)

processDispatchResult 方法根据处理后的结果来渲染视图或者处理异常。

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
                                   HandlerExecutionChain mappedHandler, ModelAndView mv, 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.isDebugEnabled()) {
            logger.debug("No view rendering, no ModelAndView: Dispatching Result.");
        }
    }
}

6. 渲染视图(render)

如果处理器返回了一个 ModelAndView,则 render 方法负责将其渲染为最终的响应。

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
    View view;
    String viewName = mv.getViewName();
    if (viewName != null) {
        view = resolveViewName(viewName, mv.getModelInternal(), request); 
        if (view == null) {
            throw new ServletException("Could not resolve view with name '" + viewName + "' in servlet with name '" + getServletName() + "'");
        }
    }
    else {
        view = mv.getView();
        if (view == null) {
            throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a View object in servlet with name '" + getServletName() + "'");
        }
    }
    try {
        view.render(mv.getModelInternal(), request, response);
    }
    catch (Exception ex) {
        if (logger.isDebugEnabled()) {
            logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'", ex);
        }
        throw ex;
    }
}

DispatcherServlet 类的职责总结

通过分析 DispatcherServlet 源码,可以看到它主要承担了以下职责:

  1. 初始化策略对象:在启动时加载和配置各种策略对象,如处理器映射、处理器适配器、视图解析器等。
  2. 分发请求:接收 HTTP 请求,将请求分发给相应的处理器。
  3. 调用处理器:使用适当的处理器适配器调用处理器(控制器)方法来处理请求。
  4. 处理返回结果:根据控制器返回的 ModelAndView 对象渲染视图并返回给客户端。
  5. 异常处理:在请求处理过程中捕获并处理任何异常。

HandlerMapping源码分析

HandlerMapping 负责将 HTTP 请求映射到相应的处理器(控制器)上。Spring 提供了多种 HandlerMapping 的实现,其中最常用的是 RequestMappingHandlerMapping。下面我们详细分析 HandlerMapping 接口及其常见实现类的源码。

HandlerMapping 接口

HandlerMapping 是一个顶层接口,定义了一个方法:getHandler

public interface HandlerMapping {
    // 用于解析请求并返回处理器执行链
    @Nullable
    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

该方法接收一个 HttpServletRequest 对象,并返回一个 HandlerExecutionChain,其中包含要处理请求的处理器和相应的拦截器。

AbstractHandlerMapping 抽象类

AbstractHandlerMappingHandlerMapping 的抽象实现类,实现了一些通用功能,如拦截器管理等。

public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered {

    @Nullable
    private Object defaultHandler;

    private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>();

    @Nullable
    private final PathMatcher pathMatcher;
    
    // 获取处理器并构建处理器执行链
    @Override
    @Nullable
    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        Object handler = getHandlerInternal(request);
        if (handler == null) {
            handler = this.defaultHandler;
        }
        if (handler == null) {
            return null;
        }
        // Build the HandlerExecutionChain with interceptors.
        HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
        return executionChain;
    }

    // 子类必须实现的方法,用于查找具体的处理器
    @Nullable
    protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;

    // 构建处理器执行链,包括全局和特定处理器的拦截器
    protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
        HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
                (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
        
        chain.addInterceptors(this.adaptedInterceptors.toArray());
        return chain;
    }
}

RequestMappingHandlerMapping 类

RequestMappingHandlerMapping 是最常用的 HandlerMapping 实现类,它通过注解(如 @RequestMapping 等)来映射请求到处理器方法上。

1. 初始化请求映射

RequestMappingHandlerMapping 在初始化时会扫描所有带有请求映射注解的控制器方法,并将这些映射关系存储起来,通常是在 afterPropertiesSet() 方法中调用 initHandlerMethods() 完成。

@Override
public void afterPropertiesSet() {
    super.afterPropertiesSet();
    initHandlerMethods();
}

protected void initHandlerMethods() {
    // 扫描所有控制器类的方法,建立请求路径与方法的映射关系
    for (String beanName : getCandidateBeanNames()) {
        if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
            processCandidateBean(beanName);
        }
    }
}

2. 查找处理器(getHandlerInternal)

当有请求到来时,RequestMappingHandlerMapping 会根据请求路径找到相应的处理器方法。

@Override
@Nullable
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    String lookupPath = getUrlPathHelper().resolveAndCacheLookupPath(request);
    this.mappingRegistry.acquireReadLock();
    try {
        HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
        return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
    } finally {
        this.mappingRegistry.releaseReadLock();
    }
}

// 查找具体的处理器方法
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
    // 从缓存中获取匹配的HandlerMethod
    List<Match> matches = new ArrayList<>();
    this.mappingRegistry.getMappingsByUrl(lookupPath).forEach((mapping) -> addMatchingMappings(mapping, matches, request));
    if (!matches.isEmpty()) {
        Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
        matches.sort(comparator);
        Match bestMatch = matches.get(0);
        return bestMatch.getHandlerMethod();
    }
    return null;
}

lookupHandlerMethod 方法中,通过 mappingRegistry.getMappingsByUrl(lookupPath) 获取与请求路径匹配的映射方法,并通过 addMatchingMappings 方法进一步筛选符合条件的 HandlerMethod

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
    List<Match> matches = new ArrayList<>();
    this.mappingRegistry.getMappingsByUrl(lookupPath)
        .forEach((mapping) -> addMatchingMappings(mapping, matches, request));
    if (!matches.isEmpty()) {
        Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
        matches.sort(comparator);
        Match bestMatch = matches.get(0);
        return bestMatch.getHandlerMethod();
    }
    return null;
}

getMappingsByUrl 方法会返回一个集合,其中包含了所有与给定 lookupPath 匹配的请求映射。

public List<T> getMappingsByUrl(String urlPath) {
    List<T> mappings = this.urlLookup.get(urlPath);
    return (mappings != null ? mappings : Collections.emptyList());
}

接下来看 addMatchingMappings 方法,它会检查每个映射是否与当前请求匹配,如果匹配则将其添加到候选列表中:

private void addMatchingMappings(T mapping, List<Match> matches, HttpServletRequest request) {
    if (getMatchingCondition(mapping, request) != null) {
        matches.add(new Match(mapping, getHandlerMethods().get(mapping)));
    }
}

@Nullable
protected abstract C getMatchingCondition(T mapping, HttpServletRequest request);

getMatchingCondition 方法在不同的子类中有不同的实现,用于根据具体的匹配条件(如请求方法、参数等)来判断是否匹配当前请求。

3. 建立处理器执行链(getHandlerExecutionChain)

找到处理器方法后,会调用 getHandlerExecutionChain 方法建立处理器执行链,这包括全局和特定处理器的拦截器。

@Override
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
    HandlerExecutionChain chain = super.getHandlerExecutionChain(handler, request);
    if (CorsUtils.isCorsRequest(request)) {
        CorsConfiguration config = getCorsConfiguration(handler, request);
        if (config != null) {
            CorsInterceptor interceptor = new CorsInterceptor(config);
            chain.addInterceptor(0, interceptor);
        }
    }
    return chain;
}

HandlerExecutionChain 包含实际的处理器对象和一系列的拦截器:

public class HandlerExecutionChain {

    private final Object handler;

    private HandlerInterceptor[] interceptors;

    // 添加拦截器
    public void addInterceptor(HandlerInterceptor interceptor) {
        this.interceptors = append(this.interceptors, interceptor);
    }

    // 添加多个拦截器
    public void addInterceptors(HandlerInterceptor... interceptors) {
        for (HandlerInterceptor interceptor : interceptors) {
            addInterceptor(interceptor);
        }
    }
}

总结

  1. HandlerMapping 接口定义了一个核心方法 getHandler(HttpServletRequest request),用于根据请求查找处理器。
  2. AbstractHandlerMapping 实现了一些通用功能,如管理拦截器、默认处理器等,同时提供模板方法 getHandlerInternal(HttpServletRequest request) 留给子类实现。
  3. RequestMappingHandlerMapping 是最常用的实现类,通过注解扫描初始化请求映射关系,并在请求到来时根据路径查找相应的处理器方法。
  4. 处理器查找过程 涉及从缓存中获取匹配的 HandlerMethod,并筛选出最合适的处理器方法。
  5. 处理器执行链 包含实际的处理器对象和一系列的拦截器,在处理请求前后对其进行拦截和处理。