1、spring mvc 应用

1、在web.xml中配置

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

2、拦截器的配置

<!-- 登录拦截器 -->
 <mvc:interceptors>    
	    <mvc:interceptor>  
	        <mvc:mapping path="/myMobileHome/**" />
	        <mvc:mapping path="/myMobileOrder/**" />
	        <bean class="*.MobileLoginInterceptor"></bean>    
	    </mvc:interceptor>
	</mvc:interceptors>
public class MobileLoginInterceptor implements HandlerInterceptor {
	/**
    *  handler :处理器(控制器) = controller对象
    *      preHandle :处理请求的(再调用controller对象方法之前执行)
    *                :对请求进行放行(继续执行进入到方法)
    *                :对请求过滤
    *      返回值:true = 放行
    *             false = 过滤
    *
    */
   public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
      System.out.println("执行MobileLoginInterceptor 的preHandle方法");
      return true; //放行
   }


   /**
    *  postHandle  调用控制器方法之后执行的方法
    *            :处理响应的
    */
   public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
      System.out.println("执行MobileLoginInterceptor 的postHandle方法");
   }


   /**
    *  afterCompletion :
    *      整个请求处理完毕,在视图渲染完毕时回调,一般用于资源的清理或性能的统计
    * 			在多个拦截器调用的过程中,
    * 				afterCompletion是否取决于preHandler是否=true
    *
    */
   public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
      System.out.println("执行MobileLoginInterceptor 的afterCompletion方法");
   }

}

3、spring mvc 常用注解
@RequestMapping注解为控制器指定可以处理哪些 URL 请求
@Resource和@Autowired 都是做bean的注入时使用
@RequestBody该注解用于读取Request请求的body部分数据,使用系统默认配置的HttpMessageConverter进行解析,然后把相应的数据绑定到要返回的对象上再把HttpMessageConverter返回的对象数据绑定到 controller中方法的参数上   @ResponseBody该注解用于将Controller的方法返回的对象,通过适HttpMessageConverter转换为指定格式后,写入到Response对象的body数据区
@ModelAttribute 在方法定义上使Spring MVC在调用目标处理方法前,会先逐个调用在方法级上标注了, 的方法 在方法的入参前使用
@ModelAttribute 注解:可以从隐含对象中获取隐含的模型数据中获取对象,再将请求参数–绑定到对象中,再传入入参将方法入参对象添加到模型中
@RequestParam在处理方法入参处使用 可以把请求参 数传递给请求方法
@PathVariable 绑定 URL 占位符到入参
@ExceptionHandler   注解到方法上,出现异常时会执行该方法
@ControllerAdvice 使一个Contoller成为全局的异常处理类,类中用
@ExceptionHandler方法注解的方法可以处理所有Controller发生的异常
4、spring mvc统一异常处理可以看下这个

2、WebApplicationContext 初始化过程

1、spring中ContextloaderListener 实现 ServletContextListener接口,在web.xml文件中也有配置,在tomcat启动时会先调用ServletContextListener的contextInitialized方法,ContextloaderListener 中的contextInitialized中代码做了什么
    1、webapplicationcontext存在性的验证
    2、创建webapplicationcontext实例
    3、将实例记录在servletContext中
    4、映射当前的类加载器与创建的实例到全局变量currentContextPerThread中
2、tomcat启动之后页面会通过请求去调用controller。spring mvc中的controller的调用其实也是通过servlet去实现的。【servlet的生命周期在文末】而servlet在初始化的时候会调用init方法进行初始化。在web.xml中只配置了一个servlet的类所以要执行一次init,init做了什么了,首先我们通过web.xml中配置的DispatcherServlet这个servlet,然后在其父类HttpServletBean中找到了init方法

