spring拓展之如何定义自己的namespace

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>标签要好的多。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。

以上是 spring拓展之如何定义自己的namespace 的全部内容, 来源链接: utcz.com/p/249354.html

回到顶部