`
weiqingfei
  • 浏览: 317403 次
  • 性别: Icon_minigender_1
  • 来自: 黑洞
社区版块
存档分类
最新评论

spring mvc启动过程简析(1)

    博客分类:
  • Java
阅读更多

上次大概讲了一下spring mvc的启动步骤,但是没有讲如何读取配置文件(servlet-context.xml)的,接下来着重讲一下这一步。

先看一下servlet-context.xml的namespace

 

xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"

 

 

对配置文件进行解析时,spring是先根据namespage分了org.springframework.beans.factory.xml.NamespaceHandler,然后每个handler又根据元素分了org.springframework.beans.factory.xml.BeanDefinitionParser

handler的定义都是在各个jar包的META-INF/spring.handlers文件里面,spring mvc默认的包合并起来大概有以下定义

 

 

http://www.springframework.org/schema/aop org.springframework.aop.config.AopNamespaceHandler
http://www.springframework.org/schema/cache org.springframework.cache.config.CacheNamespaceHandler
http://www.springframework.org/schema/task org.springframework.scheduling.config.TaskNamespaceHandler
http://www.springframework.org/schema/p org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler
http://www.springframework.org/schema/util org.springframework.beans.factory.xml.UtilNamespaceHandler
http://www.springframework.org/schema/lang org.springframework.scripting.config.LangNamespaceHandler
http://www.springframework.org/schema/c org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler
http://www.springframework.org/schema/context org.springframework.context.config.ContextNamespaceHandler
http://www.springframework.org/schema/mvc org.springframework.web.servlet.config.MvcNamespaceHandler
http://www.springframework.org/schema/jee org.springframework.ejb.config.JeeNamespaceHandler

 

两个比较典型的就是mvc和context了,看看这两个handler所对应的parser

org.springframework.context.config.ContextNamespaceHandler

	public void init() {
		registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
		registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
		registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
		registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
		registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
		registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
		registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
		registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
	}

 org.springframework.web.servlet.config.MvcNamespaceHandler

	public void init() {
		registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
		registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser());
		registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser());		
		registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser());
		registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser());
	}

 所以当在分析配置文件时,遇到annotation-driven定义时,用的是org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser,

而当遇到component-scan时,用的是org.springframework.context.annotation.ComponentScanBeanDefinitionParser

 

http://www.springframework.org/schema/beans这个namespace比较特殊,走得是另外一种处理,

文件org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader里的方法

	protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		if (delegate.isDefaultNamespace(root)) {
			NodeList nl = root.getChildNodes();
			for (int i = 0; i < nl.getLength(); i++) {
				Node node = nl.item(i);
				if (node instanceof Element) {
					Element ele = (Element) node;
					if (delegate.isDefaultNamespace(ele)) {
						parseDefaultElement(ele, delegate);
					}
					else {
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			delegate.parseCustomElement(root);
		}
	}

 

 有个分支判断,用的是以下方法来判定是否是默认namesapce

	public boolean isDefaultNamespace(String namespaceUri) {
		return (!StringUtils.hasLength(namespaceUri) || BEANS_NAMESPACE_URI.equals(namespaceUri));
	}

 然后就直接分析注册了。

 

 分析一下annotation-driven,类org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser主要注册了一下类

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#0
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#0
org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#0
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver#0
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver#0
org.springframework.web.servlet.handler.MappedInterceptor#0

//默认
org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter

 

然后再分析一下component-scan,类org.springframework.context.annotation.ComponentScanBeanDefinitionParser开始扫描指定package下的类

	public BeanDefinition parse(Element element, ParserContext parserContext) {
		String[] basePackages = StringUtils.tokenizeToStringArray(element.getAttribute(BASE_PACKAGE_ATTRIBUTE),
				ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);

		// Actually scan for bean definitions and register them.
		ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
		Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
		registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

		return null;
	}

 在扫描类ClassPathScanningCandidateComponentProvider的findCandidateComponents方法里,先读取所有类文件,然在方法isCandidateComponent里对类进行筛选,只有用以下注解的类才会被选中

写道
org.springframework.stereotype.Component
javax.annotation.ManagedBean
javax.inject.Named

 

类是扫描注册完了,对于使用了@RequestMapping注解的方法是什么时候扫描的呢?

是通过上面注册过的类org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping来处理的,处理的timing是类XmlWebApplicationContext的finishBeanFactoryInitialization方法

在类org.springframework.beans.factory.support.DefaultListableBeanFactory里有以下处理

 

	public void preInstantiateSingletons() throws BeansException {
		if (this.logger.isInfoEnabled()) {
			this.logger.info("Pre-instantiating singletons in " + this);
		}
		synchronized (this.beanDefinitionMap) {
			// Iterate over a copy to allow for init methods which in turn register new bean definitions.
			// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
			List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
			for (String beanName : beanNames) {
				RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
				if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
					if (isFactoryBean(beanName)) {
						final FactoryBean factory = (FactoryBean) getBean(FACTORY_BEAN_PREFIX + beanName);
						boolean isEagerInit;
						if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
							isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
								public Boolean run() {
									return ((SmartFactoryBean) factory).isEagerInit();
								}
							}, getAccessControlContext());
						}
						else {
							isEagerInit = (factory instanceof SmartFactoryBean &&
									((SmartFactoryBean) factory).isEagerInit());
						}
						if (isEagerInit) {
							getBean(beanName);
						}
					}
					else {
						getBean(beanName);
					}
				}
			}
		}
	}
 对以前注册过的所有的bean进行实例化,在实例化org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping的后,对其进行初始化最终会调用方法

 

 

	protected void initHandlerMethods() {
		if (logger.isDebugEnabled()) {
			logger.debug("Looking for request mappings in application context: " + getApplicationContext());
		}
		
		String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
				BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
				getApplicationContext().getBeanNamesForType(Object.class));

		for (String beanName : beanNames) {
			if (isHandler(getApplicationContext().getType(beanName))){
				detectHandlerMethods(beanName);
			}
		}
		handlerMethodsInitialized(getHandlerMethods());
	}
 

 

对所有注册过的bean再来一次循环,通过方法isHandler,只处理使用注解org.springframework.stereotype.Controller修饰过的class,然后在以下方法

 

	protected void detectHandlerMethods(final Object handler) {
		Class<?> handlerType = (handler instanceof String) ? 
				getApplicationContext().getType((String) handler) : handler.getClass();

		final Class<?> userType = ClassUtils.getUserClass(handlerType);
				
		Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
			public boolean matches(Method method) {
				return getMappingForMethod(method, userType) != null;
			}
		});
		
		for (Method method : methods) {
			T mapping = getMappingForMethod(method, userType);
			registerHandlerMethod(handler, method, mapping);
		}
	}
 这里面对方法的查找过滤是通过方法getMappingForMethod来做的,其实这个方法是通过查找方法和类的注解org.springframework.web.bind.annotation.RequestMapping信息,并组合成请求url的过程。

 

可以看到对方法筛选完,又调用了一次这个方法,这个做法有点儿低效。

最后通过方法registerHandlerMethod,把mapping信息和方法信息保存到属性handlerMethods和urlMap里

 

对服务进行请求的过程,就是一个寻找对应类方法的过程,先通过类属性handlerMappings里保存的所有HandleMapping类来寻找对应的类方法(字符串描述),通过优先级顺序,只要由一个匹配好了,就返回。默认的查找顺序为

写道
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
org.springframework.web.servlet.handler.SimpleUrlHandlerMapping

 找到方法以后,就要用handlerAdapter进行包装了,首先也是要从类属性handlerAdapters找到一个符合条件的HandleAdapter类,通过优先级顺序,只要由一个匹配好就返回。默认的查找顺序为

写道
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter

 HandleAdapter的查找是通过方法的参数和返回值来找的,每个HandleAdapter都有自己所支持的参数和返回值,是通过argumentResolvers和returnValueHandlers所定义的。

比如RequestMappingHandlerAdapter得argumentResolvers里定义着21个Resolver,returnValueHandlers里定义了9个处理器,顺序进行验证,只要参数和返回值都能找到支持的处理器就认为这个HandlerAdapter是可以包装这个方法的。

写道
org.springframework.web.method.annotation.RequestParamMethodArgumentResolver
org.springframework.web.method.annotation.RequestParamMapMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor
org.springframework.web.servlet.mvc.method.annotation.RequestPartMethodArgumentResolver
org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver
org.springframework.web.method.annotation.RequestHeaderMapMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.ServletCookieValueMethodArgumentResolver
org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.ServletResponseMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor
org.springframework.web.servlet.mvc.method.annotation.RedirectAttributesMethodArgumentResolver
org.springframework.web.method.annotation.ModelMethodProcessor
org.springframework.web.method.annotation.MapMethodProcessor
org.springframework.web.method.annotation.ErrorsMethodArgumentResolver
org.springframework.web.method.annotation.SessionStatusMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.UriComponentsBuilderMethodArgumentResolver
org.springframework.web.method.annotation.RequestParamMethodArgumentResolver
org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor

 

写道
org.springframework.web.servlet.mvc.method.annotation.ModelAndViewMethodReturnValueHandler
org.springframework.web.method.annotation.ModelMethodProcessor
org.springframework.web.servlet.mvc.method.annotation.ViewMethodReturnValueHandler
org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor
org.springframework.web.method.annotation.ModelAttributeMethodProcessor
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor
org.springframework.web.servlet.mvc.method.annotation.ViewNameMethodReturnValueHandler
org.springframework.web.method.annotation.MapMethodProcessor

 

 

ModelAndViewMethodReturnValueHandler 判断返回类是否是ModelAndView类或者ModelAndView的子类
ModelMethodProcessor 判断返回是否是Model的子
ViewNameMethodReturnValueHandler 判断返回类是否是View的子类
HttpEntityMethodProcessor 判断返回类是HttpEntity或者ResponseEntity
ModelAttributeMethodProcessor 返回注解@ModelAttribute,或者非简单类型(a primitive, a String or other CharSequence, a Number, a Date, a URI, a URL, a Locale, a Class, or a corresponding array.
RequestResponseBodyMethodProcessor 返回注解@ResponseBody
ViewNameMethodReturnValueHandler 返回值是String或者void
MapMethodProcessor 判断返回类是否是Map的子类

 

以上这些默认值得设定都是类RequestMappingHandlerAdapter在初始化后,在方法afterPropertiesSet里设定的。如果需要的话,可以通过自定义WebMvcConfigurer来注入自己的参数或者返回值处理器。

 

分享到:
评论

相关推荐

    精通Spring MVC 4

    Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。Spring MVC4是当前zuixin的版本,在众多特性上有了进一步的提升。, 在精通Spring...

    Spring MVC jar包

    1. **Spring MVC**: - **基本概念**:Spring MVC提供了一个灵活的MVC实现,包括请求映射、视图解析、模型绑定等功能。它通过DispatcherServlet作为前端控制器,负责接收请求并分发到相应的处理器。 - **组件**:...

    spring mvc中启动netty

    - **Spring MVC配置**:在Spring MVC中,我们需要配置一个适配器,以便Netty服务器能够理解Spring MVC的控制器。 - **消息编码解码器**:Netty需要消息编码解码器来将HTTP请求转换为Spring MVC可以处理的形式,...

    SPRING MVC配置过程

    SPRING MVC 配置过程 SPRING MVC 是一个基于 DispatcherServlet 的 MVC 框架,每一个请求最先访问的都是 DispatcherServlet,DispatcherServlet 负责转发每一个 Request 请求给相应的 Handler,Handler 处理以后再...

    最全最经典spring-mvc教程

    Spring MVC 是一款强大的Java Web开发框架,用于构建高效、可维护和模块化的Web应用程序。它作为Spring框架的一部分,提供了一种优雅的方式来处理HTTP请求和响应,使得开发者可以专注于业务逻辑而不是底层实现。在这...

    Spring MVC 4.2.3

    此外,配合Spring Boot,可以进一步简化Spring MVC应用的启动和配置。 总的来说,Spring MVC 4.2.3版本为开发者提供了强大且灵活的Web开发工具,帮助他们构建高效、可扩展的Web应用。它的特性涵盖了从请求处理到...

    spring mvc 4.0

    1. **依赖注入**:Spring MVC 4.0继续支持Spring框架的核心功能,依赖注入(DI),允许开发者通过配置来管理对象及其依赖关系,降低了代码耦合度,提高了可测试性。 2. **ModelAndView对象**:在处理完请求后,控制...

    Spring.MVC-A.Tutorial-Spring.MVC学习指南 高清可复制版PDF

    Spring MVC 是一个基于Java的轻量级Web应用框架,它是Spring框架的重要组成部分,主要用于构建Web应用程序的后端控制器。这个教程“Spring MVC - A Tutorial”旨在帮助开发者深入理解和掌握Spring MVC的核心概念和...

    Mastering Spring MVC 4(2015.09)源码

    Spring MVC 是一个强大的Java Web开发框架,它是Spring框架的一部分,专为构建高度可扩展和模块化的Web应用程序而设计。在2015年的版本中,Spring MVC 4已经相当成熟,提供了许多特性来简化开发流程并提高开发效率。...

    Spring MVC 教程快速入门 深入分析

    Spring MVC是一种基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架,使用了IoC容器,支持RESTful风格的应用程序开发。Spring MVC通过分离模型(Model)、视图(View)和控制器(Controller)来简化Web开发...

    Spring MVC使用Demo

    Spring MVC是Spring框架的一个核心模块...总之,"Spring MVC使用Demo"是一个很好的学习资源,涵盖了从环境搭建到实际编码的全过程。通过学习和实践,开发者能够熟练掌握Spring MVC,从而高效地开发出高质量的Web应用。

    Spring MVC + Mybatis+Spring实现的个人博客系统

    这是一个基于Spring MVC、Mybatis和Spring框架实现的个人博客系统,涵盖了Web开发中的后端架构设计、数据库管理和前端展示等多个方面。以下将详细介绍这个系统的关键知识点: **1. Spring MVC** Spring MVC是Spring...

    Spring MVC所需jar包

    1. **Spring 框架核心库**:这是 Spring MVC 的基础,它包含了 IoC(Inversion of Control,控制反转)和 AOP(Aspect Oriented Programming,面向切面编程)的核心概念。主要的 jar 包有 `spring-core.jar`、`...

    Spring MVC启动时初始化的几个常用方法

    本篇文章将深入探讨Spring MVC启动时初始化的几个常用方法,并解释它们在实际开发中的作用。 首先,我们从核心组件`DispatcherServlet`开始。`DispatcherServlet`是Spring MVC的前端控制器,它是整个流程的起点。当...

    spring mvc mybatis 整合源码,带数据库脚本,带详细注释

    1. **Spring MVC 概述** - Spring MVC是基于Model-View-Controller(MVC)设计模式的Web应用框架,提供了一种组织和处理请求的机制。 - 它的核心组件包括DispatcherServlet、HandlerMapping、HandlerAdapter、...

    spring mvc项目

    spring mvc maven项目,导入IDEA后无报错,需要在IDEA中配置Tomcat并将项目添加到tomcat才能运行。 可用于分析spring mvc源码、spring mvc父子容器初始化流程、session和cookie机制、spring session等,也可以用于...

    基本的spring mvc + spring security实现的登录(无数据库)

    1. **Spring MVC**: - **核心组件**:DispatcherServlet负责接收请求并分发到相应的控制器(Controller),ModelAndView用于封装模型数据和视图,ViewResolver解析视图。 - **配置**:通常通过Java配置或XML配置...

Global site tag (gtag.js) - Google Analytics