Spring自定義標(biāo)簽解析與實(shí)現(xiàn)

???????在Spring Bean注冊(cè)解析(一)Spring Bean注冊(cè)解析(二)中我們講到,Spring在解析xml文件中的標(biāo)簽的時(shí)候會(huì)區(qū)分當(dāng)前的標(biāo)簽是四種基本標(biāo)簽(import、alias、bean和beans)還是自定義標(biāo)簽,如果是自定義標(biāo)簽,則會(huì)按照自定義標(biāo)簽的邏輯解析當(dāng)前的標(biāo)簽。另外,即使是bean標(biāo)簽,其也可以使用自定義的屬性或者使用自定義的子標(biāo)簽。本文將對(duì)自定義標(biāo)簽和自定義屬性的使用方式進(jìn)行講解,并且會(huì)從源碼的角度對(duì)自定義標(biāo)簽和自定義屬性的實(shí)現(xiàn)方式進(jìn)行講解。

1. 自定義標(biāo)簽

1.1 使用方式

???????對(duì)于自定義標(biāo)簽,其主要包含兩個(gè)部分:命名空間和轉(zhuǎn)換邏輯的定義,而對(duì)于自定義標(biāo)簽的使用,我們只需要按照自定義的命名空間規(guī)則,在Spring的xml文件中定義相關(guān)的bean即可。假設(shè)我們有一個(gè)類Apple,并且我們需要在xml文件使用自定義標(biāo)簽聲明該Apple對(duì)象,如下是Apple的定義:

public class Apple {
  private int price;
  private String origin;

  public int getPrice() {
    return price;
  }

  public void setPrice(int price) {
    this.price = price;
  }

  public String getOrigin() {
    return origin;
  }

  public void setOrigin(String origin) {
    this.origin = origin;
  }
}

???????如下是我們使用自定義標(biāo)簽在Spring的xml文件中為其聲明對(duì)象的配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:myapple="http://www.lexueba.com/schema/apple"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.lexueba.com/schema/apple http://www.lexueba.com/schema/apple.xsd">

    <myapple:apple id="apple" price="123" origin="Asia"/>
</beans>

???????我們這里使用了myapple:apple標(biāo)簽聲明名為apple的bean,這里myapple就對(duì)應(yīng)了上面的xmlns:myapple,其后指定了一個(gè)鏈接:http://www.lexueba.com/schema/apple,Spring在解析到該鏈接的時(shí)候,會(huì)到META-INF文件夾下找Spring.handlers和Spring.schemas文件(這里META-INF文件夾放在maven工程的resources目錄下即可),然后讀取這兩個(gè)文件的內(nèi)容,如下是其定義:

Spring.handlers
http\://www.lexueba.com/schema/apple=chapter4.eg3.MyNameSpaceHandler
Spring.schemas
http\://www.lexueba.com/schema/apple.xsd=META-INF/custom-apple.xsd

???????可以看到,Spring.handlers指定了當(dāng)前命名空間的處理邏輯類,而Spring.schemas則指定了一個(gè)xsd文件,該文件中則聲明了myapple:apple各個(gè)屬性的定義。我們首先看下自定義標(biāo)簽各屬性的定義:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.lexueba.com/schema/apple"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            targetNamespace="http://www.lexueba.com/schema/apple"
            elementFormDefault="qualified">

    <xsd:complexType name="apple">
        <xsd:attribute name="id" type="xsd:string">
            <xsd:annotation>
                <xsd:documentation>
                    <![CDATA[ The unique identifier for a bean. ]]>
                </xsd:documentation>
            </xsd:annotation>
        </xsd:attribute>
        <xsd:attribute name="price" type="xsd:int">
            <xsd:annotation>
                <xsd:documentation>
                    <![CDATA[ The price for a bean. ]]>
                </xsd:documentation>
            </xsd:annotation>
        </xsd:attribute>
        <xsd:attribute name="origin" type="xsd:string">
            <xsd:annotation>
                <xsd:documentation>
                    <![CDATA[ The origin of the bean. ]]>
                </xsd:documentation>
            </xsd:annotation>
        </xsd:attribute>
    </xsd:complexType>

    <xsd:element name="apple" type="apple">
        <xsd:annotation>
            <xsd:documentation><![CDATA[ The service config ]]></xsd:documentation>
        </xsd:annotation>
    </xsd:element>

