spring拓展之如何定義自己的namespace
spring拓展 定義自己的namespace
1.查看源碼認(rèn)識(shí)spring是怎么加載xml配置的
1.1 spring是怎么創(chuàng)建對(duì)象的?
查看spring beanFactory的繼承關(guān)系

通過查看源碼可以得知,BeanFactory 中的對(duì)象創(chuàng)建是實(shí)際是根據(jù)RootBeanDefinition創(chuàng)建的, 在AbstractAutowireCapableBeanFactory中有具體的實(shí)現(xiàn),包括創(chuàng)建實(shí)例,
利用Spring拓展
java的內(nèi)省實(shí)現(xiàn)BeanWrapperImpl,創(chuàng)建對(duì)象的包裝類,使用反射給對(duì)象填充屬性,并實(shí)現(xiàn)依賴注入DI 。具體可以自行參閱源碼。
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
.....
protected abstract Object createBean(String beanName, RootBeanDefinition mbd, Object[] args)
throws BeanCreationException;
}

而RootBeanDefination定義的是什么呢?查看AbstractBeanDefination類。

可以看到這里就是Spring對(duì)對(duì)象屬性的封裝,包括類名,屬性,加載策略等等,其實(shí)也就是我們?cè)趚ml里 配置的對(duì)象。
1.2 spring是怎么將xml里配置的對(duì)象讀到BeanFactory中的?
在查看spring容器的源碼時(shí),得知spring 是使用 org.springframework.beans.factory.xml.XmlBeanDefinitionReader 進(jìn)行xml解析的
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
.....
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//讀取xml
Document doc = doLoadDocument(inputSource, resource);
//解析并注冊(cè)xml中定義的BeanDefination
return registerBeanDefinitions(doc, resource);
}
catch (BeanDefinitionStoreException ex) {
xxx
}
}
.....
}
接下來查看對(duì)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) {
//為了實(shí)現(xiàn)進(jìn)行遞歸解析
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
//對(duì)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的基礎(chǔ)命名元素解析(import、bean、beans、alias)其中在
//beans嵌套時(shí)會(huì)進(jìn)行遞歸解析
parseDefaultElement(ele, delegate);
}
else {
//拓展元素解析
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
}
查看spring是怎么實(shí)現(xiàn)拓展元素的解析的
public class BeanDefinitionParserDelegate {
public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
//獲取當(dāng)前節(jié)點(diǎn)命名空間URI
String namespaceUri = getNamespaceURI(ele);
//根據(jù)命名空間解析到自定義的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對(duì)當(dāng)前節(jié)點(diǎn)進(jìn)行解析
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
}
其中NamespaceHandlerResolver 會(huì)去查找當(dāng)前項(xiàng)目中classpath 下的META-INF目錄下所有文件名為 spring.handlers配置文件,定位到自定義namespace的解析器實(shí)現(xiàn)類。
其中在namespace 的處理器中可以通過進(jìn)行BeanDefination的注冊(cè),注冊(cè)過的BeanDefination會(huì)用來給BeanFactory創(chuàng)建對(duì)象使用,將解析好的BeanDefination注冊(cè)到parserContext.getRegistry()中即可。其實(shí)DefaultListableBeanFactory 就是一個(gè)BeanDefinitionRegistry。
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
....
parserContext.getRegistry().registerBeanDefinition(beanName, mbd);
}
2.定義自己的namespace
2.1 定義schema約束xsd文件
將自定義的xsd文件放到項(xiàng)目的 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的相關(guān)資料。
2.2創(chuàng)建自定義namespace的NamespaceHandler
使用NamespaceHandlerSupport來實(shí)現(xiàn)我們定義的NamespaceHandler。在init時(shí)去提供具體的標(biāo)簽的 解析器。
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;
}
}
實(shí)現(xiàn)自定義的NamespaceHandler
public class MynsNameSpaceHandler extends NamespaceHandlerSupport{
@Override
public void init() {
registerBeanDefinitionParser("mybean", new MybeanParser());
}
}
這里的mybean是myns namespace下的元素標(biāo)簽的具體的解析實(shí)現(xiàn)
如:
<myns:mybean></myns:mybean>
2.3配置自定義的NamespaceHandler映射
在META-INF下創(chuàng)建文件 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());
}
}
這里設(shè)置ac.setValidating(false); 是因?yàn)槿绻_啟xml約束檢查,需要配置schema的位置,
也是在META-INF 下新建spring.schemas
并加入:
http\://xxx.xxx.com/schema/myns.xsd=META-INF/myns.xsd
這樣spring 在xml解析時(shí)會(huì)調(diào)用org.springframework.beans.factory.xml.PluggableSchemaResolver
進(jìn)行獲取schema文件進(jìn)行約束檢查,
這樣配置完畢后就可以ac.setValidating(true);啦,如果文件內(nèi)容不符合規(guī)范,會(huì)啟動(dòng)時(shí)拋出異常。
此外,如果想要在使用過程開發(fā)工具能夠像使用spring 自身的一些配置時(shí)有提升功能,可以將schema文件
上傳到文件服務(wù)器上,能夠通過http 訪問到xsi:schemaLocation的地方,或者配置開發(fā)工具中的xml 約束映射,將地址映射到本 地磁盤中,這樣就能


spring-namespace實(shí)現(xiàn)自定義標(biāo)簽類
介紹如何通過spring namespace的方式進(jìn)行bean的配置
最終要達(dá)到的目的如下:
<?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.編寫B(tài)eanDefinationParse標(biāo)簽解析類
4.編寫調(diào)用標(biāo)簽解析類的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.編寫B(tài)eanDefinationParse標(biāo)簽解析類
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.編寫調(diào)用標(biāo)簽解析類的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.打包
首先把剛才的打成一個(gè)jar包,需要注意在maven的plugin中添加如下內(nèi)容, 這個(gè)shade插件能夠合并指定的內(nèi)容,比如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.在其他項(xiàng)目中使用
在一個(gè)web項(xiàng)目中使用這個(gè)類
<?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中進(jìn)行測試
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;
}
}
運(yùn)行結(jié)果:
運(yùn)行結(jié)果
最終,我們可以使用spring-namespace對(duì)bean進(jìn)行配置了。這樣比<bean>標(biāo)簽要好的多。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java基礎(chǔ)之Comparable與Comparator概述
這篇文章主要介紹了Java基礎(chǔ)之Comparable與Comparator詳解,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java基礎(chǔ)的小伙伴們有非常好的幫助,需要的朋友可以參考下2021-04-04
Spring Cloud OAuth2 實(shí)現(xiàn)用戶認(rèn)證及單點(diǎn)登錄的示例代碼
這篇文章主要介紹了Spring Cloud OAuth2 實(shí)現(xiàn)用戶認(rèn)證及單點(diǎn)登錄的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10
Java生成Echarts表圖的2種實(shí)現(xiàn)方案
這篇文章主要給大家介紹了關(guān)于Java生成Echarts表圖的2種實(shí)現(xiàn)方案,ECharts是一款功能非常強(qiáng)大的JavaScript圖表庫,文中通過代碼實(shí)例介紹的非常詳細(xì),需要的朋友可以參考下2023-09-09
jetbrain?fleet對(duì)標(biāo)vscode實(shí)際操作
Gradle是一個(gè)基于Apache Ant和Apache Maven概念項(xiàng)目自動(dòng)化構(gòu)建開源工具,jetbrain家的fleet(已獲得預(yù)覽權(quán)限)直接對(duì)標(biāo)vscode?,?fleet有望超過vscode嗎?今天我們實(shí)際操作下2021-12-12
Java純代碼實(shí)現(xiàn)導(dǎo)出pdf
在項(xiàng)目開發(fā)中,產(chǎn)品的需求越來越奇葩啦,開始文件下載都是下載為excel的,做著做著需求竟然變了,要求能導(dǎo)出pdf,所以本文就來用Java實(shí)現(xiàn)導(dǎo)出pdf功能吧2023-12-12
SpringBoot整合定時(shí)任務(wù)之實(shí)現(xiàn)Scheduled注解的過程(一個(gè)注解全解決)
這篇文章主要介紹了SpringBoot整合定時(shí)任務(wù)之實(shí)現(xiàn)Scheduled注解的過程(一個(gè)注解全解決),本文通過使用場景分析給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-09-09
分析java并發(fā)中的wait notify notifyAll
一個(gè)線程修改一個(gè)對(duì)象的值,而另一個(gè)線程則感知到了變化,然后進(jìn)行相應(yīng)的操作,這就是wait()、notify()和notifyAll()方法的本質(zhì)。本文將詳細(xì)來介紹它們概念實(shí)現(xiàn)以及區(qū)別2021-06-06

