`
come_for_dream
  • 浏览: 120249 次
  • 性别: Icon_minigender_1
  • 来自: 长沙
社区版块
存档分类
最新评论

Spring MVC源码解读之请求处理

阅读更多

自从接触Spring开始就对这个框架比较喜欢吧,所以我最近在抽时间深入研究这个框架,因为在项目中用Spring MVC比较多所以打算深入理解一下这个框架。

     首先我们要从web.xml讲起,web.xml中配置着用来进行初始化的各种配置信息:欢迎页、servlet、servlet-mapping、filter、lister、启动的记载级别等等信息。但是Spring MVC不同与Struts2(使用Filter拦截请求FilterDispatcher),Spring MVC是使用Servlet进行请求的拦截(DispatcherServlet进行拦截)。

     首先我们知道Java web的加载顺序是:content-param --> listener --> filter --> servlet。所以我像按照这个顺序对Spring mvc的加载和处理流出进行讲解。

    1、在启动Web服务器的时候会对web.xml中的<listener></listener> 和 <context-param></context-param>   进行读取并加载。context-param,它用于向 ServletContext 提供键值对,即应用程序上下文信息。我们的 listener, filter 等在初始化时会用到这些上下文中的信息。

这里在Spring mvc中的体现如下:

      

        <context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath*:config/core/spring-core.xml</param-value>
	</context-param>

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

 这里的contextConfigLocation了Spring的配置文件是非常重要的,它以context-param的方式注册并在ContextLoaderListener中监听读取,ContextLoaderListener的职责是负责Spring IOC容器在web上下文的初始化,也就是说这正是这两个参数是我们的Spring容器和我们的Web容器相结合。ContextLoaderListener在web容器启动的时候自动装配ApplicationContext的配置信息。它实现了ServletContextLister这个接口,使我们在为客户端提供请求服务之前向ServletContext中添加任意的对象,这个对象在ServletContext启动的时候被初始化,然后在ServletContext整个运行期间都是可见的。所以ContextLoaderListener的核心就是初始化WebApplication并将实例放到ServletContext中。

 

  2、当然filter也是必须的我们可以用它来进行编码过滤,权限控制等等但是在Spring mvc这里不做深入的介绍。

  3、配置Servlet,首先介绍一下Servlet,他是一个用Java编写的基于HTTP协议的在服务器端运行的Java类。它的生命周期由web容器进行控制,其生命周期包括:初始化、运行和销毁具体的过程如下:

            (初始化)

  • Servlet容器加载servlet类,把servlet类的class文件读入内存。
  • servlet容器创建一个ServletConfig对象。ServletConfig对象包括了servlet的初始化配置信息。
  • servlet容器创建一个Servle对象
  • servlet容器调用servlet的init方法进行初始化。

        (运行阶段)

  • 当servlet容器接收到一个请求的时候,servlet会针对这个请求创建一个servletRequest和servletResponse对象,然后调用service方法,service方法通过servletRequest对象获得请求信息。并处理该请求。在通过servletResponse生成这个请求的响应。然后销毁servletRequest和servletResponse对象。

       (销毁阶段)

  • 当Web应用被终止的时候,servlet容器首先会调用servlet的destory方法,然后在销毁servlet对象,同时也会删除与之关联的servletConfig对象。

     那么我们回到Spring MVC中,前面说过ContextLoaderListener只是辅助功能用于创建WebApplicationContext类型的实例,真正的逻辑其实是在DispatcherServlet中实现的。

    

public class  DispatcherServlet extends FrameworkServlet   

public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware 

 所以我在HttpServletBean中找到了其init()方法 

 

 

/**
	 * Map config parameters onto bean properties of this servlet, and
	 * invoke subclass initialization.
	 * @throws ServletException if bean properties are invalid (or required
	 * properties are missing), or if subclass initialization fails.
	 */
	@Override
	public final void init() throws ServletException {
		if (logger.isDebugEnabled()) {
			logger.debug("Initializing servlet '" + getServletName() + "'");
		}

		// Set bean properties from init parameters.
		try {
                        //解析init-param并封装到pvs中
			PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
                        //将当前的这个Servlet类转化为一BeanWrapper以便于使用Spring的方式对init-param的值进行注入
			BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
			ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
                         //注册自定义的属性编辑器,一旦遇到Resource类型的属性将会使用
			bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
			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");
		}
	}

 DispatcherServlet的初始化过程主要是通过将当前的servlet类型的实例转换为BeanWrapper类型的实例,以便于Spring对其进行对应属性的注入,这些属性我在FrameworkServlet 中看到的有:

 

/** ServletContext attribute to find the WebApplicationContext in */
	private String contextAttribute;

	/** WebApplicationContext implementation class to create */
	private Class<?> contextClass = DEFAULT_CONTEXT_CLASS;

	/** WebApplicationContext id to assign */
	private String contextId;

	/** Namespace for this servlet */
	private String namespace;

	/** Explicit context config location */
	private String contextConfigLocation;

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

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

	/** Should we publish the context as a ServletContext attribute? */
	private boolean publishContext = true;

	/** Should we publish a ServletRequestHandledEvent at the end of each request? */
	private boolean publishEvents = true;

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

	/** Should we dispatch an HTTP OPTIONS request to {@link #doService}? */
	private boolean dispatchOptionsRequest = false;

	/** Should we dispatch an HTTP TRACE request to {@link #doService}? */
	private boolean dispatchTraceRequest = false;

	/** WebApplicationContext for this servlet */
	private WebApplicationContext webApplicationContext;

	/** If the WebApplicationContext was injected via {@link #setApplicationContext} */
	private boolean webApplicationContextInjected = false;

	/** Flag used to detect whether onRefresh has already been called */
	private boolean refreshEventReceived = false;

 

 

 

其主要的步骤如下:

  1. 封装及初始化参数
  2. 将当前的servlet类型的实例转换为BeanWrapper类型的实例
  3. 注册相对于Resource属性编辑器,
  4. 属性注入
  5. servletBean初始化

 

在DispatcherServlet 中对各种方法初始化:

 

/**
	 * 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) {
                //MultipartResolver用于文件上传
		initMultipartResolver(context);
                //国际化配置
		initLocaleResolver(context);
                //初始化ThemeResolver其通常用户处理不同用户对web不同风格的要求
		initThemeResolver(context);
                //初始化HandlerMappings
		initHandlerMappings(context);
                //用于根据Hander的类型定义不同的处理规则
		initHandlerAdapters(context);
                //处理Hander的错误
		initHandlerExceptionResolvers(context);
                //给相应链接转化为自己想要的格式,比如在前后添加后缀
		initRequestToViewNameTranslator(context);
                //用于将view解析成页面,可以制定不同的解析策略默认按照jsp进行解析,也可以按照velocity模板进行解析
		initViewResolvers(context);
		initFlashMapManager(context);
	}

 

 

其中比较重要的是HandlerMappings:当客户端发出Request请求的时候DispatcherServlet将请求提交给HandlerMappings,然后HandlerMappings根据Web Application Context的配置来回传给DispatcherServle相应的Controller,源代码如下:

/**
	 * Initialize the HandlerMappings used by this class.
	 * <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace,
	 * we default to BeanNameUrlHandlerMapping.
	 */
	private void initHandlerMappings(ApplicationContext context) {
		this.handlerMappings = null;

		if (this.detectAllHandlerMappings) {
			// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
			Map<String, HandlerMapping> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
			if (!matchingBeans.isEmpty()) {
				this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
				// We keep HandlerMappings in sorted order.
				AnnotationAwareOrderComparator.sort(this.handlerMappings);
			}
		}
		else {
			try {
				HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
				this.handlerMappings = Collections.singletonList(hm);
			}
			catch (NoSuchBeanDefinitionException ex) {
				// Ignore, we'll add a default HandlerMapping later.
			}
		}

		// Ensure we have at least one HandlerMapping, by registering
		// a default HandlerMapping if no other mappings are found.
		if (this.handlerMappings == null) {
			this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
			if (logger.isDebugEnabled()) {
				logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
			}
		}
	}

 

 

 

DispatcherServlet初始化的时序图如下:

 

 

 

 ----------------上面这些完成Spring mvc的大部分初始化工作下面来分析以下请求处理的流程------------------

 

1、web服务器接收到客户端发来的请求并将其封装成一个ServletRequest对象。

2、DispatcherServlet接收到这个请求后,并将请求的处理工作委托给具体的处理器Handler,后者负责具体的业务逻辑,DispatcherServlet通过一个或者多个处理程序映射,将每个请求映射到处理程序中。处理程序映射配置在web应用程序的上下文中,是实现了HandlerMapping接口的Bean,它负责根据请求的URL将请求映射到处理程序(Controller)。 

 

3、将请求分派给处理器进行处理,处理器处理完请求后,会将模型和视图名ModelAndView返回给DispatcherServlet 

4、DispatcherServlet 将得到的ModelAndView交给ViewResolver来进行逻辑视图名和真实视图对象的对应。

5、当得到真实的视图对象后,DispatcherServlet 将请求分派给这个View对象,由其完成Model数据的渲染工作。

6、客户端的得到返回的响应,结果可能是一个HTML、Excel、pdf文档等等。

 

 

但是在上面的步骤中,DispatherServlet接受到请求后是如何映射到Controller的呢?

在Spring MVC中,web请求是被Web应用程序上下文中声明的一个或者多个实现了HanderMapping接口的Bean映射到Controller的,在HanderMapping接口中了一个根据一个URL返回一个HandlerExecutionChain 

 

HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

 

下面是 HanderMapping的默认实现

1. BeanNameUrlHandlerMapping(默认情况),他根据Controller Bean名称中指定的URL模式将请求映射到处理程序上。 

eg. <bean name="/welcome.htm" class="com.kevin.controller.WelcomeController">...</bean> 
当你访问http://******/welcome.htm这个URL时,DispatcherServlet通过BeanNameUrlHandlerMapping映射就找到了WelcomeController。 
2. ControllerClassNameHandlerMapping,它是按控制器类名称映射请求。 
3. SimpleUrlHandlerMapping,用定制的映射定义来映射请求。 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

相关推荐

    【面试资料】-(机构内训资料)看透Spring MVC源代码分析与实践.zip

    这份【面试资料】-(机构内训资料)看透Spring MVC源代码分析与实践.zip文件很可能是为了帮助求职者准备相关面试问题而设计的,包含了对Spring MVC工作原理、关键组件和源码解读的详尽解析。 1. **Spring MVC基本...

    spring mvc框架源码

    在本源码分析中,我们将探讨Spring MVC的工作原理、主要组件及其交互方式。 1. **DispatcherServlet**: 作为Spring MVC的前端控制器,DispatcherServlet是所有请求的入口点。它负责拦截请求,根据请求信息(如URL、...

    Spring MVC应用开源架构源码2021.pdf

    Spring MVC源码深度剖析部分,将会详细解读Spring MVC框架的内部实现机制。核心组件如DispatcherServlet、HandlerMapping、HandlerAdapter等,每个组件的职责和如何相互协作,以及源码中对各种复杂场景的处理。 SSM...

    spring mvc简单测试的可运行的源代码

    在这个"spring mvc简单测试的可运行的源代码"中,我们可以学习到如何配置和运行一个基础的Spring MVC应用。 1. **环境准备**:首先,你需要安装Java JDK和Apache Maven或Gradle,它们是构建和管理Java项目的工具。...

    菜鸟 Spring 源码解读 推荐流程

    4. **AOP**:面向切面编程允许我们在不修改源代码的情况下,插入跨切面关注点,如日志、事务管理等。 5. **ASPECTJ**:Spring支持AspectJ,一种强大的编译时和运行时AOP框架,可以方便地定义切面和通知。 6. **MVC...

    spring源码分析(1-10)

    Spring 源代码分析系列涵盖了多个关键模块,包括事务处理、IoC容器、JDBC、MVC、AOP以及与Hibernate和Acegi安全框架的集成。以下是对这些知识点的详细阐述: 1. **Spring 事务处理**:Spring 提供了声明式事务管理...

    spring+spring mvc+mybatis基础源码

    SSM框架,即Spring、Spring MVC和MyBatis的组合,是Java开发中常见的Web应用程序框架。这个压缩包提供了一套...同时,通过阅读源码,可以加深对Spring、Spring MVC和MyBatis原理的理解,有助于提升Java Web开发技能。

    深入剖析Spring Web源码 pdf高清版(第二版)

    这本书详细解读了Spring MVC和Spring WebFlux两大核心模块的源码,帮助读者理解Spring如何实现高效的Web应用开发。下面将根据标题、描述以及标签,深入探讨Spring MVC和Spring WebFlux的相关知识点。 一、Spring ...

    京东T5级大牛带你解读Spring核心源码——1小时手写SpringMVC~

    ### Spring核心源码解读与手动实现SpringMVC #### 一、Spring框架简介 Spring框架是由Rod Johnson在2004年发起的一个开源项目,它是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器。Spring框架的核心特性包括:...

    spring 源码中文注释

    这份"spring 源码中文注释"包含了对Spring框架源码的详细解读,对于理解其工作原理、优化代码以及定制化开发具有重要意义。 首先,我们来看看Spring的核心组件——IoC容器。IoC容器是Spring的核心,它负责管理对象...

    看透springMvc源代码分析与实践

    #### 3.1 DispatcherServlet源代码解读 DispatcherServlet的核心流程可以概括为以下几步: 1. **读取配置**:通过`doGetDispatcherServlet()`方法读取配置文件,初始化WebApplicationContext。 2. **初始化组件**:...

    spring基于5.3.x版本源码解读

    在这个版本中,Spring 源码解读可以帮助我们深入理解其内部机制,从而更好地利用其功能并优化我们的代码。 首先,让我们探讨Spring框架的基础部分。Spring的核心设计理念是依赖注入(Dependency Injection,DI),...

    spring-in-action-5-samples-master源代码.zip

    本文将对这些源代码进行深入解读,帮助读者更好地理解和应用Spring框架的核心概念。 首先,让我们关注Spring框架的基础——依赖注入(Dependency Injection,简称DI)。Spring通过DI实现了对象之间的松耦合,使得...

    官方原版源码 spring-framework-5.2.9.RELEASE.zip

    4. **MVC组件**:Spring MVC的DispatcherServlet、HandlerMapping、Controller等关键组件的源码,揭示了请求处理的流程和映射机制。 5. **事务管理**:Spring的PlatformTransactionManager接口和不同的事务策略(如...

    spring-framework-2.5.6

    2.5.6版本的DispatcherServlet、Controller、ModelAndView等组件的源码分析,可以帮助我们理解请求处理流程。 2. 事务管理:Spring的声明式事务管理是通过AOP实现的,通过@Transactional注解可以实现事务的自动开启...

    spring源码4.3

    本文将对Spring 4.3的源码进行详细解读,帮助读者深入理解Spring的工作原理。 首先,我们来看看Spring的核心模块。Spring框架主要由核心容器(Core Container)、数据访问/集成(Data Access/Integration)、Web、...

    spring源码深入解析

    《Spring源码深入解析》是一本深度探讨Spring框架核心机制的文档,主要针对Spring5版本...文档《Spring源码解析-tom.docx》应包含了上述各方面的详细解读,对于希望深入了解Spring的开发者来说,是一份宝贵的参考资料。

    初学spring项目(入门解读和新建项目)

    Spring框架是Java开发中最常用的轻量级开源框架之一,它为构建企业级应用程序提供了一种模块化、松耦合的解决方案。这篇博文“初学Spring项目(入门解读和新建项目)”将带你逐步走进Spring的世界,了解如何从零开始...

    spring cloud config 和 bus 源码解读

    Spring Cloud Config是Spring Cloud的一部分,旨在为微服务架构...本文从源码角度解读了Config Server的初始化过程、配置获取处理以及Bus的消息传播机制,以便开发者更好地理解Spring Cloud Config和Bus的工作原理。

Global site tag (gtag.js) - Google Analytics