SpringMVC源码情操陶冶-DispatcherServlet父类简析
2017-05-27 17:46
399 查看
阅读源码有助于陶冶情操,本文对springmvc作个简单的向导
springmvc-web.xml配置
<servlet> <servlet-name>dispatch</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--springmvc配置文件加载路径--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/mvc/*.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> <async-supported>true</async-supported> </servlet> <servlet-mapping> <servlet-name>dispatch</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
DispatcherServlet类结构
查看源码可以得知类结构如下DispatcherServlet extends FrameworkServlet
FrameworkServlet extends HttpServletBean implements ApplicationContextAware
HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware
本文将对Dispatcher的父类作下向导,方便读者以及博主自我查阅
HttpServletBean-web服务抽象类
具体的介绍可以详看官方注释,这里我们只关注它的init方法,代码如下
//开始初始化springmvc servlet,并实例化相应的PropertyValues供子类调用 public final void init() throws ServletException { if (logger.isDebugEnabled()) { logger.debug("Initializing servlet '" + getServletName() + "'"); } // Set bean properties from init parameters. try { //此处便是读取<servlet>节点中的<init-param>参数保存至MutablePropertyValues#propertyValueList集合中 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); //将bean与属性关联 bw.setPropertyValues(pvs, true); } catch (BeansException ex) { logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); throw ex; } // Let subclasses do whatever initialization they like.供子类复写 initServletBean(); if (logger.isDebugEnabled()) { logger.debug("Servlet '" + getServletName() + "' configured successfully"); } }
小结:
从以上代码可见只是简单的获取
servlet中的<init-param>参数并绑定到
BeanWrapper对象中,并通过
initServletBean()方法供子类完善其余的功能。
FrameworkServlet-结合spring的web抽象类
官方简单的解释为:Spring的web基本servlet框架,通过基于javaBean解决方式来集合Spring的application context上下文。ApplicationContextAware接口
该接口的使用主要是为当前类可以拥有spring application context上下文类,可方便的获取bean对象,内部只有一个接口方法
setApplicationContext(ApplicationContext app),在
FrameworkServlet中的实现
public void setApplicationContext(ApplicationContext applicationContext) { //由spring调用并进行相应的applicationContext的注入 if (this.webApplicationContext == null && applicationContext instanceof WebApplicationContext) { this.webApplicationContext = (WebApplicationContext) applicationContext; this.webApplicationContextInjected = true; } }
doService()-抽象接口
主要是供子类去实现处理servlet的请求
复写
initServletBean()方法,完善功能
@Override 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 { //此处的初始化操作类似于ContextLoader#initWebApplicationContext 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"); } }
承接
initServletBean()方法的
initWebApplicationContext(),代码如下
protected WebApplicationContext initWebApplicationContext() { //一般来说,spring初始化时间比springmvc要早,所以rootContext一般都存在 WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; //先检查spring是否已经注入了webApplicationContext,其中的刷新操作类似于`ContextLoader#initWebApplicationContext`,初次调用此处为空 if (this.webApplicationContext != null) { // A context instance was injected at construction time -> use it wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { if (cwac.getParent() == null) { //springmvc的父类为spring上下文 cwac.setParent(rootContext); } //此处方法不同于`ContextLoader`的相同方法 configureAndRefreshWebApplicationContext(cwac); } } } //再而尝试从ServletContext的`contextAttribute`对应的值去获取 if (wac == null) { wac = findWebApplicationContext(); } //不然则创建新的webApplicationContext if (wac == null) { wac = createWebApplicationContext(rootContext); } //springmvc的第一次刷新 if (!this.refreshEventReceived) { //调用子类的onRefresh(wac)方法初始化springmvc onRefresh(wac); } //保存至servletContext属性中 if (this.publishContext) { // Publish the context as a servlet context attribute. 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; }
FrameworkServlet#createWebApplicationContext()-springmvc创建上下文对象
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) { //默认为XmlWebApplicationContext.class也可指定contextClass属性在web.xml Class<?> contextClass = getContextClass(); if (this.logger.isDebugEnabled()) { this.logger.debug("Servlet with name '" + getServletName() + "' will try to create custom WebApplicationContext context of class '" + contextClass.getName() + "'" + ", using parent context [" + parent + "]"); } if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException( "Fatal initialization error in servlet with name '" + getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext"); } ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); wac.setEnvironment(getEnvironment()); wac.setParent(parent); //设置springmvc的配置文件即 contextConfigLocation属性 wac.setConfigLocation(getContextConfigLocation()); //此处与ContextLoader#configureAndRefreshWebApplicationContext()类似 configureAndRefreshWebApplicationContext(wac); return wac; }
承接createWebApplicationContext方法的
FrameworkServlet#configureAndRefreshWebApplicationContext()-springmvc的刷新处理,不同于spring
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) { //设置id属性 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()); //默认的namespace为getServletName()+"-servlet" wac.setNamespace(getNamespace()); wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener())); ConfigurableEnvironment env = wac.getEnvironment(); if (env instanceof ConfigurableWebEnvironment) { ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig()); } //空方法 postProcessWebApplicationContext(wac); //读取web.xml中的`context-param`节点中的globalInitializerClasses、contextInitializerClasses并初始化 applyInitializers(wac); //刷新,此处可见Spring源码情操陶冶-AbstractApplicationContext博文 wac.refresh(); }
小结:
FrameworkServlet实例化springmvc上下文环境,与spring初始化上下文环境类似
onRefresh()方法开放出来供子类DispatherServlet调用完善springmvc的相关初始化操作
读取
init-param为
contextConfigLocation属性的值用于加载springmvc的配置文件,其中的bean文件的读取操作可参照
Spring源码情操陶冶-AbstractApplicationContext博文
springmvc的namespace为
getServletName()+'-servlet',比如
dispatcher-servlet,用于未指定
contextConfigLocation属性的话,便会去加载
\WEB-INF\${namespace}.xml,比如
\WEB-INF\dispatcher-servlet.xml,而对于spring,默认加载的为
\WEB-INF\applicationContext.xml
applicationContext.xml和dispatcher-servlet配置文件的加载,既支持classpath方式的加载,也支持WEB-INF方式的加载。后者是通过ServletContextResourceLoader加载的,实质是通过ServletContext.getRealPath()方法加载web环境下的web-inf的资源,其中参数必须以"/"为开头
下节预告
SpringMVC源码情操陶冶-DispatcherServlet类简析
相关文章推荐
- SpringMVC源码情操陶冶-ViewResolver视图解析
- SpringMVC源码情操陶冶-AbstractHandlerMapping
- SpringMVC源码情操陶冶-AbstractUrlHandlerMapping
- SpringMVC源码情操陶冶-RequestMappingHandlerAdapter适配器
- SpringMVC源码情操陶冶-FreeMarker之web配置
- SpringMVC源码情操陶冶-AbstractHandlerExceptionResolver
- SpringMVC源码情操陶冶-DispatcherServlet
- SpringMVC源码情操陶冶-AbstractHandlerMethodMapping
- SpringMVC源码情操陶冶-DispatcherServlet简析(二)
- SpringMVC源码情操陶冶-FreeMarker之web配置
- SpringMVC源码情操陶冶-AnnotationDrivenBeanDefinitionParser注解解析器
- SpringMVC源码情操陶冶-ResourcesBeanDefinitionParser静态资源解析器
- SpringMVC源码情操陶冶-View视图渲染
- SpringMVC源码情操陶冶-HandlerAdapter适配器简析
- SpringMVC源码情操陶冶-InterceptorsBeanDefinitionParser拦截器解析器
- SpringMVC源码情操陶冶-DispatcherServlet类简析(一)
- Spring源码情操陶冶-PathMatchingResourcePatternResolver路径资源匹配溶解器
- Spring源码情操陶冶#task:scheduled-tasks解析器
- Spring源码情操陶冶-AbstractApplicationContext#prepareBeanFactory
- Spring源码情操陶冶-AbstractApplicationContext#registerBeanPostProcessors