默认标签解析入口方法:
private void parseDefaultElement(Element ele,BeanDefinitionParserDelegate dalegate){
if(delegate.nodeNameEquals(ele,IMPORT_ELEMENT)){
//说明是import变迁
importBeanDefinitionResource(ele);
}
//剩下的代码类似
//对alias、bean、beans变迁的解析
}
1.默认标签之bean标签的解析
入口代码:
protected void processBeanDefinition(Element ele,BeanDefinitionParserDelegate delegate){
//返回BeanDefinitionHolder
//BeanDefinitionHolder 里面已经包含了bean标签内所有的属性
BeanDefinitionHolder holder = delegate.parseBeanDefinitionElement(ele);
if(holder != null){
//如果有自定义子标签,那么这句代码就起作用了,继续解析自定义标签
holder =delegate.decorateBeanDefinitionIfRequired(ele,holder);
}
//注册bean
BeanDefinitionReaderUtils.registerBeanDefinition(holder,getReaderContext());
//通知监听器,bean已经完成解析
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(holder));
}
注:BeanDefinitionParserDelegate 是一个解析bean的辅助类,里面定义了很多字符串和一些默认解析的方法,比如填充默认属性等等。
(1)beanName的解析
首先就是对Element对象的解析,也就是获取BeanDefinitionHolder的过程
//解析id值
String id = ele.getAttribute(ID_ATTRIBUTE);
//解析name属性,因为name有可能是多个,所以还需要继续解析
//id="1 2 3 4" --->一个bean被一个id(1 2 3 4)标识
//name = "1 2 3 4"或者 name="1,2,3,4"--->一个bean被四个name(1)(2)(3)(4)标识
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<>();
//然后就是将name分割成字符串数组,然后转成List放到aliases中
String beanName = id;
if(”id是空串" && “alias不为null”){
//如果未设置id并且设置了name
//将第一个name作为bean的id
/*
总结:
如果设置了id 则id作为唯一标识,name作为别名
如果未设置id但是设置了name,则将name的第一个值作为唯一标识
*/
beanName = alias.remove(0);
}
//解析BeanDefinition,生成GenericBeanDefiniton
//AbstractBeanDefiniton 是其父类--->抽象类
AbstractBeanDefiniton beanDefinition = parseBeanDefinitionElement(ele,beanName,containingBean);
/*
如果未设置id和name 则将全类名作为唯一标识
如果有多个未设置id和name的bean,并且bean相同,则在全类名后面加#序号(序号是配置文件读取顺序,从1开始)
*/
if("beanName是空串"){
//自动生成beanName
if(containingBean != null){
beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition,this.readerContext().getRegistry,true);
}else{
beanName = this.readerContext().generateBeanName(BeanDefinition);
}
}
上面主要演示了bean的标识属性的处理逻辑,下面对GenericBeanDefinition的生成过程进行解析
解析class属性
解析parent属性
创建GenericBeanDefiniton
解析各种属性并且封装到GenericBeanDefiniton
(2) BeanDefinition的解析,生成GenericBeanDefinition
BeanDefinition是一个接口,在Spring中有三种实现类,并且都继承了AbstractBeanDefinition:
(1)GenericBeanDefinition:
一站式服务类,可以用于各种情况(也就是通用bean)
(2)RootBeanDefinition
没有父bean的bean可以用此类
(3)ChildBeanDefinition
如果有父bean,则需要使用该类
补:父bean和子bean
spring中的父bean和子bean并不是内部属性的形式,比如A类中有B类的一个实例,而是类似继承关系。
1. 父bean和子bean并不需要在代码中实现继承关系,而是在配置文件中实现(在子bean的parent属性中配置父bean)
2. 子bean必须与父bean保持兼容,也就是子bean必须有父类中的所有属性
3. 父子bean实际上是参数的继承,两者的类型可以是不同的,只需要对应的属性以及变量名相同--->参数值层次的继承
而继承是属性的继承,可以冲父类中继承子类没有的属性---->结构层次继承
4. 如果父类定义了class而子类未定义,则子类的class和父类相同
如果父类未定义而子类定义,父类其实就是一个参数模板,没有什么意义,可以简化配置
5. 父类如果只是作为一个配置模板,可以将abstract置为true
创建GenericBeanDefinition代码分析
protected AbstractBeanDefiniton createBeanDefinition(String className,ClassLoader classLoader){
GenericBeanDefinition bd = new GenericBeanDefinition ();
bd.setParentName(parentName);
if(className != null){
if(classLoader != null){
//使用该classLoader在虚拟机中加载此对象
bd.setBeanClass(ClassUtils.forName(className,classLoader));
}else{
//只记录className
bd.seBeanClassName(className);
}
}
return bd;
}
创建了GenericBeanDefinition 后,就可以设置各种配置的属性了
//解析scope属性
if(ele.hasAttribute(SCOPE_ATTRIBUTE)){
//spring 2.x 具有的scope属性
bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
//sington和scope属性只可以设置一个,如果还有sington属性,则报错
...
}
//解析sington属性
else if(ele.hasAttribute(SINGTON_ATTRIBUTE)){
//spring 1.x 具有的sington属性
...
}
//解析abstract属性--->略
//解析lazy-init属性(如果没有设置或者不是true,都不会被解析成false)--->略
//解析meta元素--->略
/*
解析lookup-method
lookup-method作用:
比如A类有个方法getBean,而这个方法返回B类的bean
如果想要返回的bean是在spring中配置的B类型的bean,可以使用该标签
<bean id="a" class="com.A">
<lookup-method name="getBean" bean="b"/>
</bean>
<bean id="b" class="com.B"/>
*/
这里只是获取bean标签的所有子标签,然后遍历找到所有的lookup-method标签
LookupOverride类用于封装methodName和beanRef属性
//解析replaced-method子标签
//解析contructor-arg子标签
//解析property字标签
//解析qualifier属性
注册解析出来的Bean
//使用唯一标识beanName注册
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName,definitionHolder.getBeanDefinition());
//注册别名
String aliases = definitionHolder.getAlias();
if(aliaes != null){
for(String alias : aliases){
registry.registerAlias(beanName,alias);
}
}
使用beanName注册Bean的流程如下:
//预处理逻辑省略(assert、validate等等)
//处理并发情况,使用同步锁
synchronized(this.beanDefinitionMap){
Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
if(oldBeanDefinition != null){
//处理bean已经注册过的情况
//如果不允许覆盖bean的属性,则抛出异常
if.....代码略
//允许覆盖bean的属性,记录日志
}else{
this.beanDefinitionNames.add(beanName);
this.forzenBeanDefinitionNames = null;
}
//真正注册bean(或者可以说覆盖bean)
this.beanDefinitionMap.put(beanName,beanDefinition);
//刷新缓存
resetBeanDefinition(beanName);
}
别名注册
if(alias.equals(name)){
//别名与唯一标识相同,移除
this.aliasMap.remove(alias);
}else{
//覆盖检测
//如果该别名已经指向了别的bean,需要特殊处理
if....
//alias循环检查
/*
map中已经有 A->B和B->C
如果存入C->A是需要进行循环检测
或者A->C时,如果存入A->B->C时需要进行处理
*/
}
2.Alias标签的解析—>类似注册别名
补:alias和beanName对应关系分析
alias顶层接口:AliasRegistry
实现类:SimpleAliasRegistry
//首先使用了concurrentMap来缓存alias和beanName的关系
private final Map<String,String> aliasMap = new ConcurrentMap<>(16);
注册别名逻辑方法:registerAlias(String name,String alias);
synchronized(this.aliasMap){
if(alias.equals(name)){
//传经来的beanName和alias是相同的
this.aliasMap.remove(alias);
}else{
//拿到这个别名对应的beanName
String registeredName = this.aliasMap.get(alias);
if(registeredName != null){
//说明这个别名已经存在对应的beanName了
if(registeredName.equals(name)){
//别名已经注册的beanName和即将要注册的BeanName是同一个
//说明即将要注册的beanName和alias的对应关系已经存在于map中,直接返回什么也不做
return ;
}
//到此if时,说明alias现在即将要注册第二套Alias-->beanName的映射关系
if(不允许覆盖别名){
抛出异常
}
}
}
checkForAliasCircle(name.alias);--->循环检测
this.aliasMap.put(alias,name);--->alias和beanName在map中的结构,key为alias,value为name
}
别名循环检测代码逻辑:
param:name和alias
hasAlias(alias,name);
//首先注意这里
//alias对应方法形参的name,而name对应形参的alias--->检测循环依赖a->b,而b>a?
public boolean hasAlias(String name,String alias){
for(Map.Entry<String,String> entry : this.aliasMap.entrySet()){
String registeredName = entry.getValue():
//for循环遍历entry来找到和参数中相同的name
if(registeredName.equals(name)){
String registeredAlias==entry.getKey();
/*
本次别名要放进去map的映射是a(alias)->b(beanName)
检查循环依赖传进来的参数是hasAlias(b,a)
也就是此时name才是真正的alias,而alias是真正的name
然后现在registeredName与name相同,也就是在map中存在一个entry,value为a
现在要检查的是key是否是b,也就是registeredAlias是否与当前方法中的alias相同
如果相同,则说明在当前map中已经存在了b->a的映射,会产生循环依赖,return true
如果不相同,说明现在有了c(registeredAlias)->a的映射,但是不排除会存在b->c是否有映射关系
所以还需要递归hasAlias(registeredAlias,alias);注:hasAlias(m,n)检查的是在map中有没有n->m的映射
*/
if(registeredAlias.equals(alias) || hasAlias(registeredAlias,alias)){
return true;
}
}
}
return false;
}