@Override
	public final void init() throws ServletException {
		//当dispatcherServlet初始化的时候
		if (logger.isDebugEnabled()) {
			logger.debug("Initializing servlet '" + getServletName() + "'");
		}
		//解析init-param并封装至pvs中
		PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
		if (!pvs.isEmpty()) {
			try {
				//将当前的这个servlet类转化未一个beanwrapper,从而能够以spring的方式来对init-param的只进行注入
				BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
				ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
				//注册自定义属性编辑器,一旦遇到Resource类型的属性将会使用ResourceEditor进行解析
				bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
				//空实现留给子类覆盖
				initBeanWrapper(bw);
				bw.setPropertyValues(pvs, true);
			}
			catch (BeansException ex) {
				if (logger.isErrorEnabled()) {
					logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
				}
				throw ex;
			}
		}
		//初始化这个servletBean
		initServletBean();
		if (logger.isDebugEnabled()) {
			logger.debug("Servlet '" + getServletName() + "' configured successfully");
		}
	}

    1、封装及验证初始化参数
    2、将当前servlet实例转换为BeanWrapper
    3、注册相对于rerource的属性编辑器
    4、属性的注入
    5、servletBean的初始化 initServletBean();DispatcherServlet父类的FrameworkServlet覆盖了HttpServletBean中的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 {

			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");
		}
	}

从上面的代码我们可以看出实际上只是调用了 initWebApplicationContext(); 方法这个方法的主要作用时创建或者获取webApplicationContext类的对象

