`
zhangwei_david
  • 浏览: 477208 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

DispatchServlet源码分析

 
阅读更多

 

 

DispatcheServlet类图,我们根据其类图进行源码分析

 

 



 

 

GenericServlet 源码分析

 

/**
 * 定义一个通用的,协议无关的Servlet.如果需要编写一个在Web中使用的Http Severlet需要继承HttpServlet
 * GeneraicServlet实现了Servlet接口和ServletConfig接口。GenericServlet          *可以直接通过Servlet扩展,虽然这是比较常见的一种是继承特殊协议的子类,如HttpServlet 
 * GenericServlet使写Servlet变的更容易。它提供简单的版本的生命周期方法 init,destory和ServletCOnfig接口中的方法
 * GenericServlet同样实现了生命在ServeltContext接口中的log方法s
 */
public abstract class GenericServlet 
    implements Servlet, ServletConfig, java.io.Serializable
{
    private static final String LSTRING_FILE = "javax.servlet.LocalStrings";
    private static ResourceBundle lStrings =
        ResourceBundle.getBundle(LSTRING_FILE);

    private transient ServletConfig config;
    

    /**
     *无参构造方法
     *
     */
    public GenericServlet() { }
    
    
    /**
     * 被servlet容器调用,用来指示一个servlet正在停止服务
     * 
     */
    public void destroy() {
    }
    
    
    /**
	 *  返回一个命名的初始化参数的字符串值,如果这个初始化参数不存在则返回null
     *   这个方法对从ServletConfig对象中获指定名称的参数的值提供了便利
     */ 
    public String getInitParameter(String name) {
	     //获取ServletConfig对象
        ServletConfig sc = getServletConfig();
        if (sc == null) {
            throw new IllegalStateException(
                lStrings.getString("err.servlet_config_not_initialized"));
        }
		// 从servletConfig中获取指定名称初始化参数值
        return sc.getInitParameter(name);
    }
    
    
   /**
    * 将所有参数名称作为一个Enumeration<String>返回。如果没有初始化参数则返回一个空的Enumration
    */
    public Enumeration<String> getInitParameterNames() {
		 //获取servletConfig对象
        ServletConfig sc = getServletConfig();
        if (sc == null) {
            throw new IllegalStateException(
                lStrings.getString("err.servlet_config_not_initialized"));
        }
		//获取参数名称的枚举
        return sc.getInitParameterNames();
    }   
     

    /**
	 * 获取一个ServletConfig对象
     */    
    public ServletConfig getServletConfig() {
	return config;
    }
 
    
    /**
     *返回一个servlet正在其中运行的ServletContext的引用
     *
     *
     */
    public ServletContext getServletContext() {
        ServletConfig sc = getServletConfig();
        if (sc == null) {
            throw new IllegalStateException(
                lStrings.getString("err.servlet_config_not_initialized"));
        }
		
        return sc.getServletContext();
    }


    /**
     *返回一个关于serlvet信息,比如 *作者,版本以及版权等。默认情况下,这个方法返回一个空字符串。如果需要返回有意义的值需要重写这个方法
     *
     */    
    public String getServletInfo() {
	return "";
    }


    /**
     * 被Servlet容器调用,用来标示一个servlet正准备开始服务。在该方法中将
*    * ServletConfig赋值给属性config后调用init()方法
     */
    public void init(ServletConfig config) throws ServletException {
	this.config = config;
	this.init();
    }


    /**
     * 初始化处理,具体处理过程延迟到子类
     **/
    public void init() throws ServletException {

    }
    

    
    public void log(String msg) {
	getServletContext().log(getServletName() + ": "+ msg);
    }
   
   
    
    public void log(String message, Throwable t) {
	getServletContext().log(getServletName() + ": " + message, t);
    }
    
    
    /**
     * 被servlet容器调用,让一个servlet响应一个请求
	 * 这是一个抽象方法,所以子类比如HttpServlet必须重写这个方法
     */

    public abstract void service(ServletRequest req, ServletResponse res)
	throws ServletException, IOException;
    

    /**
     * 返回servlet实例的名称
     */
    public String getServletName() {
        ServletConfig sc = getServletConfig();
        if (sc == null) {
            throw new IllegalStateException(
                lStrings.getString("err.servlet_config_not_initialized"));
        }

        return sc.getServletName();
    }
}

 HttpServlet 源码分析

/**
 *  提供了一个抽象类用来被继承创建一个Http servlet来使用一个web站点。
 * 一个HttpServlet的子类必须至少重写下述中的一个方法
 * doGet,如果servlet支持Http get请求
 * doPost,支持http post请求
 * doPut, 支持http put请求
 * doDelete 支持http delete请求
 * init 和destroy 来管理servlet声明周期资源
 * getServletInfo,servlet用来提供自身信息
 *  几乎没有理由去重写service方法。service方法处理标准的Http请求时通过将其分发给相应请求类型的处理方法。
 *  Servlet通常运行在多线程服务器上,所以必须注意一个servlet必须处理并发请求,同时谨慎地同步访问共享资源。
 *  共享资源包括内存数据例如 实例或类变量和外部对象如文件,数据库连接,网络连接
 *
 */

public abstract class HttpServlet extends GenericServlet
{
    private static final String METHOD_DELETE = "DELETE";
    private static final String METHOD_HEAD = "HEAD";
    private static final String METHOD_GET = "GET";
    private static final String METHOD_OPTIONS = "OPTIONS";
    private static final String METHOD_POST = "POST";
    private static final String METHOD_PUT = "PUT";
    private static final String METHOD_TRACE = "TRACE";

    private static final String HEADER_IFMODSINCE = "If-Modified-Since";
    private static final String HEADER_LASTMOD = "Last-Modified";
    
    private static final String LSTRING_FILE =
        "javax.servlet.http.LocalStrings";
    private static ResourceBundle lStrings =
        ResourceBundle.getBundle(LSTRING_FILE);
   
    
    /**
     *无参构造方法
     * 
     */

    public HttpServlet() { }
    

    /**
     * 服务器通过service方法调用处理一个Http的get请求
     * 重写这个方法支持get请求同时也自动支持Http Head请求。Head请求是一个响应中没有返回体,只有请求头部字段
     */

    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String protocol = req.getProtocol();
        String msg = lStrings.getString("http.method_get_not_supported");
        if (protocol.endsWith("1.1")) {
            resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
        } else {
            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
        }
    }


    /**
     * 返回HttpServletRequest对象的最后修改时间
     */

    protected long getLastModified(HttpServletRequest req) {
        return -1;
    }


   
    protected void doHead(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        NoBodyResponse response = new NoBodyResponse(resp);
        //委托给doGet方法
        doGet(req, response);
        response.setContentLength();
    }


    
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String protocol = req.getProtocol();
        String msg = lStrings.getString("http.method_post_not_supported");
        if (protocol.endsWith("1.1")) {
            resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
        } else {
            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
        }
    }


    protected void doPut(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String protocol = req.getProtocol();
        String msg = lStrings.getString("http.method_put_not_supported");
        if (protocol.endsWith("1.1")) {
            resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
        } else {
            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
        }
    }


    
    protected void doDelete(HttpServletRequest req,
                            HttpServletResponse resp)
        throws ServletException, IOException
    {
        String protocol = req.getProtocol();
        String msg = lStrings.getString("http.method_delete_not_supported");
        if (protocol.endsWith("1.1")) {
            resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
        } else {
            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
        }
    }
    

    private Method[] getAllDeclaredMethods(Class<?> c) {

        if (c.equals(javax.servlet.http.HttpServlet.class)) {
            return null;
        }

        Method[] parentMethods = getAllDeclaredMethods(c.getSuperclass());
        Method[] thisMethods = c.getDeclaredMethods();
        
        if ((parentMethods != null) && (parentMethods.length > 0)) {
            Method[] allMethods =
                new Method[parentMethods.length + thisMethods.length];
            System.arraycopy(parentMethods, 0, allMethods, 0,
                             parentMethods.length);
            System.arraycopy(thisMethods, 0, allMethods, parentMethods.length,
                             thisMethods.length);

            thisMethods = allMethods;
        }

        return thisMethods;
    }




    /**
	 * 从公开的service方法中接收标准的HTTP请求,将其分发给相应的处理方法。
     */
    protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
		// 获取请求方法
        String method = req.getMethod();
		// 如果是get方法
        if (method.equals(METHOD_GET)) {
		   //获取最后修改时间
            long lastModified = getLastModified(req);
			// 如果最后修改时间未知
            if (lastModified == -1) {
			//分发给doGet方法处理
                doGet(req, resp);
            } else {
                long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
				// 如果servlet最后修改时间小于请求的最后修改时间则调用doGet,否则返回304
                if (ifModifiedSince < lastModified) {
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else {
				
                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }

        } else if (method.equals(METHOD_HEAD)) {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);

        } else if (method.equals(METHOD_POST)) {
            doPost(req, resp);
            
        } else if (method.equals(METHOD_PUT)) {
            doPut(req, resp);
            
        } else if (method.equals(METHOD_DELETE)) {
            doDelete(req, resp);
            
        } else if (method.equals(METHOD_OPTIONS)) {
            doOptions(req,resp);
            
        } else if (method.equals(METHOD_TRACE)) {
            doTrace(req,resp);
            
        } else {
            //没有支持的处理方法,返回一个错误响应
            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[1];
            errArgs[0] = method;
            errMsg = MessageFormat.format(errMsg, errArgs);
            
            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
        }
    }
    

    /*
     * Sets the Last-Modified entity header field, if it has not
     * already been set and if the value is meaningful.  Called before
     * doGet, to ensure that headers are set before response data is
     * written.  A subclass might have set this header already, so we
     * check.
     */
    private void maybeSetLastModified(HttpServletResponse resp,
                                      long lastModified) {
        if (resp.containsHeader(HEADER_LASTMOD))
            return;
        if (lastModified >= 0)
            resp.setDateHeader(HEADER_LASTMOD, lastModified);
    }
   
    
    /**
     * 将请求委托给给protected void service(HttpServletRequest req, HttpServletResponse resp)
     */
    public void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException
    {
        HttpServletRequest  request;
        HttpServletResponse response;
        
        if (!(req instanceof HttpServletRequest &&
                res instanceof HttpServletResponse)) {
            throw new ServletException("non-HTTP request or response");
        }

        request = (HttpServletRequest) req;
        response = (HttpServletResponse) res;

        service(request, response);
    }
}


}

 

 HttpServletBean

public abstract class HttpServletBean extends HttpServlet implements EnvironmentAware {

	
	protected final Log logger = LogFactory.getLog(getClass());

	/** 
	 * 一组必须为这个servlet提供的配置参数
	 */
	private final Set<String> requiredProperties = new HashSet<String>();

	private Environment environment = new StandardServletEnvironment();


	/**
	 * 强制子类调用这个方法来指定属性,并且必须提供作为作为配置参数。这个方法应该在子类的构造方法中调用y
	 */
	protected final void addRequiredProperty(String property) {
		this.requiredProperties.add(property);
	}

	/**
	 * 映射配置参数到这个Servlet bean的书zing同时调用子类的初始化方法
	 */
	@Override
	public final void init() throws ServletException {
		if (logger.isDebugEnabled()) {
			logger.debug("Initializing servlet '" + getServletName() + "'");
		}

		// 使用初始化参数设置bean的属性
		try {
			//创建一个ServletConfigPropertyValues对象
			PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
			//获取当前对象的BeanWrapper,使用JavaBean风格访问bean的属性
			BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
			//创建一个ServletContextResourceLoader
			ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
			// 设置bw的属性编辑器
			bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment));
			//初始化当前对象的BeanWrapper
			initBeanWrapper(bw);
			//设置bean属性
			bw.setPropertyValues(pvs, true);
		}
		catch (BeansException ex) {
			logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
			throw ex;
		}

		//调用子类初始化方法
		initServletBean();

		if (logger.isDebugEnabled()) {
			logger.debug("Servlet '" + getServletName() + "' configured successfully");
		}
	}

	/**
	 * 对初始化BeanWrapper,可以使用自定义属性编辑器
	 * 默认实现是一个空方法
	 */
	protected void initBeanWrapper(BeanWrapper bw) throws BeansException {
	}


	/**
	 *  重写方法,如果没有servletConfig 则返回null
	 */
	@Override
	public final String getServletName() {
		return (getServletConfig() != null ? getServletConfig().getServletName() : null);
	}

	
	@Override
	public final ServletContext getServletContext() {
		return (getServletConfig() != null ? getServletConfig().getServletContext() : null);
	}


	/**
	 *子类通过重写这个方法实现自定义的初始化,在这个方法调用之前,这个Servlet所有bean属性都已经设置好了
	 */
	protected void initServletBean() throws ServletException {
	}

	
	public void setEnvironment(Environment environment) {
		this.environment = environment;
	}


	/**
	 * 从的ServletConfig初始化参数创建PropertyValues。
	 */
	private static class ServletConfigPropertyValues extends MutablePropertyValues {

		/**
		 * 创建一个新的ServeltConfigPropertyValues
		 */
		public ServletConfigPropertyValues(ServletConfig config, Set<String> requiredProperties)
		    throws ServletException {
			//获取所有丢失的参数
			Set<String> missingProps = (requiredProperties != null && !requiredProperties.isEmpty()) ?
					new HashSet<String>(requiredProperties) : null;
			// 获取所有初始化参数名枚举
			Enumeration en = config.getInitParameterNames();
			//遍历所有初始化参数
			while (en.hasMoreElements()) {
				String property = (String) en.nextElement();
				Object value = config.getInitParameter(property);
				//添加到property数组中
				addPropertyValue(new PropertyValue(property, value));
				if (missingProps != null) {
					missingProps.remove(property);
				}
			}

			// 如果依然有丢失的属性则抛出异常
			if (missingProps != null && missingProps.size() > 0) {
				throw new ServletException(
				    "Initialization from ServletConfig for servlet '" + config.getServletName() +
				    "' failed; the following required properties were missing: " +
				    StringUtils.collectionToDelimitedString(missingProps, ", "));
			}
		}
	}

}

 

 FrameworkServlet

/**
 * Spring's web框架的Servlet基类。提供在一个基于JavaBean集成Spring应用上下文的解决方案
 * 这个类提供了如下功能:
 *  管理每个Servlet的WebApplicationContext实例。servlet的配置servlet命名空间中的bean由决定
 *  发布请求处理事件,不论请求是否被成功处理 publishEvents=true,默认为true
 *  子类必须实现doService方法去处理请求。因为这个类继承HttpeServletBean类而不是直接继承HttpServlet类,
 *  子类可以重写initFrameworkServlet来自定义初始化
 *  在servlet初始化参数中检测contextClass并将其设置为默认的上下文类,如果没有找到则使用XmlWEbApplicationContext.一般都是使用默认值
 *  注意,默认情况下自定义的上下文类需要实现ConfigurableWebApplicationContext接口。 
 *  接受一个可选参的servelt初始化参数contextInitializerClassers指定一个或多个ApplicationContextInitializer;托管的WebApplicationContext会
 *  委托给这些指定的初始化器增加一些额外的程序化配置。
 *  ContextLoader也支持具有相同语义的contextInitializerClassers上线文参数,对根上下文进行程序化配置。
 *  传递servlet初始化配置参数contextConfigLocation到上下文中,解析成潜在可以通过逗号空格分割的多文件路径,
 *  例如“test-servlet.xml,myServlet.xml”
 *  如果没有明确指明contextConfigLocation,则从servlet命名空间中加载默认的路径的配置文件
 *  需要注意的是:在配置多个路径时,后面Bean中定义将覆盖前一个加载文件中的配置,至少Spring默认ApplicationContext实现是这么处理的。
 *  默认的命名空间是servletName-servlet,例如test-servlet, servlet的名称就是test,
 *  XmlWebApplication默认的加载路径是/WEB-INF/test-servlet.xml
 *  
 */
@SuppressWarnings("serial")
public abstract class FrameworkServlet extends HttpServletBean {

	/**
	 * *WebApplicationContext命名空间的后缀。如果一个servlet在这个类的上下文中给定的名字是“test”,这个servlet的命名空间则使**用test-servelt
	 */
	public static final String DEFAULT_NAMESPACE_SUFFIX = "-servlet";

	/**
	 *FrameworkServlet默认的上下文
	 */
	public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;

	/**
	 * WebApplicationContext在servlet 上下文中属性名称的前缀。最后.
	 */
	public static final String SERVLET_CONTEXT_PREFIX = FrameworkServlet.class.getName() + ".CONTEXT.";

	/**
	 * 在一个初始化参数的多个值之间使用分割符分割
	 * 
	 */
	private static final String INIT_PARAM_DELIMITERS = ",; \t\n";


	/**  */
	private String contextAttribute;

	/** WebApplicationContext实现类 */
	private Class<?> contextClass = DEFAULT_CONTEXT_CLASS;

	/** WebApplicationContext的Id*/
	private String contextId;

	/** 当前servlet的命名空间 */
	private String namespace;

	/** 上下文配置路径 */
	private String contextConfigLocation;

	/** 是否将WebApplicationContext发布为Servlet上下文的一个属性。默认是true */
	private boolean publishContext = true;

	/**  是否在每个请求结束发布一个ServletRequestHandledEvent */
	private boolean publishEvents = true;

	/** Expose LocaleContext and RequestAttributes as inheritable for child threads? */
	private boolean threadContextInheritable = false;

	/** 是否分发Http opetion请求 */
	private boolean dispatchOptionsRequest = false;

	/** 是否分发http trace请求 */
	private boolean dispatchTraceRequest = false;

	/**当前servlet的WebApplicationContext */
	private WebApplicationContext webApplicationContext;

	/** 标志onFreash方法是否已经被调用过*/
	private boolean refreshEventReceived = false;

	/** Comma-delimited ApplicationContextInitializer classnames set through init param */
	private String contextInitializerClasses;

	/** Actual ApplicationContextInitializer instances to apply to the context */
	private ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>> contextInitializers =
			new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>();



	public FrameworkServlet() {
	}

	
	public FrameworkServlet(WebApplicationContext webApplicationContext) {
		this.webApplicationContext = webApplicationContext;
	}

  // setter and getter

	/**
	 * 重写HttpServletBean的方法,在所有属性设置完成后调用,创建当前servlet的WebApplicationContext
	 */
	@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 {
			//初始化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;
		}

		if (this.logger.isInfoEnabled()) {
			long elapsedTime = System.currentTimeMillis() - startTime;
			this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
					elapsedTime + " ms");
		}
	}

	/**
	 * 初始化WebApplicationContext
	 */
	protected WebApplicationContext initWebApplicationContext() {
	  //获取根WebApplicationContext
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;
		// 如果一个上下文对象已经在构造方法中创建则直接使用
		if (this.webApplicationContext != null) {
			wac = this.webApplicationContext;
				//如果web应用上下文是ConfiguableWebApplicontionContext
			if (wac instanceof ConfigurableWebApplicationContext) {
			  // 强制类型转换
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				  //如果上下文不是活跃的,即上线问没有被刷新
				if (!cwac.isActive()) {
					  // 如果没有父上下文对象,则将根上下文设置为父上下文
					if (cwac.getParent() == null) {
						cwac.setParent(rootContext);
					}
					//配置同时刷新上下文
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
		//如果在构造方法中没有注入上下文,则在servlet上下文中找到一个已经注册的上下文。
		if (wac == null) {
			
			wac = findWebApplicationContext();
		}
		if (wac == null) {
		    // 如果当前servlet没有定义上下文对象则创建一个本地webApplicationContext
			wac = createWebApplicationContext(rootContext);
		}
        
		if (!this.refreshEventReceived) {
			 
			onRefresh(wac);
		}

		if (this.publishContext) {
		   //将上下文发布为一个servlet上下文属性
		    // 获取属性名称
			String attrName = getServletContextAttributeName();
			//将其设置为servlet上下文的一个属性
			getServletContext().setAttribute(attrName, wac);
			if (this.logger.isDebugEnabled()) {
				this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
						"' as ServletContext attribute with name [" + attrName + "]");
			}
		}

		return wac;
	}

	/**
     * 
	 */
	protected WebApplicationContext findWebApplicationContext() {
	     //获取上下文属性名称
		String attrName = getContextAttribute();
		// 如果属性名称为null则返回null
		if (attrName == null) {
			return null;
		}
		// 从servlet 上下文中获取Web应用上下文
		WebApplicationContext wac =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName);
		// 如果web应用上下文为null则抛出一个异常,否则返回这个上下文
		if (wac == null) {
			throw new IllegalStateException("No WebApplicationContext found: initializer not registered?");
		}
		return wac;
	}

	/**
	 *穿件一个新的WebApplicaitonContext对象
	 */
	protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
		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 + "]");
		}
		// 如果web应用上下文类不是ConfigurableWebApplicationContext类或其子类则抛出一个异常
		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");
		}
		// 创建一个ConfiguableWebApplicaitonContext对象
		ConfigurableWebApplicationContext wac =
				(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
		// 设置父上下文
		wac.setParent(parent);
		//设置配置资源路径
		wac.setConfigLocation(getContextConfigLocation());
		// 刷新
		configureAndRefreshWebApplicationContext(wac);
		//返回上下文对象
		return wac;
	}

	protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
		if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
			// 如果当前contextId不为null则将web应用上下文的Id设置为contextId
			if (this.contextId != null) {
				wac.setId(this.contextId);
			}
			else {
				// 获取servlet上下文对象
				ServletContext sc = getServletContext();
				// 如果Servlet版本小于2.5,如果web.xml中配置servlet名称则使用其作为上下文的ID
				if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
					// 获取servlet上下文名称
					String servletContextName = sc.getServletContextName();
					// 如果servetContextName不为null则使用其构建一个Id并设置为WebApplicationContext的ID
					if (servletContextName != null) {
						wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + servletContextName +
								"." + getServletName());
					}
					else {
						wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + getServletName());
					}
				}
				else {
					// servlet2.5
					wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
							ObjectUtils.getDisplayString(sc.getContextPath()) + "/" + getServletName());
				}
			}
		}
		//设置servletContext
		wac.setServletContext(getServletContext());
		// 设置servletConfig
		wac.setServletConfig(getServletConfig());
		// 设置nameSpace
		wac.setNamespace(getNamespace());
		//设置监听器
		wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
	    //后置处理
		postProcessWebApplicationContext(wac);

		applyInitializers(wac);

		wac.refresh();
	}

	
	protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
		return createWebApplicationContext((ApplicationContext) parent);
	}

	/**
	 * 在WebApplicationContext被刷新前,委派给在当前servlet初始化参数init-param中指定的contextInitializerClasses 类处理
	 */
	@SuppressWarnings("unchecked")
	protected void applyInitializers(ConfigurableApplicationContext wac) {
		if (this.contextInitializerClasses != null) {
			String[] initializerClassNames =
					StringUtils.tokenizeToStringArray(this.contextInitializerClasses, INIT_PARAM_DELIMITERS);
			for (String initializerClassName : initializerClassNames) {
				ApplicationContextInitializer<ConfigurableApplicationContext> initializer;
				try {
					Class<?> initializerClass = ClassUtils.forName(initializerClassName, wac.getClassLoader());
					initializer = BeanUtils.instantiateClass(initializerClass, ApplicationContextInitializer.class);
				}
				catch (Exception ex) {
					throw new IllegalArgumentException(
							String.format("Could not instantiate class [%s] specified via " +
							"'contextInitializerClasses' init-param", initializerClassName), ex);
				}
				this.contextInitializers.add(initializer);
			}
		}
		Collections.sort(this.contextInitializers, new AnnotationAwareOrderComparator());
		for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) {
			initializer.initialize(wac);
		}
	}

	
	protected void postProcessWebApplicationContext(ConfigurableWebApplicationContext wac) {
	}

	
	public String getServletContextAttributeName() {
		return SERVLET_CONTEXT_PREFIX + getServletName();
	}

	public final WebApplicationContext getWebApplicationContext() {
		return this.webApplicationContext;
	}


	
	protected void initFrameworkServlet() throws ServletException {
	}

	/**
	 * 刷新web应用上下文对象
	 */
	public void refresh() {
		WebApplicationContext wac = getWebApplicationContext();
		if (!(wac instanceof ConfigurableApplicationContext)) {
			throw new IllegalStateException("WebApplicationContext does not support refresh: " + wac);
		}
		((ConfigurableApplicationContext) wac).refresh();
	}

	/**
	 *应用上下文刷新事件监听器回调方法
	 */
	public void onApplicationEvent(ContextRefreshedEvent event) {
		this.refreshEventReceived = true;
		onRefresh(event.getApplicationContext());
	}

	/**
	 *模板方法模式,具体的处理逻辑延迟到子类中
	 */
	protected void onRefresh(ApplicationContext context) {
		// For subclasses: do nothing by default.
	}


	


	
	/**
	 * Http请求的具体处理方法
	 **/
	protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		
		//获取当前时间
		long startTime = System.currentTimeMillis();
		Throwable failureCause = null;

		// 获取目前线程持有的LocalContext.
		LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
		//将localContext和当前线程关联
		LocaleContextHolder.setLocaleContext(buildLocaleContext(request), this.threadContextInheritable);

		//获取当前线程持有的RequestAttributes
		RequestAttributes previousRequestAttributes = RequestContextHolder.getRequestAttributes();
		
		ServletRequestAttributes requestAttributes = null;
		// 如果当前线程没有持有RequestAttributes对象或者持有对象的是ServletReqeustAttributes则创建一个新的ServletREquestAttributes
		if (previousRequestAttributes == null || previousRequestAttributes.getClass().equals(ServletRequestAttributes.class)) {
			requestAttributes = new ServletRequestAttributes(request);
			//与当前线程关联
			RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
		}

		if (logger.isTraceEnabled()) {
			logger.trace("Bound request context to thread: " + request);
		}

		try {
		    // 将请求委托给子类的doService方法处理
			doService(request, response);
		}
		catch (ServletException ex) {
			failureCause = ex;
			throw ex;
		}
		catch (IOException ex) {
			failureCause = ex;
			throw ex;
		}
		catch (Throwable ex) {
			failureCause = ex;
			throw new NestedServletException("Request processing failed", ex);
		}

		finally {
			// 清除请求同时复位线程上下文
			LocaleContextHolder.setLocaleContext(previousLocaleContext, this.threadContextInheritable);
			//复位RequestAttributes
			if (requestAttributes != null) {
				RequestContextHolder.setRequestAttributes(previousRequestAttributes, this.threadContextInheritable);
				requestAttributes.requestCompleted();
			}
			if (logger.isTraceEnabled()) {
				logger.trace("Cleared thread-bound request context: " + request);
			}

			if (logger.isDebugEnabled()) {
				if (failureCause != null) {
					this.logger.debug("Could not complete request", failureCause);
				}
				else {
					this.logger.debug("Successfully completed request");
				}
			}
			// 如果需要发布一个请求处理完成的事件则发送该事件
			if (this.publishEvents) {
				// Whether or not we succeeded, publish an event.
				long processingTime = System.currentTimeMillis() - startTime;
				//发布一个ServeltReqeustHandledEvent事件,用户可以对该事件进行监听
				this.webApplicationContext.publishEvent(
						new ServletRequestHandledEvent(this,
								request.getRequestURI(), request.getRemoteAddr(),
								request.getMethod(), getServletConfig().getServletName(),
								WebUtils.getSessionId(request), getUsernameForRequest(request),
								processingTime, failureCause));
			}
		}
	}

	
	protected LocaleContext buildLocaleContext(HttpServletRequest request) {
		return new SimpleLocaleContext(request.getLocale());
	}

	
	protected String getUsernameForRequest(HttpServletRequest request) {
		Principal userPrincipal = request.getUserPrincipal();
		return (userPrincipal != null ? userPrincipal.getName() : null);
	}

	/**
	 *  模板方法模式,将具体实现延迟到子类中
	 **/
	protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
			throws Exception;


	/**
	 * 关闭应用上下文
	 */
	@Override
	public void destroy() {
		getServletContext().log("Destroying Spring FrameworkServlet '" + getServletName() + "'");
		if (this.webApplicationContext instanceof ConfigurableApplicationContext) {
			((ConfigurableApplicationContext) this.webApplicationContext).close();
		}
	}


	/**
	 * 应用上下文监听器
	 */
	private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {

		public void onApplicationEvent(ContextRefreshedEvent event) {
			 //
			FrameworkServlet.this.onApplicationEvent(event);
		}
	}

}

 后续请见 DispatcherServlet初始化处理

  • 大小: 35.8 KB
  • 大小: 30 KB
2
1
分享到:
评论

相关推荐

    spring mvc+ibatis+dwr实现dispatchservlet

    本文将深入探讨如何使用这些技术实现DispatchServlet,并结合省市级联的功能。 首先,Spring MVC是Spring框架的一部分,它是一个用于构建Web应用程序的模型-视图-控制器(MVC)架构。DispatchServlet是Spring MVC的...

    详解Spring mvc DispatchServlet 实现机制

    Spring MVC的DispatcherServlet是整个框架的核心组件,它作为前端控制器,负责接收HTTP请求,并调度相应的处理器来处理这些请求。在本文中,我们将深入探讨DispatcherServlet的实现机制。 首先,DispatcherServlet...

    SpringMVC学习笔记,学习路线,详细

    SpringMVC 学习笔记涵盖了从基础到高级的多个方面,如源码分析、实例代码、Maven 配置、Web.xml 配置以及注解驱动的编程。对于初学者来说,理解 SpringMVC 的工作流程至关重要。SpringMVC 的工作流程通常始于浏览器...

    2018年java技术面试题整理

    * Web 服务器解析请求 URL 并去匹配 DispatchServlet 的映射 URL * 如果匹配上就将这个请求放入到 DispatchServlet * DispatchServlet 根据 Mapping 配置去寻找相应的 Handle * Handle 封装了处理业务逻辑的代码 * ...

    2022年最新java面试题技术面试.docx

    SpringMVC 是一个基于 DispatchServlet 的分层控制框架。客户端发出 HTTP 请求,Web 服务器解析 URL 并将请求转发给 DispatchServlet,DispatchServlet 根据映射配置找到对应的 Handler,Handler 封装了业务逻辑代码...

    Springboot2.4.12启动过程方法总结.xmind

    由于我们日常工作中都是在现有框架下...3.指出了微服务是如何实现提供外部服务的能力-通过创建Tomcat容器,绑定并监听服务端口,不断拉取请求并结合DispatchServlet的映射关系找到真正提供服务的方法,处理请求并返回。

    springMVC的issue1

    在Spring MVC框架中,DispatchServlet扮演着至关重要的角色。它是一个前端控制器,负责接收HTTP请求并分发到相应的处理器。在web.xml配置文件中,DispatchServlet通常被映射为"/",这意味着任何到达应用程序的URL...

    spring1.0.zip

    闲来无事,手写Spring的mini版本 1.0, 目前只有几个核心功能, @Controller,@RequestMapping,@RequestParam,HandlerMapping,DispatchServlet等.但是从前端发起URL请求到后台可以处理结果并且返回, 实现了...

    maven打包出错解决办法,亲测绝对可以!

    在开发Java项目时,Maven是一个非常重要的构建工具,它帮助我们管理依赖、构建项目以及自动化测试。然而,有时候在使用Maven进行打包操作时,可能会遇到各种问题。本篇文章将详细阐述如何解决Maven打包出错的问题,...

    springboot2 配置多个DispatcherServlet 处理.do .htm请求,Controller分离,集成druid和mybatis

    Springboot 2.4.4 网上搜到的配置多个DispatcherServlet 都有坑,自己避坑写的一个demo,处理.do .htm请求,Controller分离不会出现一个Controller可以处理.do也处理.htm可自己扩展.action .json等,适合分离前台...

    基于JSP的OA办公管理系统的设计与实现.docx

    本文基于Java的OA办公管理系统在分析设计时遵循软件工程的思想,文中详细阐述了系统需求分析、系统设计、数据库设计、系统实现以及系统测试五方面。系统运用JSP+Spring+SpringMVC+Mybatis技术的整合进行系统的开发,...

    Java Web Framework综述

    例如,`DispatchServlet`可能会使用RequestDispatcher找到并调用相应的业务逻辑,这可能是另一个内部定义的Servlet,或者是一个Action类。 在MVC模式中,Model负责数据管理,View负责渲染用户界面,而Controller...

    Spring集成webSocket页面访问404问题的解决方法

    该问题主要是由于在 DispatchServlet 中没有正确配置拦截器和跨域请求的设置所致。 在解决该问题时,可以通过在 DispatchServlet 中添加拦截器来解决webSocket请求问题。此外,需要在配置访问地址时设置连接的域名...

    后台轻量级建站包 v2.0

    后台整合包精简版(servlet_DBC)是一个整合了部分Java后台功能的工具包。修复了部分问题,增加了一些功能;...继承dispatchservlet可以更方便使用request和response;使用更新对象可以用dbctools获取回填主键;

    顺丰科技2019 秋招视觉算法工程师笔试客观题合集.docx

    - **选项分析**: - **a. 保证每一层的感受野不变,网络深度加深,使得网络的精度更高**:正确。Inception模块通过使用不同大小的卷积核(如1×1、3×3、5×5等),能够在保持感受野大小不变的情况下增加网络的深度...

    sshDemo-new

    使用spring+springmvc+hibernate的方式整合的sshDemo,主要用于学习整合框架,新工程的创建模板。与旧版sshDemo相比,修订了dispatchServlet的配置,增加了springmvc默认静态资源配置,更好的配置系统首页

    【毕业设计】基于SSM的实验室管理系统 .zip

    MySQL 软件采用了双授权政策,分为社区版和商业版,由于其体积小、速度快、总体拥有成本低,尤其是开放源码这一特点,一般中小型网站的开发都选择 MySQL 作为网站数据库。 Tomcat Tomcat是Apache下的一个开源的...

    SpringMVC.rar

    简单登录例子,然后进行偶像投票选择,输入偶像年龄做了加1操作,讲解的是springMVC框架的登入功能的实现,首先我们应该知道springMVC框架是怎么进行业务转发的 这里的DispatchServlet是springmvc的Servlet,所有的...

    spring面试问答.pdf

    在Spring的源码层面,IoC(控制反转)的原理是通过依赖注入来实现的,它将程序中的依赖关系的管理从程序代码中抽取出来,以配置文件的形式进行管理。SpringBean的生命周期涉及一系列步骤,包括初始化前、初始化后、...

Global site tag (gtag.js) - Google Analytics