前言
前面将传统标签解析讲完了,接下来该讲自定义标签解析重点是componentScan
正文
将镜头拉回到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);
}
}
接着点入delegate.parseCustomElement(ele);方法中,我们看一下是如何解析自定义标签的
@Nullable
public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}
继续点下去,这里传入的第二个参数是null,稍微记一下
@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
//1 每一个自定义标签都有namespaceUri,这里是获取它的uri
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
//2通过uri获得对应的处理类handler
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
这里重点解释一下
看代码注释1说每一个自定义标签都有namespaceUri
什么意思呢,例如spring.xml中的这个自定义标签(带有前缀的这种标签叫自定义标签类似还有<aop:xxx>,<mvc:xxx>)
<context:component-scan base-package="XXX.xxx.xxx">
</context:component-scan>
它的namespaceUri就是这个:
http://www.springframework.org/schema/context
这个你可以去你的spring配置文件里面找一下肯定找到的到。
与namespaceUri对应的handler又是怎么会是呢?
点入NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);这个方法就是注释2
public NamespaceHandler resolve(String namespaceUri) {
//获取spring中所有jar包里面的 "META-INF/spring.handlers"文件,并且建立namespaceUri和对应handler映射关系
Map<String, Object> handlerMappings = getHandlerMappings();
//根据namespaceUri:http://www.springframework.org/schema/context,获取到这个命名空间的处理类
Object handlerOrClassName = handlerMappings.get(namespaceUri);
if (handlerOrClassName == null) {
return null;
}
else if (handlerOrClassName instanceof NamespaceHandler) {
return (NamespaceHandler) handlerOrClassName;
}
else {
String className = (String) handlerOrClassName;
try {
Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
}
NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
//调用处理类的init方法,在init方法中完成标签元素解析类的注册
namespaceHandler.init();
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;
}
catch (ClassNotFoundException ex) {
throw new FatalBeanException("Could not find NamespaceHandler class [" + className +
"] for namespace [" + namespaceUri + "]", ex);
}
catch (LinkageError err) {
throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" +
className + "] for namespace [" + namespaceUri + "]", err);
}
}
}
(注意:这里调用了namespaceHandler.init();很重要,后面会讲到)也许有人会问,你怎么知道是"META-INF/spring.handlers"文件这个文件呢?点入getHandlerMappings
private Map<String, Object> getHandlerMappings() {
Map<String, Object> handlerMappings = this.handlerMappings;
if (handlerMappings == null) {
synchronized (this) {
handlerMappings = this.handlerMappings;
if (handlerMappings == null) {
if (logger.isTraceEnabled()) {
logger.trace("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]");
}
try {
//加载"META-INF/spring.handlers"文件过程
Properties mappings =
PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
if (logger.isTraceEnabled()) {
logger.trace("Loaded NamespaceHandler mappings: " + mappings);
}
//所有"META-INF/spring.handlers"文件里面的内容建立映射关系
handlerMappings = new ConcurrentHashMap<>(mappings.size());
CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
this.handlerMappings = handlerMappings;
}
catch (IOException ex) {
throw new IllegalStateException(
"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
}
}
}
}
return handlerMappings;
}
大家看这个变量this.handlerMappingsLocation,是一个location,找一下这个变量定义的地方
public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver {
/**
* 这个变量定义了路径
*/
public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";
/** Logger available to subclasses. */
protected final Log logger = LogFactory.getLog(getClass());
/** ClassLoader to use for NamespaceHandler classes. */
@Nullable
private final ClassLoader classLoader;
/** 这个变量是看我们要找的,并未初始化*/
private final String handlerMappingsLocation;
接着看类构造器,第一个构造器传入了DEFAULT_HANDLER_MAPPINGS_LOCATION,调用第二个构造器对handlerMappingsLocation进行了赋值,所以handlerMappingsLocation的值是"META-INF/spring.handlers";
public DefaultNamespaceHandlerResolver() {
this(null, DEFAULT_HANDLER_MAPPINGS_LOCATION);
}
//中间还有一个构造,这里就不展示了
public DefaultNamespaceHandlerResolver(@Nullable ClassLoader classLoader, String handlerMappingsLocation) {
Assert.notNull(handlerMappingsLocation, "Handler mappings location must not be null");
this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
this.handlerMappingsLocation = handlerMappingsLocation;
}
看到这里应该没啥问题了吧,镜头拉回到上面
@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
//1 每一个自定义标签都有namespaceUri,这里是获取它的uri
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
//2通过uri获得对应的处理类handler
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
上面说了,在resovle方法中调用了nameSpaceHandler的init()方法,这里我们看一下如何去找
<context:component-scan base-package="XXX.xxx.xxx">
</context:component-scan>
对应处理类,找到处理类后点进去
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());
}
}
看到这个init方法没,做了什么?就是将具体的parser处理类放到spring中
比如ComponentScanBeanDefinitionParser();
点入这个类中,有一个很重要的方法parse,这个类完成了ComponentScan工作,逻辑为:
1.寻找你配置的basePackage下的所有.class文件(带有@Component)
2.找到后封装成beanDefinition放到set容器返回
3.注册到spring中
public BeanDefinition parse(Element element, ParserContext parserContext) {
//获取basePackage属性
String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
//可以用逗号分开
String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
//创建注解扫描器
// Actually scan for bean definitions and register them.
ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
//扫描并把扫描的类封装成beanDefinition对象 核心方法,重要程度 5
Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
return null;
}
那在哪里调用的呢?其实已经讲到了,就在上面
看到这里应该没啥问题了吧,镜头拉回到上面
@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
//1 每一个自定义标签都有namespaceUri,这里是获取它的uri
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
//2通过uri获得对应的处理类handler
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
最后有个return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));充分的应用的多态
最后
讲到这里自定义标签就讲完了,总结一下:
我们这ioc前三章博客,就是说了将spring.xml变成beanDefinition,存入到spring中,那么我们对那些类实例化了嘛,并没有,只是完成了准备工作,再说实例化之前先得介绍一下beanDefinition。下一章讲一下beanDefinition是个啥?在spring容器中有啥作用。