依赖组件spring-boot-starter-web和servlet3.0等;
DispatchServlet注入容器和服务器
了解servlet或者filter是怎么注入spring容器的,最典型例子莫过于DispatchServlet;这里并不说明传统的web.xml配置的方式,而主要是springboot推荐的自动配置,具体在DispatcherServletAutoConfiguration类;
@AutoConfigureAfter(EmbeddedServletContainerAutoConfiguration.class)主要是servlet容器的配置,由项目的classpath决定用tomcat、jetty或者undetow等;@ConditionalOnClass(DispatcherServlet.class)主要是要求DispatchServlet必须存在才能将继续解析这个Bean,即加入了spring-boot-starter-web依赖;
以上只是DispatcherServletAutoConfiguration自动配置的先决条件,接下来是代码内部一些Bean的注入;
以上代码包含了spring mvc需要的核心DispatcherServlet,他会随着tomcat启动注入到ServletContext;webMvcProperties就是我们在application.yml里面配置的参数;
以上代码是对DispatcherServlet的包装;由于一个web application包括很多servlet和filter,容器需要通过查找符合类型的Bean,并提取servlet注入到ServletContext,其中就包括了ServletRegistrationBean和实现了Servlet接口的Bean; ServletRegistrationBean包括了传统web.xml需要配置的参数;
类似还有FilterRegistrationBean、ServletListenerRegistrationBean和DelegatingFilterProxyRegistrationBean等;
除了reactive,springboot 容器主要是 AnnotationConfigServletWebServerApplicationContext类,而在容器refresh过程中,当BeanDefination注入spring容器后会调用子类的onRefresh方法,实现web server的创建和servlet的注入;
以上代码是AbstractApplicationContext子类ServletWebServerApplicationContext.onRefresh方法启动服务器的过程;
ServletWebServerFactory factory = getWebServerFactory()主要是根据环境依赖产生提供tomcat server或者其它server的工厂;
getSelfInitializer方法用于取得spring容器当中实现ServletContextInitializer接口的Bean;
以上代码显示了服务器创建以后,将spring上下文保存在ServletContext形成web特性的容器;调用getServletContextInitializerBeans方法创建ServletContextInitializerBeans;ServletContextInitializerBeans初始化时调用addServletContextInitializerBeans方法将RegistrationBean的子类如ServletRegistrationBean等(DispatchServlet的包装类)加入到ServletContextInitializer队伍;调用addAdaptableBeans方法将普通的servlet和filter等也通过RegistrationBean包装加入到需要onStartup的队伍;代码如下;
接着调用ServletContextInitializer实现类的onStartup方法,注册servlet和filter等;由于ServletRegistrationBean既包含了需要注册servlet实体又实现了ServletContextInitializer接口,它只需要传入ServletContext就可以注册自身;
WebMvcAutoConfiguration的自动装配
DispatcherServlet接受请求,需要将路径和对应的Controller映射,然后返回资源给客户端; WebMvcAutoConfiguration的作用正是装配DispatcherServlet需要的Bean,它包括了:EnableWebMvcConfiguration、WebMvcAutoConfigurationAdapter等内部类;
EnableWebMvcConfiguration主要是装配RequestMappingHandlerMapping、RequestMappingHandlerAdapter、ExceptionHandlerExceptionResolver;
RequestMappingHandlerMapping的作用是让DispatcherServlet调用HandlerMapping.getHandler根据请求解析出Controller对应的HandlerMethod和Interceptor;
RequestMappingHandlerMapping的父类AbstractHandlerMethodMapping实现了接口InitializingBean,在触发afterPropertiesSet的时候会调用initHandlerMethods方法解析具有Controller或者RequestMapping注解的Bean,利用反射拾取其中包含RequestMapping注解的方法,将它们注册到MappingRegistry变量,形成url和HandlerMethod的映射;
RequestMappingHandlerMapping的父类AbstractHandlerMapping实现了接口HandlerMapping,方法getHandler根据HttpServletRequest请求解析出对应的Interceptor和HandlerMethod,将其封装在HandlerExecutionChain,而HandlerMethod则封装了需要执行的目标对象的方法和方法参数等信息;
RequestMappingHandlerAdapter的主要作用是让DispatcherServlet调用HandlerAdapter.handle处理HandlerMethod的调用和返回ModelAndView;
RequestMappingHandlerAdapter实现了InitializingBean接口,在afterPropertiesSet方法触发的时候会执行: 1、调用initControllerAdviceCache, 拾取具有ControllerAdvice的注解的Bean放入局部变量;2、设置各种HandlerMethodArgumentResolver到局部变量,它们用于将请求参数转变成为执行HandlerMethod即Controller方法需要的MethodParameter参数;
RequestMappingHandlerAdapter的父类AbstractHandlerMethodAdapter实现了接口HandlerAdapter,接口方法handle会调用子类的handleInternal方法,利用反射对HandlerMethod进行调用并返回ModelAndView;
ExceptionHandlerExceptionResolver的作用是当DispatchServlet捕获异常后,交由给具有ExceptionHandler注解的方法处理异常,并定向到错误视图;
WebMvcAutoConfigurationAdapter主要是装配ViewResolver,设置资源处理策略;
Resolver的作用是根据返回的ModelAndView查找资源,处理视图;springboot自动装载了InternalResourceViewResolver、BeanNameViewResolver、ContentNegotiatingViewResolver,可以重写或者添加自己的ViewResolver以适应业务需求;对于资源处理,如果用户没自定义修改application.yml或者重写addResourceHandlers方法,程序将默认使用WebMvcProperties和ResourceProperties中的默认值,即/**和ResourceProperties.CLASSPATH_RESOURCE_LOCATIONS;
EableWebMvcConfiguration的祖父类WebMvcConfigurationSupport实际上已经具备了一个MVC需要的基本组件,包括各种资源mapping和adapter等;当我们需要自定MVC配置的时候,只需要继承WebMvcConfigurationSupport并加上EnableWebMvc注解即可;
WebMvcConfigurationSupport提供了addInterceptors和addResourceHandlers等模板方法给用户,用于实现自定义拦截器和静态资源访问策略;
viewControllerHandlerMapping方法用于创建处理简单Controller需要的SimpleUrlHandlerMapping;addViewControllers方法提供给用户注入url和Controller的关系映射;
resourceHandlerMapping方法用于创建一个处理静态资源的HandlerMapping;
DispatchServlet的初始化
DispatcherServlet注册到spring容器后需要初始化才能接受服务,包括HandlerMapping、HandlerAdapter、ViewResolver等;DispatcherServlet的父类是FrameworkServlet,而FrameworkServlet的父类是HttpServletBean;
因为FrameworkServlet实现了ApplicationContextAware接口,在spring容器初始化完成以后会调用setApplicationContext方法将上下文(对于springboot程序实际可能是AnnotationConfigServletWebServerApplicationContext)赋值给FrameworkServlet的webApplicationContext变量;
HttpServletBean的init方法是初始化的方法,它会调用FrameworkServlet.initServletBean方法,而initServletBean内部则会调用initWebApplicationContext方法创建化Web环境下需要的WebApplicationContext上下文;由于此前webApplicationContext已经被赋值,createWebApplicationContext不会再次执行;
接着调用模板方法onRefresh执行子类DispatcherServlet的initStrategies方法,初始化的各种选项;这些选项已经由WebMvcAutoConfiguration自动装配并载入spring容器;
DispatcherServlet处理业务的核心方法是doDispatch,它由Servlet的方法doService调用;对于一个Http请求,MVC主要处理流程如下:
调用checkMultipart方法,交由MultipartResolver.resolveMultipart处理请求并判断一个HttpServletRequest是否包含文件上传;
调用getHandler方法,遍历HandlerMapping的集合解析HttpServletRequest请求查找适合的HandlerExecutionChain,如果不能找到则返回错误代码404;由于MVC会默认自动配置ErrorMvcAutoConfiguration,当请求找不到HandlerMapping的时候,程序会转向/error路径,这个路径会匹配到BasicErrorController.errorHtml,用户可以定义自己的error.html显示错误信息;
调用getHandlerAdapter方法,遍历HandlerAdapter的集合选择支持handler的HandlerAdapter;例如HandlerExecutionChain的handler类型如果是HandlerMethod则返回RequestMappingHandlerAdapter,如果是Controller则返回SimpleControllerHandlerAdapter,如果是HttpRequestHandler则返回HttpRequestHandlerAdapter等;
调用HandlerExecutionChain的applyPreHandle方法,遍历当中的拦截器的preHandle方法,如果返回为false则返回;
调用HandlerAdapter的handle方法,由具体的HandlerAdapter处理需要执行的业务方法,处理结束返回ModelAndView;以 RequestMappingHandlerAdapter为例子,它的invokeHandlerMethod方法将利用HandlerMethod封装的信息,取得目标的对象及其需要执行的方法,将request当中取得请求参数转变为需要正确的方法参数,反射执行;
调用HandlerExecutionChain的applyPostHandle方法,遍历当中的拦截器的postHandle方法;
调用processDispatchResult方法处理View;如果以上过程产生了异常,则调用processHandlerException方法,将异常交给HandlerExceptionResolve.resolveException处理,并返回ModelAndView;如果前面没有异常发生,则调用render方法,从配置的ViewResolver列表中查找View,由具体的View渲染视图给客户端;
如果请求包括文件上传,最后需要调用cleanupMultipart方法做一些Multipart清理,减少资源浪费;