【spring原始碼學習】spring的IOC容器之自定義xml配置標籤擴充套件namspaceHandler向IOC容器中註冊bean

Love Lenka發表於2017-07-25

【spring以及第三方jar的案例】在spring中的aop相關配置的標籤,執行緒池相關配置的標籤,都是基於該種方式實現的。包括dubbo的配置標籤都是基於該方式實現的。
【一】原理

===>spring在解析xml標籤,一旦不是以<bean>開頭的元素,就會走org.springframework.beans.factory.xml.BeanDefinitionParserDelegate的parseCustomElement(Element ele)方法解析自定義的標籤

===>在該方法裡會根據xml中的名稱空間去查詢該標籤對應的NamespaceHandler介面的實現類去解析該配置標籤。該介面解析該配置標籤,並形成BeanDefinition註冊到IOC容器中。

===>該擴充套件需要做的內容:

(1)建立spring.handlers檔案,這是在解析xml配置檔案的時候,spring會透過xml檔案頭的名稱空間,去找該配置檔案中的NamespaceHandler的實現類。

(2)建立spring.schemas檔案,這是在xml檔案中配置自定義標籤的標籤合法驗證,也是合法檢驗。如果隨意填寫配置標籤,spring將無法解析。

(3)在所擴充套件的專案的resources目錄下,建立META-INF目錄,並將兩個檔案放置在目錄下。

(4)將spring.schemas中的xsd檔案配置在隨意的類路徑下。關於xsd檔案,可以瞭解:http://www.w3school.com.cn/schema/index.asp

(5)建立NamespaceHandler介面的實現類,建立BeanDefinitionParser的實現類。用於解析自已定義標籤的內容。

 

【二】實現例子:定義一個自定義標籤,實現一個類ZKClient的bean透過NamespaceHandler註冊IOC容器。本例子已經透過測試。不寫測試方法,只寫實現過程。

(1)spring.handlers檔案內容

http\://localhost.com/sxf=com.mobile.thinks.manages.namespaceHandler.SxfNameSpaceHandler
View Code

(2)spring.schemas檔案內容

http\://localhost.com/sxf.xsd=com/mobile/thinks/manages/namespaceHandler/sxf.xsd
View Code

(3)xsd檔案內容

<xsd:schema  xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
targetNamespace="http://localhost.com/sxf"
xmlns="http://www.w3school.com.cn"
elementFormDefault="qualified">

<xsd:element name="zk">
  <xsd:complexType>
    <xsd:attribute name="host"  use="required" >
            <xsd:simpleType>
                <xsd:restriction base="xsd:string"/>
            </xsd:simpleType>
    </xsd:attribute>
    <xsd:attribute name="port"  use="required" >
            <xsd:simpleType>
                <xsd:restriction base="xsd:integer"/>
            </xsd:simpleType>
    </xsd:attribute>
    <xsd:attribute name="user"  use="required" >
            <xsd:simpleType>
                <xsd:restriction base="xsd:string"/>
            </xsd:simpleType>
    </xsd:attribute>
     <xsd:attribute name="pwd"  use="required" >
            <xsd:simpleType>
                <xsd:restriction base="xsd:string"/>
            </xsd:simpleType>
    </xsd:attribute>
  </xsd:complexType>
</xsd:element>
</xsd:schema>
View Code

(4)spring的xml配置檔案內容

<?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:tx="http://www.springframework.org/schema/tx"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:task="http://www.springframework.org/schema/task"
       xmlns:sxf="http://localhost.com/sxf"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/tx
              http://www.springframework.org/schema/tx/spring-tx.xsd
              http://www.springframework.org/schema/aop 
              http://www.springframework.org/schema/aop/spring-aop.xsd
              http://www.springframework.org/schema/task
              http://www.springframework.org/schema/task/spring-task.xsd
              http://www.springframework.org/schema/data/jpa
           http://www.springframework.org/schema/data/jpa/spring-jpa.xsd 
           http://www.springframework.org/schema/context 
           http://www.springframework.org/schema/context/spring-context.xsd
           http://localhost.com/sxf
           http://localhost.com/sxf.xsd
           
           ">

    <sxf:zk host="127.0.0.1" port="2181" user="shangxiaofei"  pwd="smxcyx"/>

   <!-- <context:property-placeholder location="classpath:resources.properties"/> -->

    <!-- 掃描註解Bean -->
    <context:component-scan base-package="com.mobile.thinks.**">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
        <context:include-filter type="annotation" expression="org.springframework.beans.factory.annotation.Autowired"/>
    </context:component-scan>
    
   
    
</beans>
View Code

(5)NamespaceHandler介面實現類的內容

package com.mobile.thinks.manages.namespaceHandler;

import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
/**
 * 自定義標籤的註解直譯器
 * @author sxf
 *
 */
public class SxfNameSpaceHandler extends NamespaceHandlerSupport {

    /**
     * 初始化zk元素的具體解析器
     */
    @Override
    public void init() {
        registerBeanDefinitionParser("zk", new ZkBeanDefinitionParser());
    }
    

}
View Code

(6)BeanDefinitionParser介面實現類的內容

package com.mobile.thinks.manages.namespaceHandler;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;