</xsd:schema>

???????可以看到,該xsd文件中聲明了三個(gè)屬性:id、price和origin。需要注意的是,這三個(gè)屬性與我們的Apple對(duì)象的屬性price和origin沒有直接的關(guān)系,這里只是一個(gè)xsd文件的聲明,以表征Spring的applicationContext.xml文件中使用當(dāng)前命名空間時(shí)可以使用的標(biāo)簽屬性。接下來我們看一下Spring.handlers中定義的MyNameSpaceHandler聲明:

public class MyNameSpaceHandler extends NamespaceHandlerSupport {
  @Override
  public void init() {
    registerBeanDefinitionParser("apple", new AppleBeanDefinitionParser());
  }
}

???????MyNameSpaceHandler只是注冊(cè)了apple的標(biāo)簽的處理邏輯,真正的轉(zhuǎn)換邏輯在AppleBeanDefinitionParser中。這里注冊(cè)的apple必須與Spring的applicationContext.xml文件中myapple:apple標(biāo)簽后的apple保持一致,否則將找不到相應(yīng)的處理邏輯。如下是AppleBeanDefinitionParser的處理邏輯:

public class AppleBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
  @Override
  protected Class<?> getBeanClass(Element element) {
    return Apple.class;
  }

  @Override
  protected void doParse(Element element, BeanDefinitionBuilder builder) {
    String price = element.getAttribute("price");
    String origin = element.getAttribute("origin");
    if (StringUtils.hasText(price)) {
      builder.addPropertyValue("price", Integer.parseInt(price));
    }

    if (StringUtils.hasText(origin)) {
      builder.addPropertyValue("origin", origin);
    }
  }
}

???????可以看到,該處理邏輯中主要是獲取當(dāng)前標(biāo)簽中定義的price和origin屬性的值,然后將其按照一定的處理邏輯注冊(cè)到當(dāng)前的BeanDefinition中。這里還實(shí)現(xiàn)了一個(gè)getBeanClass()方法,該方法用于表明當(dāng)前自定義標(biāo)簽對(duì)應(yīng)的BeanDefinition所代表的類的類型。如下是我們的入口程序,用于檢查當(dāng)前的自定義標(biāo)簽是否正常工作的:

public class CustomSchemaApp {
  public static void main(String[] args) {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    Apple apple = applicationContext.getBean(Apple.class);
    System.out.println(apple.getPrice() + ", " + apple.getOrigin());
  }
}

???????運(yùn)行結(jié)果如下:

123, Asia

1.2 實(shí)現(xiàn)方式

???????我們還是從對(duì)整個(gè)applicationContext.xml文件開始讀取的入口方法開始進(jìn)行講解,即DefaultBeanDefinitionDocumentReader.parseBeanDefinitions()方法,如下是該方法的源碼:

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    // 判斷根節(jié)點(diǎn)使用的標(biāo)簽所對(duì)應(yīng)的命名空間是否為Spring提供的默認(rèn)命名空間,
    // 這里根節(jié)點(diǎn)為beans節(jié)點(diǎn),該節(jié)點(diǎn)的命名空間通過其xmlns屬性進(jìn)行了定義
    if (delegate.isDefaultNamespace(root)) {
        NodeList nl = root.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            if (node instanceof Element) {
                Element ele = (Element) node;
                if (delegate.isDefaultNamespace(ele)) {
                    // 當(dāng)前標(biāo)簽使用的是默認(rèn)的命名空間,如bean標(biāo)簽,
                    // 則按照默認(rèn)命名空間的邏輯對(duì)其進(jìn)行處理
                    parseDefaultElement(ele, delegate);
                } else {
                    // 判斷當(dāng)前標(biāo)簽使用的命名空間是自定義的命名空間,如這里myapple:apple所
                    // 使用的就是自定義的命名空間,那么就按照定義命名空間邏輯進(jìn)行處理
                    delegate.parseCustomElement(ele);
                }
            }
        }
    }
    else {
        // 如果根節(jié)點(diǎn)使用的命名空間不是默認(rèn)的命名空間,則按照自定義的命名空間進(jìn)行處理
        delegate.parseCustomElement(root);
    }
}

