SpringMVC源码分析(1)标签解析

本文主要内容是根据一个常见的springmvc 配置文件,剖析分解每个标签的工作内容。

  1. 一个非常熟悉的springmvc配置样例
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:context="http://www.springframework.org/schema/context"
   xmlns:mvc="http://www.springframework.org/schema/mvc"
   xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
      http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

   <!-- Scans the classpath of this application for @Components to deploy as beans -->
   <context:component-scan base-package="org.springframework.samples.mvc.basic" />

   <!-- Configures the @Controller programming model -->
   <mvc:annotation-driven />

   <!-- Forwards requests to the "/" resource to the "welcome" view -->
   <mvc:view-controller path="/" view-name="welcome"/>

   <!-- Configures Handler Interceptors -->   
   <mvc:interceptors>
      <!-- Changes the locale when a ‘locale‘ request parameter is sent; e.g. /?locale=de -->
      <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" />
   </mvc:interceptors>

   <!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources/ directory -->
   <mvc:resources mapping="/resources/**" location="/resources/" />

   <!-- Saves a locale change using a cookie -->
   <bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver" />

   <!-- Application Message Bundle -->
   <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
      <property name="basename" value="/WEB-INF/messages/messages" />
      <property name="cacheSeconds" value="0" />
   </bean>

   <!-- Resolves view names to protected .jsp resources within the /WEB-INF/views directory -->
   <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
      <property name="prefix" value="/WEB-INF/views/"/>
      <property name="suffix" value=".jsp"/>
   </bean>
</beans>

接下来,逐个标签进行分析

2.<context:component-scan>标签

public class ContextNamespaceHandler extends NamespaceHandlerSupport {

   public void init() {
       ...
      registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
      ...  
   }

ComponentScanBeanDefinitionParser负责具体解析工作。

2.1 属性值

public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser {

   private static final String BASE_PACKAGE_ATTRIBUTE = "base-package";

   private static final String RESOURCE_PATTERN_ATTRIBUTE = "resource-pattern";

   private static final String USE_DEFAULT_FILTERS_ATTRIBUTE = "use-default-filters";

   private static final String ANNOTATION_CONFIG_ATTRIBUTE = "annotation-config";
   
   private static final String NAME_GENERATOR_ATTRIBUTE = "name-generator";
   
   private static final String SCOPE_RESOLVER_ATTRIBUTE = "scope-resolver";
   
   private static final String SCOPED_PROXY_ATTRIBUTE = "scoped-proxy";

   private static final String EXCLUDE_FILTER_ELEMENT = "exclude-filter";

   private static final String INCLUDE_FILTER_ELEMENT = "include-filter";

   private static final String FILTER_TYPE_ATTRIBUTE = "type";

   private static final String FILTER_EXPRESSION_ATTRIBUTE = "expression";
   ...
   }

首先在ComponentScanBeanDefinitionParser类中定义了</context:component-scan>元素的所有属性值

