BeanDefinition的载入分析-下篇
程序员文章站
2022-05-07 18:30:07
...
继续前面中篇所说的那样就是对xml文件中数据进行解析时的一个流程,以下就是解析BeanDefiniton对象并且存储进BeanDefinitonHolder中去的
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
//这里取得bean标签中的id
String id = ele.getAttribute("id");
// 取得bean标签中的name
String nameAttr = ele.getAttribute("name");
//自定义一个list数组用来接收解析的数据
List<String> aliases = new ArrayList();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, ",; ");
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
if (!StringUtils.hasText(id) && !aliases.isEmpty()) {
beanName = (String)aliases.remove(0);
if (this.logger.isTraceEnabled()) {
this.logger.trace("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases");
}
}
if (containingBean == null) {
this.checkNameUniqueness(beanName, aliases, ele);
}
//在这里主要是对bean标签进行详细的解析
AbstractBeanDefinition beanDefinition = this.parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);
} else {
beanName = this.readerContext.generateBeanName(beanDefinition);
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (this.logger.isTraceEnabled()) {
this.logger.trace("Neither XML 'id' nor 'name' specified - using generated bean name [" + beanName + "]");
}
} catch (Exception var9) {
this.error(var9.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
//可以看见这个结果BeanDefinitionHolder对象是一个封装的对象
//其中的beanDefinition就是前面已经详细解析过的一个结果,以及获取解析过的一大串数据
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
} else {
return null;
}
}
其中的AbstractBeanDefiniton对象是对xml中的封装以及抽象,可以看见这个类中都有哪些方法以及属性
public static final String SCOPE_DEFAULT = "";
public static final int AUTOWIRE_NO = 0;
public static final int AUTOWIRE_BY_NAME = 1;
public static final int AUTOWIRE_BY_TYPE = 2;
public static final int AUTOWIRE_CONSTRUCTOR = 3;
/** @deprecated */
@Deprecated
public static final int AUTOWIRE_AUTODETECT = 4;
public static final int DEPENDENCY_CHECK_NONE = 0;
public static final int DEPENDENCY_CHECK_OBJECTS = 1;
public static final int DEPENDENCY_CHECK_SIMPLE = 2;
public static final int DEPENDENCY_CHECK_ALL = 3;
public static final String INFER_METHOD = "(inferred)";
@Nullable
private volatile Object beanClass;
@Nullable
private String scope;
private boolean abstractFlag;
private boolean lazyInit;
private int autowireMode;
private int dependencyCheck;
@Nullable
private String[] dependsOn;
private boolean autowireCandidate;
private boolean primary;
private final Map<String, AutowireCandidateQualifier> qualifiers;
@Nullable
private Supplier<?> instanceSupplier;
private boolean nonPublicAccessAllowed;
private boolean lenientConstructorResolution;
@Nullable
private String factoryBeanName;
@Nullable
private String factoryMethodName;
@Nullable
private ConstructorArgumentValues constructorArgumentValues;
@Nullable
private MutablePropertyValues propertyValues;
private MethodOverrides methodOverrides;
@Nullable
private String initMethodName;
@Nullable
private String destroyMethodName;
private boolean enforceInitMethod;
private boolean enforceDestroyMethod;
private boolean synthetic;
private int role;
@Nullable
private String description;
@Nullable
private Resource resource;
scope, description , initMethodName, destroyMethodName , beanClass , propertyValues 等这些属性可以在xml中的bean中可以看见
下面主要解析parseBeanDefinitionElement这个方法的源码
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
if (ele.hasAttribute("class")) {
className = ele.getAttribute("class").trim();
}
String parent = null;
if (ele.hasAttribute("parent")) {
parent = ele.getAttribute("parent");
}
try {
//根据className,parent这两个来确定beanDefinition
AbstractBeanDefinition bd = this.createBeanDefinition(className, parent);
//从下面这些方法的名字我们能够看见解析方法主要的作用是什么
this.parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, "description"));
this.parseMetaElements(ele, bd);
this.parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
this.parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
//是对构造解析
this.parseConstructorArgElements(ele, bd);
//是对property进行解析
this.parsePropertyElements(ele, bd);
this.parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(this.extractSource(ele));
AbstractBeanDefinition var7 = bd;
return var7;
//下面的这些异常都是在解析经常碰见的,比如有class没有找到,class未被定义异常
} catch (ClassNotFoundException var13) {
this.error("Bean class [" + className + "] not found", ele, var13);
} catch (NoClassDefFoundError var14) {
this.error("Class that bean class [" + className + "] depends on not found", ele, var14);
} catch (Throwable var15) {
this.error("Unexpected failure during bean definition parsing", ele, var15);
} finally {
this.parseState.pop();
}
这个方法主要的作用就是生成一个具体的BeanDefiniton对象
前面是this.parsePropertyElements(ele, bd)这个方法进行详细的解析过程源码
下面分析具体的property源码解析过程,在解析完这些属性值之后会被封装到PropertyValue对象中去,并且会被设置到BeanDedfinition对象中去
//首先是调用了parsepropertyElements这个方法
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
//获取elements下面的所有节点
NodeList nl = beanEle.getChildNodes();
for(int i = 0; i < nl.getLength(); ++i) {
Node node = nl.item(i);
//判断是否是elements节点,如果是就继续进行解析
if (this.isCandidateElement(node) && this.nodeNameEquals(node, "property")) {
this.parsePropertyElement((Element)node, bd);
}
}
}
//下面是使用parsePropertyElement方法来对节点进行解析
//这个方法就是解析后的数据结果全部存储进BeanDefinition对象中去
public void parsePropertyElement(Element ele, BeanDefinition bd) {
//获取到节点名称
String propertyName = ele.getAttribute("name");
if (!StringUtils.hasLength(propertyName)) {
this.error("Tag 'property' must have a 'name' attribute", ele);
} else {
this.parseState.push(new PropertyEntry(propertyName));
try {
if (!bd.getPropertyValues().contains(propertyName)) {
//解析property值得地方
Object val = this.parsePropertyValue(ele, bd, propertyName);
//新建一个propertyValue对象进行数据的封装
PropertyValue pv = new PropertyValue(propertyName, val);
this.parseMetaElements(ele, pv);
pv.setSource(this.extractSource(ele));
//封装进BeanDefintiion对象中去
bd.getPropertyValues().addPropertyValue(pv);
return;
}
this.error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
} finally {
this.parseState.pop();
}
}
}
//下面主要是来看下真正解析property值的方法中具体的源码
//这个方法主要就是判断property是否为ref或者是value类型的源码解析
@Nullable
public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
String elementName = propertyName != null ? "<property> element for property '" + propertyName + "'" : "<constructor-arg> element";
//获取到所有的子节点
NodeList nl = ele.getChildNodes();
Element subElement = null;
for(int i = 0; i < nl.getLength(); ++i) {
Node node = nl.item(i);
//判断是否是属性Element
if (node instanceof Element && !this.nodeNameEquals(node, "description") && !this.nodeNameEquals(node, "meta")) {
if (subElement != null) {
this.error(elementName + " must not contain more than one sub-element", ele);
} else {
subElement = (Element)node;
}
}
}
//这里主要就是判断是否为ref或者是value类型的属性
boolean hasRefAttribute = ele.hasAttribute("ref");
boolean hasValueAttribute = ele.hasAttribute("value");
if (hasRefAttribute && hasValueAttribute || (hasRefAttribute || hasValueAttribute) && subElement != null) {
this.error(elementName + " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
}
if (hasRefAttribute) {
String refName = ele.getAttribute("ref");
if (!StringUtils.hasText(refName)) {
this.error(elementName + " contains empty 'ref' attribute", ele);
}
//在这个地方主要就是定义一个RuntimeBeanReference对象
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
ref.setSource(this.extractSource(ele));
return ref;
} else if (hasValueAttribute) {
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute("value"));
//不是的话就会创建一个valueHolder对象去接收数据
valueHolder.setSource(this.extractSource(ele));
return valueHolder;
} else if (subElement != null) {
return this.parsePropertySubElement(subElement, bd);
} else {
this.error(elementName + " must specify a ref or value", ele);
return null;
}
}
下面主要是对parseListElement解析list集合中的元素进行的方法,结果
//manageList是spring中对BeanDefinition中的list集合的封装对象
public List<Object> parseListElement(Element collectionEle, @Nullable BeanDefinition bd) {
String defaultElementType = collectionEle.getAttribute("value-type");
//获取到所有的子节点元素
NodeList nl = collectionEle.getChildNodes();
ManagedList<Object> target = new ManagedList(nl.getLength());
target.setSource(this.extractSource(collectionEle));
target.setElementTypeName(defaultElementType);
//添加进到manageList集合中去
target.setMergeEnabled(this.parseMergeAttribute(collectionEle));
this.parseCollectionElements(nl, target, bd, defaultElementType);
return target;
}
//下面是对集合元素的解析过程的源码分析
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);
//判断是否是elements元素,如果是就调用递归添加的方法添加进去
if (node instanceof Element && !this.nodeNameEquals(node, "description")) {
target.add(this.parsePropertySubElement((Element)node, bd, defaultElementType));
}
}
}
经过前面的源码分析,我们能够将自定义的bean转换成spring中内部数据结构,或者说可以看成是POJO对象在IOC容器中的抽象,这些数据结构可以以AbstractBeanDefinition为入口,让ioc容器执行索引,查询,操作等
推荐阅读
-
CSS对Web页面载入效率的影响分析总结
-
spring源码分析6: ApplicationContext的初始化与BeanDefinition的搜集入库
-
Spring5源码 - 03 普通对象对应的BeanDefinition是如何存入DefaultListableBeanFactory#beanDefinitionMap 源码分析
-
python数据分析学习(6)输入载入,存储及文件格式(1)文本格式数据的读写
-
Mybaits 源码解析 (七)----- Select 语句的执行过程分析(下篇)全网最详细,没有之一
-
CSS对Web页面载入效率的影响分析总结
-
在Yii2中使用Pjax导致Yii2内联脚本载入失败的原因分析_php实例
-
Spring源码分析-从@ComponentScan注解配置包扫描路径到IoC容器中的BeanDefinition,经历了什么(一)?
-
在Yii2中使用Pjax导致Yii2内联脚本载入失败的原因分析_PHP
-
在Yii2中使用Pjax导致Yii2内联脚本载入失败的原因分析,yii2pjax_PHP教程