???????可以看到,該方法首先會(huì)判斷當(dāng)前文件指定的xmlns命名空間是否為默認(rèn)命名空間,如果是,則按照默認(rèn)命名空間進(jìn)行處理,如果不是則直接按照自定義命名空間進(jìn)行處理。這里需要注意的是,即使在默認(rèn)的命名空間中,當(dāng)前標(biāo)簽也可以使用自定義的命名空間,我們定義的myapple:apple就是這種類型,這里myapple就關(guān)聯(lián)了xmlns:myapple后的myapple。如下是自定義命名空間的處理邏輯:

@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
    // 獲取當(dāng)前標(biāo)簽對(duì)應(yīng)的命名空間指定的url
    String namespaceUri = getNamespaceURI(ele);
    if (namespaceUri == null) {
        return null;
    }
    
    // 獲取當(dāng)前url所對(duì)應(yīng)的NameSpaceHandler處理邏輯,也即我們定義的MyNameSpaceHandler
    NamespaceHandler handler = this.readerContext
        .getNamespaceHandlerResolver()
        .resolve(namespaceUri);
    if (handler == null) {
        error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + 
              namespaceUri + "]", ele);
        return null;
    }
    
    // 調(diào)用當(dāng)前命名空間處理邏輯的parse()方法,以對(duì)當(dāng)前標(biāo)簽進(jìn)行轉(zhuǎn)換
    return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

???????這里getNamespaceURI()方法的作用是獲取當(dāng)前標(biāo)簽對(duì)應(yīng)的命名空間url。在獲取url之后,會(huì)調(diào)用NamespaceHandlerResolver.resolve(String)方法,該方法會(huì)通過當(dāng)前命名空間的url獲取META-INF/Spring.handlers文件內(nèi)容,并且查找當(dāng)前命名空間url對(duì)應(yīng)的處理邏輯類。如下是該方法的聲明:

@Nullable
public NamespaceHandler resolve(String namespaceUri) {
    // 獲取handlerMapping對(duì)象,其鍵為當(dāng)前的命名空間url,
    // 值為當(dāng)前命名空間的處理邏輯類對(duì)象,或者為處理邏輯類的包含全路徑的類名
    Map<String, Object> handlerMappings = getHandlerMappings();
    // 查看是否存在當(dāng)前url的處理類邏輯,沒有則返回null
    Object handlerOrClassName = handlerMappings.get(namespaceUri);
    if (handlerOrClassName == null) {
        return null;
    } else if (handlerOrClassName instanceof NamespaceHandler) {
        // 如果存在當(dāng)前url對(duì)應(yīng)的處理類對(duì)象,則直接返回該處理對(duì)象
        return (NamespaceHandler) handlerOrClassName;
    } else {
        // 如果當(dāng)前url對(duì)應(yīng)的處理邏輯還是一個(gè)沒初始化的全路徑類名,則通過反射對(duì)其進(jìn)行初始化
        String className = (String) handlerOrClassName;
        try {
            Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
            // 判斷該全路徑類是否為NamespaceHandler接口的實(shí)現(xiàn)類
            if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
                throw new FatalBeanException("Class [" + className + "] for namespace [" + 
                    namespaceUri + "] does not implement the [" + 
                    NamespaceHandler.class.getName() + "] interface");
            }
            NamespaceHandler namespaceHandler = 
                (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
            namespaceHandler.init();  // 調(diào)用處理邏輯的初始化方法
            handlerMappings.put(namespaceUri, namespaceHandler);  //緩存處理邏輯類對(duì)象
            return namespaceHandler;
        }
        catch (ClassNotFoundException ex) {
            throw new FatalBeanException("Could not find NamespaceHandler class [" 
               + className + "] for namespace [" + namespaceUri + "]", ex);
        }
        catch (LinkageError err) {
            throw new FatalBeanException("Unresolvable class definition for" 
               + "NamespaceHandler class [" + className + "] for namespace [" 
               +  namespaceUri + "]", err);
        }
    }
}

