看透 Spring MVC 源代码分析与实践 —— Spring MVC 组件分析

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> 看透 Spring MVC 源代码分析与实践 —— Spring MVC 组件分析

组件概览

HandlerMapping

根据 request 找到对应的处理器 Handler 和 Interceptors。内部只有一个方法

1
HandlerExecutionChain  getHandler(HttpServletRequest  request)  throws  Exception;

HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

HandlerAdapter

Handler 适配器,内部方法如下:

12345
boolean supports(Object handler);//判断是否可以使用某个 Handler ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; //具体使用 long getLastModified(HttpServletRequest request, Object handler);//获取资源上一次修改的时间

boolean supports(Object handler);//判断是否可以使用某个 Handler

ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; //具体使用

long getLastModified(HttpServletRequest request, Object handler);//获取资源上一次修改的时间

HandlerExceptionResolver

根据异常设置 ModelAndView ,再交给 render 方法进行渲染。

123
ModelAndView resolveException(            HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex)

ModelAndView resolveException(

           HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex)

ViewResolver

用来将 String 类型的视图名和 Locale 解析为 View 类型的视图。

1
View resolveViewName(String viewName, Locale locale) throws Exception;

View resolveViewName(String viewName, Locale locale) throws Exception;

它的一个实现类 BeanNameViewResolver,它重写 resolveViewName 方法如下:

