`
keepaneye
  • 浏览: 40743 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

Spirng 源代码学习笔记 Web 篇(一)DispatcherServlet

 
阅读更多

Spirng 源代码学习笔记 Web 篇(一)DispatcherServlet 

 

Spring MVC 将控制器、模型 、视图进行了很好的分离,请求的粗略处理过程如下图:

图中的 Front controller 指的就是

 

org.springframework.web.servlet.DispatcherServlet

 DispatcherServlet 是请求的唯一入口,控制着 MVC 的整个流程。

 

 一个 http 请求被 DispatcherServlet 拦截时,首先,根据 http 方法会映射到父类 FrameworkServlet 对应的 doGet、doPost、doPut 等方法上,最后都会转到 processRequest 方法,

然后调用 DispatcherServlet  的 doService 方法:

 

 

	/**
	 * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
	 * for the actual dispatching.
	 */
	@Override
	protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		if (logger.isDebugEnabled()) {
			String requestUri = urlPathHelper.getRequestUri(request);
			logger.debug("DispatcherServlet with name '" + getServletName() + "' processing " + request.getMethod() +
					" request for [" + requestUri + "]");
		}

		// Keep a snapshot of the request attributes in case of an include,
		// to be able to restore the original attributes after the include.
		Map<String, Object> attributesSnapshot = null;
		if (WebUtils.isIncludeRequest(request)) {
			logger.debug("Taking snapshot of request attributes before include");
			attributesSnapshot = new HashMap<String, Object>();
			Enumeration attrNames = request.getAttributeNames();
			while (attrNames.hasMoreElements()) {
				String attrName = (String) attrNames.nextElement();
				if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
					attributesSnapshot.put(attrName, request.getAttribute(attrName));
				}
			}
		}

		// Make framework objects available to handlers and view objects.
		request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
		request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
		request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
		request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

		try {
			doDispatch(request, response);
		}
		finally {
			// Restore the original attribute snapshot, in case of an include.
			if (attributesSnapshot != null) {
				restoreAttributesAfterInclude(request, attributesSnapshot);
			}
		}
	}
 

 该方法绑定部分常用信息到当前线程,便于在后续处理中共享信息。然后调用 doDispatch 方法

 

	/**
	 * 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 {
				processedRequest = checkMultipart(request);

				// Determine handler for the current request.
				mappedHandler = getHandler(processedRequest, false);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
				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.
				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.
				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);
			}
		}
	}
 

     getHandler 根据请求 以及 handlerMapping 映射到实际的处理类(即 Controller),找到对应的 HandlerAdapter 。

     如果存在 HandlerInterceptor ,则在调用实际处理类前,调用 preHandler方法;在完成后,调用 postHandler。

     调用实际 Controller 后,返回的视图一般只是视图的名称。viewResolve 会根据名称解析实际的视图对象,然后调用视图对象的 render 方法进行实际的视图渲染。这样,视图的解析、渲染过程和请求的处理过程就分开了,互不影响。

 

DispatcherServlet 初始化

 

DispatcherServlet 是定义在 web.xml 中的 Servlet ,在服务器启动时调用父类 HttpServletBean  的 init() 方法开始初始化。

 

	public final void init() throws ServletException {
		if (logger.isDebugEnabled()) {
			logger.debug("Initializing servlet '" + getServletName() + "'");
		}

		// Set bean properties from init parameters.
		try {
			PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
			BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
			ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
			bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader));
			initBeanWrapper(bw);
			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 配置,然后调用 FrameworkServlet 的 initServletBean() ,initServletBean()中调用 initWebApplicationContext () 创建 Spring 应用上下文 ,

 

	protected WebApplicationContext initWebApplicationContext() {
		WebApplicationContext wac = findWebApplicationContext();
		if (wac == null) {
			// No fixed context defined for this servlet - create a local one.
			WebApplicationContext parent =
					WebApplicationContextUtils.getWebApplicationContext(getServletContext());
			wac = createWebApplicationContext(parent);
		}

		if (!this.refreshEventReceived) {
			// Apparently not a ConfigurableApplicationContext with refresh support:
			// triggering initial onRefresh manually here.
			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;
	}

创建完成后调用 DispatcherServlet 的 onRefresh(wac) 方法进行 DispatcherServlet 的初始化工作。

  

	/**
	 * This implementation calls {@link #initStrategies}.
	 */
	@Override
	protected void onRefresh(ApplicationContext context) {
		initStrategies(context);
	}

	/**
	 * Initialize the strategy objects that this servlet uses.
	 * <p>May be overridden in subclasses in order to initialize further strategy objects.
	 */
	protected void initStrategies(ApplicationContext context) {
		initMultipartResolver(context);
		initLocaleResolver(context);
		initThemeResolver(context);
		initHandlerMappings(context);
		initHandlerAdapters(context);
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
		initViewResolvers(context);
	}

 

初始化中的每个方法负责初始化一个类型的组件,这些组件用户都可以自定义。当没有自定义时,自动根据 DispatcherServlet.properties 文件中配置的类型进行初始化。配置多个的,说明该项组件可以是一个集合。

 

 

# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
	org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
	org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
	org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter

org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\
	org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
	org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
 

 

默认的 HandlerMapping 为根据 url 映射为 Controller 的 BeanName,以及注解解析类;注解解析类在创建时会解析 spring 上下文中所有的类,解析类或方法上标注的 @RequestMapping,以后将其中的 url 都映射为对应的 Handler。 

 

默认的视图解析类为 jsp 解析类。

 

至此,DispatcherServlet 的初始化工作完成。

 

  • 大小: 123.7 KB
分享到:
评论

相关推荐

    Spring笔记示例源代码

    "Spring笔记示例源代码"这个资源很可能是为了帮助学习者深入理解Spring框架的各种功能和用法而提供的实际代码示例。 1. **Spring IoC**:IoC是Spring的核心特性,它将对象的创建和管理权交给Spring容器,使得开发者...

    spring编程学习笔记

    在"Spring笔记.pdf"、"Spring笔记1.pdf"和"Spring编程学习笔记2.pdf"中,你将找到关于这些概念的详细解释和实例,包括XML配置、注解驱动的编程、AOP的实现方式、Spring Boot的快速启动指南以及Spring MVC的...

    Spring MVC学习笔记MD.7z

    这个压缩包“Spring MVC学习笔记MD.7z”包含了作者在学习Spring MVC过程中的笔记、源代码以及相关文档,非常适合初学者或希望深入理解Spring MVC的开发者。 首先,`SpringMVC-Study.7z` 可能是作者整理的Spring MVC...

    Spring+SpringMVC基础学习笔记(b站课程学习笔记)

    Spring框架是Java开发中广泛应用的一个轻量级框架,它的核心特性是依赖注入(Dependency Injection,DI),这使得组件之间的耦合度大大降低,提高了代码的可测试性和可维护性。Spring框架提供了大量的功能模块,包括...

    尚硅谷spring5视频笔记.zip

    《尚硅谷spring5视频笔记》是一份详尽的Spring框架第五版的学习资料,它涵盖了Spring 5的关键概念、新特性和实战应用。Spring是Java领域中最流行的企业级应用框架之一,Spring 5作为其最新版本,引入了许多重要的...

    Spring学习笔记

    本学习笔记旨在深入探讨Spring框架的主要概念和功能,帮助读者从基础到进阶全面掌握Spring。 一、Spring概述 Spring是一个开源的Java平台,它提供了全面的企业级应用开发解决方案,包括数据访问、事务管理、远程...

    java框架学习笔记spring笔记

    4. **Spring MVC**:Spring 提供了一个用于构建 Web 应用的模块——Spring MVC。它遵循 Model-View-Controller 设计模式,使得开发人员能够清晰地分离业务逻辑、数据模型和用户界面。Spring MVC 包括 ...

    【原创】Mybatis学习笔记(一)——Spring集成Mybatis

    在本篇【原创】Mybatis学习笔记(一)——Spring集成Mybatis中,我们将探讨如何将流行的持久层框架Mybatis与Spring框架进行整合,以便在实际项目开发中实现灵活、高效的数据库操作。以下是对相关知识点的详细说明: ...

    spring2.0宝典一书笔记

    Spring MVC 是一个用于构建 Web 应用的轻量级框架,它包含了一系列组件,如控制器(Controller)、验证器(Validator)、命令对象(Command Object)、表单对象(Form Object)、模型对象(Model Object)、分发器...

    JSF2整合Spring3------JSF学习笔记4

    **JSF2整合Spring3——JSF学习笔记4** 在Java服务器端开发中,JavaServer Faces(JSF)和Spring框架都是重要的技术。JSF是一个用于构建用户界面的MVC(Model-View-Controller)框架,而Spring则是一个全面的企业级...

    spring培训-笔记

    本笔记将深入探讨Spring框架的关键概念和技术,帮助你掌握这一强大的工具。 1. **依赖注入(Dependency Injection,DI)**: DI是Spring的核心特性,它允许我们解耦组件之间的关系,通过外部容器来管理对象的创建...

    传智播客2016spring资料4

    4. **Spring MVC**:如果涉及Web开发,可能会介绍Spring的Model-View-Controller架构,包括DispatcherServlet、控制器(Controller)、模型对象、视图解析等。 5. **Spring的IoC容器**:深入理解IoC容器的工作原理...

    SpringMVC+Mybatis学习笔记

    Spring的AOP支持在不修改源代码的情况下,对代码进行功能增强,如日志记录、事务管理等。AOP的关键概念包括: - **切面(Aspect)**:业务流程中的一个关注点,可以横切多个对象。 - **连接点(Joinpoint)**:程序...

    spring笔记.zip

    AOP则是Spring提供的另一种强大工具,它允许我们在不修改源代码的情况下,对程序进行功能增强。比如,我们可以通过AOP实现日志记录、事务管理、性能监控等横切关注点。Spring AOP支持使用注解定义切面,简化了切面的...

    SSM 框架整合教程:一、MyBatis-尚硅谷学习笔记 2022 年

    - "SSM框架整合教程:一、MyBatis——尚硅谷学习笔记 2022 年.md":Markdown格式的学习笔记,详细记录了教程中的关键知识点和步骤。 - "SSM框架整合教程:一、MyBatis——尚硅谷学习笔记 2022 年.pdf":PDF版本的...

    Spring Boot 笔记.rar

    尚硅谷的 Spring Boot 笔记涵盖了以上这些核心概念,并可能深入到实际应用案例、问题解决、最佳实践等方面,对于学习和进阶 Spring Boot 的开发者来说是一份宝贵的资源。通过阅读这些笔记,你可以更好地理解 Spring ...

    学习mybatis、spring、mvc、springboot的笔记

    3. Spring MVC:Spring MVC是Spring框架的一部分,它是一个模型-视图-控制器(MVC)架构,用于开发Web应用程序。Spring MVC通过分离关注点,简化了Web开发,允许开发者专注于业务逻辑,而无需关心基础设施细节。它...

    spring 笔记大全

    - **Spring Web**:提供了 Web 应用程序开发所需的支持,如 ServletListener、DispatcherServlet 等。 - **Spring MVC**:这是一个用于构建 Web 应用程序的模型-视图-控制器框架,它是 Spring Web 模块的一部分,...

    Spring框架黑马程序员的2016版第二天的相关的资料

    5. **Spring MVC**:对于Web应用,Spring MVC是Spring提供的一个用于构建MVC模式的框架。我们将学习DispatcherServlet、Controller、ModelAndView、视图解析器等核心组件,以及如何处理HTTP请求和响应。 6. **...

    ssm.zip_2Y7_ssm_ssm学习笔记_ssm整合

    这个"ssm.zip_2Y7_ssm_ssm学习笔记_ssm整合"压缩包文件很可能是某个开发者的学习资料,包含了他对SSM整合的理解和实践总结。以下是对SSM整合及相关知识点的详细解释。 1. **Spring框架**:Spring是一个全面的Java...

Global site tag (gtag.js) - Google Analytics