???????可以看到,在處理命名空間url的時(shí)候,首先會(huì)判斷是否存在當(dāng)前url的處理邏輯,不存在則直接返回。如果存在,則會(huì)判斷其為一個(gè)NamespaceHandler對(duì)象,還是一個(gè)全路徑的類名,是NamespaceHandler對(duì)象則強(qiáng)制類型轉(zhuǎn)換后返回,否則通過反射初始化該類,并調(diào)用其初始化方法,然后才返回。

???????我們繼續(xù)查看NamespaceHandler.parse()方法,如下是該方法的源碼:

@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
    // 獲取當(dāng)前標(biāo)簽使用的parser處理類
    BeanDefinitionParser parser = findParserForElement(element, parserContext);
    // 按照定義的parser處理類對(duì)當(dāng)前標(biāo)簽進(jìn)行處理,這里的處理類即我們定義的AppleBeanDefinitionParser
    return (parser != null ? parser.parse(element, parserContext) : null);
}

???????這里的parse()方法首先會(huì)查找當(dāng)前標(biāo)簽定義的處理邏輯對(duì)象,找到后則調(diào)用其parse()方法對(duì)其進(jìn)行處理。這里的parser也即我們定義的AppleBeanDefinitionParser.parse()方法。這里需要注意的是,我們?cè)谇懊嬷v過,在MyNameSpaceHandler.init()方法中注冊(cè)的處理類邏輯的鍵(即apple)必須與xml文件中myapple:apple后的apple一致,這就是這里findParserForElement()方法查找BeanDefinitionParser處理邏輯的依據(jù)。如下是findParserForElement()方法的源碼:

@Nullable
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
    // 獲取當(dāng)前標(biāo)簽命名空間后的局部鍵名,即apple
    String localName = parserContext.getDelegate().getLocalName(element);
    // 通過使用的命名空間鍵獲取對(duì)應(yīng)的BeanDefinitionParser處理邏輯
    BeanDefinitionParser parser = this.parsers.get(localName);
    if (parser == null) {
        parserContext.getReaderContext().fatal(
           "Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
    }
    return parser;
}

???????這里首先獲取當(dāng)前標(biāo)簽的命名空間后的鍵名,即myapple:apple后的apple,然后在parsers中獲取該鍵對(duì)應(yīng)的BeanDefinitionParser對(duì)象。其實(shí)在MyNameSpaceHandler.init()方法中進(jìn)行的注冊(cè)工作就是將其注冊(cè)到了parsers對(duì)象中。

2. 自定義屬性

2.1 使用方式

???????自定義屬性的定義方式和自定義標(biāo)簽非常相似,其主要也是進(jìn)行命名空間和轉(zhuǎn)換邏輯的定義。假設(shè)我們有一個(gè)Car對(duì)象,我們需要使用自定義標(biāo)簽為其添加一個(gè)描述屬性。如下是Car對(duì)象的定義:

public class Car {
  private long id;
  private String name;
  private String desc;

  public long getId() {
    return id;
  }

  public void setId(long id) {
    this.id = id;
  }

  public String getDesc() {
    return desc;
  }

  public void setDesc(String desc) {
    this.desc = desc;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }
}

