本人本科毕业,21届毕业生,一年工作经验,简历专业技能如下,现根据简历,并根据所学知识复习准备面试。
记录日期:2022.1.4
大部分知识点只做大致介绍,具体内容根据推荐博文链接进行详细复习。
文章目录
- 框架原理 - Spring(四)之Spring IOC 源码obtainFreshBeanFactory()
-
- AbstractApplicationContext#obtainFreshBeanFactory()
-
- refreshBeanFactory()
- 引入:BeanDefinition
- customizeBeanFactory()
- 加载bean - loadBeanDefinitions(beanFactory)【重点】
- loadBeanDefinitions(reader)
- loadBeanDefinitions(configLocations)
- loadBeanDefinitions(configResources)
- doLoadBeanDefinitions()
- doLoadDocument()
- registerBeanDefinitions()
- createReaderContext()
- doRegisterBeanDefinitions()
- parseBeanDefinitions()
- parseDefaultElement()【默认标签】
- processBeanDefinition()
- parseBeanDefinitionElement(ele,containingBean)
- parseBeanDefinitionElement(ele,bd,containingBean)
- parseBeanDefinitionAttributes()
- parseConstructorArgElements()
- parseConstructorArgElement()
- parsePropertyValue()
- parsePropertySubElement()
- parseValueElement()
- parseListElement()
- parsePropertyElements()
- parsePropertyElement()
- 注册bean - registerBeanDefinition(bdh,registry) 【重点】
- registerBeanDefinition(beanName,bd)
- resetBeanDefinition()
- registerAlias()
- parseCustomElement()【自定义标签】
- DefaultNamespaceHandlerResolver.resolve()
- getHandlerMappings()
- nameSpaceHandler.init()
- registerBeanDefinitionParser() & registerBeanDefinitionDecorator()
- parse()
框架原理 - Spring(四)之Spring IOC 源码obtainFreshBeanFactory()
AbstractApplicationContext#obtainFreshBeanFactory()
注意,这个方法是全文最重要的部分之一,这里将会初始化 BeanFactory、加载 Bean、注册 Bean 等等。
该方***解析所有 Spring 配置文件(通常我们会放在 resources
目录下),将所有 Spring 配置文件中的 bean 定义封装成 BeanDefinition
,加载到 BeanFactory
中。常见的,如果解析到<context:component-scan base-package="" />
注解时,会扫描 base-package
指定的目录,将该目录下使用指定注解(@Controller
、@Service
、@Component
、@Repository
)的 bean 定义也同样封装成 BeanDefinition
,加载到 BeanFactory
中。
在 BeanFactory 中,有三个重要的缓存:
- beanDefinitionNames缓存:所有被加载到 BeanFactory 中的 bean 的 beanName 集合。
- beanDefinitionMap缓存:所有被加载到 BeanFactory 中的 bean 的 beanName 和 BeanDefinition 映射。
- aliasMap缓存:所有被加载到 BeanFactory 中的 bean 的 beanName 和别名映射。
前两者都在 DefaultListableBeanFactory
中:
/** List of bean definition names, in registration order. */
private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
/** Map of bean definition objects, keyed by bean name. */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
最后一个在 SimpleAliasRegistry
中:
/** Map from alias to canonical name. */
private final Map<String, String> aliasMap = new ConcurrentHashMap<>(16);
当然,obtainFreshBeanFactory()方法结束后,Bean 并没有完成初始化。
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
// 1. 关闭旧的 BeanFactory (如果有),创建新的 BeanFactory,加载 Bean 定义、注册 Bean 等等
refreshBeanFactory();
// 2. 返回刚刚创建的 BeanFactory
return getBeanFactory();
}
上面提到 refreshBeanFactory()
和 getBeanFactory()
都是留给子类实现的抽象方法。
protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;
public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
refreshBeanFactory()
我们来看 obtainFreshBeanFactory()
代码块 1
中。
因为我们现在讲的都是ClassPathXmlApplicationContext
,所以我们来看一下AbstractRefreshableApplicationContext
中的实现:
@Override
protected final void refreshBeanFactory() throws BeansException {
// 如果 ApplicationContext 中已经加载过 BeanFactory 了,销毁所有 Bean,关闭 BeanFactory
// 注意,应用中 BeanFactory 本来就是可以多个的,这里可不是说应用全局是否有 BeanFactory,而是当前
// ApplicationContext 是否有 BeanFactory
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
// 初始化一个 DefaultListableBeanFactory,为什么用这个实现类下面会有解释
DefaultListableBeanFactory beanFactory = createBeanFactory();
// 用于 BeanFactory 的序列化,一般情况下用不太到
beanFactory.setSerializationId(getId());
// 下面这两个方法很重要
// 设置 BeanFactory 的两个配置属性:是否允许 Bean 覆盖、是否允许循环引用
customizeBeanFactory(beanFactory);
// 加载 Bean 到 BeanFactory 中
loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
@Override
public final ConfigurableListableBeanFactory getBeanFactory() {
DefaultListableBeanFactory beanFactory = this.beanFactory;
if (beanFactory == null) {
throw new IllegalStateException("BeanFactory not initialized or already closed - " +
"call 'refresh' before accessing beans via the ApplicationContext");
}
return beanFactory;
}
ApplicationContext
继承自 BeanFactory
,但是它不应该被理解为 BeanFactory
的实现类,而是说其内部持有一个实例化的 BeanFactory
(DefaultListableBeanFactory
)。以后所有的 BeanFactory
相关的操作其实是给这个实例来处理的。
但是这里为什么选择实现DefaultListableBeanFactory
的原因,需要再看一下接口ConfigurableListableBeanFactory
,再看一遍继承图吧,我上面那张看的不是很明白,拿一张别人的图来看一下:
我们可以看到 ConfigurableListableBeanFactory
只有一个实现类 DefaultListableBeanFactory
,而且实现类 DefaultListableBeanFactory
还实现了右边的 AbstractAutowireCapableBeanFactory
。
所以结论就是,最底下这个Bean工厂 DefaultListableBeanFactory
基本上是功能最全面的 BeanFactory
。
引入:BeanDefinition
在继续往下说 customizeBeanFactory(beanFactory)
和 loadBeanDefinitions(beanFactory)
前,我们需要先了解 BeanDefinition
。
BeanFactory
是 Bean 容器,而这里的 BeanDefinition
就是我们所说的 Spring 的 Bean,我们自己定义的各个 Bean 其实会转换成一个个 BeanDefinition
存在于 Spring 的 BeanFactory
中。
BeanDefinition
中保存了我们的 Bean 信息,比如这个 Bean 指向的是哪个类、是否是单例的、是否懒加载、这个 Bean 依赖了哪些 Bean 等等…
我们来看一下 BeanDefinition
的接口代码:
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
// 我们可以看到,默认只提供 sington 和 prototype 两种,
// 不过我们在平时开发中还有 request, session, globalSession, application, websocket 这几种,
// 不过,它们属于基于 web 的扩展。
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
// 比较不重要,直接跳过吧
int ROLE_APPLICATION = 0;
int ROLE_SUPPORT = 1;
int ROLE_INFRASTRUCTURE = 2;
// 设置父 Bean,这里涉及到 bean 继承,不是 java 继承。请参见附录介绍
void setParentName(String parentName);
// 获取父 Bean
String getParentName();
// 设置 Bean 的类名称
void setBeanClassName(String beanClassName);
// 获取 Bean 的类名称
String getBeanClassName();
// 设置 bean 的 scope
void setScope(String scope);
String getScope();
// 设置是否懒加载
void setLazyInit(boolean lazyInit);
boolean isLazyInit();
// 设置该 Bean 依赖的所有的 Bean,注意,这里的依赖不是指属性依赖(如 @Autowire 标记的),
// 是 depends-on="" 属性设置的值。
void setDependsOn(String... dependsOn);
// 返回该 Bean 的所有依赖
String[] getDependsOn();
// 设置该 Bean 是否可以注入到其他 Bean 中,只对根据类型注入有效,
// 如果根据名称注入,即使这边设置了 false,也是可以的
void setAutowireCandidate(boolean autowireCandidate);
// 该 Bean 是否可以注入到其他 Bean 中
boolean isAutowireCandidate();
// 主要的。同一接口的多个实现,如果不指定名字的话,Spring 会优先选择设置 primary 为 true 的 bean
void setPrimary(boolean primary);
// 是否是 primary 的
boolean isPrimary();
// 如果该 Bean 采用工厂方法生成,指定工厂名称。对工厂不熟悉的读者,请参加附录
void setFactoryBeanName(String factoryBeanName);
// 获取工厂名称
String getFactoryBeanName();
// 指定工厂类中的 工厂方法名称
void setFactoryMethodName(String factoryMethodName);
// 获取工厂类中的 工厂方法名称
String getFactoryMethodName();
// 构造器参数
ConstructorArgumentValues getConstructorArgumentValues();
// spring5 后的方法 对构造器参数判空
default boolean hasConstructorArgumentValues();
// Bean 中的属性值,后面给 bean 注入属性值的时候会说到
MutablePropertyValues getPropertyValues();
// spring5 后的方法 对bean的属性值判空
default boolean hasPropertyValues();
// spring5.1 后的方法,设置初始化方法名称
void setInitMethodName(@Nullable String initMethodName);
// spring5.1 后的方法,获取初始化方法名称
String getInitMethodName();
// spring5.1 后的方法,设置销毁方法名称
void setDestroyMethodName(@Nullable String destroyMethodName);
// spring5.1 后的方法,获取销毁方法名称
String getDestroyMethodName();
// 是否 singleton
boolean isSingleton();
// 是否 prototype
boolean isPrototype();
// 如果这个 Bean 原生是抽象类,那么不能实例化
boolean isAbstract();
void setRole(int role);
int getRole();
ResolvableType getResolvableType();
String getDescription();
String getResourceDescription();
BeanDefinition getOriginatingBeanDefinition();
}
这里接口虽然那么多,但是没有类似 getInstance() 这种方法来获取我们定义的类的实例,真正的我们定义的类生成的实例在后面才会说到。
customizeBeanFactory()
customizeBeanFactory(beanFactory)
比较简单,就是配置是否允许 BeanDefinition
覆盖、是否允许循环引用。
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
if (this.allowBeanDefinitionOverriding != null) {
// 是否允许 Bean 定义覆盖
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.allowCircularReferences != null) {
// 是否允许 Bean 间的循环依赖
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}
}
- allowBeanDefinitionOverriding 参数
BeanDefinition 的覆盖问题大家也许会碰到,就是在配置文件中定义 bean 时使用了相同的 id 或 name,默认情况下,allowBeanDefinitionOverriding
属性为 null,如果在同一配置文件中重复了,会抛错,但是如果不是同一配置文件中,会发生覆盖。
Spring 默认是不同文件的时候可以覆盖的。
- allowCircularReferences 参数
循环引用也很好理解:A 依赖 B,而 B 依赖 A。或 A 依赖 B,B 依赖 C,而 C 依赖 A。
默认情况下,Spring 允许循环依赖,当然如果你在 A 的构造方法中依赖 B,在 B 的构造方法中依赖 A 是不行的。
加载bean - loadBeanDefinitions(beanFactory)【重点】
接下来是最重要的 loadBeanDefinitions(beanFactory)
方法了,这个方法将根据配置,加载注册各个 Bean,然后放到 BeanFactory 中。
读取配置的操作在 XmlBeanDefinitionReader
中,其负责加载配置、解析。
这里我们来看一下AbstractXmlApplicationContext
中的loadBeanDefinitions
实现:
// 我们可以看到,此方法将通过一个 XmlBeanDefinitionReader 实例来加载各个 Bean。
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 1. 给这个 BeanFactory 实例化一个 XmlBeanDefinitionReader
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 2. 使用上下文资源加载环境配置 beanDefinitionReader
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 3.初始化 BeanDefinitionReader,自身实现就是设置了validating
initBeanDefinitionReader(beanDefinitionReader);
// 4.加载 bean 定义【重点代码】
loadBeanDefinitions(beanDefinitionReader);
}
现在还在 AbstractXmlApplicationContext
中,接下来用刚刚初始化的 Reader 开始来加载 xml 配置,接下来的一大串代码可以跳过。
loadBeanDefinitions(reader)
我们来看 loadBeanDefinitions
代码块 4
中,这里是加载 bean 定义最重要的实现。
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
/* 获取配置文件路径方式: 如果 configLocations 属性不为空,则返回 configLocations 的值; 否则,调用 getDefaultConfigLocations() 方法。 ① configLocations 属性会被赋值为我们在web.xml中配置的 contextConfigLocation 的参数值,如下 <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:config/spring/appcontext-*.xml</param-value> </context-param> ② 如果 web.xml 中没有配置 contextConfigLocation 参数, 则会拿到 Spring 默认的配置路径:/WEB-INF/applicationContext.xml */
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
loadBeanDefinitions(configLocations)
上面虽然有两个分支,不过第二个分支很快通过解析路径转换为 Resource
以后也会进到 loadBeanDefinitions(Resource... resources)
中,所以我们先来看如何解析配置文件。
// AbstarctBeanDefinitionReader 253
@Override
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null");
int count = 0;
for (String location : locations) {
// 根据配置文件路径加载 bean 定义
count += loadBeanDefinitions(location);
}
return count;
}
// AbstarctBeanDefinitionReader 194
@Override
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return loadBeanDefinitions(location, null);
}
// AbstarctBeanDefinitionReader 213
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
// 1. 获取 resourceLoader,这边为 XmlWebApplicationContext
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
}
// 2. 判断 resourceLoader 是否为 ResourcePatternResolver 的实例,这里可以看一下继承图
// resourceLoader 为 XmlWebApplicationContext,它实现了 ResourcePatternResolver 接口
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
// 2.1 根据路径拿到该路径下所有符合的配置文件,并封装成Resource
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
// 根据Resource,加载Bean的定义,这里就会走到下面要说的loadBeanDefinitions(Resource... resources)代码了
int count = loadBeanDefinitions(resources);
if (actualResources != null) {
Collections.addAll(actualResources, resources);
}
if (logger.isTraceEnabled()) {
logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
}
return count;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
else {
// Can only load single resources by absolute URL.
// 3.只能通过绝对URL加载单个资源
Resource resource = resourceLoader.getResource(location);
// 3.1 同上,根据Resource,加载Bean的定义
int count = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isTraceEnabled()) {
logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
}
return count;
}
}
loadBeanDefinitions(configResources)
第二分支和第一个分支都会到达这里,根据 Resource
加载 bean 定义,由 XmlBeanDefinitionReader
实现。
@Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
Assert.notNull(resources, "Resource array must not be null");
int counter = 0;
// 注意这里是个 for 循环,也就是每个文件是一个 resource
for (Resource resource : resources) {
// 继续往下看
counter += loadBeanDefinitions(resource);
}
return counter;
}
// XmlBeanDefinitionReader 309
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
// 加载 bean 定义
return loadBeanDefinitions(new EncodedResource(resource));
}
// XmlBeanDefinitionReader 329
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isInfoEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
// 用一个 ThreadLocal 来存放所有的配置文件资源
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
// 2. 将当前encodedResource添加到currentResources
if (!currentResources.add(encodedResource)) {
// 如果添加失败,代表当前的encodedResource已经存在,则表示出现了循环加载
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
// 3. 拿到Resource 的 inputStream
try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
// 4. 将inputStream 封装成 org.xml.sax.InputSource
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
// 5. 加载 bean 定义【核心部分】
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
// 加载完成后移除正在加载的缓存集合
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
doLoadBeanDefinitions()
在 loadBeanDefinitions(configResources)
的代码块 5
中,这才是真正的加载 bean 定义的核心方法。
// XmlBeanDefinitionReader 386
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 1. 根据inputSource和resource加载XML文件,并封装成Document
Document doc = doLoadDocument(inputSource, resource);
// 2. 根据返回的Document注册Bean信息(对配置文件的解析,核心逻辑)
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
// 疯狂catch
catch (...) {
...
}...
}
doLoadDocument()
在 doLoadBeanDefinitions()
代码块 1
中,根据 inputSource
和 resource
加载 XML
文件,并封装成 Document
。
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
// 1. getValidationModeForResource(resource): 获取XML配置文件的验证模式
// 2. documentLoader.loadDocument: 加载XML文件,并得到对应的 Document
return this.documentLoader.loadDocument(inputSource,
getEntityResolver(),
this.errorHandler,
getValidationModeForResource(resource),
isNamespaceAware());
}
首先我们来看 getValidationModeForResource()
中:
该方法用于获取 XML 配置文件的验证模式。XML 文件的验证模式是用来保证 XML 文件的正确性,常见的验证模式有两种:DTD
和 XSD
。
DTD 验证模式
(停用)- DTD 即
文档类型定义
,是一种XML约束模式语言,是XML文件的验证机制,属于XML文件组成的一部分。 - DTD 是一种保证XML文档格式正确的有效方法,可以通过比较XML文档和DTD文件来看文档是否符合规范,元素和标签使用是否正确。
- DTD 文档包含内容:
- 元素的定义规则
- 元素间关系的定义规则
- 元素可使用的属性
- 可使用的实体或符号规则
- DTD 文档缺点:
- DTD 是使用非 XML 语法编写的。
- DTD 不可扩展,不支持命名空间,只提供非常有限的数据类型。
- DTD 即
XSD 验证模式
- XML Schema语言也就是XSD。XML Schema描述了XML文档的结构。
- 可以用一个指定的XML Schema来验证某个XML文档,以检查该XML文档是否符合其要求。文档设计者可以通过XML Schema指定一个XML文档所允许的结构和内容,并可据此检查一个XML文档是否是有效的。XML Schema本身是一个XML文档,它符合XML语法结构。可以用通用的XML解析器解析它。
- XSD 文档包含内容:
- 文档中出现的元素
- 文档中出现的属性
- 子元素
- 子元素的数量
- 子元素的顺序
- 元素是否为空
- 元素和属性的数据类型
- 元素或属性的默认
- 固定值
- 替代 DTD 文档的原因:一是据将来的条件可扩展,二是比DTD丰富和有用,三是用XML书写,四是支持数据类型,五是支持命名空间。
- XSD 文档优点:
- XSD 基于XML,没有专门的语法。
- 可以像其他XML文件一样解析和处理。
- XSD 比 DTD 提供了更丰富的数据类型。
- 提供可扩充的数据模型。
- 支持综合命名空间。
- 支持属性组。
从Spring 2.0 后版本都开始使用 XSD 验证模式。
protected int getValidationModeForResource(Resource resource) {
int validationModeToUse = getValidationMode();
// 1.1 如果手动指定了XML文件的验证模式则使用指定的验证模式
if (validationModeToUse != VALIDATION_AUTO) {
return validationModeToUse;
}
// 1.2 如果未指定则使用自动检测
int detectedMode = detectValidationMode(resource);
// 1.3 如果检测出的验证模式不为 VALIDATION_AUTO, 则返回检测出来的验证模式
if (detectedMode != VALIDATION_AUTO) {
return detectedMode;
}
// Hmm, we didn't get a clear indication... Let's assume XSD,
// since apparently no DTD declaration has been found up until
// detection stopped (before finding the document's root tag).
// 1.4 如果最终没找到验证模式,则使用 XSD
return VALIDATION_XSD;
}
protected int detectValidationMode(Resource resource) {
// 1.2.1 校验resource是否为open stream
if (resource.isOpen()) {
throw new BeanDefinitionStoreException(
"Passed-in Resource [" + resource + "] contains an open stream: " +
"cannot determine validation mode automatically. Either pass in a Resource " +
"that is able to create fresh streams, or explicitly specify the validationMode " +
"on your XmlBeanDefinitionReader instance.");
}
InputStream inputStream;
try {
// 1.2.2 校验resource是否可以打开InputStream
inputStream = resource.getInputStream();
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Unable to determine validation mode for [" + resource + "]: cannot open InputStream. " +
"Did you attempt to load directly from a SAX InputSource without specifying the " +
"validationMode on your XmlBeanDefinitionReader instance?", ex);
}
try {
// 1.2.3 根据inputStream检测验证模式
return this.validationModeDetector.detectValidationMode(inputStream);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("Unable to determine validation mode for [" +
resource + "]: an error occurred whilst reading from the InputStream.", ex);
}
}
public int detectValidationMode(InputStream inputStream) throws IOException {
// Peek into the file to look for DOCTYPE.
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
boolean isDtdValidated = false;
String content;
// 1.2.3.1 按行遍历xml配置文件,获取xml文件的验证模式
while ((content = reader.readLine()) != null) {
content = consumeCommentTokens(content);
// 如果读取的行是空或者注释则略过
if (this.inComment || !StringUtils.hasText(content)) {
continue;
}
// 内容包含"DOCTYPE"则为DTD,否则为XSD
if (hasDoctype(content)) {
isDtdValidated = true;
break;
}
// 如果content带有 '<' 开始符号,则结束遍历。因为验证模式一定会在开始符号之前,所以到此可以认为没有验证模式
if (hasOpeningTag(content)) {
// End of meaningful data...
break;
}
}
// 1.2.3.2 根据遍历结果返回验证模式是 DTD 还是 XSD
return (isDtdValidated ? VALIDATION_DTD : VALIDATION_XSD);
}
catch (CharConversionException ex) {
// Choked on some character encoding...
// Leave the decision up to the caller.
return VALIDATION_AUTO;
}
}
我们再来看一下 documentLoader.loadDocument(...)
的实现:
// DefaultDocumentLoader 69
@Override
public Document loadDocument(InputSource inputSource,
EntityResolver entityResolver,
ErrorHandler errorHandler,
int validationMode,
boolean namespaceAware)
throws Exception {
// 2.1 创建DocumentBuilderFactory
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isTraceEnabled()) {
logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
}
// 2.2 通过DocumentBuilderFactory创建DocumentBuilder
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
// 2.3 使用DocumentBuilder解析inputSource返回Document对象
return builder.parse(inputSource);
}
registerBeanDefinitions()
我们回到 doLoadBeanDefinitions()
代码块 2
中。
// XmlBeanDefinitionReader 508
// 返回从当前配置文件加载了多少数量的 Bean
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 1. 使用DefaultBeanDefinitionDocumentReader 实例化 BeanDefinitionDocumentReader
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// 2. 记录统计前BeanDefinition的加载个数
int countBefore = getRegistry().getBeanDefinitionCount();
// 3. createReaderContext():根据resource创建一个XmlReaderContext
// 4. registerBeanDefinitions():加载及注册Bean定义
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
// 5. 返回本次加载的BeanDefinition个数
return getRegistry().getBeanDefinitionCount() - countBefore;
}
// DefaultBeanDefinitionDocumentReader 94
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
doRegisterBeanDefinitions(doc.getDocumentElement());
}
createReaderContext()
回到 registerBeanDefinitions()
代码块的 3
中,它是用来根据 resource
创建一个 XmlReaderContext
。
这边会根据 resource 构建一个 XmlReaderContext
,用于存放解析时会用到的一些上下文信息。
public XmlReaderContext createReaderContext(Resource resource) {
return new XmlReaderContext(
resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, getNamespaceHandlerResolver()
);
}
public NamespaceHandlerResolver getNamespaceHandlerResolver() {
if (this.namespaceHandlerResolver == null) {
this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
}
return this.namespaceHandlerResolver;
}
protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
ClassLoader cl = (getResourceLoader() != null ?
getResourceLoader().getClassLoader() : getBeanClassLoader());
return new DefaultNamespaceHandlerResolver(cl);
}
// DefaultNamespaceHandlerResolver
public DefaultNamespaceHandlerResolver(@Nullable ClassLoader classLoader) {
this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION);
}
public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";
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;
}
其中 namespaceHandlerResolver
会创建默认的 DefaultNamespaceHandlerResolver
,DefaultNamespaceHandlerResolver
的handlerMappingsLocation
属性会使用默认的值 "META-INF/spring.handlers"
,并且这边有个重要的属性 handlerMappings
,handlerMappings
用于存放命名空间和该命名空间 handler
类的映射。
public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver {
public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";
@Nullable
private final ClassLoader classLoader;
private final String handlerMappingsLocation;
@Nullable
private volatile Map<String, Object> handlerMappings;
}
如下图:
handlerMappings
的值默认位于"META-INF/spring.handlers"
文件下,一般在我们定义自定义注解时需要用到。
doRegisterBeanDefinitions()
我们回到 registerBeanDefinitions()
的代码块 4
中。
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
doRegisterBeanDefinitions(doc.getDocumentElement());
}
经过漫长的链路方法调用,将配置文件转换为一颗 DOM 树。
注意,这里指的是其中一个配置文件,不是所有的,可以在上面有个 for 循环。
下面从根节点开始解析:
// DefaultBeanDefinitionDocumentReader 121
protected void doRegisterBeanDefinitions(Element root) {
// 我们看名字就知道,BeanDefinitionParserDelegate 必定是一个重要的类,它负责解析 Bean 定义,
// 这里为什么要定义一个 parent? 看到后面就知道了,是递归问题,
// 因为 <beans /> 内部是可以定义 <beans /> 的,所以这个方法的 root 其实不一定就是 xml 的根节点,也可以是嵌套在里面的 <beans /> 节点,从源码分析的角度,我们当做根节点就好了
BeanDefinitionParserDelegate parent = this.delegate;
// 构建BeanDefinitionParserDelegate
this.delegate = createDelegate(getReaderContext(), root, parent);
// 1. 校验root节点的命名空间是否为默认的命名空间(默认命名空间http://www.springframework.org/schema/beans)
if (this.delegate.isDefaultNamespace(root)) {
// 2. 处理 profile 属性
// 这块说的是根节点 <beans ... profile="dev" /> 中的 profile 是否是当前环境需要的,
// 如果当前环境配置的 profile 不包含此 profile,那就直接 return 了,不对此 <beans /> 解析
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); // PROFILE_ATTRIBUTE = profile
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// 校验当前节点的 profile 是否符合当前环境定义的, 如果不是则直接跳过, 不解析该节点下的内容
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isInfoEnabled()) {
logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
// 3.解析前处理, 留给子类实现
preProcessXml(root); // 钩子
// 4.解析并注册bean定义
parseBeanDefinitions(root, this.delegate);
// 5.解析后处理, 留给子类实现
postProcessXml(root); // 钩子
this.delegate = parent;
}
parseBeanDefinitions()
接下来,看核心解析方法 parseBeanDefinitions(root, this.delegate)
的代码实现:
// DefaultBeanDefinitionDocumentReader 168
// default namespace 涉及到的就四个标签 <import />、<alias />、<bean /> 和 <beans />,
// 其他的属于 custom 的
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// 1.默认命名空间的处理
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
// 遍历root的子节点列表
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)) {
// 1.1 默认命名空间节点的处理,例如: <bean id="test" class="" />
parseDefaultElement(ele, delegate);
}
else {
// 1.2 自定义命名空间节点的处理,例如:<context:component-scan/>、<aop:aspectj-autoproxy/>
delegate.parseCustomElement(ele);
}
}
}
}
else {
// 2.自定义命名空间的处理
delegate.parseCustomElement(root);
}
}
从上面的代码,我们可以看到,对于每个配置来说,分别进入到 parseDefaultElement(ele, delegate)
和 delegate.parseCustomElement(ele)
这两个分支了。
parseDefaultElement(ele, delegate)
代表解析的节点是<import />
、<alias />
、<bean />
、<beans />
这几个。
这里的四个标签之所以是 default 的,是因为它们是处于这个 namespace 下定义的:http://www.springframework.org/schema/beans
而对于其他的标签,将进入到 delegate.parseCustomElement(element)
这个分支。如我们经常会使用到的 <mvc />
、<task />
、<context />
、<aop />
等。
这些属于扩展,如果需要使用上面这些 ”非 default“ 标签,那么上面的 xml 头部的地方也要引入相应的 namespace 和 .xsd 文件的路径,如下所示。同时代码中需要提供相应的 parser
来解析,如 MvcNamespaceHandler
、TaskNamespaceHandler
、ContextNamespaceHandler
、AopNamespaceHandler
等。
parseDefaultElement()【默认标签】
我们先来看一下处理 default 标签的parseDefaultElement(ele, delegate)
方法:
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
// 1. 处理 <import /> 标签
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
// 2. 处理 <alias /> 标签定义
// <alias name="fromName" alias="toName"/>
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
// 3. 处理 <bean /> 标签定义,【最复杂、最重点】
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// 4. 如果碰到的是嵌套的 <beans /> 标签,需要递归
doRegisterBeanDefinitions(ele);
}
}
processBeanDefinition()
挑选具有代表性的一个来举例说明,就拿<bean />
来说。
先来看一下<bean />
标签可以配置的参数字段:
property | 含义 |
---|---|
class | 类的全限定名 |
name | 可指定 id、name(用逗号、分号、空格分隔) |
scope | 作用域 |
constructor arguments | 指定构造参数 |
properties | 设置属性的值 |
autowiring mode | no(默认值)、byName、byType、 constructor |
lazy-initialization mode | 是否懒加载(如果被非懒加载的bean依赖了那么其实也就不能懒加载了) |
initialization method | bean 属性设置完成后,会调用这个方法 |
destruction method | bean 销毁后的回调方法 |
xml配置文件中举例说明:
<bean id="testBean" name="name1, name2, name3" class="com.test.TestBean" scope="singleton" lazy-init="true" init-method="init" destroy-method="cleanup">
<!-- 可以用下面三种形式指定构造参数 -->
<constructor-arg type="int" value="7500000"/>
<constructor-arg name="years" value="7500000"/>
<constructor-arg index="0" value="7500000"/>
<!-- property 的几种情况 -->
<property name="beanOne">
<ref bean="anotherTestBean"/>
</property>
<property name="beanTwo" ref="yetBean"/>
<property name="integerProperty" value="1"/>
</bean>
看一下 processBeanDefinition(ele, delegate)
方法的实现:
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 1. 进行节点定义解析
// 将 <bean /> 节点中的信息提取出来,然后封装到一个 BeanDefinitionHolder 中
// 经过这个方法后,bdHolder会包含一个Bean节点的所有属性,例如name、class、id
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 2.若存在默认标签的子节点下再有自定义属性,需要再次对自定义标签再进行解析(基本不用,不做深入解析)
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
// 3.解析节点定义完成后,需要对解析后的bdHolder进行注册
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
// 4.最后发出响应事件,通知相关的***,这个Bean已经加载完成了
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
parseBeanDefinitionElement(ele,containingBean)
通过delegate.parseBeanDefinitionElement(ele)
获取BeanDefinitionHolder
,我们来看一下里面具体是如何解析bean元素,然后封装到BeanDefinitionHolder
的,具体代码实现如下:
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
// 1.解析name和id属性
// 解析id属性
String id = ele.getAttribute(ID_ATTRIBUTE);
// 解析name属性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<String>();
/* 将 name 属性的定义按照 ”逗号、分号、空格“ 切分,形成一个别名列表数组 例如: <bean name="demoService,demoServiceAlias" class=""/> 分割后 aliases 为 [demoService, demoServiceAlias] 当然,如果你不定义的话,就是空的了 */
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
// beanName默认使用id
String beanName = id;
// 如果没有指定id, 那么用别名列表的第一个名字作为beanName
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
// 如果id为空,并且aliases不为空,则取aliases的第一个元素作为beanName,其他的仍作为别名
beanName = aliases.remove(0);
if (logger.isDebugEnabled()) {
logger.debug("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
if (containingBean == null) {
// 检查beanName和aliases是否在同一个 <beans> 下已经存在
checkNameUniqueness(beanName, aliases, ele);
}
// 2.进一步解析bean的其他所有属性并统一封装至GenericBeanDefinition类型实例中
// 根据 <bean ...>...</bean> 中的配置创建 BeanDefinition,然后把配置中的信息都设置到实例
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
// 到这里,整个 <bean /> 标签就算解析结束了,一个 BeanDefinition 就形成了。
if (beanDefinition != null) {
// 如果都没有设置 id 和 name,那么此时的 beanName 就会为 null,进入下面这块代码产生
if (!StringUtils.hasText(beanName)) {
try {
// 3.如果bean定义存在,但是 beanName 为空,则用Spring默认的生成规则为当前bean生成beanName
if (containingBean != null) {
// 按照我们的思路,这里 containingBean 是 null 的
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
// 如果我们不定义 id 和 name,那么就拿前面IOC容器引入的那个例子来说
// ①. beanName 为:com.test.example.MessageServiceImpl#0
// ②. beanClassName 为:com.test.example.MessageServiceImpl
beanName = this.readerContext.generateBeanName(beanDefinition);
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
// 把 beanClassName 设置为 Bean 的别名
aliases.add(beanClassName);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]");
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
// 4. 返回 BeanDefinitionHolder
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
parseBeanDefinitionElement(ele,bd,containingBean)
看一下parseBeanDefinitionElement()
方法是如何根据配置属性来生成 BeanDefinition
的:
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
// 1.解析class、parent属性
// 解析class属性
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
try {
String parent = null;
// 解析parent属性
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
// 2.创建用于承载属性的AbstractBeanDefinition类型的GenericBeanDefinition,然后设置类信息而已,很简单不贴代码
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 3. 解析 BeanDefinition 的各种属性,这些属性定义在 AbstractBeanDefinition 中
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
// 提取description
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
/** * 下面的一堆是解析 <bean>......</bean> 内部的子元素, * 解析出来以后的信息都放到 bd 的属性中 */
// 解析 <meta />,元数据子节点(基本不用)
parseMetaElements(ele, bd);
// 解析 <lookup-method />,lookup-method子节点(基本不用)
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// 解析 <replaced-method />,replaced-method子节点(基本不用)
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// 4. 解析 <constructor-arg />,constructor-arg子节点
parseConstructorArgElements(ele, bd);
// 5. 解析 <property />,property子节点
parsePropertyElements(ele, bd);
// 解析 <qualifier />,qualifier子节点(基本不用, 不深入介绍)
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
// 三个catch
catch () {
...
}...
finally {
this.parseState.pop();
}
return null;
}
parseBeanDefinitionAttributes()
我们来看 parseBeanDefinitionElement()
代码块的 3
中。
调用 parseBeanDefinitionAttributes
解析 BeanDefinition
的各种属性,这些属性定义在 AbstractBeanDefinition
中。
public AbstractBeanDefinition parseBeanDefinitionAttributes(
Element ele,
String beanName,
@Nullable BeanDefinition containingBean,
AbstractBeanDefinition bd
) {
// 解析singleton属性
if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
// singleton 属性已经不支持, 如果使用了会直接抛出异常, 请使用scope属性代替
error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
}
// 解析scope属性
else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
}
else if (containingBean != null) {
// Take default from containing bean in case of an inner bean definition.
bd.setScope(containingBean.getScope());
}
// 解析abstract属性
if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
}
// 解析lazy-init属性, 默认为false
String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
if (isDefaultValue(lazyInit)) {
lazyInit = this.defaults.getLazyInit();
}
bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
// 解析autowire属性
String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
bd.setAutowireMode(getAutowireMode(autowire));
// 解析dependency-on属性
if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
}
// 解析autowire-candidate属性
String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
if (isDefaultValue(autowireCandidate)) {
String candidatePattern = this.defaults.getAutowireCandidates();
if (candidatePattern != null) {
String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
}
}
else {
bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
}
// 解析primary属性
if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
}
// 解析init-method属性
if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
bd.setInitMethodName(initMethodName);
}
else if (this.defaults.getInitMethod() != null) {
bd.setInitMethodName(this.defaults.getInitMethod());
bd.setEnforceInitMethod(false);
}
// 解析destroy-method属性
if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
bd.setDestroyMethodName(destroyMethodName);
}
else if (this.defaults.getDestroyMethod() != null) {
bd.setDestroyMethodName(this.defaults.getDestroyMethod());
bd.setEnforceDestroyMethod(false);
}
// 解析factory-method属性
if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
}
// 解析factory-bean属性
if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
}
return bd;
}
parseConstructorArgElements()
我们来看 parseBeanDefinitionElement()
代码块的 4
中。
public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
// 拿到beanEle节点的所有子节点
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {
// 解析constructor-arg
parseConstructorArgElement((Element) node, bd);
}
}
}
parseConstructorArgElement()
在 parseConstructorArgElements
代码中遍历解析beanEle节点的所有子节点
中所有是 constructor-arg节点的子节点
。
constructor-arg
节点类似于构造函数,bean 中必须要有相应的构造函数才可以使用,否则会报错。
public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
// 1. 提取基础属性index、type、name属性值
// 提取index属性
String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
// 提取type属性
String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
// 提取name属性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
if (StringUtils.hasLength(indexAttr)) {
try {
int index = Integer.parseInt(indexAttr);
if (index < 0) {
error("'index' cannot be lower than 0", ele);
}
else {
try {
// 2. index不为空的处理
// 记录解析状态
this.parseState.push(new ConstructorArgumentEntry(index));
// 2.1 解析ele节点对应的属性值
/* 核心代码 解析<constructor-arg/>子标签元素的值, 可能是value属性、ref属性、或者子标签,返回相应的值封装对象 */
Object value = parsePropertyValue(ele, bd, null);
// 2.2 使用ConstructorArgumentValues.ValueHolder类型来封装解析出来的元素
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
// 2.3 将type属性封装到ConstructorArgumentValues.ValueHolder
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
// 2.4 将name属性封装到ConstructorArgumentValues.ValueHolder
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
// 2.5 判断index是否重复, 如果是则抛出异常
if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {
error("Ambiguous constructor-arg entries for index " + index, ele);
}
else {
/* 将当前constructor-arg子标签的解析结果以key-value的形式封装 key就是index,value就是valueHolder 封装到BeanDefinition中的ConstructorArgumentValues属性中的indexedArgumentValues属性中 */
bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder);
}
}
finally {
this.parseState.pop();
}
}
}
catch (NumberFormatException ex) {
//无法转换为数值,抛出异常
error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele);
}
}
// 3. 如果indexAttr为null或者"",即没有设置<constructor-arg/>标签的index属性值
else {
try {
this.parseState.push(new ConstructorArgumentEntry());
// 3.1 解析ele节点对应的属性值,同上
Object value = parsePropertyValue(ele, bd, null);
// 3.2 使用ConstructorArgumentValues.ValueHolder类型来封装解析出来的元素
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
// 3.3 将type属性封装到ConstructorArgumentValues.ValueHolder
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
// 3.4 将name属性封装到ConstructorArgumentValues.ValueHolder
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
// 3.5 将valueHolder添加至当前BeanDefinition的constructorArgumentValues的genericArgumentValues属性中
// 与上面的indexedArgumentValues类似,上面有index存为map,这边没index存为list
valueHolder.setSource(extractSource(ele));
bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
}
finally {
this.parseState.pop();
}
}
}
从源码可知,所有的 <constructor-arg/>
子标签的结果都存入 BeanDefinition
中的 ConstructorArgumentValues
属性中。其中具有 index
索引的以 “index-valueHolder”
的形式存入该属性对象内部的 indexedArgumentValues
属性中,就是一个 Map
;没有设置 index
索引的以单个 valueHolder
的形式存入该属性对象内部的 genericArgumentValues
属性中,就是一个 List
。
parsePropertyValue()
我们看一下上面代码中,用于解析ele节点对应的属性值的 parsePropertyValue()
方法。
parsePropertyValue()
方法用于解析、获取 <property />
标签和 <constructor-arg />
标签的值,这里的值可能来自于 value
属性、 ref
属性,或者是一个<list/>
、 <map/>
等等子标签,因此返回的值可能是标签上的一个 value
属性、ref
属性的解析结果,也可能是标签内部的值标签,比如 list
、 map
等标签的解析结果。
在解析 <constructor-arg/>
标签的值时,"propertyName"
属性为 null
,在解析 <property/>
标签的值时, "propertyName"
属性为不为 null
。这个 propertyName
属性主要用于异常日志输出,没啥其他用处。
@Nullable
public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
String elementName = (propertyName != null ?
"<property> element for property '" + propertyName + "'" :
"<constructor-arg> element");
// Should only have one child element: ref, value, list, etc.
/* 1. 校验值子标签只能有一个 对于<property/>、<constructor-arg/>标签内部不属于<description/>和<meta/>标签的其他子标签 比如<ref/>, <value/>, <list/>, <etc/>,<map/>等等,它们都被算作"值子标签" 值子标签就是对应着<property/>、<constructor-arg/>标签的值,Spring要求最多只能有其中一个值子标签 */
NodeList nl = ele.getChildNodes();
Element subElement = null;
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 如果node属于标签,并且不属于<description/>和<meta/>标签,比如<ref/>,<value/>,<list/>,<etc/>,<map/>等等
if (node instanceof Element &&
!nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
!nodeNameEquals(node, META_ELEMENT)) {
// Child element is what we're looking for.
//如果subElement不为null,说明在循环中找到了多个值子标签,那么直接抛出异常
if (subElement != null) {
// 只能有1个子节点,否则抛出异常
error(elementName + " must not contain more than one sub-element", ele);
}
else {
// 找到子节点,赋值给subElement
subElement = (Element) node;
}
}
}
// 到这里表示前面校验通过,可能有一个值子标签,也可能一个都没有
// 可能有一个值子标签,也可能一个都没有
// 2. 解析constructor-arg上的ref属性
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
// 3. 解析constructor-arg上的value属性
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
// 4.合法性校验。在constructor-arg上:ref属性、value属性、子节点,三者只能有1个,因为这三个都是用来表示该节点的值。
if ((hasRefAttribute &&
hasValueAttribute) ||
((hasRefAttribute || hasValueAttribute) &&
subElement != null)) {
error(elementName +
" is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element",
ele);
}
/*校验通过,开始解析*/
/* 5. ref属性的处理 使用RuntimeBeanReference封装对应的ref值(该ref值指向另一个bean的beanName), RuntimeBeanReference起到占位符的作用,ref指向的 beanName 将在运行时被解析成真正的bean实例引用 */
if (hasRefAttribute) {
// 获取ref属性值refName
String refName = ele.getAttribute(REF_ATTRIBUTE);
if (!StringUtils.hasText(refName)) {
error(elementName + " contains empty 'ref' attribute", ele);
}
// 创建一个运行时bean引用对象,持有refName,相当于占位符,在后面运行时将会被解析为refName对应的bean实例的引用
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
// 设置源,运行时将根据源解析
ref.setSource(extractSource(ele));
return ref;
}
// 6. value属性的处理,使用TypedStringValue封装
else if (hasValueAttribute) {
//创建一个TypedStringValue对象,仅仅持有value字符串的,运行时值可以转换为其它类型,比如int、long等等
//不过,转换的这个功能是由conversion转换服务提供的
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
//设置源,运行时将根据源解析
valueHolder.setSource(extractSource(ele));
return valueHolder;
}
// 否则,如果具有值子标签,那么肯定没有ref属性以及value属性
else if (subElement != null) {
// 7. 继续解析值子标签
return parsePropertySubElement(subElement, bd);
}
else {
// 8. 既没有ref属性,也没有value属性,也没有子节点,没法获取ele节点的值,直接抛异常
// Neither child element nor "ref" or "value" attribute found.
error(elementName + " must specify a ref or value", ele);
return null;
}
}
parsePropertySubElement()
根据上方继续解析子标签。
<property />
或者 <constructor-arg />
标签元素的值也是可以使用子标签表示的,可能是 <bean/>
、 <ref/>
、 <idref/>
等标签,此时调用 parsePropertySubElement()
方法,用于解析 <property />
或者 <constructor-arg />
标签内部对应的值子标签。
该方法可以解析的值子标签类型有:<bean/>
、<ref/>
、<idref/>
、<value/>
、<null/>
、<array/>
、<list/>
、<set/>
、<map/>
、<props/>
等标签。
@Nullable
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd) {
return parsePropertySubElement(ele, bd, null);
}
@Nullable
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {
// 1. 如果不是默认命名空间下的标签,那么走自定义的解析逻辑
if (!isDefaultNamespace(ele)) {
return parseNestedCustomElement(ele, bd);
}
// 2. 否则,如果值子标签是<bean/>标签,这表示内部bean,解析bean节点
else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
// 那么递归调用parseBeanDefinitionElement解析内部bean
// 这里的db就不为null,使用的就是外部包含bean的BeanDefinition
BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
if (nestedBd != null) {
// 对于<bean/>子标签下的自定义属性和子标签进行解析
nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
}
// 返回BeanDefinitionHolder对象作为解析后的值
return nestedBd;
}
// 3. 否则,如果子标签是<ref/>标签,这表示引用其他bean,解析ref节点
else if (nodeNameEquals(ele, REF_ELEMENT)) {
// 获取bean属性的值,即引用的其他bean的名字refName
String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
boolean toParent = false;
// 如果当前标签的refName为null或者""
if (!StringUtils.hasLength(refName)) {
//那么获取parent属性的值,将其作为引用的其他bean的名字refName
refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
toParent = true;
//如果还是空的,那么抛出异常
if (!StringUtils.hasLength(refName)) {
error("'bean' or 'parent' is required for <ref> element", ele);
return null;
}
}
// 如果还是空的或者不是文本,那么抛出异常
if (!StringUtils.hasText(refName)) {
error("<ref> element contains empty target attribute", ele);
return null;
}
// 同样使用RuntimeBeanReference包装该属性值作为解析后的值,将会在运行时转换为对其他bean的引用
RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
ref.setSource(extractSource(ele));
return ref;
}
// 4. 否则,如果子标签是<idref/>标签,这表示引用字符串并校验bean,解析idref节点
else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
return parseIdRefElement(ele);
}
// 5. 否则,如果子标签是<value/>标签,这表示引用字面量值,解析value节点
else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
return parseValueElement(ele, defaultValueType);
}
// 6. 否则,如果子标签是<null/>标签,这表示使用null作为值,解析null节点
else if (nodeNameEquals(ele, NULL_ELEMENT)) {
// 包装成为 TypedStringValue 作为解析后的值返回
TypedStringValue nullHolder = new TypedStringValue(null);
nullHolder.setSource(extractSource(ele));
return nullHolder;
}
// 7. 否则,如果子标签是<array/>标签,这表示值是一个数组,解析array节点
else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
return parseArrayElement(ele, bd);
}
// 8. 否则,如果子标签是<list/>标签,这表示值是一个list集合,解析list节点
else if (nodeNameEquals(ele, LIST_ELEMENT)) {
return parseListElement(ele, bd);
}
// 9. 否则,如果子标签是<set/>标签,这表示值是一个set集合,解析set节点
else if (nodeNameEquals(ele, SET_ELEMENT)) {
return parseSetElement(ele, bd);
}
// 10. 否则,如果子标签是<set/>标签,这表示值是一个set集合,解析map节点
else if (nodeNameEquals(ele, MAP_ELEMENT)) {
return parseMapElement(ele, bd);
}
// 11. 否则,如果子标签是<props/>标签,这表示值是一个properties集合,解析props节点
else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
return parsePropsElement(ele);
}
else {
// 12. 否则,没找到任何值子标签,抛出异常
error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
return null;
}
}
这里我们就来看一下 parseValueElement()
解析 <value />
标签,以及 parseListElement()
解析 <list />
标签,集合相关的标签解析基本类似。
parseValueElement()
我们就来看一下解析 <value />
的具体过程。
该方法用于解析 <value />
值标签,将结果封装成为一个 TypedStringValue
对象返回,内部还可以包含指定的类型,在运行时会进行转换。
public Object parseValueElement(Element ele, @Nullable String defaultTypeName) {
// It's a literal value.
// 获取字面值
String value = DomUtils.getTextValue(ele);
// 获取 type 属性值 specifiedTypeName
String specifiedTypeName = ele.getAttribute(TYPE_ATTRIBUTE);
String typeName = specifiedTypeName;
// 如果没有指定类型,那么使用外部标签指定的 type
if (!StringUtils.hasText(typeName)) {
typeName = defaultTypeName;
}
try {
// 使用typeName和value创建TypedStringValue对象typedValue返回
TypedStringValue typedValue = buildTypedStringValue(value, typeName);
typedValue.setSource(extractSource(ele));
typedValue.setSpecifiedTypeName(specifiedTypeName);
return typedValue;
}
catch (ClassNotFoundException ex) {
error("Type class [" + typeName + "] not found for <value> element", ele, ex);
return value;
}
}
/* 使用给定的值和目标类型,构建一个TypedStringValue TypedStringValue用于包装<value/>值标签元素的解析结果,内部还可以指定需要转换的类型 */
protected TypedStringValue buildTypedStringValue(String value, @Nullable String targetTypeName)
throws ClassNotFoundException {
ClassLoader classLoader = this.readerContext.getBeanClassLoader();
TypedStringValue typedValue;
// 如果没有指定类型targetTypeName,那么使用默认那么使用要给value的参数构建一个TypedStringValue
if (!StringUtils.hasText(targetTypeName)) {
typedValue = new TypedStringValue(value);
}
// 否则,如果类加载器不为null
else if (classLoader != null) {
// 那么获取targetTypeName类型的Class对象,并构建一个TypedStringValue
Class<?> targetType = ClassUtils.forName(targetTypeName, classLoader);
typedValue = new TypedStringValue(value, targetType);
}
// 否则,使用两个字符串参数构建一个
else {
typedValue = new TypedStringValue(value, targetTypeName);
}
return typedValue;
}
parseListElement()
我们再来看一下解析 <list />
的具体过程。
public List<Object> parseListElement(Element collectionEle, @Nullable BeanDefinition bd) {
// 获取value-type属性值elementType
String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE);
// 获取<list/>标签的子标签集合,里面的一个子标签相当于一个list集合元素, 一般为<value>
NodeList nl = collectionEle.getChildNodes();
// 创建用于托管List元素的标记集合类ManagedList,其中可能包括运行时 bean 引用(要解析为 bean 对象)。
// 将会返回该对象作为解析结果
ManagedList<Object> target = new ManagedList<>(nl.getLength());
target.setSource(extractSource(collectionEle));
target.setElementTypeName(defaultElementType);
// 设置mergeEnabled属性,表示是否需要合并父bean的集合属性
target.setMergeEnabled(parseMergeAttribute(collectionEle));
// 调用parseCollectionElements继续解析
parseCollectionElements(nl, target, bd, defaultElementType);
return target;
}
// 处理集合标签的merge属性,用于与父bean配置的集合合并
public boolean parseMergeAttribute(Element collectionElement) {
// 获取merger属性
String value = collectionElement.getAttribute(MERGE_ATTRIBUTE);
// 如果是默认值,那么获取外部<beans/>的default-merge属性或者自己继承的属性
if (isDefaultValue(value)) {
value = this.defaults.getMerge();
}
// 如果value等于"true"字符串,那么返回true,表示合并;否则就是false,不合并
return TRUE_VALUE.equals(value);
}
// 解析单value类型的集合标签
protected void parseCollectionElements(
NodeList elementNodes,
Collection<Object> target,
@Nullable BeanDefinition bd,
String defaultElementType) {
//循环子标签集合
for (int i = 0; i < elementNodes.getLength(); i++) {
Node node = elementNodes.item(i);
//如果属于标签并且不是description标签
if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT)) {
//那么递归调用parsePropertySubElement解析这些标签,将返回的结果添加到target中
target.add(parsePropertySubElement((Element) node, bd, defaultElementType));
}
}
}
至此 parseConstructorArgElements(ele, bd)
解析<constructor-arg />
标签就结束了,下面我们再来看一下解析 <property />
节点。
parsePropertyElements()
回到 parseBeanDefinitionElement()
的解析 <property />
标签的 parsePropertyElements()
方法上。
首先获取 <bean/>
标签元素下面的所有子标签元素,然后循环遍历,如果如果当前节点是一个标签节点并且标签名为 "property"
,那么继续调用 parsePropertyElement
方法解析该 <property/>
标签。
<property />
标签对应着我们手动设置的 setter
方法注入的标签,解析后的结果将统一封装到 BeanDefinition
中的 propertyValues
属性中。
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
// 获取<bean/>标签元素下的所有子节点标签元素
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 获取<bean />标签元素下的所有子节点标签元素
if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
// 那么解析这个<property />子标签
parsePropertyElement((Element) node, bd);
}
}
}
parsePropertyElement()
parsePropertyElement()
用于解析一个 <property/>
子标签,并将相关属性和值存入当前 <bean />
标签元素对应的 BeanDefinition
的 propertyValues
属性中。
public void parsePropertyElement(Element ele, BeanDefinition bd) {
// 获取name属性值propertyName,即属性名称
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
// 如果属性名称为null或者"",那么抛出异常
if (!StringUtils.hasLength(propertyName)) {
error("Tag 'property' must have a 'name' attribute", ele);
return;
}
// 记录解析
this.parseState.push(new PropertyEntry(propertyName));
try {
// 如果当前<bean/>标签元素对应的BeanDefinition的propertyValues属性中已经包含了当前属性名称,那么抛出异常
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
/*【参考前面解析constructor-arg标签】 核心代码 解析<property/>子标签元素的值,可能是value属性、ref属性、或者子标签,返回相应的值封装对象 这个方法和解析constructor-arg标签的代码中调用的是同一个方法,区别是propertyName属性不为null propertyName属性仅仅是为了异常日志打印而已,没有其他作用 */
Object val = parsePropertyValue(ele, bd, propertyName);
// 将属性名和解析出来的值封装成为一个PropertyValue对象pv
PropertyValue pv = new PropertyValue(propertyName, val);
// 解析meta节点,很少使用
parseMetaElements(ele, pv);
// 设置源
pv.setSource(extractSource(ele));
// 将当前<property/>子标签的解析结果pv封装到BeanDefinition中的propertyValues属性中
bd.getPropertyValues().addPropertyValue(pv);
}
finally {
this.parseState.pop();
}
}
到这里,我们已经完成了根据<bean />
配置创建了一个 BeanDefinitionHolder
实例。
现在我们再回到返回BeanDefinitionHolder
对象的 processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)
方法里:
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 将 <bean /> 节点中的信息提取出来,然后封装到一个 BeanDefinitionHolder 中
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
这里就已经根据一个 <bean />
标签产生了一个 BeanDefinitionHolder
的实例了。
这个实例里面也就是一个 BeanDefinition
的实例和它的 beanName
、aliases
这三个信息,我们只要关注 BeanDefinition
就好了,我们来看一下 BeanDefinitionHolder
的代码:
public class BeanDefinitionHolder implements BeanMetadataElement {
private final BeanDefinition beanDefinition; // BeanDefinition 实例
private final String beanName; // bean名称
@Nullable
private final String[] aliases; // 别名组
...
}
加载bean说完了,下面就要说注册bean了。
注册bean - registerBeanDefinition(bdh,registry) 【重点】
当我们将 BeanDefinitionHolder
创建完毕之后,所谓的 bean
定义就创建完了,随后会调用 BeanDefinitionReaderUtils.registerBeanDefinition()
方法将 BeanDefinitionHolder
注册到 注册表Registry
中,这个 Registry
实际上就是上下文容器内部的 DefaultListableBeanFactory
实例。
如果该方法注册完毕并且发送注册事件完毕之后,那么 refresh()
方法中的 obtainFreshBeanFactory()
方法步骤就算真正结束了,随后将会进入下一步。
先简单说明一下 registerBeanDefinition()
方法的大概逻辑:
- 获取
BeanDefinitionHolder
对象中持有的BeanDefinition
、beanName
和aliases
。 - 调用
registerBeanDefinition()
方法。将beanName
和对应的BeanDefinition
注册到registry
的对应缓存集合中,这个缓存实际上就是上下文容器内部的DefaultListableBeanFactory
实例中的beanDefinitionMap
和beanDefinitionNames
两个缓存。 - 调用
registerAlias
方法,将beanName
和aliases
中的每一个别名注册到registry
的缓存中。这个缓存实际上就是上下文容器内部的DefaultListableBeanFactory
的父类SimpleAliasRegistry
注册表实例中的aliasMap
缓存。
这些缓存并不是对象实例的缓存,实际上到目前为止还没有bean被初始化,但是很明显,这些缓存定义了一个bean实例的全部特点,是为后面的bean实例初始化做准备的。
通过 registerBeanDefinition()
来注册bean对象:
// BeanDefinitionReaderUtils 158
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// 获取bean的名称
String beanName = definitionHolder.getBeanName();
/* 将beanName和对应的BeanDefinition注册到registry中的相关缓存中 这个Registry实际上就是容器内部的DefaultListableBeanFactory实例 */
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// 如果还有别名的话,也要根据别名统统注册一遍,不然根据别名就找不到 Bean 了
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
// alias -> beanName 保存它们的别名信息,这个很简单,用一个 map 保存一下就可以了,
// 获取的时候,会先将 alias 转换为 beanName,然后再查找
registry.registerAlias(beanName, alias);
}
}
}
这段代码包括通过beanName注册、别名注册,别名注册的流程比较简单。
registerBeanDefinition(beanName,bd)
我们先来看通过 beanName
注册的代码。
这里先梳理一下 registerBeanDefinition(beanName,bd)
的大致步骤:
将 beanName
和 BeanDefinition
注册到 registry
的缓存中。这个缓存实际上就是上下文容器内部的 DefaultListableBeanFactory
实例中的 beanDefinitionMap
和 beanDefinitionNames
两个缓存。同时还会使用到 allowBeanDefinitionOverriding
属性判断是否可以进行bean定义的覆盖。
registerBeanDefinition(beanName,bd)
的详细步骤也过一遍:
- 首先尝试从注册表的缓存
beanDefinitionMap
中查找当前beanName
的 bean 定义existingDefinition
。 - 如果
existingDefinition
不为 null,即找到了同名 bean 定义,那么进行ban定义覆盖的校验和操作:- 通过
allowBeanDefinitionOverriding
属性判断是否不允许 bean 的覆盖,如果不允许,那么抛出异常;如果允许,那么进行覆盖,即设置beanDefinitionMap
缓存的该beanName的value值为最新的BeanDefinition
。
- 通过
- 如果
existingDefinition
为 null,即没找到同名bean定义,那么将beanName以及对应的beanDefinition
设置到beanDefinitionMap
缓存,将当前的beanName存入beanDefinitionNames
缓存,将当前的beanName从手动注册bean名称集合manualSingletonNames
缓存中移除。 - 如果找到了同名bean定义,或者单例bean实例的缓存
singletonObjects
已中包含给定beanName的实例。那么将当前beanName对应的在DefaultSingletonBeanRegistry
中的实例缓存清除,重置给定 beanName 的所有 bean 定义缓存,包括从它派生的 bean 的缓存(merge),以及重置以给定beanName为父类bean的子类Bean缓存。- 否则如果此工厂的 bean 定义被冻结,即不应进一步修改或后处理,那么删除所有的按类型映射的任何缓存:
allBeanNamesByType
和singletonBeanNamesByType
。在finishBeanFactoryInitialization
方法中就会冻结 bean 定义,并且进行bean初始化操作。
- 否则如果此工厂的 bean 定义被冻结,即不应进一步修改或后处理,那么删除所有的按类型映射的任何缓存:
// DefaultListableBeanFactory 976
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
// 断言校验 bean名称 & bean实例
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate(); // 校验
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
// 之后会看到,所有的 Bean 注册后会放入这个 beanDefinitionMap 中
// 尝试从注册表缓存中查找当前beanName的BeanDefinition
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
// 1. 如果找到了同名的BeanDefinition,进行ban定义覆盖的校验和操作
if (existingDefinition != null) {
// 判断是否不允许 bean 的覆盖,spring中默认为true,springboot中默认为false
if (!isAllowBeanDefinitionOverriding()) {
// 如果不允许覆盖的话,抛异常
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
// 否则,表示允许,继续判断角色相关,不必关心
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
// log...用框架定义的 Bean 覆盖用户自定义的 Bean
if (logger.isInfoEnabled()) {
logger.info("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
existingDefinition + "] with [" + beanDefinition + "]");
}
}
// 否则,表示允许,继续如果当前的beanDefinition不等于找到的此前的existingDefinition
else if (!beanDefinition.equals(oldBeanDefinition)) {
// log...用新的 Bean 覆盖旧的 Bean
if (logger.isDebugEnabled()) {
logger.debug("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
// log...用同等的 Bean 覆盖旧的 Bean,这里指的是 equals 方法返回 true 的 Bean
if (logger.isTraceEnabled()) {
logger.trace("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
// 覆盖
this.beanDefinitionMap.put(beanName, beanDefinition);
}
// 2. 如果没找到同名的BeanDefinition,这是正常情况
else {
// 判断是否已经有其他的 Bean 开始初始化了.
// 注意,"注册Bean" 这个动作结束,Bean 依然还没有初始化,我们后面还会说初始化过程,
// 在 Spring 容器启动的最后,会 【预初始化】 所有的 singleton beans
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
// 加锁防止并发操作集合
synchronized (this.beanDefinitionMap) {
// 当前的beanName和beanDefinition存入beanDefinitionMap缓存
this.beanDefinitionMap.put(beanName, beanDefinition);
// 当前的beanName存入beanDefinitionNames缓存
// 重新生成一个list集合替换
List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
//当前的beanName从手动注册bean名称集合manualSingletonNames缓存中移除
//因为如果这里自动注册了beanName,那么需要从manualSingletonNames缓存中移除代表手动注册的单例beanName。
removeManualSingletonName(beanName);
}
}
// 否则,其他任何bean实例没有开始初始化
else {
// 最正常的应该是进到这里。
// 仍处于启动注册阶段,不加锁
// 当前的beanName和beanDefinition存入beanDefinitionMap缓存
this.beanDefinitionMap.put(beanName, beanDefinition);
// 当前的beanName存入beanDefinitionNames缓存
this.beanDefinitionNames.add(beanName);
// 这是个 LinkedHashSet,代表的是手动注册的 singleton bean,
// 注意这里是 remove 方法,到这里的 Bean 当然不是手动注册的
// 手动指的是通过调用以下方法注册的 bean:
// registerSingleton(String beanName, Object singletonObject)
// Spring 会在后面"手动"注册一些 Bean
// 如 "environment"、"systemProperties" 等 bean
removeManualSingletonName(beanName);
}
// 这个不重要,在预初始化的时候会用到,不必管它。
this.frozenBeanDefinitionNames = null;
}
/* 3. 如果找到的旧的BeanDefinition不为null,或者单例bean实例的缓存singletonObjects已中包含给定beanName的实例 那么将当前beanName对应的在DefaultSingletonBeanRegistry中的实例缓存清除,需要重新生成实例 */
if (existingDefinition != null || containsSingleton(beanName)) {
/* 将当前beanName对应的在DefaultSingletonBeanRegistry中的实例缓存清除 重置给定 beanName 的所有 bean 定义缓存,包括从它派生的 bean 的缓存(merge)。 以及重置以给定beanName为父类bean的子类Bean缓存。 */
resetBeanDefinition(beanName);
}
/* 4. 否则,如果此工厂的 bean 定义是否冻结,即不应进一步修改或后处理。 那么删除所有的按类型映射的任何缓存:allBeanNamesByType和singletonBeanNamesByType 在finishBeanFactoryInitialization方法中就会冻结 bean 定义,并且进行bean初始化操作 一般不会出现 */
else if (isConfigurationFrozen()) {
clearByTypeCache();
}
}
resetBeanDefinition()
该方法用于重置 BeanDefinition
的缓存。
如果此前已经注册了同名的 BeanDefinition
,或者单例bean实例的缓存 singletonObjects
中已经包含给定beanName的实例,那么需要重置 BeanDefinition
的相关缓存。
将 DefaultSingletonBeanRegistry
注册表中的当前beanName对应的实例缓存清除(destroySingleton),并且重置给定 beanName 的 BeanDefinition
缓存,包括已合并的 bean定义 缓存(mergedBeanDefinitions
),以及重置以给定beanName为父类bean的子类Bean。
/* 重置给定 bean 的所有 bean 定义缓存,包括从它派生的 bean 的缓存。 通知所有后处理器已重置指定的 bean 定义。 */
protected void resetBeanDefinition(String beanName) {
// 删除给定beanName的mergedBeanDefinitions的缓存,这是已合并的bean定义缓存
clearMergedBeanDefinition(beanName);
// 从单例缓存中删除相应的单例(如果有),这个方法之前讲过了
// 实际上就是删除DefaultSingletonBeanRegistry中的关于单例bean实现的缓存
destroySingleton(beanName);
// 通知所有后处理器已重置指定的 bean 定义
for (MergedBeanDefinitionPostProcessor processor : getBeanPostProcessorCache().mergedDefinition) {
processor.resetBeanDefinition(beanName);
}
// 重置所有以当前beanName为父类bean的子类Bean
for (String bdName : this.beanDefinitionNames) {
if (!beanName.equals(bdName)) {
BeanDefinition bd = this.beanDefinitionMap.get(bdName);
// 如果 bd 不为null,并且给定beanName等于bd的parentName属性
if (bd != null && beanName.equals(bd.getParentName())) {
// 递归调用resetBeanDefinition重置BeanDefinition
resetBeanDefinition(bdName);
}
}
}
}
registerAlias()
在 registerBeanDefinition()
中,同时也注册了别名的映射。
别名映射缓存 aliasMap
位于 SimpleAliasRegistry
注册表中, SimpleAliasRegistry
是 DefaultListableBeanFactory
的父类。
aliasMap
是一个map,key为一个alias别名,value就是beanName,通过名称获取bean实例的时候就会从别名映射缓存中先获取真实的beanName,然后再获取beanName对应的bean实例。
@Override
public void registerAlias(String name, String alias) {
// 断言校验null或空字符串
Assert.hasText(name, "'name' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
// 加锁
synchronized (this.aliasMap) {
// 如果beanName就等于alias别名,那么不算做别名
if (alias.equals(name)) {
// 从缓存中移除指定alias为key的缓存
this.aliasMap.remove(alias);
if (logger.isDebugEnabled()) {
logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
}
}
// 否则,注册别名映射缓存
else {
// 尝试通过给定的别名从当aliasMap中获取以存在的beanName
String registeredName = this.aliasMap.get(alias);
// 如果registeredName不为null,说明存在相同的别名
if (registeredName != null) {
// 如果registeredName等于name,说明已经注册过该beanName的别名了,直接返回,无需重新注册
if (registeredName.equals(name)) {
// An existing alias - no need to re-register
return;
}
/* 如果不允许别名覆盖,则抛出异常,默认是允许的 就是使用的前面讲的customizeBeanFactory方法设置的allowBeanDefinitionOverriding属性来判断 */
if (!allowAliasOverriding()) {
throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" +
name + "': It is already registered for name '" + registeredName + "'.");
}
// 输出日志,别名映射缓存将会覆盖
if (logger.isDebugEnabled()) {
logger.debug("Overriding alias '" + alias + "' definition for registered name '" +
registeredName + "' with new target name '" + name + "'");
}
}
/* 检查beanName和alias是否存在循环的name引用 比如a的别名是b,b的别名是a,那么抛出异常 */
checkForAliasCircle(name, alias);
// 没有循环的name引用,那么加入缓存
this.aliasMap.put(alias, name);
if (logger.isTraceEnabled()) {
logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
}
}
}
}
到这里已经初始化了 Bean 容器,<bean />
配置也相应的转换为了一个个 BeanDefinition
,然后注册了各个 BeanDefinition
到注册中心,并且发送了注册事件。
parseCustomElement()【自定义标签】
在 parseBeanDefinitions()
代码块的 1.2
中,进行自定义标签的解析。
在parseBeanDefinitions()
方法中:
- 对于基本标签也就是 默认命名空间【官网】 下的标签的解析使用的是
parseDefaultElement
方法,比如<import/>
、<alias/>
、<bean/>
、<beans/>
。 - 对于我们引入的其他命名空间(比如context、aop)下的标签的解析则是采用的
parseCustomElement
方法,比如<mvc/>
、<task/>
、<context/>
、<aop/>
标签的解析。扩展标签的解析方法parseCustomElement
直接位于BeanDefinitionParserDelegate
解析器中。
现在我们就来看看自定义命名空间下的标签的解析方法 parseCustomElement()
的方法实现,这个方法也非常重要。比如annotation-config
、component-scan
等等注解支持的标签都是由它解析的。
该方法的大致逻辑为:
- 首先获取
readerContext
中的namespaceHandlerResolver
,在前面的createReaderContext
方法中已被初始化了,随后通过namespaceHandlerResolver
的resolve()
方法根据namespaceUri
获取对应的handler
对象,比如AopNamespaceHandler
、ContextNamespaceHandler
等等。 - 调用
handler
对象的parse()
方法解析标签节点元素,并传递一个新建的解析上下文ParserContext
对象,同样是返回一个BeanDefinition
类型的对象,但是有可能直接返回 null,比如后面讲的<context:component-scan/>
标签的解析,最终就直接返回 null。
@Nullable
public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}
@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
// 1.拿到节点ele的命名空间,例如常见的:
// <context> 节点对应命名空间URL: http://www.springframework.org/schema/context
// <aop> 节点对应命名空间URL: http://www.springframework.org/schema/aop
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
// 2. 拿到命名空间对应的的handler
// 例如:http://www.springframework.org/schema/context 对应 ContextNameSpaceHandler
// 2.1 getNamespaceHandlerResolver: 拿到namespaceHandlerResolver
// 2.2 resolve: 使用namespaceHandlerResolver解析namespaceUri, 拿到namespaceUri对应的NamespaceHandler
// 例如:ContextNamespaceHandler
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
// 如果没找到handler,那么返回并抛出异常
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
// 3.使用拿到的handler解析节点(ParserContext用于存放解析需要的一些上下文信息)
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
DefaultNamespaceHandlerResolver.resolve()
readerContext
的 namespaceHandlerResolver
属性的 resolve()
方法用于解析 namespaceUri
返回对应的 NamespaceHandler
。
在前面的 createReaderContext()
方法中有提到, namespaceHandlerResolver
的默认实现就是 DefaultNamespaceHandlerResolver
。
DefaultNamespaceHandlerResolver.resolve()
的大致逻辑如下:
- 首先会调用
getHandlerMappings()
方法获取全部的处理器映射缓存集合handlerMappings
:- 这个缓存是在
getHandlerMappings()
第一次调用时才会加载的(懒加载),将会加载本项目以及引入的外部jar包中的指定路径下的handler
文件,默认位置就是"META-INF/spring.handlers"
。 - 加载进来的数据被存入该缓存map中,**默认是
namespaceUri
字符串以及对应的NamespaceHandler
的全路径类名字符串键值对。**后续调用该方法时将不会加载而是直接从缓存获取。
- 这个缓存是在
- 随后根据给定的
namespaceUri
作为key从该缓存中查找对应的value。如果是第一次查找给定的namespaceUri
,那么获取的value仅仅是一个NamespaceHandler
的全路径类名字符串:- 因此在第一次调用到某个
namespaceUri
的resolve()
方法时,需要将value表示的全路径类名字符串反射创建为一个NamespaceHandler
对象,并通过init()
方法进行初始化。 - 随后使用该对象作为新的value替换
handlerMappings
缓存集合中原来的全路径类名字符串,方便后续直接从缓存中获取handler
对象而不需要再次解析。
- 因此在第一次调用到某个
// 懒加载handlerMappings,并且根据给定的namespaceUri解析NamespaceHandler
@Override
@Nullable
public NamespaceHandler resolve(String namespaceUri) {
/* 1. 获取全部handlerMappings集合 handlerMappings在使用时才会被加载,懒加载,刚加载进来(第一次解析)时的样式为: http://www.springframework.org/schema/aop | v org.springframework.aop.config.AopNamespaceHandler key和value都是字符串,key为namespaceUri字符串,value就是对应的NamespaceHandler的全路径类名字符串 */
Map<String, Object> handlerMappings = getHandlerMappings();
// 2. 拿到当前命名空间对应的handler (可能是handler的className,也可能是已经实例化的handler)
Object handlerOrClassName = handlerMappings.get(namespaceUri);
// 2.1 如果没有获取到,直接返回null
if (handlerOrClassName == null) {
return null;
}
// 2.2 否则,如果获取的value已经被解析为NamespaceHandler对象,那么直接返回
else if (handlerOrClassName instanceof NamespaceHandler) {
return (NamespaceHandler) handlerOrClassName;
}
// 2.3 否则,第一次解析handlerOrClassName
else {
String className = (String) handlerOrClassName;
try {
// 2.3.1 获取class对象
Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
// 2.3.2 校验是否是继承自NamespaceHandler(所有的handler都继承自NamespaceHandler)
if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
}
// 2.3.3 使用无参构造函数实例化NamespaceHandler对象
NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
// 2.3.4 初始化NamespaceHandler对象,为当前命名空间下的不同标签节点指定不同的解析器
namespaceHandler.init();
// 2.3.5 重新存入该namespaceUri,将handlerOrClassName,即全路径类名,替换为namespaceHandler,即解析后的对象,避免后续再次解析
// 即原来为: namespaceUri -> handler的className,会被覆盖成: namespaceUri -> 实例化的handler
handlerMappings.put(namespaceUri, namespaceHandler);
// 返回实例化后的handler对象
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);
}
}
}
getHandlerMappings()
在 resolve()
代码块 1
中,拿到配置文件的所有命名空间和对应的 handler
。
getHandlerMappings
方法用于获取处理器映射缓存,第一次调用该方法时将会从指定路径懒加载指定的mapping映射文件到 handlerMappings
缓存集合中,后续再次调用该方法时将直接返回缓存的map。
private Map<String, Object> getHandlerMappings() {
// 获取handlerMappings
Map<String, Object> handlerMappings = this.handlerMappings;
// 如果为null,那么加载,不为null直接返回
if (handlerMappings == null) {
// 加锁
synchronized (this) {
//再次判断
handlerMappings = this.handlerMappings;
if (handlerMappings == null) {
if (logger.isTraceEnabled()) {
logger.trace("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]");
}
try {
// 加载指定路径下的mapping文件为Properties集合,默认路径为"META-INF/spring.handlers"
Properties mappings =
PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
if (logger.isTraceEnabled()) {
logger.trace("Loaded NamespaceHandler mappings: " + mappings);
}
// Properties转换为ConcurrentHashMap
handlerMappings = new ConcurrentHashMap<>(mappings.size());
CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
// 赋给handlerMappings
this.handlerMappings = handlerMappings;
}
catch (IOException ex) {
throw new IllegalStateException(
"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
}
}
}
}
// 返回handlerMappings
return handlerMappings;
}
nameSpaceHandler.init()
在 resolve()
代码块 2.3.4
中,该方法存在非常多的实现类,对应不同的命名空间 Handler
。
NamespaceHandler
的 init()
方法,用于为当前为命名空间下的不同标签节点指定不同的解析/装饰器,即 BeanDefinitionParser
和 BeanDefinitionDecorator
。
比如 context
命名空间下的 ContextNamespaceHandler
的 init()
方法如下,我们可以看到常见的标签节点比如"component-scan"
,该标签的解析器就是一个 ComponentScanBeanDefinitionParser
实例。
/* NamespaceHandler继承了NamespaceHandlerSupport,而NamespaceHandlerSupport实现了NamespaceHandler */
public class ContextNamespaceHandler extends NamespaceHandlerSupport {
/* 为context 命名空间下的不同标签节点指定不同的解析器 */
@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());
}
}
内容很简单,就是给 context
命名空间下的不同节点指定了不同的 BeanDefinition
解析器,并将节点名和对应的解析器注册到缓存中。例如,最常用的 component-scan
对应 ComponentScanBeanDefinitionParser
。
registerBeanDefinitionParser() & registerBeanDefinitionDecorator()
init()
方法中的注册解析器
和装饰器
的方法就是调用的 registerBeanDefinitionParser()
和 registerBeanDefinitionDecorator()
方法,这两个方法位于 NamespaceHandlerSupport
中,用于注册处理特定标签节点的 BeanDefinitionParser
和 BeanDefinitionDecorator
实现,注册的所有解析器和装饰器保存在 NamespaceHandlerSupport
内部的parsers
& decorators
缓存map中。
NamespaceHandlerSupport
直接实现了NamespaceHandler
,而其他的自定义的 NamespaceHandler
则是继承的 NamespaceHandlerSupport
。
一个 NamespaceHandler
的具体实现仅仅是将解析某个命名空间所需要的全部解析器和装饰器统一调用 registerBeanDefinitionParser
和 registerBeanDefinitionDecorator
方法注册到 NamespaceHandlerSupport
中而已,比如 ContextNamespaceHandler
。
NamespaceHandlerSupport
是用于实现自定义 NamespaceHandler
的支持类,内部的 parsers
映射缓存了所有注册的解析器, decorators
映射缓存了所有注册的装饰器,格式为:标签名字符串->解析器/装饰器实例
。
真正的的单个Node节点的解析和装饰则分别是通过 BeanDefinitionParser
解析器和 BeanDefinitionDecorator
装饰器这两个策略接口的实现来完成的。
public abstract class NamespaceHandlerSupport implements NamespaceHandler {
/* value存储BeanDefinitionParser的实现,对应的所处理的Element标签元素节点的本地名称作为key */
private final Map<String, BeanDefinitionParser> parsers = new HashMap<>();
/* value存储BeanDefinitionDecorator的实现,对应的所处理的Element标签元素节点的本地名称作为key */
private final Map<String, BeanDefinitionDecorator> decorators = new HashMap<>();
/* NamespaceHandlerSupport的方法 子类可以调用它来注册提供的BeanDefinitionParser, 用来以处理指定的Element标签元素节点。元素名称是本地(非命名空间限定)名称。 */
protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
this.parsers.put(elementName, parser);
}
/* NamespaceHandlerSupport的方法 子类可以调用它来注册提供的BeanDefinitionDecorator来处理指定的元素。元素名称是本地(非命名空间限定)名称。 */
protected final void registerBeanDefinitionDecorator(String elementName, BeanDefinitionDecorator dec) {
this.decorators.put(elementName, dec);
}
}
parse()
在 parseCustomElement()
代码块的 3
中,使用拿到的 handler
解析节点。
在 parseCustomElement()
方法的最后是调用具体 handler
实现的 parse
方法来解析对应的标签节点元素。实际上是调用的父类 NamespaceHandlerSupport
实现的逻辑,而 NamespaceHandlerSupport
的 parse
方法同样是通过委派给此前通过 init()
方法的注册到 parsers
缓存中的 BeanDefinitionParser
来解析对应的 Element
标签元素的。
// NamespaceHandlerSupport 72
/** * 具体handler实现的父类NamespaceHandlerSupport的方法 * 通过委派给的注册的BeanDefinitionParser来解析对应的Element标签元素 */
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 查找该element标签元素对应的BeanDefinitionParser解析器parser
BeanDefinitionParser parser = findParserForElement(element, parserContext);
// 如果parser不为null,那么就使用解析器的parse方法来解析该标签,返回解析后的BeanDefinition
return (parser != null ? parser.parse(element, parserContext) : null);
}
/** * NamespaceHandlerSupport的方法 * 使用提供的Element的本地名称从缓存parsers中查找BeanDefinitionParser */
@Nullable
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
// 获取element的本地名称
String localName = parserContext.getDelegate().getLocalName(element);
// 从parsers缓存中查找BeanDefinitionParser
BeanDefinitionParser parser = this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal(
"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
}
return parser;
}