加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

DispatcherServlet初始化过程

发布时间:2020-12-16 22:47:44 所属栏目:百科 来源:网络整理
导读:前言 我们知道在使用SpringMVC的时候,我们会在web.xml中配置如下内容,DispatcherServlet会拦截住所有的请求然后处理。 context-param param-namecontextConfigLocation / param-name param-valueclasspath*:application-context.xml / param-value / conte

前言

我们知道在使用SpringMVC的时候,我们会在web.xml中配置如下内容,DispatcherServlet会拦截住所有的请求然后处理。

<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> 

首先我们先看下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(); } 

ServletConfig

public interface ServletConfig { /** * 返回Servlet的名字,就是<servlet-name>中配置的名字 */ public String getServletName(); /** * 返回应用本身的一些配置 */ public ServletContext getServletContext(); /** * 返回<init-param>配置的参数 */ public String getInitParameter(String name); /** * 返回<init-param>配置的参数的名字 */ public Enumeration<String> getInitParameterNames(); } 

GenericServlet

GenericServlet是Servlet的默认实现,主要做了如下几件事

  • 提供了无参的init方法,init()是一个模板方法,留给子类实现
public void init(ServletConfig config) throws ServletException { this.config = config; this.init(); } public void init() throws ServletException { } 
  • 实现了ServletConfig的接口,可以直接调用ServletConfig中的方法,这样做的好处是如果我们想获取ServletConfig中的内容,不必先调用getServletConfig()了,比如获取ServletContext的代码
public ServletContext getServletContext() { ServletConfig sc = getServletConfig(); if (sc == null) { throw new IllegalStateException( lStrings.getString("err.servlet_config_not_initialized")); } return sc.getServletContext(); } 

HttpServlet

HttpServlet是用HTTP协议实现的Servlet的基类,一般我们写的Servlet就是继承于它,我们注意到HttpServlet并没有实现init方法

HttpServletBean

从HttpServletBean开始我们就进入Spring的范围了,HttpServletBean重写了init方法

public final void init() throws ServletException { if (logger.isDebugEnabled()) { logger.debug("Initializing servlet ‘" + getServletName() + "‘"); } // 从ServletConfig中获取初始配置,比如contextConfigLocation PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(getServletConfig(),this.requiredProperties); if (!pvs.isEmpty()) { try { BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); bw.registerCustomEditor(Resource.class,new ResourceEditor(resourceLoader,getEnvironment())); // 模板方法,做一些初始化的工作,bw代表DispatcherServlet,但是没有子类重写 initBeanWrapper(bw); // 把初始配置设置给DispatcherServlet,比如contextConfigLocation bw.setPropertyValues(pvs,true); } catch (BeansException ex) { if (logger.isErrorEnabled()) { logger.error("Failed to set bean properties on servlet ‘" + getServletName() + "‘",ex); } throw ex; } } // 模板方法,子类重写,做进一步初始化的工作 initServletBean(); if (logger.isDebugEnabled()) { logger.debug("Servlet ‘" + getServletName() + "‘ configured successfully"); } } 

FrameworkServlet

FrameworkServlet实现了initServletBean方法,其简化代码如下

protected final void initServletBean() throws ServletException { try { // 初始化WebApplicationContext this.webApplicationContext = initWebApplicationContext(); // 初始化FrameworkServlet,模板方法,并没有子类实现 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; } } 

我们可以看到最重要的代码只有两句,而这两句之中最主要的是initWebApplicationContext(),下面我们就来看下initWebApplicationContext的简化代码

protected WebApplicationContext initWebApplicationContext() { // 获取父WebApplicationContext,如果我们在web.xml中配置了ContextLoaderListener,那么它加载的就是父WebApplicationContext WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; // 如果通过构造方法传入了webApplicationContext,就使用它 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) { // 从ServletContext中获取webApplicationContext,一般情况下是没有的 wac = findWebApplicationContext(); } if (wac == null) { // 自己创建一个webApplicationContext wac = createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { // 当ContextRefreshedEvent事件没有触发时调用此方法,模板方法,子类实现,是DispatcherServlet中重要的方法 onRefresh(wac); } if (this.publishContext) { // 把webApplicationContext保存到ServletContext中 String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName,wac); } return wac; } 

正常情况下都是自己创建一个webApplicationContext,我们看下创建的过程

protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) { // 获取创建类型 Class<?> contextClass = getContextClass(); // 具体创建 ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); wac.setEnvironment(getEnvironment()); wac.setParent(parent); // 将设置的contextConfigLocation参数传给wac,默认传入WEB-INFO/[ServletName]-Servlet.xml wac.setConfigLocation(getContextConfigLocation()); // 配置和刷新wac configureAndRefreshWebApplicationContext(wac); return wac; } protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) { if (ObjectUtils.identityToString(wac).equals(wac.getId())) { if (this.contextId != null) { wac.setId(this.contextId); } else { wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(getServletContext().getContextPath()) + ‘/‘ + getServletName()); } } wac.setServletContext(getServletContext()); wac.setServletConfig(getServletConfig()); wac.setNamespace(getNamespace()); wac.addApplicationListener(new SourceFilteringListener(wac,new FrameworkServlet.ContextRefreshListener())); ConfigurableEnvironment env = wac.getEnvironment(); if (env instanceof ConfigurableWebEnvironment) { ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(),getServletConfig()); } postProcessWebApplicationContext(wac); applyInitializers(wac); // 根据contextConfigLocation的值刷新webApplicationContext wac.refresh(); } 

DispatcherServlet

从上面的分析可以知道DispactcherServlet初始化的入口方法是onRefresh(wac),下面我们来具体看下

protected void onRefresh(ApplicationContext context) { initStrategies(context); } protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); } 

我们可以看到onRefresh方法只是调用了initStrategies方法,而initStrategies方法内部调用了九个初始化SpringMVC组件的方法,这九个组件的初始化过程类似,我们就以initHandlerMappings为例分析下

private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; // detectAllHandlerMappings默认是true if (this.detectAllHandlerMappings) { // 从ApplicationContext中找到所有的HandlerMapping Map<String,HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context,HandlerMapping.class,true,false); if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values()); AnnotationAwareOrderComparator.sort(this.handlerMappings); } } else { try { // 从ApplicationContext中获取名称为handlerMapping的Bean HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME,HandlerMapping.class); this.handlerMappings = Collections.singletonList(hm); } catch (NoSuchBeanDefinitionException ex) { } } // 如果没有HandlerMapping,则使用默认策略 if (this.handlerMappings == null) { this.handlerMappings = getDefaultStrategies(context,HandlerMapping.class); } } 

默认策略其实就是根据DispatcherServlet.properties中的配置加载对应的组件,下面就是文件中具体的内容,我要说的是默认配置并不是SpringMVC的推荐配置,比如DefaultAnnotationHandlerMapping现在已经是废弃状态。

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter

org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

总结

下面用表格的形式简要总结下DispatcherServlet初始化过程

类或接口 初始化入口方法 具体作用
Servlet init(ServletConfig config) 接口定义,由Web容器调用
GenericServlet init(ServletConfig config) 保存ServletConfig,内部调用无参的init方法
HttpServlet - -
HttpServletBean init() 设置contextConfigLocation的值,内部调用initServletBean()
FrameworkServlet initServletBean() 初始化webApplicationContext,内部调用onRefresh(ApplicationContext context)
DispatcherServlet onRefresh(ApplicationContext context) 初始化九大组件

文章转载自:https://www.jianshu.com/p/be981b92f1d2

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读