介绍
- 本文仅做学习记录,若有不正确的地方,请多多指教
- Spring版本:5.2.x(代码可能略有不同)
配置文件的加载
测试代码
//替代xmlBeanFactory
//bean工厂
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//资源管理器
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
//资源 - 配置文件
ClassPathResource resource = new ClassPathResource("DefaultApplicationContext.xml");
//解析bean
reader.loadBeanDefinitions(resource);
准备工作
- DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//跟踪代码会发现一共会实例化2个父类
//1. AbstractAutowireCapableBeanFactory
//2. AbstractBeanFactory
//期间,AbstractAutowireCapableBeanFactory的构造器如下
public AbstractAutowireCapableBeanFactory() {
super();
//将Class类型加入集合ignoredDependencyInterfaces中 即add操作
ignoreDependencyInterface(BeanNameAware.class);
ignoreDependencyInterface(BeanFactoryAware.class);
ignoreDependencyInterface(BeanClassLoaderAware.class);
}
- XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
//在XmlBeanFactoryReader构造器中调用了super(registry)父类构造器方法
//以下是父类AbstractBeanFactoryReader构造方法
//ps:DefaultListableBeanFactory实现了BeanFactoryRegistry接口,所以它也是BeanDefinitionRegistry类型
protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
//将DefaultListableBeanFactory赋值给当前类属性
this.registry = registry;
// Determine ResourceLoader to use.
//因为当前beanFactory并没有实现ResourceLoader接口 不能解析xml if进入失败
if (this.registry instanceof ResourceLoader) {
this.resourceLoader = (ResourceLoader) this.registry;
}
else {
//PathMatchingResourcePatternResolver是作为资源路径的解析器 支持解析通配符和classpath:等
//在实例化时,会new DefaultResourceLoader();给成员变量resourceLoader赋值
//ps:PathMatchingResourcePatternResolver和DefaultResourceLoader都是ResourceLoader的子类
//DefaultResourceLoader的getResources方法可以获取资源(这里可以当做配置文件)
//而PathMatchingResourcePatternResolver加入了Ant解析资源路径 所以它的getResources功能相对更强大一些
this.resourceLoader = new PathMatchingResourcePatternResolver();
}
// Inherit Environment if possible
// DefaultListableBeanFactory也不是Environment子类 所以走else
if (this.registry instanceof EnvironmentCapable) {
this.environment = ((EnvironmentCapable) this.registry).getEnvironment();
}
else {
//StandardEnvironment:包含了环境配置数据 如spring.profiles.active
//但是StandardEnvironment无参构造器并无任何操作 所以这里一切都是默认值 仅做了实例化操作
this.environment = new StandardEnvironment();
}
}
- ClassPathResource resource = new ClassPathResource("DefaultApplicationContext.xml");
//在ClassPathResource单参构造器中调用了以下构造器 其中classLoader默认为null
public ClassPathResource(String path, @Nullable ClassLoader classLoader) {
Assert.notNull(path, "Path must not be null");
//对资源路径做一些转换 如window的""转为"/"
String pathToUse = StringUtils.cleanPath(path);
//如果以“/”开头 则去除
if (pathToUse.startsWith("/")) {
pathToUse = pathToUse.substring(1);
}
//记录资源路径
this.path = pathToUse;
this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
}
一切准备工作结束,就可以开始解析xml文件了。
XML文件解析(无关代码或步骤将略过)
1. 入口:reader.loadBeanDefinitions(resource)
2. 参数:
- reader:XmlBeanDefinitionReader
- resource:ClassPathResource
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
//EncodedResource是记录资源编码字符的对象
//这里实例化EncodedResource 在构造器中仅将resource赋值给了成员变量作以记录 编码字符均默认为null
return loadBeanDefinitions(new EncodedResource(resource));
}
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
//值得一提的是 resourcesCurrentlyBeingLoaded是ThreadLocal 保存的类型是Set<EncodingResource>类型 默认容量为4
//这里获取了当前线程所加载过的资源文件
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
//如果add失败 则说明当前文件已加载过
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
//检测到xx(某资源文件)循环加载
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
//获取当前文件的输入流
try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
//inputSource XML实体的单个输入源
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//加载xml核心逻辑开始 (查看下一代码片段
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()) {
//如果当前线程完成了所有资源的加载 则移除LocalThread当前线程数据
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
/**
* XML解析核心逻辑
* param: inputSource 当前资源ClassPathResource输入流
* param: resource 当前资源ClassPathResource对象
*/
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//加载xml文件 获取document对象 用于解析xml
Document doc = doLoadDocument(inputSource, resource);
//根据返回的document对象注册bean 并返回注册个数
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
//返回注册个数
return count;
}
catch (xxException ex) {
//略
}
}
/**
* 根据资源获取Document对象
*/
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
//this.documentLoader = new DefaultDocumentLoader(); 默认生成的无参构造器 无任何操作
//其余参数方法的解析请查看下一代码块
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
//DefaultCocumentLoader类
/**
* param: inputSource XML实体的单个输入源
* param: entityResolver -> ResourceEntityResolver 用于解析DTD或XSD的XML文件
* param:errorHandler 错误处理器
* param:validationMode XML文件验证模式 这里是XSD(3)
* param:namespaceAware 为false (true时表示开启解析命名空间)
*/
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
//在factory的创建中 如果验证模式是XSD 则会设置namespaceAware为true 这里是开启了命名空间感知
//实际类型是生成了com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isTraceEnabled()) {
logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
}
//使用factory创建一个 JAXP DocumentBuilder,将使用它来解析 XML 文档
//com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
//生成com.sun.org.apache.xerces.internal.dom.DeferredDocumentImpl对象
return builder.parse(inputSource);
}
生成的Document对象:
对于doLoadDocument方法中的参数方法的解释
- getEntityResolver()
/**
*
* 当解析xml时 需要寻找DTD定义 以便对文档进行验证 而默认的方式是通过声明DTD的uri地址去下载对应文件
* 但是这种方式可能会因为网络原因下载失败而导致报错
* 所以该方法是提供一个寻找DTD声明的方法 即读取本地DTD文件路径
*/
protected EntityResolver getEntityResolver() {
//初次调用 entityResolver默认为null
if (this.entityResolver == null) {
//获取资源加载器
//在实例化XmlBeanDefinitionReader时就给resourceLoader赋值了 所以get出来的对象是PathMatchingResourcePatternResolver
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader != null) {
//详见下一代码片段
this.entityResolver = new ResourceEntityResolver(resourceLoader);
}
else {
this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader());
}
}
return this.entityResolver;
}
public ResourceEntityResolver(ResourceLoader resourceLoader) {
//调用父类DelegatingEntityResolver构造器
super(resourceLoader.getClassLoader());
//记录resourceLoader 即PathMatchingResourcePatternResolver
this.resourceLoader = resourceLoader;
}
public DelegatingEntityResolver(@Nullable ClassLoader classLoader) {
//初始化dtd文件的解析器
this.dtdResolver = new BeansDtdResolver();
//解析META-INF/spring.schemas文件(该路径是默认值)这里边包含了XML所有命名空间对应的xsd文件路径 直接本地获取 无需网络下载
this.schemaResolver = new PluggableSchemaResolver(classLoader);
}
- getValidationModeForResource(Resource resource)
/**
* 获取验证xml的方式 主要用于验证xml文件格式是否正确 文件是否有效
* 验证XML的方式有两种:
* 1. DTD:需要在XML文件头部声明
* <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "https://www.springframework.org/dtd/spring-beans-2.0.dtd">
* 意思就是使用spring-beans-2.0.dtd该文件来验证当前文件
*
* 2. XSD:XML Schema语言。该语言用于描述XML文件的结构 也是用于验证XML格式是否正确的
* xmlns="http://www.springframework.org/schema/beans"
* xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
* xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
* http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"
* xmlns=表示命名空间
* xsi:schemaLocation=命名空间 + 该命名空间所标识的XML Schema文件位置
*
* 默认验证模式:VALIDATION_AUTO(其实最后返回的默认验证模式是XSD
*/
protected int getValidationModeForResource(Resource resource) {
//可以通过 setValidationMode设定验证模式
int validationModeToUse = getValidationMode();//成员变量validationMode 默认VALIDATION_AUTO
if (validationModeToUse != VALIDATION_AUTO) {
return validationModeToUse;
}
//如果没有【显式】的配置验证模式 则从inputStream中读取XML资源内容获取 VALIDATION_DTD or VALIDATION_XSD
int detectedMode = detectValidationMode(resource);
//如果发生了异常 则会返回VALIDATION_AUTO
if (detectedMode != VALIDATION_AUTO) {
return detectedMode;
}
return VALIDATION_XSD;
}
- isNamespaceAware()
public boolean isNamespaceAware() {
/**
* 该属性在关闭XML验证模式后会设置为true 即开启解析命名空间
* 以使无验证模式下 仍能正确使用命名空间解析XML文件标签
*/
return this.namespaceAware;
}
继续执行int count = registerBeanDefinitions(doc, resource);
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//创建DefaultBeanDefinitionDocumentReader根据“spring-beans”DTD 和 XSD 格式(Spring 的默认 XML bean 定义格式)读取 bean 定义。
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//registry就是XmlBeanFactory对象 这里是已注册的bean数量
//即DefaultListableBeanFactory的beanDefinitionMap属性的长度 默认256的容量
int countBefore = getRegistry().getBeanDefinitionCount();//map.size()
//核心代码 注册bean
////createReaderContext方法返回XmlReaderContext对象 俗称文档阅读器
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//返回注册数 即DefaultListableBeanRegistry的beanDefinitionMap的size - 注册前的size
return getRegistry().getBeanDefinitionCount() - countBefore;
}
//DefaultBeanDefinitionDocumentReader类方法
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
//把文档阅读器赋值给了DefaultBeanDefinitionDocumentReader
this.readerContext = readerContext;
//这里的getDocumentElement 返回的是代表Beans标签的com.sun.org.apache.xerces.internal.dom.DeferredElementNSImpl对象(父类Element)
//beans标签就是spring配置文件的根节点
doRegisterBeanDefinitions(doc.getDocumentElement());
}
protected void doRegisterBeanDefinitions(Element root) {
//BeanDefinitionParserDelegate用于解析 XML bean 定义的有状态委托类。供主解析器和任何扩展使用
BeanDefinitionParserDelegate parent = this.delegate;//第一次为null
//创建BeanDefinitionParserDelegate对象 它包含着一个属性DocumentDefaultsDefinition
//这个成员属性中包含了当前标签上的定义 如lazy-init值 init-method值
//并在其中初始化完成BeanDefinitionParserDelegate对象后 触发注册事件来做一些事情(默认为空 开发者自行实现)
this.delegate = createDelegate(getReaderContext(), root, parent);
/**
* 1. 空
* 2. http://www.springframework.org/schema/beans
* 校验是否是spring的配置文件
*/
if (this.delegate.isDefaultNamespace(root)) {//判断是不是beans标签 拿命名空间做比较
//获取profile属性 配置分离
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
//以下是加载不同配置文件的逻辑 若无指定profile 直接跳过
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// We cannot use Profiles.of(...) since profile expressions are not supported
// in XML config. See SPR-12458 for details.
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
//空方法 用于开发者实现
preProcessXml(root);
//标签解析开始 (查看下一代码片段
parseBeanDefinitions(root, this.delegate);
//空方法 用于开发者实现
postProcessXml(root);
//设置当前节点为父节点 进行子节点的遍历
this.delegate = parent;
}
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//
if (delegate.isDefaultNamespace(root)) {
//获取子标签 其实是把xml转换成了一行一行的文本
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
//顺序获取标签对象 这里不要误解 是获取xml一行一行的文本内容 然后判断当前行是不是标签 或者是空行 又或者是xml注释
//如果是标签 则会读取整个标签体内容 封装成标签对象了
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);
}
}
默认标签的解析
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//对import标签解析
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
//对alias标签解析
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
//对bean标签解析 ★ 这里作为我们的入口
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
//对beans标签解析
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
//bean标签的解析
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//解析bean标签 见下一代码片段
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
/**
* 用于处理自定义属性 适用以下场景
* <bean id="xx" class="xx">
* <custom-element></custom-element>
* </bean>
* 再次遍历所有标签 通过命名空间来解析自定义属性
*/
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance. 注册最终的装饰实例 ★
//这里就是bean要注册进ioc容器的入口了(请查看下一代码块
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.发送注册事件。
// 对注册BeanDefinition事件进行监听 这个方法并未实现 继承EmptyReaderEventListener实现
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
/**
* 参数:
* ele: bean标签
* containingBean:null
*/
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
//获取当前元素的id
String id = ele.getAttribute(ID_ATTRIBUTE);
//获取当前元素的name属性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
//别名数组
List<String> aliases = new ArrayList<>();
//如果当前元素有name属性 则使用“,;”拆分
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
//默认把id为bean注册的name
String beanName = id;
//如果当前bean没有指定id作为注册时使用的name 则用name属性定义的第一个名称作为beanName
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
}
if (containingBean == null) {
//官方解释:验证指定的 bean 名称和别名尚未在当前级别的 beans 元素嵌套中使用
//校验bean名称是否重复 如果不重复 则将beanName及其定义的别名通通加入this.usedName 防止其他bean重名
checkNameUniqueness(beanName, aliases, ele);
}
//bean的所有属性都封装在子类 GenericBeanDefinition中 见下一代码片段
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
//内部bean名称生成
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
beanName = this.readerContext.generateBeanName(beanDefinition);
// Register an alias for the plain bean class name, if still possible,
// if the generator returned the class name plus a suffix.
// This is expected for Spring 1.2/2.0 backwards compatibility.
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (logger.isTraceEnabled()) {
logger.trace("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]");
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
//这里的别名是通过name属性指定的 不是alias
String[] aliasesArray = StringUtils.toStringArray(aliases);
//返回beanDefinitionHolder对象 这个对象封装了对于bean维度的信息 即GenericBeanDefinition,bean名称,别名
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
//this.parseState是ArrayDeque类型 双队列,BeanEntry对象没什么特别的 就是一个包含了beanName的对象
//用于存储正在解析的标签 如bean/property等 解析完毕后会弹出 先进先出 这里当作栈来使用
this.parseState.push(new BeanEntry(beanName));
//记录当前类的class路径
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
//bean元素可以指定parent属性 将其作为当前bean的父类 具有继承意义
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
//以下仅仅是解析工作 并没有真正设置到真实的对象当中
try {
//用于创建 承载解析标签得到的所有内容的AbstractBeanDefinition子类的GenericBeanDefinition
//指定父类名称 如果存在classLoader 则立马加载当前bean(this.readerContext.getBeanClassLoader
//在xml中可以使用parent属性指定父类引用
AbstractBeanDefinition bd = createBeanDefinition(className, parent);//这里仅设置了上面获取的parent属性值到GenericBeanDefinition对象中
//解析bean的各种属性到GenericBeanDefinition 如abstract/auto-wire/depend-on/primary等
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
//会遍历当前bean的所有子节点 解析当前bean的元数据<meta>标签内容并并封装到GenericBeanDefinition(父类AttributeAccessorSupport的成员属性attributes[map]中
parseMetaElements(ele, bd);
//解析lookup-method属性 同上
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
//解析replaced-method属性 同上
/**
* replaced-method
* A类实现了MethodReplacer类后就可以注册到容器中 当B类中的某个方法xx需要被替换
* 则在XML中写入
* <bean class="B">
* <replaced-method name="xx" replacer="A"></replaced-method>
* </bean>
* 这样 方法xx就可以被替换 所以返回值也可能会变化
*/
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
//解析constructor-arg属性
parseConstructorArgElements(ele, bd);
//解析property属性 把解析出来的属性放在MutablePropertyValues类的propertyValueList[List]中
//仅解析bean本身 不包括父类
parsePropertyElements(ele, bd);//如果遇上子元素为bean标签 则进入方法parsePropertySubElement后,重新递归parseBeanDefinitionElement方法进行解析
//解析qualifier属性
//存储在GenericBeanDefinition的父类AbstractBeanDefinition的qualifiers[map]属性中
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
//设置来源
bd.setSource(extractSource(ele));
return bd;
}
catch (ClassNotFoundException ex) {
//略
}
finally {
this.parseState.pop();
}
return null;
}
Bean的注册
在上面的方法:processBeanDefinition中,有这段代码:
try {
// Register the final decorated instance. 注册最终的装饰实例 ★
//这里就是bean要注册进ioc容器的入口了(请查看下一代码块
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
//略
}
跟踪代码查看实现:
/**
* 参数:
* definitionHolder: 当前bean的BeanDefinitionHolder对象
* registry: defaultListableBeanFactory
*/
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// 当前bean名称
String beanName = definitionHolder.getBeanName();
/**
* 使用beanName将BeanDefinition注册进beanDefinitionMap
*/
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// 如果有别名 则要注册别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
/**
* registerAlias:SimpleAliasRegistry的方法 也是DefaultListableBeanFactory的父类
* 见下面的代码片段
*/
registry.registerAlias(beanName, alias);
}
}
}
/**
* 参数:
* beanName 对象注册bean名称
* beanDefinition 就是上面用于封装bean所有属性及子标签的GenericBeanDefinition对象
*/
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
//是GenericBeanDefinition对象(AbstractBeanDefinition子类 这里需要对该对象做一次校验
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
/**
* 这里校验不能同时指定<lookup-method/><replaced-method/>和<factory-method/>二者
* 并且如果指定了<lookup-method/><replaced-method/>
* 则会校验方法是否存在 并给他们的MethodOverride对象上的属性overLoading(是否重载)设为false
*/
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
//从已注册的bean集合中获取当前beanName的对象
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
//不为null 则说明当前bean已经注册过了
if (existingDefinition != null) {
//是否允许当前bean覆盖第一个bean(同名情况下) 默认true
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
//这里开始就无论如何都会覆盖当前已存在的bean 部分日志输出代码省略
//覆盖
this.beanDefinitionMap.put(beanName, beanDefinition);
}
//不存在同名的bean 即当前bean还未被注册
else {
//这里去判断在此期间是否有任何 bean 被标记为已创建, 存在则返回true 不存在返回false
if (hasBeanCreationStarted()) {
//加锁保证线程安全
synchronized (this.beanDefinitionMap) {
//将当前bean注册进beanDefinitionMap
this.beanDefinitionMap.put(beanName, beanDefinition);
/**
* beanDefinitionNames是实例注册的名称排序集合List 按注册先后顺序排列
* 以下操作就是把当前beanName加入beanDefinitionNames集合中
*/
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
//将当前实例放入map 注册完成
this.beanDefinitionNames = updatedDefinitions;
removeManualSingletonName(beanName);
}
}
//还没有实例注册 则当前容器内是空的
else {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
removeManualSingletonName(beanName);
}
//缓存beanName的数组清空
this.frozenBeanDefinitionNames = null;
}
/**
* existingDefinition:若存在同名bean时,该对象是当前对象注册前就已注册的“可能”被覆盖的bean
* containsSingleton()会去DefaultSingletonBeanRegistry的singletonObjects[map]中查找是否存在
* 当前bean的单例实例缓存。注意:【singletonObjects是缓存单例实例bean的map集合】
*/
if (existingDefinition != null || containsSingleton(beanName)) {
//重置所有beanName对应的缓存
resetBeanDefinition(beanName);
}
else if (isConfigurationFrozen()) {
//删除关于按类型映射的任何假设
clearByTypeCache();
}
}
//别名的注册
public void registerAlias(String name, String alias) {
//aliasMap: Map<String, String> aliasMap = new ConcurrentHashMap<>(16);
synchronized (this.aliasMap) {
if (alias.equals(name)) {
//如果别名和bean注册名一致 则不保存 其实也是覆盖的一种方式
this.aliasMap.remove(alias);
}
else {
//获取别名注册的beanName
String registeredName = this.aliasMap.get(alias);
// 如果当前别名已经被注册
if (registeredName != null) {
//如果当前beanName和已经注册的别名的bean名称一致 则返回
if (registeredName.equals(name)) {
// An existing alias - no need to re-register 翻译:已存在别名与当前beanName相同 不需要重复注册
return;
}
//如果允许覆盖 则直接覆盖 默认为true
if (!allowAliasOverriding()) {
throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" +
name + "': It is already registered for name '" + registeredName + "'.");
}
}
/**
* <bean name="B" class="MDJBean.Car"/>
* <bean name="C" class="MDJBean.Car"/>
* <alias name="B" alias="A"/> // B类的名字有两个 一个是B 一个是A 即大名叫B 小名叫A 只要是这两个名字 都可以指向B
* <alias name="C" alias="B"/> // C类的名称也有两个 一个是C 一个是B
* //单单以上是不会报错的 因为别名不同 不出现闭环 getBean("B")返回的是C对象 详见参考获取对象方法(getBean("A")也会返回C对象
* //假设现在添加一个别名 A可以叫C (同时需要定义一个A的bean 否则这个别名是没用的 也就不存在循环了)
* <alias name="A" alias="C"/> //那么通过C别名可以找到A名称的类(这时候spring会再次调用getBean("A") 但getBean又会来aliasMap中找 找到A对应的名称是B 以此产生循环
* 这样就出现了闭环 循环引用
* 以下方法就是校验防止出现以上情况
*/
checkForAliasCircle(name, alias);
//注册别名
this.aliasMap.put(alias, name);
if (logger.isTraceEnabled()) {
logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
}
}
}
}