• 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类简析

  • 相关阅读:
    我参与过的开源项目
    chineking / WeiboCrawler / wiki / Home — Bitbucket
    PIL应用之生成验证码图片
    hurl
    Hadoop笔记之安装及伪分布式模式配置
    httpbin(1): HTTP Client Testing Service
    动态规划求编辑距离 残阳似血的博客
    cppreference.com
    sscanf
    在python中定义二维数组
  • 原文地址:https://www.cnblogs.com/question-sky/p/6913927.html
Copyright © 2020-2023  润新知