文章內容參考了《Spring源碼深度解析》一書。自己照着書中內容做了一遍,不懂的地方以及采坑的地方會在文中記錄。
推薦一篇post,關於Spring配置文件的命名空間:
https://www.cnblogs.com/gonjan-blog/p/6637106.html
我們暫時只是知道使用Spring的常規標簽,加個bean,事務,Aop等等。隨着滿足業務的需求,同時降低程序員的工作量,我們有時需要自己定制一些標簽。話補多少,下面進入主題。
自定義標簽的使用
擴展Spring自定義標簽大致需要如下幾步:(把大象裝冰箱,需要三步,開門,放,關門。。。)
1.創建需要擴展的組件
2.定義XSD文件描述組件內容
3.創建一個文件,實現BeanDefinitionParser接口,用來解析XSD文件中的定義和組件定義
4.創建Handler文件,擴展字NamespaceHandlerSupport,目的是將組件注冊到Spring容器
5.編寫Spring.handlers和Spring.schemas文件
上述,五點看完了,我最開始時一頭霧水,這是什么啊?別着急,跟哥往下走。下面有好東西,保證不打暈你。
例子:
1.創建POJO,接收配置文件
1 package test.customtag; 2 3 public class User { 4 private String userName; 5 public String getUserName() { 6 return userName; 7 } 8 public void setUserName(String userName) { 9 this.userName = userName; 10 } 11 private String email; 12 public String getEmail() { 13 return email; 14 } 15 public void setEmail(String email) { 16 this.email = email; 17 } 18 }
2.定義一個XSD文件描述組件內容
src/main/resources/META-INF/user.xsd
1 <?xml version="1.0" encoding="UTF-8"?> 2 <schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/schema/user" xmlns:tns="http://www.example.org/schema/user" elementFormDefault="qualified"> 3 <element name="user2"> 4 <complexType> 5 <attribute name ="id" type = "string"/> 6 <attribute name ="userName" type = "string"/> 7 <attribute name ="email" type = "string"/> 8 </complexType> 9 </element> 10 </schema>
描述了一個targetNamespace,並且創建了一個element user2。里面有三個attribute。主要是為了驗證Spring配置文件中的自定義格式。再進一步解釋,就是,Spring位置文件中使用的user2自定義標簽中,屬性只能是上面的三種,有其他的屬性的話,就會報錯。
網上有人說element ,complexType等等這些標簽要用XSD開頭的標簽,但是我這里沒有用也能成功的執行,我猜測可能是spring的版本問題。(我這個猜測沒有依據)
大家對這個XSD可能會有疑問,不要擔心。推薦一篇post,里面講解的很好,地址放在文章開頭那里。
3.創建文件,實現BeanDefinitionParser接口,用來解析XSD文件中的定義和組件定義
1 package test.customtag; 2 3 4 import org.springframework.beans.factory.support.BeanDefinitionBuilder; 5 import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; 6 import org.springframework.util.StringUtils; 7 import org.w3c.dom.Element; 8 9 public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { 10 11 @SuppressWarnings("rawtypes") 12 protected Class getBeanClass(Element element) { 13 return User.class; 14 } 15 16 protected void doParse(Element element, BeanDefinitionBuilder bean) { 17 String userName = element.getAttribute("userName"); 18 String email = element.getAttribute("email"); 19 if (StringUtils.hasText(userName)) { 20 bean.addPropertyValue("userName", userName); 21 } 22 if (StringUtils.hasText(email)){ 23 bean.addPropertyValue("email", email); 24 } 25 26 } 27 }
上文,雖說是繼承了AbstractSingleBeanDefinitionParser ,但根本上來說,是實現了BeanDefinitionParser接口。
4.創建Handler文件,擴展自NamespaceHandlerSupport,目的是將組件注冊到Spring容器中。
1 package test.customtag; 2 3 import org.springframework.beans.factory.xml.NamespaceHandlerSupport; 4 5 public class MyNamespaceHandler extends NamespaceHandlerSupport { 6 7 public void init() { 8 9 registerBeanDefinitionParser("user2", new UserBeanDefinitionParser()); 10 } 11 12 }
5.編寫Spring.handlers和Spring.schemas文件
路徑:src/main/resources/META-INF/Spring.handlers
1 http\://www.example.org/schema/user=test.customtag.MyNamespaceHandler
路徑:src/main/resources/META-INF/Spring.schemas
1 http\://www.example.org/schema/user.xsd=META-INF/user.xsd
\ 是轉義字符的概念。
6.創建測試配置文件
路徑:src/main/resources/test.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:myname2="http://www.example.org/schema/user" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 6 http://www.example.org/schema/user http://www.example.org/schema/user.xsd"> 7 8 <myname2:user2 id = "testbean" userName = "lee" email = "bbb"/> 9 </beans>
在第4行這里引入了myname2對應的命名空間,它會到第六行(http://www.example.org/schema/user.xsd)找到對應XSD文件進行check。第六行的user.xsd文件的位置,到步驟5里的Spring.schemas里去參照。
在這里說明一下自己遇到的疑問:第八行,自己eclipse報了紅叉,信息如下:
Multiple annotations found at this line:
- cvc-complex-type.2.4.c: The matching wildcard is strict, but no declaration can be found for element 'myname2:user2'.
- schema_reference.4: Failed to read schema document 'http://www.example.org/schema/user.xsd', because 1) could not find the
document; 2) the document could not be read; 3) the root element of the document is not <xsd:schema>.
但是,我沒有解決它,直接執行第6步的代碼,最后成功執行了,搞不懂原因。
6.測試。
1 package test.customtag; 2 3 import org.springframework.context.ApplicationContext; 4 import org.springframework.context.support.ClassPathXmlApplicationContext; 5 6 public class Tst { 7 public static void main(String[] args) { 8 ApplicationContext beans=new ClassPathXmlApplicationContext("classpath:test.xml"); 9 User user=(User)beans.getBean("testbean"); 10 System.out.println("username:"+user.getUserName()+" "+"email:"+user.getEmail()); 11 } 12 }
文章記錄的不是特別的詳細,有一些知識點沒有全面的記錄。但是,跟着一步步做,Spring自定義標簽的實現,應該是沒有問題了。稍后,有時間,會記錄Sping源碼中,關於自定義標簽解析的部分。與君共勉。