上次大概讲了一下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来注入自己的参数或者返回值处理器。
相关推荐
Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。Spring MVC4是当前zuixin的版本,在众多特性上有了进一步的提升。, 在精通Spring...
1. **Spring MVC**: - **基本概念**:Spring MVC提供了一个灵活的MVC实现,包括请求映射、视图解析、模型绑定等功能。它通过DispatcherServlet作为前端控制器,负责接收请求并分发到相应的处理器。 - **组件**:...
- **Spring MVC配置**:在Spring MVC中,我们需要配置一个适配器,以便Netty服务器能够理解Spring MVC的控制器。 - **消息编码解码器**:Netty需要消息编码解码器来将HTTP请求转换为Spring MVC可以处理的形式,...
SPRING MVC 配置过程 SPRING MVC 是一个基于 DispatcherServlet 的 MVC 框架,每一个请求最先访问的都是 DispatcherServlet,DispatcherServlet 负责转发每一个 Request 请求给相应的 Handler,Handler 处理以后再...
Spring MVC 是一款强大的Java Web开发框架,用于构建高效、可维护和模块化的Web应用程序。它作为Spring框架的一部分,提供了一种优雅的方式来处理HTTP请求和响应,使得开发者可以专注于业务逻辑而不是底层实现。在这...
此外,配合Spring Boot,可以进一步简化Spring MVC应用的启动和配置。 总的来说,Spring MVC 4.2.3版本为开发者提供了强大且灵活的Web开发工具,帮助他们构建高效、可扩展的Web应用。它的特性涵盖了从请求处理到...
1. **依赖注入**:Spring MVC 4.0继续支持Spring框架的核心功能,依赖注入(DI),允许开发者通过配置来管理对象及其依赖关系,降低了代码耦合度,提高了可测试性。 2. **ModelAndView对象**:在处理完请求后,控制...
Spring MVC 是一个基于Java的轻量级Web应用框架,它是Spring框架的重要组成部分,主要用于构建Web应用程序的后端控制器。这个教程“Spring MVC - A Tutorial”旨在帮助开发者深入理解和掌握Spring MVC的核心概念和...
Spring MVC 是一个强大的Java Web开发框架,它是Spring框架的一部分,专为构建高度可扩展和模块化的Web应用程序而设计。在2015年的版本中,Spring MVC 4已经相当成熟,提供了许多特性来简化开发流程并提高开发效率。...
Spring MVC是一种基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架,使用了IoC容器,支持RESTful风格的应用程序开发。Spring MVC通过分离模型(Model)、视图(View)和控制器(Controller)来简化Web开发...
Spring MVC是Spring框架的一个核心模块...总之,"Spring MVC使用Demo"是一个很好的学习资源,涵盖了从环境搭建到实际编码的全过程。通过学习和实践,开发者能够熟练掌握Spring MVC,从而高效地开发出高质量的Web应用。
这是一个基于Spring MVC、Mybatis和Spring框架实现的个人博客系统,涵盖了Web开发中的后端架构设计、数据库管理和前端展示等多个方面。以下将详细介绍这个系统的关键知识点: **1. Spring MVC** Spring MVC是Spring...
本篇文章将深入探讨Spring MVC启动时初始化的几个常用方法,并解释它们在实际开发中的作用。 首先,我们从核心组件`DispatcherServlet`开始。`DispatcherServlet`是Spring MVC的前端控制器,它是整个流程的起点。当...
1. **Spring MVC 概述** - Spring MVC是基于Model-View-Controller(MVC)设计模式的Web应用框架,提供了一种组织和处理请求的机制。 - 它的核心组件包括DispatcherServlet、HandlerMapping、HandlerAdapter、...
spring mvc maven项目,导入IDEA后无报错,需要在IDEA中配置Tomcat并将项目添加到tomcat才能运行。 可用于分析spring mvc源码、spring mvc父子容器初始化流程、session和cookie机制、spring session等,也可以用于...
1. **Spring MVC**: - **核心组件**:DispatcherServlet负责接收请求并分发到相应的控制器(Controller),ModelAndView用于封装模型数据和视图,ViewResolver解析视图。 - **配置**:通常通过Java配置或XML配置...
Spring Boot简化了这一过程,通过自动配置和@SpringBootApplication注解可以快速启动一个包含Spring MVC的应用。 3. **请求处理:Controller** Controller类使用@Controller和@RequestMapping注解来定义处理HTTP...