从自己所做的工程开始梳理自己技术栈


夫人之相与,俯仰一世,或取诸怀抱,晤言一室之内;或因寄所托,放浪形骸之外。虽趣(取/趋)舍万殊,静躁不同,当其欣于所遇,暂得于己,怏然自足,不知老之将至;及其所之既倦,情随事迁,感慨系之矣。向之所欣,俯仰之间,已为陈迹,犹不能不以之兴怀;况修短随化,终期于尽。古人云:“死生亦大矣。”岂不痛哉!

疲惫期,慢慢在堕落

SpringMVC,Servlet 和设计模式

前面我们已经说过了 servlet的规范,讲述的是HTTP请求和返回的数据的规范,现在我们就是说springMVC的具体的处理的过程,看到spring是如何与servlet相结合的。

从servlet的规范,我们知道,servlet的加载的过程和Spring的配合的一个点就是: Web.xml 中的一句,

<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

这里的ContextLoaderListener 就是实现了:ServletContextListener接口,这个接口定义个servlet的加载和销毁的两个必须的过程,可以算是模板方法(template method) ServletContextListener接口定义了整个servet监听的模板的方法:加载和销毁,然后不同的实现类监听到这两个的时间可以做不同的事情。听起来又像是观察者模式,但是从不同的实现的方式来看,这个确实是模板方法。

spring实现的模板方法:

 /**
	* Initialize the root web application context.
	* 监听加载servlet的方法
	*/
	public void contextInitialized(ServletContextEvent event) {
		this.contextLoader = createContextLoader();
		if (this.contextLoader == null) {
			this.contextLoader = this;
		}
		this.contextLoader.initWebApplicationContext(event.getServletContext());
	}

 /**
	 * Close the root web application context.
	 * 销毁servlet的方法
	 */
	public void contextDestroyed(ServletContextEvent event) {
		if (this.contextLoader != null) {
			this.contextLoader.closeWebApplicationContext(event.getServletContext());
		}
		ContextCleanupListener.cleanupAttributes(event.getServletContext());
	}

servlet加载过程的监听,也就是在这个时刻完成对:WebApplicationContext的初始化,我们知道这个时候WEB项目还没有完全启动完成.这个动作会比所有的Servlet都要早。换句话说,*这个时候,你对中的键值做的操作,将在你的WEB项目完全启动之前被执行*

在初始化的servet的代码中,就有:

servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE的值,在上面WebApplicationContext的源码中的第一个常量中就被声明,是WebApplicationContext.class.getName() + “.ROOT”,更直接一点,它是“org.springframework.web.context.WebApplicationContext.ROOT”。ContextLoaderListener所初始化的这个Spring容器上下文,被称为根上下文。

SpringMVC在DispatcherServlet的初始化过程中,同样会初始化一个WebApplicationContext的实现类,作为自己独有的上下文,这个独有的上下文,会将上面的根上下文作为自己的父上下文,来存放SpringMVC的配置元素,然后同样作为ServletContext的一个属性,被设置到ServletContext中,只不过它的key就稍微有点不同,key和具体的DispatcherServlet注册在web.xml文件中的名字有关, 从这一点也决定了,我们可以在web.xml文件中注册多个DispatcherServlet,因为Servlet容器中注册的Servlet名字肯定不一样,设置到Servlet环境中的key也肯定不同。由于在Spring容器中,子上下文可以访问到所有父上下文中的信息,而父上下文访问不到子上下文的信息,这个根上下文, 就很适合作为多个子上下文配置的集中点。以官方文档中的图来说明:
Servlet和SpringDispatch

至此,完成配置元素的对象化,以及Servlet和spring 容器的结合!


2.一个请求从浏览器到SpringMVC框架

DNS域名解析,Ngnix的负载均衡,这部分我们先接过,直接说这个请求到了服务器以后的流程。请求是按照Servlet的规范,封装为了:HttpServletRequest,通过web.xml定义的URL匹配Servlet。

<servlet>
		<servlet-name>springmvc</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:springmvc-servlet.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
