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

作者:试水流连 时间:2022-01-12 05:13:33 

spring拓展 定义自己的namespace

1.查看源码认识spring是怎么加载xml配置的

1.1 spring是怎么创建对象的?

查看spring beanFactory的继承关系

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

通过查看源码可以得知,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;
}

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

而RootBeanDefination定义的是什么呢?查看AbstractBeanDefination类。

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

可以看到这里就是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

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中使用

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

目录结构

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

运行结果

最终,我们可以使用spring-namespace对bean进行配置了。这样比<bean>标签要好的多。

来源:https://blog.csdn.net/m0_38043362/article/details/78329188

标签:spring,定义,namespace
0
投稿

猜你喜欢

  • Java 实现判定顺序表中是否包含某个元素(思路详解)

    2023-09-11 01:03:04
  • 浅析Java类和数据结构中常用的方法

    2022-03-03 19:39:53
  • Java中Prime算法的原理与实现详解

    2022-06-11 23:16:29
  • java中Scanner输入用法实例

    2023-09-03 18:25:36
  • MyBatis查询结果resultType返回值类型的说明

    2021-10-25 21:37:39
  • 基于C语言实现井字棋游戏

    2023-06-28 21:23:18
  • springboot整合token的实现代码

    2023-11-10 19:02:03
  • c#线程Thread示例

    2022-10-21 00:38:56
  • 微服务通过Feign调用进行密码安全认证操作

    2023-07-30 02:43:38
  • 浅析Spring Boot单体应用熔断技术的使用

    2022-05-10 02:37:08
  • 解决SpringMVC使用@RequestBody注解报400错误的问题

    2022-02-26 16:06:43
  • Java仿12306图片验证码

    2022-09-29 05:36:08
  • 详解SpringBoot中如何使用布隆过滤器

    2023-11-10 17:43:06
  • C#计算字符串哈希值(MD5、SHA)的方法小结

    2022-07-02 15:47:55
  • Java 字符串转float运算 float转字符串的方法

    2022-04-09 10:09:06
  • 深入了解Java中String、Char和Int之间的相互转换

    2022-09-14 10:18:54
  • SSM项目使用拦截器实现登录验证功能

    2023-06-17 16:12:38
  • Android布局之帧布局FrameLayout详解

    2023-08-07 04:45:29
  • 老生常谈比较排序之归并排序(递归)

    2022-05-15 11:43:15
  • java二叉树面试题详解

    2021-06-13 08:40:23
  • asp之家 软件编程 m.aspxhome.com