在springmvc.xml的配置文件中,所有以mvc开头的标签,如<mvc:resource>等这样的标签,这些mvc:相关的有哪些内容,每个标签又是在那里被注册,又被哪些类来解析的呢,此文章让我们来看一下。
首先第一个问题:<mvc:***/>,这个后面的***都可以写哪些东西呢?
找到spring-webmvc-4.3.8.RELEASE.jar\org\springframework\web\servlet\config文件夹,里面会发现有个spring-mvc-4.3.xsd文件,这里面就定义了<mvc:***/>中的***可以写哪些内容。截图如下:
黑体字部分就是***可以写的内容。例如以<mvc:resource/>为例,在上面图片的黑体字中就可以发现有个name="resource"的。
这里的每一个黑体,就代表<mvc:***/>中的***可以些什么东西。
问题二:每一个标签是谁来解析的呢?
问题一种,我们知道了在哪里可以找到<mvc:**/>标签都可以写那些东西,那么当我们写了这些标签后,又是那个类来帮我们解析这个标签,然后根据标签中的属性来生成相关的对象的呢。看下面这个类:
public class MvcNamespaceHandler extends NamespaceHandlerSupport {
@Override
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());
registerBeanDefinitionParser("redirect-view-controller", new ViewControllerBeanDefinitionParser());
registerBeanDefinitionParser("status-controller", new ViewControllerBeanDefinitionParser());
registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser());
registerBeanDefinitionParser("tiles-configurer", new TilesConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("freemarker-configurer", new FreeMarkerConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("velocity-configurer", new VelocityConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("script-template-configurer", new ScriptTemplateConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("cors", new CorsBeanDefinitionParser());
}
}
相信看到这里,是不是廓然开朗,我们发现这里绿色部分的字符串跟上面图片中的黑体字都是一一对应,那么绿色后面建立的对象,就是将来用于解析对应标签的对象了。
以 <mvc:resources mapping="/style/**" location="/style/"/>为例,这个标签的解析会由ResourcesBeanDefinitionParser这个类来进行,看下图:
这个类的registerResourceHandler方法,获取了标签中<mvc:resource/>标签中的location属性以及其他各个属性,并将这些写在标签中的属性放到MutablePropertyValues这个对象中,然后创建了以后专门用来处理静态资源对象ResourceHttpRequestHandler【后期写一篇这个类的介绍的文章】。然后将这个类注册到容器中,将来就用这个类来判断请求是不是对资源的请求。具体的就不多说了,不是本章重点,这里不做过多解释。
下面再通过几个源码来解析下整个过程:
1. 当我们启动程序或者启动tomcat时,他们都会去加载我们xml文件来解析,比如spring-mvc.xml,拿到这个xml后,便解析beas标签中的字标签,如下:
<?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" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<mvc:annotation-driven/>
<context:component-scan base-package="com.lhb">
</context:component-scan>
</beans>
2.spring加载这个xml时,会挨个读取beans中的子标签,每个字标签都有一个对应的namespaceUri,例如,当读取到<mvc:annotation-driven/>时,namespaceUri会为Index of /schema/mvc,通过这个namespaceUri便可以获取到对应的MvcNamespaceHandler类,然后初始化,初始化方法中对每个mvc标签都映射了一个解析的类,如下:
public class MvcNamespaceHandler extends NamespaceHandlerSupport {
@Override
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());
registerBeanDefinitionParser("redirect-view-controller", new ViewControllerBeanDefinitionParser());
registerBeanDefinitionParser("status-controller", new ViewControllerBeanDefinitionParser());
registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser());
registerBeanDefinitionParser("tiles-configurer", new TilesConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("freemarker-configurer", new FreeMarkerConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("velocity-configurer", new VelocityConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("script-template-configurer", new ScriptTemplateConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("cors", new CorsBeanDefinitionParser());
}
}
然后就开始解析这个<mvc:annotation-driven/>标签,就会调用上面对应mvc标签的解析类的parse()方法,在这个解析方法中会生成RequestMappingHandlerMapping和RequestMappingHandlerAdapter注册到容器中
同理,再比如,当读取beans标签中的子标签<context:component-scan base-package="com.*"/>时,便会调用ContextNamespaceHandler的初始化方法,来映射context标签对应的解析类,如下:
public class ContextNamespaceHandler extends NamespaceHandlerSupport {
@Override
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());
}
}
我们看到component-scan对应的是生成一个会ComponentScanBeanDefinitionParser类,他的parse()方法中会调用
Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
doScan则会调用ClassPathMapperScanner父类ClassPathBeanDefinitionScanner的doScan方法,这个方法中就是扫描把所有带有注解的类,放到容器中。如下:
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
for (String basePackage : basePackages) {
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
同理,其他的标签比如aop等都是同样的道理。
经过上面的过程,程序启动时,就会根据xml中的各种标签,把所有需要的东西初始化完毕。然后程序运行时,只管从容器中拿来使用就行了。
希望大家看完本篇文章后,能对springmvc中mvc标签的处理有所了解。后期将会有更多的详细解析文章,请关注。谢谢!