</servlet>
	<servlet-mapping>
		<servlet-name>springmvc</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>

然后就到了:

void org.springframework.web.servlet.DispatcherServlet.doService(HttpServletRequest request, HttpServletResponse response) throws Exception

具体的请求的过程,网络上面有N多的源码追踪,我们只看一下大致的过程,详细的可以搜一下:springMVC disPatcherServlet源码解析。

1.寻找URL所对应的mappedHandler处理器,如果找不到的话,执行noHandlerFound方法,其中就是会返回404。
2.如果是get请求的话,并且HTTP请求头标签中包含If-Modified-Since,在发送HTTP请求时,把浏览器端缓存页面的最后修改时间一起发到服务器去,服务器会把这个时间与服务器上实际文件的最后修改时间进行比较。如果时间一致,那么返回HTTP状态码304(不返回文件内容),客户端接到之后,就直接把本地缓存文件显示到浏览器。 3.获取到自己定义的拦截器,循环invoke拦截器。
4.回调我们所定义的mapperHandler 也就是我们再controller中所定义的处理这个请求的方法。
5.获取到返回的 MV 通过response写出。

/**
	 * Process the actual dispatching to the handler.
	 * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
	 * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
	 * to find the first that supports the handler class.
	 * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
	 * themselves to decide which methods are acceptable.
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @throws Exception in case of any kind of processing failure
	 */
	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		int interceptorIndex = -1;
		try {
			ModelAndView mv;//返回结果
			boolean errorView = false;

			try {
			  // 检查请求是否是上传文件的请求①
			  // 请求为POST, String contentType = request.getContentType();
			  // contentType 按照:multipart/ 开头的
				processedRequest = checkMultipart(request);

				//找到handlerMapping ②
				mappedHandler = getHandler(processedRequest, false);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
				//寻找URL所对应的mappedHandler处理器,如果找不到的话,执行noHandlerFound方法,其中就是会返回404。
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
				// 找到Handler的HandlerAdapter,这个handlerAdapter的就是:
				// 是:org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter  

				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (logger.isDebugEnabled()) {
						String requestUri = urlPathHelper.getRequestUri(request);
						logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
					}
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

				// Apply preHandle methods of registered interceptors.
				// 拦截器的运行,3.2.1源代码已经抽象抽来成为一个方法了:
				//		if (!mappedHandler.applyPreHandle(processedRequest, response)) {
				//		return;
				//		}  

				HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
				if (interceptors != null) {
					for (int i = 0; i < interceptors.length; i++) {
						HandlerInterceptor interceptor = interceptors[i];
						if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
							triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
							return;
						}
						interceptorIndex = i;
					}
				}

				// Actually invoke the handler.
				// 怎么找到Controller中URL对应的方法的:http://cheng-xinwei.iteye.com/blog/2008507  
				// ③ 下面会根据这个博客,看一下具体的逻辑
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				// Do we need view name translation?
				if (mv != null && !mv.hasView()) {
					mv.setViewName(getDefaultViewName(request));
				}

				// Apply postHandle methods of registered interceptors.
				if (interceptors != null) {
					for (int i = interceptors.length - 1; i >= 0; i--) {
						HandlerInterceptor interceptor = interceptors[i];
						interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
					}
				}
			}
			catch (ModelAndViewDefiningException ex) {
				logger.debug("ModelAndViewDefiningException encountered", ex);
				mv = ex.getModelAndView();
			}
			catch (Exception ex) {
				Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
				mv = processHandlerException(processedRequest, response, handler, ex);
				errorView = (mv != null);
			}

			// Did the handler return a view to render?
			if (mv != null && !mv.wasCleared()) {
				render(mv, processedRequest, response);
				if (errorView) {
					WebUtils.clearErrorRequestAttributes(request);
				}
			}
			else {
				if (logger.isDebugEnabled()) {
					logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
							"': assuming HandlerAdapter completed request handling");
				}
			}

			// Trigger after-completion for successful outcome.
			triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
		}

		catch (Exception ex) {
			// Trigger after-completion for thrown exception.
			triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
			throw ex;
		}
		catch (Error err) {
			ServletException ex = new NestedServletException("Handler processing failed", err);
			// Trigger after-completion for thrown exception.
			triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
			throw ex;
		}

		finally {
			// Clean up any resources used by a multipart request.
			if (processedRequest != request) {
				cleanupMultipart(processedRequest);
			}
		}
	}
