死磕Spring之IoC篇 - 解析自定义标签(XML 文件) (4)

到这里,对于 Spring XML 文件中的自定义标签的处理逻辑你是不是清晰了,接下来我们来看看 <context:component-scan /> 标签的具体实现

ContextNamespaceHandler

org.springframework.context.config.ContextNamespaceHandler,继承 NamespaceHandlerSupport 抽象类,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()); } }

init() 方法在 DefaultNamespaceHandlerResolver#resolve 方法中可以看到,初始化该对象的时候会被调用,注册该命名空间下各种标签的解析器

registerBeanDefinitionParser 方法

registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser),注册标签的解析器,方法如下:

// NamespaceHandlerSupport.java private final Map<String, BeanDefinitionParser> parsers = new HashMap<>(); protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) { this.parsers.put(elementName, parser); }

将标签名称和对应的解析器保存在 Map 中

parse 方法

parse(Element element, ParserContext parserContext) 方法,解析标签节点,方法如下:

@Override @Nullable public BeanDefinition parse(Element element, ParserContext parserContext) { // <1> 获得元素对应的 BeanDefinitionParser 对象 BeanDefinitionParser parser = findParserForElement(element, parserContext); // <2> 执行解析 return (parser != null ? parser.parse(element, parserContext) : null); } @Nullable private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) { // 获得元素名 String localName = parserContext.getDelegate().getLocalName(element); // 获得 BeanDefinitionParser 对象 BeanDefinitionParser parser = this.parsers.get(localName); if (parser == null) { parserContext.getReaderContext().fatal( "Cannot locate BeanDefinitionParser for element [" + localName + "]", element); } return parser; }

逻辑很简单,从 Map<String, BeanDefinitionParser> parsers 找到标签对象的 BeanDefinitionParser 解析器,然后进行解析

ComponentScanBeanDefinitionParser

org.springframework.context.annotation.ComponentScanBeanDefinitionParser,实现了 BeanDefinitionParser 接口,<context:component-scan /> 标签的解析器

parse 方法

parse(Element element, ParserContext parserContext) 方法,<context:component-scan /> 标签的解析过程,方法如下:

@Override @Nullable public BeanDefinition parse(Element element, ParserContext parserContext) { // <1> 获取 `base-package` 属性 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. // <2> 创建 ClassPathBeanDefinitionScanner 扫描器,用于扫描指定路径下符合条件的 BeanDefinition 们 ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element); // <3> 通过扫描器扫描 `basePackages` 指定包路径下的 BeanDefinition(带有 @Component 注解或其派生注解的 Class 类),并注册 Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages); // <4> 将已注册的 `beanDefinitions` 在当前 XMLReaderContext 上下文标记为已注册,避免重复注册 registerComponents(parserContext.getReaderContext(), beanDefinitions, element); return null; }

过程如下:

获取 base-package 属性,处理占位符,根据分隔符进行分割

创建 ClassPathBeanDefinitionScanner 扫描器,用于扫描指定路径下符合条件的 BeanDefinition 们,调用 configureScanner(ParserContext parserContext, Element element) 方法

通过扫描器扫描 basePackages 指定包路径下的 BeanDefinition(带有 @Component 注解或其派生注解的 Class 类),并注册

将已注册的 beanDefinitions 在当前 XMLReaderContext 上下文标记为已注册,避免重复注册

上面的第 3 步的解析过程和本文的主题有点不符,过程也比较复杂,下一篇文章再进行分析

configureScanner 方法

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwdfxz.html