夫人之相与,俯仰一世,或取诸怀抱,晤言一室之内;或因寄所托,放浪形骸之外。虽趣(取/趋)舍万殊,静躁不同,当其欣于所遇,暂得于己,怏然自足,不知老之将至;及其所之既倦,情随事迁,感慨系之矣。向之所欣,俯仰之间,已为陈迹,犹不能不以之兴怀;况修短随化,终期于尽。古人云:“死生亦大矣。”岂不痛哉!
疲惫期,慢慢在堕落
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都要早。换句话说,*这个时候,你对
在初始化的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和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 了,重新开始一篇,重新写。