spring mvc启动过程简析(1)

上次大概讲了一下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来注入自己的参数或者返回值处理器。

猜你喜欢

转载自weiqingfei.iteye.com/blog/2275484