默认bean的解析切入函数是parseBeanDefinitionAttributes,
/**
* 硬编码解析默认属性
*/
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
@Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
//获取bean的作用域
if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
error("Old 1.x ‘singleton‘ attribute in use - upgrade to ‘scope‘ declaration", ele);
}
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());
}
//获取是否为抽象类配置
if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
}
//是否为懒加载 默认为false
String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
if (isDefaultValue(lazyInit)) {
lazyInit = this.defaults.getLazyInit();
}
bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
//是否为自动注入 如果此处配置为自动注入
String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
bd.setAutowireMode(getAutowireMode(autowire));
//解析depends-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;
}
元数据配置xml实例:
<bean id="test" name="testA,testBean" class="test.TestBean">
<meta key="name" value="aaaaaaa"></meta>
</bean>
获取实例meta元素的实例:
beanDefinition.getAttribute("name");
元数据的解析函数是parseMetaElements,具体实现细节如下:
public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
NodeList nl = ele.getChildNodes();
//遍历子节点
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//判断是否为meta元素
if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
Element metaElement = (Element) node;
//获取meta元素的key值
String key = metaElement.getAttribute(KEY_ATTRIBUTE);
//获取meta元素的value
String value = metaElement.getAttribute(VALUE_ATTRIBUTE);
//创建存储对象
BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
//将解析结果注入beanDefination中
attribute.setSource(extractSource(metaElement));
attributeAccessor.addMetadataAttribute(attribute);
}
}
}
以上便是meta元素的解析过程,包含如下几个步骤:
1、硬编码获取meta元素的key与value;
2、创建meta元素的存储对象,并将解析的结果封装进去;
3、将解析的结果注入到beanDefination中。
Ps:meta元素并不会体现在bean的属性中,而是额外的声明,需要读取信息的时候可以通过beanDefination的getAttribute(key)方法获取。
首先创建一个父类接口:
public interface User {
public void showMe();
}
创建一个实现类:
public class Teacher implements User {
@Override
public void showMe() {
System.out.println("I am Teacher");
}
}
创建调用的抽象类:
public abstract class GetBeanTest {
public void showMe(){
this.getBean().showMe();
}
public abstract User getBean();
}
配置xml:
<bean id="getBeanTest" class="lookupmethod.GetBeanTest">
<lookup-method name="getBean" bean="teacher"></lookup-method>
</bean>
<bean id="teacher" class="lookupmethod.Teacher"></bean>
创建调用视图:
public class Test {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml");
GetBeanTest getBeanTest = applicationContext.getBean("getBeanTest",GetBeanTest.class);
getBeanTest.showMe();
System.out.println(getBeanTest.getBean());
}
}
运行结果:
I am Teacher
lookupmethod.Teacher@200a570f
总结:1、利用此标签可以可以将抽象类中没有get方法教给配置文件进行配置,同时也解决了抽象类不能实例化的问题。
1、利用此标签可以用在设计有些可插拨的功能。
Spring采用和上面相同的手段解析lookup-method属性,实现细节如下:
public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//判断是否为lookup-method标签
if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {
Element ele = (Element) node;
//硬编码形式获取值,并创建实体
String methodName = ele.getAttribute(NAME_ATTRIBUTE);
String beanRef = ele.getAttribute(BEAN_ELEMENT);
LookupOverride override = new LookupOverride(methodName, beanRef);
//注入beanDefination
override.setSource(extractSource(ele));
overrides.addOverride(override);
}
}
}
replaced-method顾名思义是进行方法替换的一个标签,可以在运行时替换现有方法。
创建需要替换的类和方法:
public class TestChangeMethod {
public void changeMe(){
System.out.println("change me");
}
}
创建新的方法:
public class TestMethodReplacer implements MethodReplacer {
@Override
public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
System.out.println("我替换了" + method.getName() + "方法");
return null;
}
}
Ps:要使用replaced-method标签那么就需要使用约定的接口进行实例化,MethodReplacer 接口定义了替换的约定。使用参考如上。
Spring进行replaced-method标签解析的源码如下:
public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, REPLACED_METHOD_ELEMENT)) {
Element replacedMethodEle = (Element) node;
String name = replacedMethodEle.getAttribute(NAME_ATTRIBUTE);
String callback = replacedMethodEle.getAttribute(REPLACER_ATTRIBUTE);
ReplaceOverride replaceOverride = new ReplaceOverride(name, callback);
// Look for arg-type match elements.
List<Element> argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT);
for (Element argTypeEle : argTypeEles) {
String match = argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE);
match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle));
if (StringUtils.hasText(match)) {
replaceOverride.addTypeIdentifier(match);
}
}
replaceOverride.setSource(extractSource(replacedMethodEle));
overrides.addOverride(replaceOverride);
}
}
}
以上代码就是解析改标签的全过程,包含如下几个步骤:
1、判断是否为replaced-method标签;
2、硬编码解析参数,并封装入ReplaceOverride中;
3、获取replaced-method标签下的子标签(替换方法的参数);
4、如果有那么也转入ReplaceOverride中;
5、将局部变量注入到beanDefination中。
需要使用构造函数注入的bean:
public class TestBean {
private String name;
private boolean isRight;
private Date birthday;
private int age;
public TestBean(String name, boolean isRight, Date birthday, int age) {
this.name = name;
this.isRight = isRight;
this.birthday = birthday;
this.age = age;
}
@Override
public String toString() {
return "TestBean{" +
"name=‘" + name + ‘\‘‘ +
", isRight=" + isRight +
", birthday=" + birthday +
", age=" + age +
‘}‘;
}
}
Xml配置:
<bean id="testBean" class="constructorarg.TestBean">
<constructor-arg name="name">
<value>张三</value>
</constructor-arg>
<constructor-arg name="age">
<value>10</value>
</constructor-arg>
<constructor-arg name="isRight">
<value>true</value>
</constructor-arg>
<constructor-arg name="birthday">
<!--内部bean 由于构造器注入是string 格式,在注入是如果不手动转换那么将无法注入-->
<bean factory-bean="dateFormat" factory-method="parse">
<constructor-arg value="2007-12-20"></constructor-arg>
</bean>
</constructor-arg>
</bean>
<bean id="dateFormat" class="java.text.SimpleDateFormat">
<constructor-arg value="yyyy-MM-dd"></constructor-arg>
</bean>
源码如下:
public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
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)) {
parseConstructorArgElement((Element) node, bd);
}
}
}
Spring监测到有构造器注入时遍历所有的constructor-arg标签并将解析的工作交给parseConstructorArgElement进行完成。
parseConstructorArgElement函数如下:
public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
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 {
this.parseState.push(new ConstructorArgumentEntry(index));
//解析子元素
Object value = parsePropertyValue(ele, bd, null);
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
//不允许重复指定相同参数
if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {
error("Ambiguous constructor-arg entries for index " + index, ele);
}
else {
bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder);
}
}
finally {
this.parseState.pop();
}
}
}
catch (NumberFormatException ex) {
error("Attribute ‘index‘ of tag ‘constructor-arg‘ must be an integer", ele);
}
}
else {
try {
this.parseState.push(new ConstructorArgumentEntry());
//解析子元素
Object value = parsePropertyValue(ele, bd, null);
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
}
finally {
this.parseState.pop();
}
}
}
解析逻辑:
1、分别获取index name type的配置
2、如果配置了index那么按照index配置的位置进行匹配构造函数的入参
2.1、检查index是否符合约定(>=0),如果不符合约定抛出error
2.2、记录解析动作记录到parseState中
2.3、解析constructor-arg的子元素
2.4、使用index type ref 属性一并封装入ConstructorArgumentValues.ValueHolder类型中,并添加至当前beanDefination的ConstructorArgumentValues的IndexedArgumentValue中
3、如果未配置index
3.1、记录解析动作记录到parseState中
3.2、解析constructor-arg的子元素
3.3、使用index type ref 属性一并封装入ConstructorArgumentValues.ValueHolder类型中,并添加至当前beanDefination的ConstructorArgumentValues的GenericArgumentValue中
ps:是否采用index对于spring而言只存在信息保存的位置,解析过程时相同的
子元素解析过程如下:
@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.
//一个constructor-arg只能允许有 ref, value, list, etc中的一个子元素
NodeList nl = ele.getChildNodes();
Element subElement = null;
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//过滤掉meta、description
if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
!nodeNameEquals(node, META_ELEMENT)) {
// Child element is what we‘re looking for.
if (subElement != null) {
error(elementName + " must not contain more than one sub-element", ele);
}
else {
subElement = (Element) node;
}
}
}
//获取ref属性
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
//获取value属性
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
//如果同时有ref、value给出error
if ((hasRefAttribute && hasValueAttribute) ||
((hasRefAttribute || hasValueAttribute) && subElement != null)) {
error(elementName +
" is only allowed to contain either ‘ref‘ attribute OR ‘value‘ attribute OR sub-element", ele);
}
//处理ref类型
if (hasRefAttribute) {
String refName = ele.getAttribute(REF_ATTRIBUTE);
if (!StringUtils.hasText(refName)) {
error(elementName + " contains empty ‘ref‘ attribute", ele);
}
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
ref.setSource(extractSource(ele));
return ref;
}
//处理value
else if (hasValueAttribute) {
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
valueHolder.setSource(extractSource(ele));
return valueHolder;
}
//解析子元素
else if (subElement != null) {
return parsePropertySubElement(subElement, bd);
}
else {
// Neither child element nor "ref" or "value" attribute found.
error(elementName + " must specify a ref or value", ele);
return null;
}
}
Ps:Value属性和ref属性的封装对象不同。
其他标签是放入parsePropertySubElement函数中进行解析的,解析如下:
@Nullable
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {
if (!isDefaultNamespace(ele)) {
return parseNestedCustomElement(ele, bd);
}
//bean元素处理
else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
//直接调用parseBeanDefinitionElement函数进行处理
BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
if (nestedBd != null) {
nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
}
return nestedBd;
}
//ref元素处理
else if (nodeNameEquals(ele, REF_ELEMENT)) {
// A generic reference to any name of any bean.
String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
boolean toParent = false;
if (!StringUtils.hasLength(refName)) {
// A reference to the id of another bean in a parent context.
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 ref = new RuntimeBeanReference(refName, toParent);
ref.setSource(extractSource(ele));
return ref;
}
//idref元素处理
else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
return parseIdRefElement(ele);
}
//value元素处理
else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
return parseValueElement(ele, defaultValueType);
}
//null元素处理
else if (nodeNameEquals(ele, NULL_ELEMENT)) {
// It‘s a distinguished null value. Let‘s wrap it in a TypedStringValue
// object in order to preserve the source location.
TypedStringValue nullHolder = new TypedStringValue(null);
nullHolder.setSource(extractSource(ele));
return nullHolder;
}
//以下为各种集合处理
else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
return parseArrayElement(ele, bd);
}
else if (nodeNameEquals(ele, LIST_ELEMENT)) {
return parseListElement(ele, bd);
}
else if (nodeNameEquals(ele, SET_ELEMENT)) {
return parseSetElement(ele, bd);
}
else if (nodeNameEquals(ele, MAP_ELEMENT)) {
return parseMapElement(ele, bd);
}
else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
return parsePropsElement(ele);
}
else {
error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
return null;
}
}
Spring对各种不同的标签都做了详细的分类处理。
对于时间格式的参数进行构造注入时需要进行如下处理:
<bean id="dateFormat" class="java.text.SimpleDateFormat">
<constructor-arg value="yyyy-MM-dd"></constructor-arg>
</bean>
<constructor-arg name="birthday">
<!--内部bean 由于构造器注入是string 格式,在注入是如果不手动转换那么将无法注入-->
<bean factory-bean="dateFormat" factory-method="parse">
<constructor-arg value="2007-12-20"></constructor-arg>
</bean>
</constructor-arg>
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
parsePropertyElement((Element) node, bd);
}
}
}
parsePropertyElement函数如下:
public void parsePropertyElement(Element ele, BeanDefinition bd) {
//获取name
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
if (!StringUtils.hasLength(propertyName)) {
error("Tag ‘property‘ must have a ‘name‘ attribute", ele);
return;
}
this.parseState.push(new PropertyEntry(propertyName));
try {
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple ‘property‘ definitions for property ‘" + propertyName + "‘", ele);
return;
}
//复用构造参数解析的parsePropertyValue函数
Object val = parsePropertyValue(ele, bd, propertyName);
PropertyValue pv = new PropertyValue(propertyName, val);
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
bd.getPropertyValues().addPropertyValue(pv);
}
finally {
this.parseState.pop();
}
}
Property标签的解析是和构造函数的子标签解析是一样的,复用parsePropertyValue方法。
原文:https://www.cnblogs.com/wangerxiaoblog/p/12336651.html