protected WebApplicationContext initWebApplicationContext() {
		//从servletContext中获取webApplicationContext
		WebApplicationContext rootContext =WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;
		if (this.webApplicationContext != null) {			
			//context实例在构造函数中被注入
			wac = this.webApplicationContext;
			if (wac instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				if (!cwac.isActive()) {				
					if (cwac.getParent() == null) {
						cwac.setParent(rootContext);
					}
					//刷新上下文环境
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
		if (wac == null) {
			//根据contextAttribute属性加载webApplicationContext
			wac = findWebApplicationContext();
		}
		if (wac == null) {
			// No context instance is defined for this servlet -> create a local one
			wac = createWebApplicationContext(rootContext);
		}

		//
		if (!this.refreshEventReceived) {
			//上下文不是具有刷新功能的ConfigurableApplicationContext
			//在构建时注入的支持或上下文已经
			//刷新->在此处手动触发初始刷新。
			onRefresh(wac);
		}

		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;
	}

    1、通过构造函数的注入进行初始化WebApplicationContext 【WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());】
    2、通过contextAttribute进行初始化WebApplicationContext【wac = findWebApplicationContext();】
    3、重新创建WebApplicationContext【wac = createWebApplicationContext(rootContext);】无论是通过构造函数注入的还是单独创建,都会掉用 configureAndRefreshWebApplicationContext方法对已经创建的webApplicationContext实例进行配置及刷新。

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
		if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
			// The application context id is still set to its original default value
			// -> assign a more useful id based on available information
			if (this.contextId != null) {
				wac.setId(this.contextId);
			}
			else {
				// Generate default id...
				wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
						ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
			}
		}

		wac.setServletContext(getServletContext());
		wac.setServletConfig(getServletConfig());
		wac.setNamespace(getNamespace());
		wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

		// The wac environment's #initPropertySources will be called in any case when the context
		// is refreshed; do it eagerly here to ensure servlet property sources are in place for
		// use in any post-processing or initialization that occurs below prior to #refresh
		ConfigurableEnvironment env = wac.getEnvironment();
		if (env instanceof ConfigurableWebEnvironment) {
			((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
		}

		postProcessWebApplicationContext(wac);
		applyInitializers(wac);
		wac.refresh();
	}

看代码我们可以看到最后又到了 wac.refresh(); 【AbstractApplicationContext.refresh】,这个方法是用来初始化spring的ioc容器并,将容器单例的beanFactory进行初始化,在执行refresh的时候会调用finishRefresh();【作用:初始化容器的生命周期事件处理器,并发布容器的生命周期事件】方法去发布事件,然后再将bean注入到对应的对象中,即在bean依赖注入成功之后,在finishRefresh发布完事件之后DispatcherServlet类监听到事件发布了会调用 onApplicationEvent 这个方法
主要作用是初始springmvc的一些组件,好到这里 webapplicationcontext初始化完毕,即 DispatcherServlet的init方法执行完了

spring的事件是个什么的可以看博主的关于spring事件的博客

org.springframework.web.servlet.FrameworkServlet#onApplicationEvent	
	public void onApplicationEvent(ContextRefreshedEvent event) {
		this.refreshEventReceived = true;
		onRefresh(event.getApplicationContext());
	}
	org.springframework.web.servlet.DispatcherServlet#onRefresh/initStrategies
	protected void onRefresh(ApplicationContext context) {
		initStrategies(context);
	}
	protected void initStrategies(ApplicationContext context) {
		initMultipartResolver(context);
		initLocaleResolver(context);
		initThemeResolver(context);
		initHandlerMappings(context);
		initHandlerAdapters(context);
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
		initViewResolvers(context);
		initFlashMapManager(context);
	}

MultipartResolver:解析多部分请求,以支持从HTML表单上传文件。
LocaleResolver:解决客户正在使用的区域设置以及可能的时区,以便能够提供国际化视野。
ThemeResolver:解决Web应用程序可以使用的主题,例如提供个性化布局。

HandlerMapping:用于handlers映射请求和一系列的对于拦截器的前处理和后处理,大部分用@Controller注解。
HandlerAdapter:帮助DispatcherServlet处理映射请求处理程序的适配器,而不用考虑实际调用的是 哪个处理程序。
HandlerExceptionResolver:处理映射异常。
initRequestToViewNameTranslator:用于处理没有返回视图名时的情况下如何得到一个默认的视图名。
ViewResolver:根据实际配置解析实际的View类型。
FlashMapManager:存储并检索可用于将一个请求属性传递到另一个请求的input和output的FlashMap,通常用于重定向。
到这里webapplicationcontext初始化完毕

3、一个请求的的处理过程【DispatcherServlet的逻辑处理】

1、springmvc 是通过servlet实现的servlet中,前端的请求都是到doPost、doGet、doDelete、doPut、doDelete、doTrace、DispatcherServlet继承了FrameworkServlet类,FrameworkServlet继承了HttpServletBean抽象类,HttpServletBean继承了HttpServlet抽象类,FrameworkServlet类中实现了doPost、doGet等内部都是调用的 processRequest方法,内部又会去调用doService,然后其内部又会去调用doDispatcher放法,其内部就是具体处理当前这个请求的具体方法了
2、doDispatcher处理请求分为根据《spring源码深度解析》书,将请求的处理逻辑分为了9步
    1 MultipartContent类型的request处理
    2 根据request信息寻找对应的Handler
    3 根据当前Handler寻找对应的HandlerAdapter
    4 缓存处理
    5 HandlerInterceptor的处理
    6 逻辑处理
    7 异常视图的处理
    8 根据视图跳转页面

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

		try {
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
				// 1.检查是否是文件上传的请求
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// Determine handler for the current request.
				// 2.取得处理当前请求的controller,这里也称为hanlder,处理器,
				// 	 第一个步骤的意义就在这里体现了.这里并不是直接返回controller,
				//	 而是返回的HandlerExecutionChain请求处理器链对象,
				//	 该对象封装了handler和interceptors.
				mappedHandler = getHandler(processedRequest);
				// 如果handler为空,则返回404
				if (mappedHandler == null) {
					//如果没有找到对应的请求处理链则通过response反馈错误信息
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
				//3. 获取处理request的处理器适配器handler adapter
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

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

				//拦截器中的prehandler方法的调用
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// Actually invoke the handler.
				// 4.实际的处理器处理请求,返回结果视图对象
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}

				// 结果视图对象的处理
				applyDefaultViewName(processedRequest, mv);

				//应用所有拦截器的postHandle方法
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			catch (Throwable err) {
				// As of 4.3, we're processing Errors thrown from handler methods as well,
				// making them available for @ExceptionHandler methods and other scenarios.
				dispatchException = new NestedServletException("Handler dispatch failed", err);
			}
			//处理最后的结果,渲染之类的都在这里
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Throwable err) {
			triggerAfterCompletion(processedRequest, response, mappedHandler,
					new NestedServletException("Handler processing failed", err));
		}
		finally {
			if (asyncManager.isConcurrentHandlingStarted()) {
				// Instead of postHandle and afterCompletion
				if (mappedHandler != null) {
					// 请求成功响应之后的方法					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
			else {
				// Clean up any resources used by a multipart request.
				if (multipartRequestParsed) {
					cleanupMultipart(processedRequest);
				}
			}
		}
	}
下面是doDispatch 中的具体步骤
1 MultipartContent类型的request处理
		对于请求的处理如果是multipart的处理,即文件类型的请求,会将request转换为MyltipartHttpServletRequest类型的request
		processedRequest = checkMultipart(request);
	2、根据request信息寻找对应的Handler【包含当前请求对应的方法和当前请求需要被哪些拦截器进行拦截】
		mappedHandler = getHandler(processedRequest);
		对应下方的 getHandler方法的代码
	3、根据当前Handler寻找对应的HandlerAdapter
		通过HandlerAdapter得 子类的supports去做校验当前handler是否适合这个HandlerAdapter
		HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
		关于HandlerAdapter是干什么的博主有博客写关于handlerAdapter是干什么的博客
	4、缓存处理
		last-Modified缓存机制:第一次请求和第二次请求时间一样只返回304的状态码
		304(not changed) 没有改变
	5、拦截器的处理
		//拦截器中的prehandler方法的调用
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}
	6、执行链接对应的方法
		
	7、拦截器的后置处理和结果视图处理
		// 结果视图对象的处理
		applyDefaultViewName(processedRequest, mv);
		//应用所有拦截器的postHandle方法
		mappedHandler.applyPostHandle(processedRequest, response, mv);
	8、根据视图跳转到页面
		//处理最后的结果,渲染之类的都在这里
		processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		这个方法中主要有两步
			1、视图名称解析
			2、处理页面跳转
				View接口的子类的实现调用不同 的renderMergedOutputModel方法
				org.springframework.web.servlet.view.InternalResourceView 实现页面的跳转
				renderMergedOutputModel	
				//org.springframework.web.servlet.view.json.AbstractJackson2View 实现返回json[不确定是不是这个类]
				renderMergedOutputModel
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		//根据request获取对应的handler
		Object handler = getHandlerInternal(request);
		if (handler == null) {
			//如果没有对应的request的handler则使用默认的handler
			handler = getDefaultHandler();
		}
		//如果没有默认的无法处理返回null
		if (handler == null) {
			return null;
		}
		// Bean name or resolved handler?
		if (handler instanceof String) {
			String handlerName = (String) handler;
			handler = obtainApplicationContext().getBean(handlerName);
		}
		//获取这个请求需要被哪些拦截器拦截
		HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
		if (CorsUtils.isCorsRequest(request)) {
			CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
			CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
			CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
			executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
		}
		return executionChain;
	}
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
			@Nullable Exception exception) throws Exception {

		boolean errorView = false;

		//如果有异常就走统一异常处理拦截器
		if (exception != null) {
			if (exception instanceof ModelAndViewDefiningException) {
				logger.debug("ModelAndViewDefiningException encountered", exception);
				mv = ((ModelAndViewDefiningException) exception).getModelAndView();
			}
			else {
				Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
				mv = processHandlerException(request, response, handler, exception);
				errorView = (mv != null);
			}
		}

		// Did the handler return a view to render?
		if (mv != null && !mv.wasCleared()) {
			//渲染这个页面
			render(mv, request, 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");
			}
		}

		if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
			// Concurrent handling started during a forward
			return;
		}

		if (mappedHandler != null) {
			mappedHandler.triggerAfterCompletion(request, response, null);
		}
	}
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
		// Determine locale for request and apply it to the response.
		Locale locale =
				(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
		response.setLocale(locale);

		View view;
		String viewName = mv.getViewName();
		if (viewName != null) {
			// We need to resolve the view name.
			//解析视图名称
			view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
			if (view == null) {
				throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
						"' in servlet with name '" + getServletName() + "'");
			}
		}
		else {
			// No need to lookup: the ModelAndView object contains the actual View object.
			view = mv.getView();
			if (view == null) {
				throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
						"View object in servlet with name '" + getServletName() + "'");
			}
		}

		// Delegate to the View object for rendering.
		if (logger.isDebugEnabled()) {
			logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
		}
		try {
			if (mv.getStatus() != null) {
				response.setStatus(mv.getStatus().value());
			}
			view.render(mv.getModelInternal(), request, response);
		}
		catch (Exception ex) {
			if (logger.isDebugEnabled()) {
				logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
						getServletName() + "'", ex);
			}
			throw ex;
		}
	}
