點選上方“Java技術驛站”,選擇“置頂公眾號”。
有內涵、有價值的文章第一時間送達!
精品專欄
在上篇部落格【死磕Spring】—– IOC 之解析 Bean 標簽:BeanDefinition中已經完成了對 Bean 標簽屬性的解析工作,這篇博文開始分析子元素的解析。完成 Bean 標簽基本屬性解析後,會依次呼叫 parseMetaElements()
、 parseLookupOverrideSubElements()
、 parseReplacedMethodSubElements()
對子元素 meta、lookup-method、replace-method 完成解析。三個子元素的作用如下:
-
meta:元資料。
-
lookup-method:Spring 動態改變 bean 裡方法的實現。方法執行傳回的物件,使用 Spring 內原有的這類物件替換,透過改變方法傳回值來動態改變方法。內部實現為使用 cglib 方法,重新生成子類,重寫配置的方法和傳回物件,達到動態改變的效果。
-
replace-method:Spring 動態改變 bean 裡方法的實現。需要改變的方法,使用 Spring 內原有其他類(需要繼承介面
org.springframework.beans.factory.support.MethodReplacer
)的邏輯,替換這個方法。透過改變方法執行邏輯來動態改變方法。
meta 子元素
meta :元資料。當需要使用裡面的資訊時可以透過key獲取
meta 所宣告的 key 並不會在 Bean 中體現,只是一個額外的宣告,當我們需要使用裡面的資訊時,透過 BeanDefinition 的 getAttribute()
獲取。該子元素的解析過程如下:
public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
NodeList nl = ele.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
Element metaElement = (Element) node;
String key = metaElement.getAttribute(KEY_ATTRIBUTE);
String value = metaElement.getAttribute(VALUE_ATTRIBUTE);
BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
attribute.setSource(extractSource(metaElement));
attributeAccessor.addMetadataAttribute(attribute);
}
}
}
解析過程較為簡單,獲取相應的 key – value 構建 BeanMetadataAttribute 物件,然後透過 addMetadataAttribute()
加入到 AbstractBeanDefinition 中。 “ 如下:
public void addMetadataAttribute(BeanMetadataAttribute attribute) {
super.setAttribute(attribute.getName(), attribute);
}
委託 AttributeAccessorSupport 實現,如下:
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);
}
}
AttributeAccessorSupport 是介面 AttributeAccessor 的實現者。 AttributeAccessor 介面定義了與其他物件的元資料進行連線和訪問的約定,可以透過該介面對屬性進行獲取、設定、刪除操作。
設定元資料後,則可以透過 getAttribute()
獲取,如下:
public Object getAttribute(String name) {
BeanMetadataAttribute attribute = (BeanMetadataAttribute) super.getAttribute(name);
return (attribute != null ? attribute.getValue() : null);
}
lookup-method 子元素
lookup-method :獲取器註入,是把一個方法宣告為傳回某種型別的 bean 但實際要傳回的 bean 是在配置檔案裡面配置的。該方法可以用於設計一些可插拔的功能上,解除程式依賴。
直接上例子:
public interface Car {
void display();
}
public class Bmw implements Car{
@Override
public void display() {
System.out.println("我是 BMW");
}
}
public class Hongqi implements Car{
@Override
public void display() {
System.out.println("我是 hongqi");
}
}
public abstract class Display {
public void display(){
getCar().display();
}
public abstract Car getCar();
}
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring.xml");
Display display = (Display) context.getBean("display");
display.display();
}
}
配置內容如下:
id="display" class="org.springframework.core.test1.Display">
name="getCar" bean="hongqi"/>
執行結果為:
我是 hongqi
如果將 bean="hognqi"
替換為 bean="bmw"
,則執行結果變成:
我是 BMW
看了這個示例,我們初步瞭解了 looku-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);
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);
override.setSource(extractSource(ele));
overrides.addOverride(override);
}
}
}
解析過程和 meta 子元素沒有多大區別,同樣是解析 methodName、beanRef 構造一個 LookupOverride 物件,然後改寫即可。在實體化 Bean 的時候,再詳細闡述具體的實現過程,這裡僅僅只是一個標記作用。
replace-method 子元素
replaced-method :可以在執行時呼叫新的方法替換現有的方法,還能動態的更新原有方法的邏輯
該標簽使用方法和 lookup-method 標簽差不多,只不過替代方法的類需要實現 MethodReplacer 介面。如下:
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;
}
}
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring.xml");
Method method = (Method) context.getBean("method");
method.display();
}
如果 spring.xml 檔案如下:
id="methodReplace" class="org.springframework.core.test1.MethodReplace"/>
id="method" class="org.springframework.core.test1.Method"/>
則執行結果為:
我是原始方法
增加 replaced-method 子元素:
id="methodReplace" class="org.springframework.core.test1.MethodReplace"/>
id="method" class="org.springframework.core.test1.Method">
name="display" replacer="methodReplace"/>
執行結果為:
我是替換方法
上面程式碼演示了 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);
}
}
}
該子元素和 lookup-method 資源的解析過程差不多,同樣是提取 name 和 replacer 屬性構建 ReplaceOverride 物件,然後記錄到 AbstractBeanDefinition 中的 methodOverrides 屬性中。
對於 lookup-method 和 replaced-method 兩個子元素是如何使用以完成他們所提供的功能,在後續實體化 Bean 的時候會做詳細說明。
【死磕 Spring】----- IOC 之解析 bean 標簽:開啟解析行程
【死磕 Spring】----- IOC 之解析 bean 標簽:BeanDefinition
【死磕 Spring】----- IOC 之 獲取 Document 物件
【死磕 Spring】----- IOC 之 註冊 BeanDefinition
【死磕 Spring】----- IOC 之 Spring 統一資源載入策略
【死磕 Spring】----- IOC 之深入理解 Spring IoC
END