???????如下是在applicationContext.xml中該對(duì)象的定義:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:car="http://www.lexueba.com/schema/car-desc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <bean id="car" class="chapter4.eg2.Car" car:car-desc="This is test custom attribute">
        <property name="id" value="1"/>
        <property name="name" value="baoma"/>
    </bean>
</beans>

???????可以看到,car對(duì)象的定義使用的就是一般的bean定義,只不過其多了一個(gè)屬性car:car-desc的使用。這里的car:car-desc對(duì)應(yīng)的命名空間就是上面的http://www.lexueba.com/schema/car-desc。同自定義標(biāo)簽一樣,自定義屬性也需要在META-INF下的Spring.handlers和Spring.schemas文件中指定當(dāng)前的處理邏輯和xsd定義,如下是這兩個(gè)文件的定義:

Spring.handlers
http\://www.lexueba.com/schema/car-desc=chapter4.eg2.MyCustomAttributeHandler
Spring.schemas
http\://www.lexueba.com/schema/car.xsd=META-INF/custom-attribute.xsd

???????對(duì)應(yīng)的xsd文件的定義如下:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.lexueba.com/schema/car-desc"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            targetNamespace="http://www.lexueba.com/schema/car-desc"
            elementFormDefault="qualified">

    <xsd:attribute name="car-desc" type="xsd:string"/>

</xsd:schema>

???????可以看到,該xsd文件中只定義了一個(gè)屬性,即car-desc。如下是MyCustomAttributeHandler的聲明:

public class MyCustomAttributeHandler extends NamespaceHandlerSupport {
  @Override
  public void init() {
    registerBeanDefinitionDecoratorForAttribute("car-desc", 
      new CarDescInitializingBeanDefinitionDecorator());
  }
}

???????需要注意的是,和自定義標(biāo)簽不同的是,自定義標(biāo)簽是將處理邏輯注冊(cè)到parsers對(duì)象中,這里自定義屬性是將處理邏輯注冊(cè)到attributeDecorators中。如下CarDescInitializingBeanDefinitionDecorator的邏輯:

public class CarDescInitializingBeanDefinitionDecorator implements BeanDefinitionDecorator {
  @Override
  public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
    String desc = ((Attr) node).getValue();
    definition.getBeanDefinition().getPropertyValues().addPropertyValue("desc", desc);
    return definition;
  }
}

???????可以看到,對(duì)于car-desc的處理邏輯就是獲取當(dāng)前定義的屬性的值,由于知道其是當(dāng)前標(biāo)簽的一個(gè)屬性,因而可以將其強(qiáng)轉(zhuǎn)為一個(gè)Attr類型的對(duì)象,并獲取其值,然后將其添加到指定的BeandDefinitionHolder中。這里需要注意的是,自定義標(biāo)簽繼承的是AbstractSingleBeanDefinitionParser類,實(shí)際上是實(shí)現(xiàn)的BeanDefinitionParser接口,而自定義屬性實(shí)現(xiàn)的則是BeanDefinitionDecorator接口。

2.2 實(shí)現(xiàn)方式

???????關(guān)于自定義屬性的實(shí)現(xiàn)方式,需要注意的是,自定義屬性只能在bean標(biāo)簽中使用,因而我們可以直接進(jìn)入對(duì)bean標(biāo)簽的處理邏輯中,即DefaultBeanDefinitionDocumentReader.processBeanDefinition()方法,如下是該方法的聲明:

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    // 對(duì)bean標(biāo)簽的默認(rèn)屬性和子標(biāo)簽進(jìn)行處理,將其封裝為一個(gè)BeanDefinition對(duì)象,
    // 并放入BeanDefinitionHolder中
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    if (bdHolder != null) {
        // 進(jìn)行自定義屬性或自定義子標(biāo)簽的裝飾
        bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
        try {
            // 注冊(cè)當(dāng)前的BeanDefinition
            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,
               getReaderContext().getRegistry());
        }catch (BeanDefinitionStoreException ex) {
            getReaderContext().error("Failed to register bean definition with name '" +
                                     bdHolder.getBeanName() + "'", ele, ex);
        }
        
        // 調(diào)用注冊(cè)了bean標(biāo)簽解析完成的事件處理邏輯
        getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
    }
}

