springmvc源码解析
1、主要组件
springmvc包括以下组件,主要有以下作用
1、前端控制器(DispatcherServlet) 接收用户请求,发送响应
2、处理器映射器(HandlerMapping)根据请求的url来查找handler 如:
SimpleUrlHandlerMapping:基于手动配置 url 与control 映谢
RequestMappingHandlerMapping:基于@RequestMapping注解配置对应映谢
3、处理器适配器(HandlerAdapter)适配handle然后执行
SimpleControllerHandlerAdapter 对应实现controller接口的处理器
HttpRequestHandlerAdapter 对应实现HttpRequestHandler 接口的处理器
SimpleServletHandlerAdapter对应实现HttpServlet 接口的处理器
RequestMappingHandlerAdapter 对应@requestMapping注解的处理器
4、处理器(Handler)按照适配器的要求的规则去编写handler
Controller 接口:
HttpRequestHandler 接口:
HttpServlet 接口:
@RequestMapping方法注解
可以看出 Handler 没有统一的接口,这时候就需要处理器适配器适配,然后通过适配器指定handle
5、视图解析器(ViewResolver)
2、源码解析
2.1容器初始化
在没使用springboot的时候我们配置springmvc的时候,会在web.xml中配置如下内容
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:application-context.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
可以看到我们配置了ContextLoaderListener,他实现了ServletContextListener,在xml中配置了这个监听器,启动容器时,会自动执行实现的contextInitialized()方法,
在ServletContextListener中的核心逻辑便是初始化WebApplicationContext实例并存放至ServletContext中,这样我们只要得到Servlet就可以得到WebApplicationContext对象,并利用这个对象访问spring容器管理的bean。
public void contextInitialized(ServletContextEvent event) { initWebApplicationContext(event.getServletContext()); }
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) { throw new IllegalStateException( "Cannot initialize context because there is already a root application context present - " + "check whether you have multiple ContextLoader* definitions in your web.xml!"); } servletContext.log("Initializing Spring root WebApplicationContext"); Log logger = LogFactory.getLog(ContextLoader.class); if (logger.isInfoEnabled()) { logger.info("Root WebApplicationContext: initialization started"); } long startTime = System.currentTimeMillis(); try { // Store context in local instance variable, to guarantee that // it is available on ServletContext shutdown.
// 创建得到WebApplicationContext,也就是ioc容器 // createWebApplicationContext最后返回值被强制转换为ConfigurableWebApplicationContext类型
if (this.context == null) { this.context = createWebApplicationContext(servletContext); } if (this.context instanceof ConfigurableWebApplicationContext) {
//强制转换为ConfigurableWebApplicationContext ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
// cwac尚未被激活,目前还没有进行配置文件加载 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 -> // determine parent for root web application context, if any. ApplicationContext parent = loadParentContext(servletContext); cwac.setParent(parent); }
//加载配置文件,也就是上面配置的applicationContext.xml文件 configureAndRefreshWebApplicationContext(cwac, servletContext); } }
//将ioc设置为自己的父容器,这样的话子容器可以访问父容器,但是父容器(ioc容器)不能访问子容器(DispatcherServlet) servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); ClassLoader ccl = Thread.currentThread().getContextClassLoader(); if (ccl == ContextLoader.class.getClassLoader()) { currentContext = this.context; } else if (ccl != null) { currentContextPerThread.put(ccl, this.context); } if (logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; logger.info("Root WebApplicationContext initialized in " + elapsedTime + " ms"); } return this.context; } catch (RuntimeException | Error ex) { logger.error("Context initialization failed", ex); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex); throw ex; }
然后开始初始化DispatcherServlet,DispatcherServlet实现了Servlet接口,
public interface Servlet { /** * 容器启动时被调用(当load-on-startup为负数或者不设置时,会在第一次被使用时才调用),只会调用一次 * 它有一个参数ServletConfig,是容器传进来的,表示的是这个Servlet的一些配置,比如DispatcherServlet配置的<init-param> */ public void init(ServletConfig config) throws ServletException; /** * 获取Servlet的配置 */ public ServletConfig getServletConfig(); /** * 最重要的一个方法,是具体处理请求的地方 */ public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException; /** * 获取Servlet的一些信息,比如作者、版本、版权等,需要子类实现 */ public String getServletInfo(); /** * 用于Servlet销毁(主要指关闭容器)时释放一些资源,只会调用一次 */ public void destroy(); }
init方法开始初始化容器
public final void init() throws ServletException { if (logger.isDebugEnabled()) { logger.debug("Initializing servlet '" + getServletName() + "'"); } // 获取Servlet的初始化参数,对Bean属性进行配置 try { PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); initBeanWrapper(bw); bw.setPropertyValues(pvs, true); } catch (BeansException ex) { logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); throw ex; } // 调用子类的initServletBean方法进行具体的初始化工作 initServletBean(); if (logger.isDebugEnabled()) { logger.debug("Servlet '" + getServletName() + "' configured successfully"); } } // initServletBean这个初始化工作位于FrameworkServlet类中 protected final void initServletBean() throws ServletException { getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'"); if (this.logger.isInfoEnabled()) { this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started"); } long startTime = System.currentTimeMillis(); // 这里初始化上下文 try { this.webApplicationContext = initWebApplicationContext(); initFrameworkServlet(); } catch (ServletException ex) { this.logger.error("Context initialization failed", ex); throw ex; } catch (RuntimeException ex) { this.logger.error("Context initialization failed", ex); throw ex; } if (this.logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " + elapsedTime + " ms"); } } protected WebApplicationContext initWebApplicationContext() { // 在这里获取父容器,那什么时候放进去的呢,也就是上面的ContextLoaderListener时放的 WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) { wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { if (cwac.getParent() == null) { cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { wac = findWebApplicationContext(); } if (wac == null) { wac = createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { //DispatcherServlet作为spring的bean,将SpringMVC容器注入,这里我们直接拿来作为SpringMVC容器 onRefresh(wac); } // 把SpringMVC容器注册到ServletContext中去,根据context id去获取 if (this.publishContext) { String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); if (this.logger.isDebugEnabled()) { this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() + "' as ServletContext attribute with name [" + attrName + "]"); } } return wac; } protected void onRefresh(ApplicationContext context) { initStrategies(context); } protected void initStrategies(ApplicationContext context) { //这里就到了初始化DispatcherServlet //实现文件上传功能 initMultipartResolver(context); //初始化本地解析器 initLocaleResolver(context); //初始化主题解析器 initThemeResolver(context); //初始化处理器映射器,将请求映射到处理器 initHandlerMappings(context); //初始化处理器适配器 initHandlerAdapters(context); //初始化处理器异常解析器,如果执行过程中遇到异常将交给HandlerExceptionResolver来解析 initHandlerExceptionResolvers(context); //初始化请求到具体视图名称解析器 initRequestToViewNameTranslator(context); //初始化视图解析器,通过ViewResolver解析逻辑视图名到具体视图实现 initViewResolvers(context); //初始化flash映射管理 initFlashMapManager(context); }
2.2处理请求
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 = "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. 处理器方法执行,包括参数处理和返回值处理 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) { //调用拦截器的afterCompletion方法 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); } } } }
3、拦截器
public interface HandlerInterceptor { /** *预处理回调方法,实现处理器的预处理(如检查登陆),第三个参数为响应的处理器,自定义Controller
* 返回值:true表示继续流程(如调用下一个拦截器或处理器);
* false表示流程中断(如登录检查失败),不会继续调用其他的拦截器或处理器,此时我们需要通过response来产生响应
*/
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return true; } /** * 后处理回调方法,实现处理器的后处理(但在渲染视图之前),此时我们可以通过modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理,modelAndView也可能为null。*/ default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { } /** * 整个请求处理完毕回调方法,即在视图渲染完毕时回调,
* 但仅调用处理器执行链中preHandle返回true的拦截器的afterCompletion。
*/ default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { } }
可以一些权限验证或者日志输出,请求耗时等等
当然filter也可以实现这些,
4、参数处理器
4.1HandlerMethodArgumentResolver 自定义参数处理器
默认处理器:RequestParamMethodArgumentResolver
public interface HandlerMethodArgumentResolver { /** * 所有方法中的参数都需要调用此方法判断此解析器是否支持此参数 */ boolean supportsParameter(MethodParameter parameter); /** * 将方法参数解析为来自给定请求的参数值 */ @Nullable Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception; }
这时候自定义的处理器可能会不生效,因为HandlerMethodArgumentResolverComposite保存了HandlerMethodArgumentResolver的数组对象,一旦supportsParameter返回true就会使用这个参数处理器,所以可能需要手动改变HandlerMethodArgumentResolverComposite
里保存的Resolver数组的顺序,将自定义的放在第一位。
4.2、HandlerMethodReturnValueHandler 返回值处理器
public interface HandlerMethodReturnValueHandler { /** * 支持哪类类型的返回值将将使用该返回值处理器 */ boolean supportsReturnType(MethodParameter returnType); /** * 主要处理返回值的处理逻辑,并且将处理好的值返回给model */ void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception; }
HandlerMethodReturnValueHandler 和上面一样,也是保存在HandlerMethodReturnValueHandlerComposite类的数组中,也需要注意下顺序
这种可以运用在比如系统同一封装返回json的格式类型
4.3 HandlerExceptionResolver 异常处理
public interface HandlerExceptionResolver { /** * 对请求返回的异常同一处理 */ @Nullable ModelAndView resolveException( HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex); }
在Springboot中可以直接用全局异常处理器(@ControllerAdvice结合@ExceptionHandler(Exception.class))
5、springmvc父子容器
上面有简短的介绍,这里总结下
spring总的上下文容器有父子之分,父容器和子容器。 ** 父容器对子容器可见,子容器对父容器不可见 ** 。
对于传统的spring mvc来说,spring mvc容器为子容器,也就是说ServletDispatcher对应的容器为子容器,而web.xml中通过ConextLoaderListener的contextConfigLocation属性配置的为父容器。
也就是@controller是有springmvc加载的子容器,@Service等等其他的注解是有父容器加载的,所以可能导致修饰@Controller的事务不可用,因为他们不由spring管理
不过现在springboot没有这种问题,不需要使用tomact容器,使用内嵌的tomact,是一个容器。
6、流程图
网上找了个流程图,画的还不错 如下: