spring拓展 定義自己的namespace
1.查看源碼認識spring是怎么加載xml配置的
1.1 spring是怎么創建對象的?
查看spring beanFactory的繼承關系
通過查看源碼可以得知,BeanFactory 中的對象創建是實際是根據RootBeanDefinition創建的, 在AbstractAutowireCapableBeanFactory中有具體的實現,包括創建實例,
利用Spring拓展
java的內省實現BeanWrapperImpl,創建對象的包裝類,使用反射給對象填充屬性,并實現依賴注入DI 。具體可以自行參閱源碼。
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory { ..... protected abstract Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException; }
而RootBeanDefination定義的是什么呢?查看AbstractBeanDefination類。
可以看到這里就是Spring對對象屬性的封裝,包括類名,屬性,加載策略等等,其實也就是我們在xml里 配置的對象。
1.2 spring是怎么將xml里配置的對象讀到BeanFactory中的?
在查看spring容器的源碼時,得知spring 是使用 org.springframework.beans.factory.xml.XmlBeanDefinitionReader 進行xml解析的
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { ..... protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { //讀取xml Document doc = doLoadDocument(inputSource, resource); //解析并注冊xml中定義的BeanDefination return registerBeanDefinitions(doc, resource); } catch (BeanDefinitionStoreException ex) { xxx } } ..... }
接下來查看對dom 解析部分的源碼
public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader { @Override public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; logger.debug("Loading bean definitions"); Element root = doc.getDocumentElement(); doRegisterBeanDefinitions(root); } protected void doRegisterBeanDefinitions(Element root) { //為了實現進行遞歸解析 BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent); if (this.delegate.isDefaultNamespace(root)) { //對profile的支持 String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { return; } } } preProcessXml(root); //開始解析dom樹 parseBeanDefinitions(root, this.delegate); postProcessXml(root); this.delegate = parent; } protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { 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)) { //spring的基礎命名元素解析(import、bean、beans、alias)其中在 //beans嵌套時會進行遞歸解析 parseDefaultElement(ele, delegate); } else { //拓展元素解析 delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } } }
查看spring是怎么實現拓展元素的解析的
public class BeanDefinitionParserDelegate { public BeanDefinition parseCustomElement(Element ele) { return parseCustomElement(ele, null); } public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { //獲取當前節點命名空間URI String namespaceUri = getNamespaceURI(ele); //根據命名空間解析到自定義的NamespaceHandler NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler == null) { error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); return null; } //使用拓展的Handler對當前節點進行解析 return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); } }
其中NamespaceHandlerResolver 會去查找當前項目中classpath 下的META-INF目錄下所有文件名為 spring.handlers配置文件,定位到自定義namespace的解析器實現類。
其中在namespace 的處理器中可以通過進行BeanDefination的注冊,注冊過的BeanDefination會用來給BeanFactory創建對象使用,將解析好的BeanDefination注冊到parserContext.getRegistry()中即可。其實DefaultListableBeanFactory 就是一個BeanDefinitionRegistry。
@Override public BeanDefinition parse(Element element, ParserContext parserContext) { .... parserContext.getRegistry().registerBeanDefinition(beanName, mbd); }
2.定義自己的namespace
2.1 定義schema約束xsd文件
將自定義的xsd文件放到項目的 META-INF 目錄下。
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <xsd:schema xmlns="http://xxx.xxx.com/schema/myns" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:tool="http://www.springframework.org/schema/tool" targetNamespace="http://xxx.xxx.com/schema/myns"> <xsd:import namespace="http://www.w3.org/XML/1998/namespace"/> <xsd:import namespace="http://www.springframework.org/schema/beans"/> <xsd:import namespace="http://www.springframework.org/schema/tool"/> <xsd:annotation> <xsd:documentation><![CDATA[ Namespace support for the myns test. ]]></xsd:documentation> </xsd:annotation> <xsd:complexType name="mybeanType"> <xsd:attribute name="id" type="xsd:ID"> <xsd:annotation> <xsd:documentation><![CDATA[ The unique identifier for a bean. ]]></xsd:documentation> </xsd:annotation> </xsd:attribute> <xsd:attribute name="name" type="xsd:string" use="required"> <xsd:annotation> <xsd:documentation><![CDATA[ The mybean name. ]]></xsd:documentation> </xsd:annotation> </xsd:attribute> <xsd:attribute name="class" type="xsd:string" use="required"> <xsd:annotation> <xsd:documentation><![CDATA[ The version. ]]></xsd:documentation> </xsd:annotation> </xsd:attribute> </xsd:complexType> <xsd:element name="mybean" type="mybeanType"> <xsd:annotation> <xsd:documentation><![CDATA[ The mybean config ]]></xsd:documentation> </xsd:annotation> </xsd:element> </xsd:schema>
更多xsd寫法可以參閱xml的相關資料。
2.2創建自定義namespace的NamespaceHandler
使用NamespaceHandlerSupport來實現我們定義的NamespaceHandler。在init時去提供具體的標簽的 解析器。
BeanDefinitionParser
public class MybeanParser implements BeanDefinitionParser { @Override public BeanDefinition parse(Element element, ParserContext parserContext) { RootBeanDefinition mbd = new RootBeanDefinition(); mbd.setBeanClassName(element.getAttribute("class")); String beanName = element.getAttribute("id"); MutablePropertyValues mutablePropertyValues = new MutablePropertyValues(); mutablePropertyValues.add("name", element.getAttribute("name")); mbd.setPropertyValues(mutablePropertyValues); parserContext.getRegistry().registerBeanDefinition(beanName, mbd); return mbd; } }
實現自定義的NamespaceHandler
public class MynsNameSpaceHandler extends NamespaceHandlerSupport{ @Override public void init() { registerBeanDefinitionParser("mybean", new MybeanParser()); } }
這里的mybean是myns namespace下的元素標簽的具體的解析實現
如:
<myns:mybean></myns:mybean>
2.3配置自定義的NamespaceHandler映射
在META-INF下創建文件 spring.handlers
http\://xxx.xxx.com/schema/myns=com.xxx.MynsNameSpaceHandler
2.4使用自定義的Namespace
<?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:myns="http://xxx.xxx.com/schema/myns" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://xxx.xxx.com/schema/myns http://xxx.xxx.com/schema/myns.xsd "> <myns:mybean class="com.xxx.People" id="mybean123" name="testMybean"></myns:mybean> </beans>
2.5測試
public class MybeanNamespaceTestCase { @SuppressWarnings("resource") @Test public void testGetBean(){ ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext(new String[]{"myapp.xml"},false); ac.setValidating(false); ac.refresh(); People bean = ac.getBean("mybean123",People.class); System.out.println(bean.getName()); } }
這里設置ac.setValidating(false); 是因為如果開啟xml約束檢查,需要配置schema的位置,
也是在META-INF 下新建spring.schemas
并加入:
http\://xxx.xxx.com/schema/myns.xsd=META-INF/myns.xsd
這樣spring 在xml解析時會調用org.springframework.beans.factory.xml.PluggableSchemaResolver
進行獲取schema文件進行約束檢查,
這樣配置完畢后就可以ac.setValidating(true);啦,如果文件內容不符合規范,會啟動時拋出異常。
此外,如果想要在使用過程開發工具能夠像使用spring 自身的一些配置時有提升功能,可以將schema文件
上傳到文件服務器上,能夠通過http 訪問到xsi:schemaLocation的地方,或者配置開發工具中的xml 約束映射,將地址映射到本 地磁盤中,這樣就能
spring-namespace實現自定義標簽類
介紹如何通過spring namespace的方式進行bean的配置
最終要達到的目的如下:
<?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:user="http://www.wuxueyou.cn/schema/user" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.wuxueyou.cn/schema/user http://www.wuxueyou.cn/schema/user.xsd"> <user:self-user id="user2" userId="12" name="aaa"/> <bean id="user1" class="com.xueyou.User"> <property name="userId" value="33"/> <property name="name" value="xiaoming"/> </bean> </beans>
好,下面上貨。
1.配置java bean
2.編寫xsd文件
3.編寫BeanDefinationParse標簽解析類
4.編寫調用標簽解析類的NamespaceHandler類
5.編寫spring.handlers和spring.schemas供spring讀取
6.在spring中使用
1.配置java Bean
package com.xueyou; public class User { private int userId; private String name; public User() { } public int getUserId() { return userId; } public void setUserId(int userId) { this.userId = userId; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "userId='" + userId + '\'' + ", name='" + name + '\'' + '}'; } }
2.編寫xsd文件
<?xml version="1.0" encoding="UTF-8"?> <xsd:schema xmlns="http://www.wuxueyou.cn/schema/user" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:beans="http://www.springframework.org/schema/beans" targetNamespace="http://www.wuxueyou.cn/schema/user" elementFormDefault="qualified"> <xsd:import namespace="http://www.springframework.org/schema/beans" schemaLocation="http://www.springframework.org/schema/beans/spring-beans.xsd"/> <xsd:element name="self-user"> <xsd:complexType> <!--這里的最外層的id是spring用的,用來定義bean的名稱用的,不要和類中的id混淆了--> <xsd:attribute name="id" type="xsd:string" use="required"/> <xsd:attribute name="userId" type="xsd:int" use="required"/> <xsd:attribute name="name" type="xsd:string" use="required"/> </xsd:complexType> </xsd:element> </xsd:schema>
3.編寫BeanDefinationParse標簽解析類
package com.xueyou.parser; import com.xueyou.User; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser; import org.w3c.dom.Element; public class UserDefinationParser extends AbstractSimpleBeanDefinitionParser { @Override protected Class<?> getBeanClass(Element element) { return User.class; } @Override protected void doParse(Element element, BeanDefinitionBuilder builder) { int userId = Integer.valueOf(element.getAttribute("userId"), 0); String name = element.getAttribute("name"); builder.addPropertyValue("userId", userId); builder.addPropertyValue("name", name); } }
4.編寫調用標簽解析類的NamespaceHandler類
package com.xueyou.handler; import com.xueyou.parser.UserDefinationParser; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; public class UserNamespaceHandler extends NamespaceHandlerSupport { public void init() { registerBeanDefinitionParser("self-user", new UserDefinationParser()); } }
5.編寫spring.handlers和spring.schemas以供spring讀取
spring.handlers
http\://www.wuxueyou.cn/schema/user=com.xueyou.handler.UserNamespaceHandler
spring.schemas
http\://www.wuxueyou.cn/schema/user.xsd=namespace/user.xsd
6.打包
首先把剛才的打成一個jar包,需要注意在maven的plugin中添加如下內容, 這個shade插件能夠合并指定的內容,比如spring.schema等等
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.2.0</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> <resource>META-INF/spring.handlers</resource> </transformer> <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> <resource>META-INF/spring.schemas</resource> </transformer> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass></mainClass> </transformer> </transformers> </configuration> </execution> </executions> </plugin> </plugins> </build>
7.在其他項目中使用
在一個web項目中使用這個類
<?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:user="http://www.wuxueyou.cn/schema/user" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.wuxueyou.cn/schema/user http://www.wuxueyou.cn/schema/user.xsd"> <user:self-user id="user2" userId="12" name="aaa"/> <bean id="user1" class="com.xueyou.User"> <property name="userId" value="33"/> <property name="name" value="xiaoming"/> </bean> </beans>
在controller中進行測試
package com.example.demo.controller; import com.xueyou.User; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; @RestController @RequestMapping("/namespacetest") public class NamespaceController { @Resource(name = "user2") private User user; @RequestMapping("/user") public User namespacetest() { return user; } }
運行結果:

運行結果
最終,我們可以使用spring-namespace對bean進行配置了。這樣比<bean>標簽要好的多。
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持服務器之家。
原文鏈接:https://blog.csdn.net/m0_38043362/article/details/78329188