//找到handlerMapping ②
这个执行的链,是由我们定义的:
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
        <property name="interceptors">
            <list>        
                <bean class="com.chanjet.imp.push.rest.interceptor.PushRestAuthInterceptor">
                    <property name="debugMode" value="false" />
                    <property name="returnJson" value="true" />
                </bean>
            </list>
        </property>
</bean>
返回执行的链:

执行链

③,我发现自己的代码spring3.2.1的实现有了变化,很有意思的事情,我们慢慢的看,也就是关注4的工作,通过URL定位到:controller的具体的方法。

// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		Class<?> clazz = ClassUtils.getUserClass(handler);
		Boolean annotatedWithSessionAttributes = this.sessionAnnotatedClassesCache.get(clazz);
		if (annotatedWithSessionAttributes == null) {
			annotatedWithSessionAttributes = (AnnotationUtils.findAnnotation(clazz, SessionAttributes.class) != null);
			this.sessionAnnotatedClassesCache.put(clazz, annotatedWithSessionAttributes);
		}

		if (annotatedWithSessionAttributes) {
			// Always prevent caching in case of session attribute management.
			checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
			// Prepare cached set of session attributes names.
		}
		else {
			// Uses configured default cacheSeconds setting.
			checkAndPrepare(request, response, true);
		}

		// Execute invokeHandlerMethod in synchronized block if required.
		if (this.synchronizeOnSession) {
			HttpSession session = request.getSession(false);
			if (session != null) {
				Object mutex = WebUtils.getSessionMutex(session);
				synchronized (mutex) {
					return invokeHandlerMethod(request, response, handler);
				}
			}
		}
		//上面的代码就是http session的限制判断,并且做了缓存设置,我们主要看的是invokeHandlerMethod
		return invokeHandlerMethod(request, response, handler);
	}
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		//private final Set<Method> handlerMethods = new LinkedHashSet<Method>();
		//首先就是得到handler中的标注(Annotion)的方法,也就是响应具体URL请求的方法
		ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);

		Method handlerMethod = methodResolver.resolveHandlerMethod(request);
		ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		ExtendedModelMap implicitModel = new BindingAwareModelMap();

		Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
		ModelAndView mav =
				methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
		methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
		return mav;
	}


	/**
	 * Build a HandlerMethodResolver for the given handler type.
	 */
	private ServletHandlerMethodResolver getMethodResolver(Object handler) {
	  // 首先是得到handler的URL映射的方法名称,参数名称,放入缓存methodResolverCache
	  // 这个缓存设计的
		Class handlerClass = ClassUtils.getUserClass(handler);//④
		ServletHandlerMethodResolver resolver = this.methodResolverCache.get(handlerClass);
		if (resolver == null) {
			synchronized (this.methodResolverCache) {
				resolver = this.methodResolverCache.get(handlerClass);
				if (resolver == null) {
					resolver = new ServletHandlerMethodResolver(handlerClass);
					this.methodResolverCache.put(handlerClass, resolver);
				}
			}
		}
		return resolver;
	}

	/**④
	 * Return the user-defined class for the given class: usually simply the given
	 * class, but the original class in case of a CGLIB-generated subclass.
	 * @param clazz the class to check
	 * @return the user-defined class
	 */
	public static Class<?> getUserClass(Class<?> clazz) {
		// CGLIB-generated 的子类的检查
		if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) {
			Class<?> superClass = clazz.getSuperclass();
			if (superClass != null && !Object.class.equals(superClass)) {
				return superClass;
			}
		}
		return clazz;
	}

这个都从08-01 写到 08-03 了,重新开始一篇,重新写。



上一篇  SpringMVC 中找到响应URL的具体的方法 下一篇   servlet总结(三)servlet规范很有意思