@Override
	protected void renderMergedOutputModel(
			Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {

		// Expose the model object as request attributes.
		exposeModelAsRequestAttributes(model, request);

		// Expose helpers as request attributes, if any.
		exposeHelpers(request);

		// Determine the path for the request dispatcher.
		String dispatcherPath = prepareForRendering(request, response);

		// Obtain a RequestDispatcher for the target resource (typically a JSP).
		RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
		if (rd == null) {
			throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
					"]: Check that the corresponding file exists within your web application archive!");
		}

		// If already included or response already committed, perform include, else forward.
		if (useInclude(request, response)) {
			response.setContentType(getContentType());
			if (logger.isDebugEnabled()) {
				logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
			}
			rd.include(request, response);
		}

		else {
			// Note: The forwarded resource is supposed to determine the content type itself.
			if (logger.isDebugEnabled()) {
				logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
			}
			rd.forward(request, response);
		}
	}

servlet是什么

是一个java编写的程序,此程序是基于http协议的,在服务器端运行的是按照servlet规范编写的一个java类。主要是处理客户端的请求,并将其结果发送到客户端。servlet的生命周期是由servlet容器控制的,分为3个阶段:初始化,运行和销毁
1、初始化阶段

  • servlet容器加载servlet类,把servlet类的class文的数据读到内存中
  • servlet 容器创建一个servletconfig对象。servletconfig对象包含了servlet的初始化配置信息。
  • servlet容器创建一个servlet对象。
  • servlet容器调用servlet对象的init方法进行初始化。
    2、运行阶段
    当servlet容器结束到一个请求时,servlet容器会针对这个请求创建servletRequest和servletResponse对象,然后调用service方法。并把这两个参数传递给service方法。service方法通过servletRequest对象获得请求的信息。并处理该请求。再通过servletResponser对象生成这个请求的响应结果。然后销毁servletRequest和servletResponse对象。我们不管这个请求1时post提交还是get提交的,最终这个请求都会由service方法来处理。
    3、销毁阶段
    当web应用被终止时,servlet容器会先调用servlet对象的destory方法,然后销毁servlet对象,同时也会销毁与servlet对象相关联的servletConfig对象。我们可以在destory方法的实现中,释放servlet所占用的资源,如果关闭数据库连接,关闭文件输入流等。

Last-Modified

在浏览器第一次请求某一个URL时,服务器端的返回状态会是200,内容是客户端请求的资 源,同时有一个Last-Modified的属性标记此文件在服务器端最后被修改的时间。Last-Modified格式类似这样:
Last-Modified : Fri , 12 May 2006 18:53:33 GMT
客户端第二次请求此URL时,根据HTTP协议的规定,浏览器会向服务器传送If-Modified-Since 报头,询问该时间之后文件是否有被修改过:
If-Modified-Since : Fri , 12 May 2006 18:53:33 GMT
如果服务器端的资源没有变化,则自动返回 HTTP 304(Not Changed.)状态码,内容为空,这样就节省了传输数据量。当服务器端代码发生改变或者重启服务器时,则重新发出资源,返回和第一次请求时类似。从而保证不向客户端重复发出资源,也保证当服务器有变化时,客户端能够得到最新的资源。