public class ZkBeanDefinitionParser  implements BeanDefinitionParser{

    private static final String HOST ="host";
    private static final String PORT="port";
    private static final String USER="user";
    private static final String PWD="pwd";
    
    /**
     * 解析標籤,形成特定的beanDefinition加入到ioc容器中
     */
    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ZkClient.class);
        String host=element.getAttribute("host");
        String port=element.getAttribute("port");
        String user=element.getAttribute("user");
        String pwd=element.getAttribute("pwd");
        builder.addPropertyValue("host",host);
        builder.addPropertyValue("port", Integer.valueOf(port));
        builder.addPropertyValue("user", user);
        builder.addPropertyValue("pwd", pwd);
        parserContext.getRegistry().registerBeanDefinition("zkClient", builder.getBeanDefinition());
        return builder.getBeanDefinition();
    }
    
    

}
View Code

(7)Zkclient類的內容,將來在專案中用一下方式直接使用該類

 @Autowired
    private ZkClient zkClient;

package com.mobile.thinks.manages.namespaceHandler;
/**
 * 該類用自定義的NameSpaceHandler類解析配置檔案向IOC容器中註冊
 * @author sxf
 *
 */
public class ZkClient {

    private String host;
    private int port;
    private String user;
    private String pwd;
    
    public String getHost() {
        return host;
    }
    public void setHost(String host) {
        this.host = host;
    }
    public int getPort() {
        return port;
    }
    public void setPort(int port) {
        this.port = port;
    }
    public String getUser() {
        return user;
    }
    public void setUser(String user) {
        this.user = user;
    }
    public String getPwd() {
        return pwd;
    }
    public void setPwd(String pwd) {
        this.pwd = pwd;
    }
    
}
View Code

 

【三】專案結構圖

 

【&&】需要注意的點,當maven打包的時候,預設不會將sxf.xsd檔案打包進jar包,將來專案用到的時候就不會從本地讀取到該sxf.xsd檔案,則需要在pom.xml檔案配置如下,才可以將檔案打包到相應的位置。也可以將其他格式的檔案,打包到jar包相應的位置,只需要修改相應檔案的字尾。

<build>    
          <!-- 打成jar包的名字 -->
        <finalName>test</finalName>    
        <!--  這樣也可以把所有的readme檔案,打包到相應位置。其他的比如XX.xml檔案,也是同樣配置  -->  
        <resources>    
            <resource>    
                <directory>src/main/resources</directory>    
                <includes>    
                    <include>**/*.txt</include>    
                    <include>**/*.xml</include>  
                </includes>    
            </resource>    
            <resource>    
                <directory>src/main/java</directory>    
                <includes>    
                    <include>**/*.xsd</include>  
                </includes>     
            </resource>    
        </resources>    
    </build>    
View Code

該專案的jar完整的pom.xml檔案

<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>com.mobile</groupId>
    <artifactId>thinks</artifactId>
    <version>0.0.1-SNAPSHOT</version>
  </parent>
  <groupId>com.mobile</groupId>
  <artifactId>thinks-manages</artifactId>
  <version>1.0.0</version>
  <name>thinks-manages</name>
  <url>http://maven.apache.org</url>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
      <build>    
        <!--  <finalName>test</finalName> 打包成預設名字 -->   
        <!--  這樣也可以把所有的readme檔案,打包到相應位置。其他的比如XX.xml檔案,也是同樣配置  -->  
        <resources>    
            <resource>    
                <directory>src/main/resources</directory>    
                <includes>    
                    <include>**/*.txt</include>    
                    <include>**/*.xml</include>
                    <include>**/*.handlers</include>
                    <include>**/*.schemas</include>
                    <include>**/*.drl</include>  
                </includes>    
            </resource>    
            <resource>    
                <directory>src/main/java</directory>    
                <includes>    
                    <include>**/*.xsd</include>  
                </includes>     
            </resource>    
        </resources>    
    </build>    
  <dependencies>
      <dependency>
          <groupId>com.mobile</groupId>
          <artifactId>thinks-service</artifactId>
          <version>${thinks.service.version}</version>
      </dependency>
      <dependency>
          <groupId>com.mobile</groupId>
          <artifactId>thinks-core</artifactId>
          <version>${thinks.core.version}</version>
      </dependency>
      <dependency>
          <groupId>com.mobile</groupId>
          <artifactId>thinks-commons</artifactId>
          <version>${thinks.commons.version}</version>
      </dependency>
  
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    
    <!-- drools規則引擎 -->
    <dependency>
                <groupId>org.drools</groupId>
                <artifactId>drools-core</artifactId>
            </dependency>
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-compiler</artifactId>
        </dependency>
        <dependency>
          <groupId>org.drools</groupId>
          <artifactId>knowledge-api</artifactId>
        </dependency>
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-decisiontables</artifactId>
        </dependency>
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-jsr94</artifactId>
            <version>${drools.version}</version><!--$NO-MVN-MAN-VER$-->
        </dependency>
          <!-- drools升級6.5.0.final版本依賴 -->
        <dependency>
                <groupId>org.eclipse.jdt.core.compiler</groupId>
                <artifactId>ecj</artifactId>
        </dependency>
  </dependencies>
</project>
View Code

 

相關文章