十、解析bean标签子元素一

解析bean标签子元素一

完成bean标签属性解析后,会进行子元素的解析。

本篇先看metalookup-metodreplace-method三个子元素是如何解析的。

meta子元素

meta :元数据。当需要使用里面的信息时可以通过 key 获取。

meta 所声明的 key 并不会在 Bean 中体现,只是一个额外的声明,当我们需要使用里面的信息时,通过调用 BeanDefinition 的 getAttribute(String name) 方法来获取。

通过BeanDefinitionParserDelegate.parseMetaElements()来完成对meta子元素的解析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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 key="special-data" value="sprecial stragey" />
if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
Element metaElement = (Element) node;
String key = metaElement.getAttribute(KEY_ATTRIBUTE);
String value = metaElement.getAttribute(VALUE_ATTRIBUTE);
// 创建 BeanMetadataAttribute 对象
BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
attribute.setSource(extractSource(metaElement));
// 添加到 BeanMetadataAttributeAccessor 中
attributeAccessor.addMetadataAttribute(attribute);
}
}
}

解析过程:获取相应的 key - value 构建BeanMetadataAttribute对象,然后调用 BeanMetadataAttributeAccessor.addMetadataAttribute(BeanMetadataAttribute) 方法,添加 BeanMetadataAttribute加入到AbstractBeanDefinition中。

AbstractBeanDefinition继承BeanMetadataAttributeAccessor类。调用parseMetaElements()上送的就是AbstractBeanDefinition。

addMetadataAttribute()方法

调用 BeanMetadataAttributeAccessor.addMetadataAttribute(BeanMetadataAttribute) 方法,添加 BeanMetadataAttribute加入到AbstractBeanDefinition中。

1
2
3
public void addMetadataAttribute(BeanMetadataAttribute attribute) {
super.setAttribute(attribute.getName(), attribute);
}

BeanMetadataAttributeAccessor 继承AttributeAccessorSupport类。AttributeAccessorSupport类实现了AttributeAccessor接口,如上篇提到,该接口提供了对属性的获取、设置、删除。

1
2
3
4
5
6
7
8
9
10
@Override
public void setAttribute(String name, @Nullable Object value) {
Assert.notNull(name, "Name must not be null");
if (value != null) {
this.attributes.put(name, value);
}
else {
removeAttribute(name);
}
}
getAttribute()方法

设置元数据后,则可以通过调用 BeanDefinition 的 .getAttribute(String name) 方法来获取属性。

AbstractBeanDefinition继承BeanMetadataAttributeAccessor类。BeanMetadataAttributeAccessor 继承AttributeAccessorSupport类。

1
2
3
4
5
6
7
8
9
//AttributeAccessorSupport.java
/** Map with String keys and Object values. */
private final Map<String, Object> attributes = new LinkedHashMap<>();
@Override
@Nullable
public Object getAttribute(String name) {
Assert.notNull(name, "Name must not be null");
return this.attributes.get(name);
}

lookup-method子元素

依赖注入-方法注入,使用标签。和类似,一个注入bean,一个替代原有方法。

实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 定义一个水果类
public class Fruit {
public Fruit() {
System.out.println("I got Fruit");
}
}

// 苹果
public class Apple extends Fruit {
public Apple() {
System.out.println("I got a fresh apple");
}
}

// 香蕉
public class Bananer extends Fruit {
public Bananer () {
System.out.println("I got a fresh bananer");
}
}

// 水果盘,可以拿到水果
public abstract class FruitPlate{
// 抽象方法获取新鲜水果
protected abstract Fruit getFruit();
}

//spring配置
<bean id="apple" class="com.jievhaha.test.Apple" scope="prototype"/>
<bean id="bananer" class="com.jievhaha.test.Bananer " scope="prototype"/>

<bean id="fruitPlate1" class="com.jievhaha.test.FruitPlate">
<lookup-method name="getFruit" bean="apple"/>
</bean>
<bean id="fruitPlate2" class="com.jievhaha.test.FruitPlate">
<lookup-method name="getFruit" bean="bananer"/>
</bean>

public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext("classpath:resource/applicationContext.xml");

FruitPlate fp1= (FruitPlate)app.getBean("fruitPlate1");
FruitPlate fp2 = (FruitPlate)app.getBean("fruitPlate2");

fp1.getFruit();//"I got Fruit","I got a fresh apple"
fp2.getFruit();//"I got Fruit","I got a fresh bananer"
}

lookup-method为抽象方法指定返回类型,应用了CGLIB(动态代理)类库。

parseLookupOverrideSubElements()方法

通过BeanDefinitionParserDelegate.parseLookupOverrideSubElements()来完成对lookup-method子元素的解析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public void parseLookupOverrideSubElements(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, LOOKUP_METHOD_ELEMENT)) {
Element ele = (Element) node;
String methodName = ele.getAttribute(NAME_ATTRIBUTE);//name属性,指定了要返回指定类型对象的抽象方法
String beanRef = ele.getAttribute(BEAN_ELEMENT);//bean属性,指定了抽象方法要返回的对象的具体类型
// 创建 LookupOverride 对象
LookupOverride override = new LookupOverride(methodName, beanRef);
//设置数据源配置文件,对象的确切类型取决于配置文件的配置机制
override.setSource(extractSource(ele));
// 添加到 MethodOverrides 中
overrides.addOverride(override);
}
}
}

解析过程和 meta 子元素没有多大区别,同样是解析 methodName、beanRef 构造一个 LookupOverride 对象,然后记录到 AbstractBeanDefinition 中的 methodOverrides 属性中。

replace-method()方法

replaced-method :可以在运行时调用新的方法替换现有的方法,还能动态的更新原有方法的逻辑。

该标签使用方法和lookup-method标签大同小异,不过替代原有方法的类需要实现org.springframework.beans.factory.support.MethodReplacer 接口,一个注入bean,一个替代原有方法。

实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Method {
public void display(){
System.out.println("我是原始方法");
}
}

public class MethodReplace implements MethodReplacer {
@Override
public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
System.out.println("我是替换方法");
return null;
}
}

//spring配置文件内容
<bean id="methodReplace" class="com.jievhaha.test.MethodReplace"/>
<bean id="method" class="com.jievhaha.test.Method">
<replaced-method name="display" replacer="methodReplace"/>
</bean>

//测试
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring.xml");
Method method = (Method) context.getBean("method");
method.display();//"我是替换方法"
}
parseReplacedMethodSubElements()方法

通过 BeanDefinitionParserDelegate.parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides)来解析replace-method标签:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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);//name属性,替代的方法名
String callback = replacedMethodEle.getAttribute(REPLACER_ATTRIBUTE);//replacer,用哪个类的方法替代
// 创建 ReplaceOverride 对象
ReplaceOverride replaceOverride = new ReplaceOverride(name, callback);
// Look for arg-type match elements.
List<Element> argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT);// arg-type 子标签
for (Element argTypeEle : argTypeEles) {
String match = argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE);// arg-type 子标签的 match 属性
match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle));
if (StringUtils.hasText(match)) {
replaceOverride.addTypeIdentifier(match);
}
}
replaceOverride.setSource(extractSource(replacedMethodEle));
// 添加到 MethodOverrides 中
overrides.addOverride(replaceOverride);
}
}
}

十、解析bean标签子元素一
http://www.muzili.ren/2022/06/11/解析bean标签子元素一/
作者
jievhaha
发布于
2022年6月11日
许可协议