spring mvc启动过程简析(一)
上次大概讲了一下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里对类进行筛选,只有用以下注解的类才会被选中
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.handler.BeanNameUrlHandlerMapping
org.springframework.web.servlet.handler.SimpleUrlHandlerMapping
找到方法以后,就要用handlerAdapter进行包装了,首先也是要从类属性handlerAdapters找到一个符合条件的HandleAdapter类,通过优先级顺序,只要由一个匹配好就返回。默认的查找顺序为
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.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.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来注入自己的参数或者返回值处理器。