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檔案報錯。
至此,簡單的自定義標籤完成。