zoukankan      html  css  js  c++  java
  • SpringMVC源码情操陶冶-DispatcherServlet父类简析

    阅读源码有助于陶冶情操,本文对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-paramcontextConfigLocation属性的值用于加载springmvc的配置文件,其中的bean文件的读取操作可参照Spring源码情操陶冶-AbstractApplicationContext博文

    • springmvc的namespace为getServletName()+'-servlet',比如dispatcher-servlet,用于未指定contextConfigLocation属性的话,便会去加载WEB-INF${namespace}.xml,比如WEB-INFdispatcher-servlet.xml,而对于spring,默认加载的为WEB-INFapplicationContext.xml

    • applicationContext.xml和dispatcher-servlet配置文件的加载,既支持classpath方式的加载,也支持WEB-INF方式的加载。后者是通过ServletContextResourceLoader加载的,实质是通过ServletContext.getRealPath()方法加载web环境下的web-inf的资源,其中参数必须以"/"为开头

    下节预告

    SpringMVC源码情操陶冶-DispatcherServlet类简析

  • 相关阅读:
    Sum Root to Leaf Numbers 解答
    459. Repeated Substring Pattern
    71. Simplify Path
    89. Gray Code
    73. Set Matrix Zeroes
    297. Serialize and Deserialize Binary Tree
    449. Serialize and Deserialize BST
    451. Sort Characters By Frequency
    165. Compare Version Numbers
    447. Number of Boomerangs
  • 原文地址:https://www.cnblogs.com/question-sky/p/6913927.html
Copyright © 2011-2022 走看看