本文主要内容是根据一个常见的springmvc 配置文件,剖析分解每个标签的工作内容。
-
一个非常熟悉的springmvc配置样例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
<?
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>标签
1
2
3
4
5
6
7
|
public
class
ContextNamespaceHandler
extends
NamespaceHandlerSupport {
public
void
init() {
...
registerBeanDefinitionParser(
"component-scan"
,
new
ComponentScanBeanDefinitionParser());
...
}
|
ComponentScanBeanDefinitionParser负责具体解析工作。
2.1 属性值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
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 解析
1
2
3
4
5
6
7
8
9
10
11
|
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 />标签
1
2
3
4
5
6
7
8
9
10
11
|
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 具体解析方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
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.2Configures 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.3Configures 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实现)
1
2
3
4
5
|
@Override
public
void
doParse(Element element, ParserContext parserContext) {
Object source = parserContext.extractSource(element);
registerResourceMappings(parserContext, element, source);
}
|