???????這里我們直接進(jìn)入BeanDefinitionParserDelegate.decorateBeanDefinitionIfRequired()方法中:

public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
    Element ele, BeanDefinitionHolder definitionHolder, @Nullable BeanDefinition containingBd) {

    BeanDefinitionHolder finalDefinition = definitionHolder;

    // 處理自定義屬性
    NamedNodeMap attributes = ele.getAttributes();
    for (int i = 0; i < attributes.getLength(); i++) {
        Node node = attributes.item(i);
        finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
    }

    // 處理自定義子標(biāo)簽
    NodeList children = ele.getChildNodes();
    for (int i = 0; i < children.getLength(); i++) {
        Node node = children.item(i);
        if (node.getNodeType() == Node.ELEMENT_NODE) {
            finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
        }
    }
    return finalDefinition;
}

???????可以看到,自定義屬性和自定義子標(biāo)簽的解析都是通過decorateIfRequired()方法進(jìn)行的,如下是該方法的定義:

public BeanDefinitionHolder decorateIfRequired(
    Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {

    // 獲取當(dāng)前自定義屬性或子標(biāo)簽的命名空間url
    String namespaceUri = getNamespaceURI(node);
    // 判斷其如果為spring默認(rèn)的命名空間則不對(duì)其進(jìn)行處理
    if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) {
        // 獲取當(dāng)前命名空間對(duì)應(yīng)的NamespaceHandler對(duì)象
        NamespaceHandler handler = this.readerContext
            .getNamespaceHandlerResolver()
            .resolve(namespaceUri);
        if (handler != null) {
            // 對(duì)當(dāng)前的BeanDefinitionHolder進(jìn)行裝飾
            BeanDefinitionHolder decorated =
                handler.decorate(node, originalDef, 
                   new ParserContext(this.readerContext, this, containingBd));
            if (decorated != null) {
                return decorated;
            }
        }
        else if (namespaceUri.startsWith("http://www.springframework.org/")) {
            error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + 
                  namespaceUri + "]", node);
        }
        else {
            // A custom namespace, not to be handled by Spring - maybe "xml:...".
            if (logger.isDebugEnabled()) {
                logger.debug("No Spring NamespaceHandler found for XML schema namespace [" 
                             + namespaceUri + "]");
            }
        }
    }
    return originalDef;
}

???????decorateIfRequired()方法首先會(huì)獲取當(dāng)前自定義屬性或子標(biāo)簽對(duì)應(yīng)的命名空間url,然后根據(jù)該url獲取當(dāng)前命名空間對(duì)應(yīng)的NamespaceHandler處理邏輯,并且調(diào)用其decorate()方法進(jìn)行裝飾,如下是該方法的實(shí)現(xiàn):

@Nullable
public BeanDefinitionHolder decorate(
    Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
    // 獲取當(dāng)前自定義屬性或子標(biāo)簽注冊(cè)的BeanDefinitionDecorator對(duì)象
    BeanDefinitionDecorator decorator = findDecoratorForNode(node, parserContext);
    // 調(diào)用自定義的BeanDefinitionDecorator.decorate()方法進(jìn)行裝飾,
    // 這里就是我們實(shí)現(xiàn)的CarDescInitializingBeanDefinitionDecorator類
    return (decorator != null ? decorator.decorate(node, definition, parserContext) : null);
}

???????和自定義標(biāo)簽不同的是,自定義屬性或自定義子標(biāo)簽查找當(dāng)前Decorator的方法是需要對(duì)屬性或子標(biāo)簽進(jìn)行分別判斷的,如下是findDecoratorForNode()的實(shí)現(xiàn):

