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