  • base-package:为必须配置属性,指定了spring需要扫描的跟目录名称,可以使用”,” “;” “\t\n(回车符)”来分割多个包名
  • resource-pattern:配置扫描资源格式.默认”**/*.class
  • use-default-filters:是否使用默认扫描策略,默认为”true”,会自动扫描指定包下的添加了如下注解的类,@Component, @Repository, @Service,or @Controller
  • annotation-config:是否启用默认配置,默认为”true”,该配置会在BeanDefinition注册到容器后自动注册一些BeanPostProcessors对象到容器中.这些处理器用来处理类中Spring’s @Required and
    @Autowired,  JSR 250’s @PostConstruct, @PreDestroy and @Resource (如果可用),
    JAX-WS’s @WebServiceRef (如果可用), EJB 3’s @EJB (如果可用), and JPA’s
    @PersistenceContext and @PersistenceUnit (如果可用),但是该属性不会处理Spring’s @Transactional 和 EJB 3中的@TransactionAttribute注解对象,这两个注解是通过<tx:annotation-driven>元素处理过程中对应的BeanPostProcessor来处理的.
  • include-filter:如果有自定义元素可以在该处配置
  • exclude-filter:配置哪些类型的类不需要扫描,如上面指定了类中添加了”Controller”元素的类不扫描.
    注意:</context:component-scan>元素中默认配置了annotation-config,所以不需要再单独配置</annotation-config>元素.

2.2 解析

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;
}

ClassPathBeanDefinitionScanner

ClassPathBeanDefinitionScanner主要用来完成类路径下符合条件的bean扫描功能,并将扫描出来的bean注册到指定的BeanFactory中。
- 默认过滤器主要扫描@Component @Repository @Service @Controller注解的类,同样可以通过配置类扫描过滤器来扫描自定义注解的类。
- 当类路径下有javax.annotation.ManagedBean和javax.inject.Named类库时支持这2个注解扫描。

3 <mvc:annotation-driven />标签

public class MvcNamespaceHandler extends NamespaceHandlerSupport {

   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());
   }

}

AnnotationDrivenBeanDefinitionParser具体负责解析工作。

3.2 具体解析方法

class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
...
public BeanDefinition parse(Element element, ParserContext parserContext) {
   Object source = parserContext.extractSource(element);

   CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
   parserContext.pushContainingComponent(compDefinition);
   
   RootBeanDefinition annMappingDef = new RootBeanDefinition(DefaultAnnotationHandlerMapping.class);
   annMappingDef.setSource(source);
   annMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
   annMappingDef.getPropertyValues().add("order", 0);
   String annMappingName = parserContext.getReaderContext().registerWithGeneratedName(annMappingDef);

   RuntimeBeanReference conversionService = getConversionService(element, source, parserContext);
   RuntimeBeanReference validator = getValidator(element, source, parserContext);
   
   RootBeanDefinition bindingDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);
   bindingDef.setSource(source);
   bindingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
   bindingDef.getPropertyValues().add("conversionService", conversionService);
   bindingDef.getPropertyValues().add("validator", validator);

   RootBeanDefinition annAdapterDef = new RootBeanDefinition(AnnotationMethodHandlerAdapter.class);
   annAdapterDef.setSource(source);
   annAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
   annAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);
   annAdapterDef.getPropertyValues().add("messageConverters", getMessageConverters(source));
   String annAdapterName = parserContext.getReaderContext().registerWithGeneratedName(annAdapterDef);

   RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceExposingInterceptor.class);
   csInterceptorDef.setSource(source);
   csInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, conversionService);    
   RootBeanDefinition mappedCsInterceptorDef = new RootBeanDefinition(MappedInterceptor.class);
   mappedCsInterceptorDef.setSource(source);
   mappedCsInterceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
   mappedCsInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, (Object) null);
   mappedCsInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(1, csInterceptorDef);
   String mappedInterceptorName = parserContext.getReaderContext().registerWithGeneratedName(mappedCsInterceptorDef);
   
   parserContext.registerComponent(new BeanComponentDefinition(annMappingDef, annMappingName));
   parserContext.registerComponent(new BeanComponentDefinition(annAdapterDef, annAdapterName));
   parserContext.registerComponent(new BeanComponentDefinition(mappedCsInterceptorDef, mappedInterceptorName));
   parserContext.popAndRegisterContainingComponent();
   
   return null;
}
...
}

(1)注册DefaultAnnotationHandlerMapping bean,具体负责请求与@Controller的mapping.

(2)注册AnnotationMethodHandlerAdapter bean,调用@Controller的方法,并注入webBindingInitializer属性和messageConverters。

(3)其中webBindingInitializer指定conversionService,validator属性。

3.1

Configures the conversionService if specified, otherwise defaults to a fresh ConversionService instance created by the default FormattingConversionServiceFactoryBean.
3.2

Configures the validator if specified, otherwise defaults to a fresh Validator instance created by the default LocalValidatorFactoryBean if the JSR-303 API is present on the classpath.
3.3

Configures standard HttpMessageConverters, including the Jaxb2RootElementHttpMessageConverter if JAXB2 is present on the classpath, and the MappingJacksonHttpMessageConverter if Jackson is present on the classpath.

4.<mvc:view-controller>标签

ViewControllerBeanDefinitionParser 负责具体解析。

主要完成 注册一个ParameterizableViewController.和SimpleUrlHandlerMapping ,SimpleControllerHandlerAdapter

SimpleUrlHandlerMapping注册的名称为"org.springframework.web.servlet.config.viewControllerHandlerMapping";

5.<mvc:interceptors>标签

注册一组MappedInterceptor 。有公共和私有之分。

<bean>标签对应公共interceptor;

<mvc:interceptor和<mvc:mapping>对应Mapping私有。

6.<mvc:resources>标签

ResourcesBeanDefinitionParser负责解析。

完成工作

(6.1) 注册一个 ResourceHttpRequestHandler

(6.2)注册一个 SimpleUrlHandlerMapping for mapping resource

(6.3) 可能会注册一个HttpRequestHandlerAdapter.

(由AbstractHttpRequestHandlerBeanDefinitionParser.registerHandlerAdapterIfNecessary实现)

@Override
public void doParse(Element element, ParserContext parserContext) {
   Object source = parserContext.extractSource(element);
   registerResourceMappings(parserContext, element, source);
}
时间: 2024-09-30 16:39:37

SpringMVC源码分析(1)标签解析的相关文章

springMVC源码分析--HttpMessageConverter数据转化(一)

之前的博客我们已经介绍了很多springMVC相关的模块,接下来我们介绍一下springMVC在获取参数和返回结果值方面的处理.虽然在之前的博客老田已经分别介绍了参数处理器和返回值处理器: (1)springMVC参数值处理器 springMVC源码分析--HandlerMethodArgumentResolver参数解析器(一) (2)springMVC返回值处理器 springMVC源码分析--HandlerMethodReturnValueHandler返回值解析器(一) 但对参数和返回值

SpringMVC源码分析6:SpringMVC的视图解析原理

title: SpringMVC源码分析6:SpringMVC的视图解析原理 date: 2018-06-07 11:03:19 tags: - SpringMVC categories: - 后端 - SpringMVC --- 转自 SpringMVC视图机制详解[附带源码分析] 本系列文章首发于我的个人博客:https://h2pl.github.io/ 欢迎阅览我的CSDN专栏:Spring源码解析 https://blog.csdn.net/column/details/21851.h

SpringMVC源码分析(3)DispatcherServlet的请求处理流程

<SpringMVC源码分析(1)标签解析>:介绍了解析过程中,初始化若干组件. <SpringMVC源码分析(2)DispatcherServlet的初始化>:初始化DispatcherServlet的多个组件. 本文继续分析DispatcherServlet解析请求的过程. 概览 ①:DispatcherServlet是springmvc中的前端控制器(front controller),负责接收request并将request转发给对应的处理组件. ②:HanlerMappi

SpringMVC源码分析(6)剖析DefaultAnnotationHandlerMapping

接<SpringMVC源码分析(5)剖析重要组件HandlerMapping>,继续剖析HandlerMapping,DefaultAnnotationHandlerMapping是SpringMVC 中最重要的HandlerMapping组件.虽然它在spring3.1版本后被废弃了. 包括2部分内容 DefaultAnnotationHandlerMapping剖析 HandlerMapping的拦截器 1.DefaultAnnotationHandlerMapping剖析 鉴于它的重要地

SpringMVC源码分析(2)DispatcherServlet的初始化

DispatcherServlet的初始化,是在org.springframework.web.context.ContextLoaderListener完成加载后,才开始的.这时候WebApplicationContext(包含DAO,Service等)已经初始完毕. DispatcherServlet的初始过程主要完成 1.WebApplicationContext父子容器维护 2.初始化Servlet策略 本文主要内容 DispatcherServlet的集成体系 DispatcherSe

springmvc源码分析系列-请求处理流程

接上一篇-springmvc源码分析开头片 上一节主要说了一下springmvc与struts2的作为MVC中的C(controller)控制层的一些区别及两者在作为控制层方面的一些优缺点.今天就结合下面的一张图和上一篇中关于springmvc各个模块之间及各个模块中的类的继承关系的一张图对springmvc的请求处理流程进行一个分析.当然有了springmvc的请求处理流程我们就知道了springmvc是如何在启动的时候去加载或者去解析对应的具体控制器,以及modleAndView使干什么用的

SpringMVC源码分析(4)剖析DispatcherServlet重要组件

<SpringMVC源码分析(3)DispatcherServlet的请求处理流程 >简单介绍了一个请求的处理过程, 简略描述了调用过程,并没有涉及过多细节,如url匹配,报文解析转换等. <SpringMVC源码分析(2)DispatcherServlet的初始化>:介绍了servlet的初始化过程,尤其initStrategies方法. 本文主要总结DispatcherServlet几个重要组件的关系. 1.类图 该类图并没有全面的描述SpringMVC相关类,重点说明组件的关

7、SpringMVC源码分析(2):分析HandlerAdapter.handle方法,了解handler方法的调用细节以及@ModelAttribute注解

从上一篇 SpringMVC源码分析(1) 中我们了解到在DispatcherServlet.doDispatch方法中会通过 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()) 这样的方式来执行request的handler方法. 先来分析一下ha.handle方法的调用过程:HandlerAdapter接口有一个抽象实现类AbstractHandlerMethodAdapter,在该抽象类中通过具体方法

【转】springmvc源码分析链接

SpringMVC源码 SpringMVC源码分析系列 说到java的mvc框架,struts2和springmvc想必大家都知道,struts2的设计基本上完全脱离了Servlet容器,而 springmvc是依托着Servlet容器元素来设计的,同时springmvc基于Spring框架,Spring框架想必搞java的同学都很熟 悉. 一进Spring的官网就发现了这样一排醒目的文字, spring可以让我们构造简单的.便携的.又快又易于扩展的基于jvm的系统和应用程序. 没错,基于Spr

SpringMVC源码分析系列

说到java的mvc框架,struts2和springmvc想必大家都知道,struts2的设计基本上完全脱离了Servlet容器,而springmvc是依托着Servlet容器元素来设计的,同时springmvc基于Spring框架,Spring框架想必搞java的同学都很熟悉. 一进Spring的官网就发现了这样一排醒目的文字, spring可以让我们构造简单的.便携的.又快又易于扩展的基于jvm的系统和应用程序. 没错,基于Spring的MVC框架SpringMVC同样也可以构造具有这些特