@Nullable
private BeanDefinitionDecorator findDecoratorForNode(Node node,
        ParserContext parserContext) {
    BeanDefinitionDecorator decorator = null;
    // 獲取當(dāng)前標(biāo)簽或?qū)傩缘木植挎I名
    String localName = parserContext.getDelegate().getLocalName(node);
    // 判斷當(dāng)前節(jié)點(diǎn)是屬性還是子標(biāo)簽,根據(jù)情況不同獲取不同的Decorator處理邏輯
    if (node instanceof Element) {
        decorator = this.decorators.get(localName);
    } else if (node instanceof Attr) {
        decorator = this.attributeDecorators.get(localName);
    } else {
        parserContext.getReaderContext().fatal(
            "Cannot decorate based on Nodes of type [" + node.getClass().getName() 
            + "]", node);
    }
    if (decorator == null) {
        parserContext.getReaderContext().fatal(
            "Cannot locate BeanDefinitionDecorator for " + (node instanceof Element 
            ? "element" : "attribute") + " [" + localName + "]", node);
    }
    return decorator;
}

???????對(duì)于BeanDefinitionDecorator處理邏輯的查找,可以看到,其會(huì)根據(jù)節(jié)點(diǎn)的類型進(jìn)行判斷,根據(jù)不同的情況獲取不同的BeanDefinitionDecorator處理對(duì)象。

3. 自定義子標(biāo)簽

???????對(duì)于自定義子標(biāo)簽的使用,其與自定義標(biāo)簽的使用非常相似,不過需要注意的是,根據(jù)對(duì)自定義屬性的源碼解析,我們知道自定義子標(biāo)簽并不是自定義標(biāo)簽,自定義子標(biāo)簽只是起到對(duì)其父標(biāo)簽所定義的bean的一種裝飾作用,因而自定義子標(biāo)簽的處理邏輯定義與自定義標(biāo)簽主要有兩點(diǎn)不同:①在NamespaceHandler.init()方法中注冊(cè)自定義子標(biāo)簽的處理邏輯時(shí)需要使用registerBeanDefinitionDecorator(String, BeanDefinitionDecorator)方法;②自定義子標(biāo)簽的處理邏輯需要實(shí)現(xiàn)的是BeanDefinitionDecorator接口。其余部分的使用都和自定義標(biāo)簽一致。

4. 總結(jié)

???????本文主要對(duì)自定義標(biāo)簽,自定義屬性和自定義子標(biāo)簽的使用方式和源碼實(shí)現(xiàn)進(jìn)行了講解,有了對(duì)自定義標(biāo)簽的理解,我們可以在Spring的xml文件中根據(jù)自己的需要實(shí)現(xiàn)自己的處理邏輯。另外需要說明的是,Spring源碼中也大量使用了自定義標(biāo)簽,比如spring的AOP的定義,其標(biāo)簽為<aspectj-autoproxy />。從另一個(gè)角度來看,我們前面兩篇文章對(duì)Spring的xml文件的解析進(jìn)行了講解,可以知道,Spring默認(rèn)只會(huì)處理import、alias、bean和beans四種標(biāo)簽,對(duì)于其余的標(biāo)簽,如我們所熟知的事務(wù)處理標(biāo)簽,這些都是使用自定義標(biāo)簽實(shí)現(xiàn)的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,991評(píng)論 19 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,971評(píng)論 6 342
  • Spring Web MVC Spring Web MVC 是包含在 Spring 框架中的 Web 框架,建立于...
    Hsinwong閱讀 22,589評(píng)論 1 92
  • 堅(jiān)守 文||與你相識(shí) 三十六載的風(fēng)雨 承載你的愛心 桃李滿天下 豈能說盡所有的故事 你用最好的年華 滋養(yǎng)了大山的希...
    與你相識(shí)_40fa閱讀 161評(píng)論 2 3
  • 主宰演講臺(tái) 作者:美國~比爾胡戈特伯 來源:行動(dòng)營之未讀書籍書籍。 感悟: 這本書是之前在行動(dòng)營的時(shí)候買的,但是一...
    星星愛L歡閱讀 128評(píng)論 0 1