1 自定义标签
遗留问题:
- 在AbstractApplicationContext 中的prepareBeanFactory方法设置了addBeanPostProcessor ,是否和Bean 中的生命周期有关。在ClassPathXmlApplicationContext中调用了AsbstractApplicationContext的refresh方法。
- 依据RootBeanDefinition 实例化Bean
代码总结:
- 缓存数据模式 读缓存(未命中) - 加锁 - 读缓存 (未命中)- 缓存数据
- SPI实现依据参数读取不同的实现类。
dubbo-demo-consumer.xml 中使用了Dubbo自定义标签,例如:dubbo:application;dubbo:reference;在spring中使用自定义标签,有如下几步:
-
设置配置属性和JavaBean
设置一个与标签对应的JavaBean,用于存放标签中的属性值, 例如Dubbo中的ApplicationConfig、ReferenceConfig
-
设置XSD文件
文件内容如 xsd链接 所示
-
编写BeanDefinitionParser
实现对配置文件中的标签解析。如 DubboBeanDefinitionParser
-
编写NamespaceHandler
实现标签和解析类BeanDefinitionParser链接,如DubboNamespaceHandler,其中的init()代码如下:
// DubboNamespaceHandler.init()
@Override
public void init() {
registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
registerBeanDefinitionParser("config-center", new DubboBeanDefinitionParser(ConfigCenterBean.class, true));
registerBeanDefinitionParser("metadata-report", new DubboBeanDefinitionParser(MetadataReportConfig.class, true));
registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
registerBeanDefinitionParser("metrics", new DubboBeanDefinitionParser(MetricsConfig.class, true));
registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
}
/*NamespaceHandle的init方法主要调用继承NamespaceHandlerSupport的registerBeanDefinitionParser方法.*/
private final Map<String, BeanDefinitionParser> parsers = new HashMap<>();
/**
* Subclasses can call this to register the supplied {@link BeanDefinitionParser} to
* handle the specified element. The element name is the local (non-namespace qualified)
* name.
*/
protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
this.parsers.put(elementName, parser);
//因此parsers中保存标签和BeanDefinitionParser的映射。
可以看出,dubbo新建的标签主要有application,consumer、reference等,spring 在根据标签查找对应的DubboNamespaceHandler的调用位置如下图:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0TPR10drdVWvJ1MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL5EDN4QTM0gTM1AjMxkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
调用链如图:
resolve的代码如下:
@Override
@Nullable
public NamespaceHandler resolve(String namespaceUri) {
Map<String, Object> handlerMappings = getHandlerMappings();
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方法,注册标签和对应的DubboBeanDefinitionParser类
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);
}
}
}
入参namespaceUri 来源于配置文件中的标签中的内容。方法getHandlerMappings()代码如下:
/**
* Load the specified NamespaceHandler mappings lazily.
*/
private Map<String, Object> getHandlerMappings() {
// 如果map中缓存数据,则直接去map中的数,map的格式是<namespaceUri, NamespaceHandler对象>
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 {
//默认情况下handlerMappingsLocation = "META-INF/spring.handlers",
Properties mappings =
PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
if (logger.isTraceEnabled()) {
logger.trace("Loaded NamespaceHandler mappings: " + mappings);
}
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;
}
开始时,handlerMappings = null, 只能通过PropertiesLoaderUtils.loadAllProperties来加载所有的"META-INF/spring.handlers" 文件,该方法的代码如下:
/**
* Load all properties from the specified class path resource
* (in ISO-8859-1 encoding), using the given class loader.
* <p>Merges properties if more than one resource of the same name
* found in the class path.
* @param resourceName the name of the class path resource
* @param classLoader the ClassLoader to use for loading
* (or {@code null} to use the default class loader)
* @return the populated Properties instance
* @throws IOException if loading failed
*/
public static Properties loadAllProperties(String resourceName, @Nullable ClassLoader classLoader) throws IOException {
Assert.notNull(resourceName, "Resource name must not be null");
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = ClassUtils.getDefaultClassLoader();
}
Enumeration<URL> urls = (classLoaderToUse != null ? classLoaderToUse.getResources(resourceName) :
ClassLoader.getSystemResources(resourceName));
Properties props = new Properties();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
URLConnection con = url.openConnection();
ResourceUtils.useCachesIfNecessary(con);
InputStream is = con.getInputStream();
try {
if (resourceName.endsWith(XML_FILE_EXTENSION)) {
props.loadFromXML(is);
}
else {
props.load(is);
}
}
finally {
is.close();
}
}
return props;
}
}
其中Dubbo-2.7.3.jar中的文件内容为:
http\://dubbo.apache.org/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandler
http\://code.alibabatech.com/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandler
查找DubboNamespaceHandler总结:加载类路径下所有的META-INF/spring.handlers文件,其中存放namespaceUri和NamespaceHandler的映射;加载并实例化对应的NamespaceHandler类,调用init方法,并存入map中,便于下次调用。
2 解析Dubbo标签
调用链如图:
//BeanDefinitionParserDelegate.parseCustomerElement方法
/**
* Parse a custom element (outside of the default namespace).
* @param ele the element to parse
* @param containingBd the containing bean definition (if any)
* @return the resulting bean definition
*/
@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
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));
}
//DubboNamespaceHanler中的parse方法,继承自NamespaceHandlerSupport
/**
* Parses the supplied {@link Element} by delegating to the {@link BeanDefinitionParser} that is
* registered for that {@link Element}.
*/
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
BeanDefinitionParser parser = findParserForElement(element, parserContext);
//详细代码见下
return (parser != null ? parser.parse(element, parserContext) : null);
}
//DubboBeanDefinitionParser,parse方法
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
return parse(element, parserContext, beanClass, required);
}
DubboBeanDefinitionParser.parse的方法可以,一个方法就可以实现把所有的标签内容存放在RootBeanDefinition,不过生成的RootBeanDefinition结果没有保存?
3 依据BeanDefinition实例化Bean
这一段代码与Dubbo关系不大,也有相关文档解析,就不贴代码了,实例化的调用链如图所示: