1. 程式人生 > >Spring自定義標籤實現及踩過的坑(親測)

Spring自定義標籤實現及踩過的坑(親測)

專案結構

先來一張專案結構圖,因為LZ是用的IDEAL,網上的大部分都是用的eclipse來實現:

這裡也大致說一下專案的新建,考慮到有的讀者會想LZ一樣對IDEAL的使用不是很熟練。

新建一個spring專案(不會的話網上搜索一下,很簡單的),建好之後,再新建這些資料夾,點選File—>Project Structure --->Modules,設定資料夾的意義。LZ設定的java原始檔是(Source Folders):src\main\java;資原始檔(Resource Folders):src\main\resources;測試檔案(Test Source Folders):src\test\java。

設定好這些以後,就可以正式開始自定義Spring標籤了。

自定義Spring標籤

1.定義一個JavaBean:

package com.joe.mytag;

public class Hero {

        private  String name;
        private  int    age;

        public String getName() {
            return name;
        }

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

        
public int getAge() { return age; } public void setAge(int age) { this.age = age; } }

2.hero.xsd檔案:

<xsd:schema
        xmlns="http://joe.com/schema"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        targetNamespace="http://joe.com/schema">
<!-- 目標名稱空間,xmlns要與它一致 --> <xsd:complexType name="myTagcomplexType"> <xsd:attribute name="name" type="xsd:string"> <xsd:annotation> <xsd:documentation><![CDATA[ The myTag name. ]]></xsd:documentation> </xsd:annotation> </xsd:attribute> <xsd:attribute name="age" type="xsd:int"> <xsd:annotation> <xsd:documentation><![CDATA[ The myTag age. ]]></xsd:documentation> </xsd:annotation> </xsd:attribute> </xsd:complexType> <xsd:element name="myTag" type="myTagcomplexType"> <xsd:annotation> <xsd:documentation><![CDATA[ myTag的文件 ]]></xsd:documentation> </xsd:annotation> </xsd:element> </xsd:schema>

這裡需要注意幾點:

  (1)定義targetNamespace(目標名稱空間),xmlns的值要與其一致;

  (2)<xsd:element>定義的就是將會在xml檔案中使用的元素,例如<hero:myTag>中的myTag;

  (3)<xsd:attribute>定義的就是JavaBean中的屬性,例如<hero:myTag name="age">中的name,就是bean中的屬性name,並且還可以通過type來指定屬性型別,進而起到檢測的作用(比如我們定義的型別是int,如果xml中的值是非int型別的,就會直接報錯)。

3.編寫spring.schemas:

http\://joe.com/schema/hero.xsd=META-INF/hero.xsd

注意:這裡紅色加粗的部分要與hero.xsd檔案中的targetNamespace一致。

4.編寫自定義BeanDefinition解析器

package com.joe.mytag;

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

//自定義BeanDefinition解析器
public class HeroBeanDefinitionParser implements BeanDefinitionParser {

        private final Class<?> beanClass;

        public HeroBeanDefinitionParser(Class<?> beanClass) {
            this.beanClass = beanClass;
        }

        public BeanDefinition parse(Element element, ParserContext parserContext) {
            //建立用於屬性承載的BeanDefinition
            RootBeanDefinition beanDefinition = new RootBeanDefinition();
            //設定bean的對應class
            beanDefinition.setBeanClass(beanClass);
            //設定延遲載入為否
            beanDefinition.setLazyInit(false);
            //獲取對應的屬性值,並設定到BeanDefinition中
            beanDefinition.getPropertyValues().add("name", element.getAttribute("name"));
            beanDefinition.getPropertyValues().add("age", element.getAttribute("age"));
            //獲取BeanDefinition的註冊器
            BeanDefinitionRegistry beanDefinitionRegistry = parserContext.getRegistry();
            //註冊bean到BeanDefinitionRegistry中
            beanDefinitionRegistry.registerBeanDefinition(beanClass.getName(),beanDefinition);
            return beanDefinition;
        }
}

5.編寫名稱空間的處理器

  其作用主要是用來註冊自定義的BeanDefinition解析器。

package com.joe.mytag;

import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

public class HeroNamespaceHandler  extends NamespaceHandlerSupport {
    public void init() {
        //註冊自定義的BeanDefinition解析處理器
        registerBeanDefinitionParser("myTag", new HeroBeanDefinitionParser(Hero.class));
    }
}

說明:通常為每一個xsd:element都要註冊一個BeanDefinitionParser。

6.編寫spring.handlers檔案:

http\://joe.com/schema=com.joe.mytag.HeroNamespaceHandler

作用:主要用於關聯名稱空間處理器和xsd中的targetNamespace。

注意:這裡紅色加粗的部分要與hero.xsd檔案中的targetNamespace一致。

7.編寫hero.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:hero="http://joe.com/schema"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://joe.com/schema http://joe.com/schema/hero.xsd">

    <hero:myTag name="joe" age="25"/>
</beans>

說明:

  (1)xmlns:hero的value是xsd檔案中的targetNamespace;

  (2)xmlns:hero可以寫成xmlns:xxx,此時<hero:myTag/>就要寫成<xxx:myTag/>;

8.測試自定義標籤:

package com.joe.mytag;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("hero.xml");
        Hero hero = (Hero) applicationContext.getBean(Hero.class.getName());
        System.out.println("name: " + hero.getName() + " age: " + hero.getAge());
    }
}

輸出結果:

十二月 17, 2018 10:49:59 上午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
資訊: Refreshing org[email protected]5910e440: startup date [Mon Dec 17 10:49:59 CST 2018]; root of context hierarchy
十二月 17, 2018 10:49:59 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
資訊: Loading XML bean definitions from class path resource [hero.xml]
name: joe age: 25

如何在spring中自定義xml標籤的方法就結束了。在實際中,隨著註解和javaconfg的盛行,xml的方式漸漸的會淡出舞臺,但是spring的啟動流程還是會的。說一下上述程式碼涉及到的流程:

  (1)使用ResourceLoader將配置檔案xml裝載為Resource物件;

  (2)使用BeanDefinitionReader解析配置資訊:將每一個<bean>解析為一個BeanDefinition物件,然後儲存到BeanDefinitionRegistry中;

  (3)實際上是BeanDefinitionReader呼叫BeanDefinitionParser進行了解析操作,解析完成後註冊到BeanDefinitionRegistry(程式碼看上邊的HeroBeanDefinitionParser);

踩過的坑

(1)專案中spring包錯誤,在新建專案時,有的包(不止一個)不會載入完全導致專案出錯。(解決方式:網上下載新包,替換即可)。

(2)沒有加上xsd:字首,導致解析hero.xsd檔案報錯。

至此,簡單的自定義標籤完成。