123456789101112131415161718192021222324252627282930313233343536373839404142434445
public View resolveViewName(String viewName, Locale locale) throws BeansException {        ApplicationContext context = getApplicationContext();        //如果应用上下文没有找到视图,返回 null        if (!context.containsBean(viewName)) {            if (logger.isDebugEnabled()) {                logger.debug("No matching bean found for view name '" + viewName + "'");            }            // Allow for ViewResolver chaining...            return null;        }        //如果找到的视图类型不匹配,也返回 null        if (!context.isTypeMatch(viewName, View.class)) {            if (logger.isDebugEnabled()) {                logger.debug("Found matching bean for view name '" + viewName +                        "' - to be ignored since it does not implement View");            }            // Since we're looking into the general ApplicationContext here,            // let's accept this as a non-match and allow for chaining as well...            return null;        }        //根据视图名称从 Spring 容器中查找 Bean,返回找到的 bean        return context.getBean(viewName, View.class);    }

public View resolveViewName(String viewName, Locale locale) throws BeansException {

       ApplicationContext context = getApplicationContext();

       //如果应用上下文没有找到视图,返回 null

       if (!context.containsBean(viewName)) {

           if (logger.isDebugEnabled()) {

               logger.debug(“No matching bean found for view name ‘“ + viewName + “‘“);

           }

           // Allow for ViewResolver chaining…

           return null;

       }

       //如果找到的视图类型不匹配,也返回 null

       if (!context.isTypeMatch(viewName, View.class)) {

           if (logger.isDebugEnabled()) {

               logger.debug(“Found matching bean for view name ‘“ + viewName +

                       “‘ - to be ignored since it does not implement View”);

           }

           // Since we’re looking into the general ApplicationContext here,

           // let’s accept this as a non-match and allow for chaining as well…

           return null;

       }

       //根据视图名称从 Spring 容器中查找 Bean,返回找到的 bean

       return context.getBean(viewName, View.class);

   }

RequestToViewNameTranslator

获取 request 中的视图名。接口里面也是只有一个方法:

123
String getViewName(HttpServletRequest request) throws Exception; //根据 request 查找视图名

String getViewName(HttpServletRequest request) throws Exception;

//根据 request 查找视图名

LocaleResolver

用于从 request 解析出 Locale。

123456789
public interface LocaleResolver {    //从 request 解析出 Locale    Locale resolveLocale(HttpServletRequest request);     //根据 request 设置  locale    void setLocale(HttpServletRequest request, HttpServletResponse response,        @Nullable    Locale locale);}

public interface LocaleResolver {
//从 request 解析出 Locale
Locale resolveLocale(HttpServletRequest request);


//根据 request 设置  locale
void setLocale(HttpServletRequest request, HttpServletResponse response,
    @Nullable
Locale locale);

}

ThemeResolver

解析主题

12345678
public interface ThemeResolver {    //通过给定的 request 查找主题名    String resolveThemeName(HttpServletRequest request);     //根据给定的 request 设置主题名    void setThemeName(HttpServletRequest request, HttpServletResponse response,        String themeName);}

public interface ThemeResolver {
//通过给定的 request 查找主题名
String resolveThemeName(HttpServletRequest request);


//根据给定的 request 设置主题名
void setThemeName(HttpServletRequest request, HttpServletResponse response,
    String themeName);

}

在 RequestContext.java 文件中可以获取主题:

1234567891011121314151617181920212223242526272829303132
public String getThemeMessage(String code, String defaultMessage) {        //获取主题的信息        return getTheme().getMessageSource().getMessage(code, null, defaultMessage, this.locale);    }  public Theme getTheme() {        //判断主题是否为空        if (this.theme == null) {            // 通过 RequestContextUtils 获取 request 中的主题名            this.theme = RequestContextUtils.getTheme(this.request);            if (this.theme == null) {   //如果还是为空的话                //那就是没有有效的主题解析器和主题                this.theme = getFallbackTheme();            }        }        return this.theme;    }

public String getThemeMessage(String code, String defaultMessage) {

       //获取主题的信息

       return getTheme().getMessageSource().getMessage(code, null, defaultMessage, this.locale);

   }

public Theme getTheme() {

       //判断主题是否为空

       if (this.theme == null) {

           // 通过 RequestContextUtils 获取 request 中的主题名

           this.theme = RequestContextUtils.getTheme(this.request);

           if (this.theme == null) {   //如果还是为空的话

               //那就是没有有效的主题解析器和主题

               this.theme = getFallbackTheme();

           }

       }

       return this.theme;

   }

RequestContextUtils.getTheme() 方法:

123456789101112131415161718192021
public static Theme getTheme(HttpServletRequest request) {        ThemeResolver themeResolver = getThemeResolver(request);        ThemeSource themeSource = getThemeSource(request);        if (themeResolver != null && themeSource != null) {            String themeName = themeResolver.resolveThemeName(request);            return themeSource.getTheme(themeName);        }        else {            return null;        }    }

public static Theme getTheme(HttpServletRequest request) {

       ThemeResolver themeResolver = getThemeResolver(request);

       ThemeSource themeSource = getThemeSource(request);

       if (themeResolver != null && themeSource != null) {

           String themeName = themeResolver.resolveThemeName(request);

           return themeSource.getTheme(themeName);

       }

       else {

           return null;

       }

   }

MultipartResolver

用于处理上传请求,处理方法:将普通的 request 包装成 MultipartHttpServletRequest

1234567891011
public interface MultipartResolver {    //根据 request 判断是否是上传请求    boolean isMultipart(HttpServletRequest request);     //将 request 包装成 MultipartHttpServletRequest    MultipartHttpServletRequest resolveMultipart(HttpServletRequest request)        throws MultipartException;     //清理上传过程中产生的临时资源    void cleanupMultipart(MultipartHttpServletRequest request);}

public interface MultipartResolver {
//根据 request 判断是否是上传请求
boolean isMultipart(HttpServletRequest request);


//将 request 包装成 MultipartHttpServletRequest
MultipartHttpServletRequest resolveMultipart(HttpServletRequest request)
    throws MultipartException;

//清理上传过程中产生的临时资源
void cleanupMultipart(MultipartHttpServletRequest request);

}

FlashMapManager

FlashMap 主要在 redirect 中传递参数,FlashMapManager 用来管理 FlashMap 的。

12345678910
public interface FlashMapManager {    //恢复参数,并将恢复过的和超时的参数从保存介质中删除    @Nullable    FlashMap retrieveAndUpdate(HttpServletRequest request,        HttpServletResponse response);     //将参数保存起来    void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request,        HttpServletResponse response);}

public interface FlashMapManager {
//恢复参数,并将恢复过的和超时的参数从保存介质中删除
@Nullable
FlashMap retrieveAndUpdate(HttpServletRequest request,
HttpServletResponse response);


//将参数保存起来
void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request,
    HttpServletResponse response);

}

小结

介绍 Spring MVC 中九大组件的接口、作用、内部方法实现及作用进行了简单的介绍,详细的还需大家自己去看源码。

 

总结

Spring MVC 原理总结

本质是一个 Servlet,这个 Servlet 继承自 HttpServlet。Spring MVC 中提供了三个层次的 Servlet:HttpServletBean、FrameworkServlet 和 DispatcherServlet。他们相互继承, HttpServletBean 直接继承自 Java 的 HttpServlet。HttpServletBean 用于将 Servlet 中的 Servlet 中配置的参数设置到相应的属性中,FrameworkServlet 初始化了 Spring MVC 中所使用的 WebApplicationContext,具体处理请求的 9 大组件是在 DispatcherServlet 中初始化的,整个继承图如下:

看透 Spring MVC 源代码分析与实践 —— Spring MVC 组件分析

相关文章

看透 Spring MVC 源代码分析与实践 —— Spring MVC 组件分析

 

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> 看透 Spring MVC 源代码分析与实践 —— Spring MVC 组件分析