spring.schemas、spring.handlers的使用

2024-03-18 18:32

本文主要是介绍spring.schemas、spring.handlers的使用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

报错信息:Configuration problem: Unable to locate Spring NamespaceHandler for XML schema namespace [http://www.springframework.org/schema/p],

一桩事故引发的连锁思考。。。开幕——

-------------------------------------------------------------------------------------------------------------------------------------

spring加载XML时,从系统中加载配置信息到把<bean>配置信息解析成BeanDefinition(把xml中<bean>的属性转化成的配置类),然后把BeanDefinition放到注册表中,然后取出,装饰。这个过程就是BeanDefinition的解析过程。那么具体是怎么实现的呢?我想先从一个让人揪心的报错信息开始:

为了方便阅读spring的源码,我把spring的java源码引进我的工程当中(那么之前引入的等价的jar包就要删除),引入后如下图:

一般的XML加载的小测试主要用到的是beans包。和以前一样,我做一个小测试,下面是简单的spring启动三件套(配置文件,bean类,启动类):

配置文件:

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:p="http://www.springframework.org/schema/p"5     xsi:schemaLocation="http://www.springframework.org/schema/beans 6        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">7    <bean id="car" class="com.mesopotamia.test1.Car" 8             p:name="汽车"9          p:brand="宝马"
10          p:maxSpeed="200"/>
11          
12    <bean id="car1" 
13            p:brand="宝马X5"
14         parent="car"
15         />
16 </beans>

bean类:

1 package com.mesopotamia.test1;2 3 import org.apache.commons.logging.Log;4 import org.apache.commons.logging.LogFactory;5 6 public class Car {7     private String name;8     private String brand;9     private double maxSpeed;
10     public double getMaxSpeed() {
11         return maxSpeed;
12     }
13     public void setMaxSpeed(double maxSpeed) {
14         this.maxSpeed = maxSpeed;
15     }
16 
17 
18     private Log log=LogFactory.getLog(Car.class);
19     
20     public Car(){
21         //name="宝马";
22         log.info("调用了Car的构造函数,实例化了Car..");
23     }
24     public String getName() {
25         return name;
26     }
27     public void setName(String name) {
28         this.name = name;
29     }
30     public String getBrand() {
31         return brand;
32     }
33     public void setBrand(String brand) {
34         this.brand = brand;
35     }
36     
37     
38     public String toString(){
39         return "名字"+name+" 型号"+brand+" 速度:"+maxSpeed;
40     }
41     
42 
43 }

启动:

1 public static void main(String args[]){
2         ApplicationContext ctx = new ClassPathXmlApplicationContext("com/mesopotamia/test1/*.xml");
3         Car car1 = ctx.getBean("car1",Car.class);
4         log.info(car1.toString());
5     }

路径是正确的,自己的java代码也没有报错,spring源码也没有报错,看来前景很美好。然而,现实总是很骨感,看一下运行结果:

1 2015-11-19 21:21:43,901  INFO [main] (AbstractApplicationContext.java:456) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@45a877: startup date [Thu Nov 19 21:21:43 CST 2015]; root of context hierarchy2 2015-11-19 21:21:44,088  INFO [main] (XmlBeanDefinitionReader.java:315) - Loading XML bean definitions from file [C:\MySoftware\workspace\springtest2\resources\WEB-INF\classes\com\mesopotamia\test1\beans.xml]3 Exception in thread "main" org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Unable to locate Spring NamespaceHandler for XML schema namespace [http://www.springframework.org/schema/p]4 Offending resource: file [C:\MySoftware\workspace\springtest2\resources\WEB-INF\classes\com\mesopotamia\test1\beans.xml]5 6     at org.springframework.beans.factory.parsing.FailFastProblemReporter.error(FailFastProblemReporter.java:68)7     at org.springframework.beans.factory.parsing.ReaderContext.error(ReaderContext.java:85)8     at org.springframework.beans.factory.parsing.ReaderContext.error(ReaderContext.java:80)9     at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.error(BeanDefinitionParserDelegate.java:277)
10     at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.decorateIfRequired(BeanDefinitionParserDelegate.java:1375)
11     at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.decorateBeanDefinitionIfRequired(BeanDefinitionParserDelegate.java:1351)
12     at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.decorateBeanDefinitionIfRequired(BeanDefinitionParserDelegate.java:1339)
13     at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.processBeanDefinition(DefaultBeanDefinitionDocumentReader.java:260)
14     at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseDefaultElement(DefaultBeanDefinitionDocumentReader.java:153)
15     at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:132)
16     at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:93)
17     at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.registerBeanDefinitions(XmlBeanDefinitionReader.java:493)
18     at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:390)
19     at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:334)
20     at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:302)
21     at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:143)
22     at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:178)
23     at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:149)
24     at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:212)
25     at org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:126)
26     at org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:92)
27     at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:130)
28     at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:467)
29     at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:397)
30     at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
31     at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
32     at com.mesopotamia.test1.Main.main(Main.java:13)

暂且先不要焦虑为什么会报错,从下往上看报错信息你会发现,原来跟踪错误提示可以看到整个BeanDefinition的加载过程。从三十行开始向上看:

refresh:开始加载XML
  obtainFreshBeanFactory:告诉下面的类,要刷新bean factory了。
  refreshBeanFactory:正式开始关闭当前的bean factory,初始化一个崭新的bean factory,开始容器的新生命周期。
  loadBeanDefinitions:通过一个XmlBeanDefinitionReader来从XML中加载bean definitions.
  doLoadBeanDefinitions:正式开始从XML中加载bean definitions(开始干实事儿啦)
  registerBeanDefinitions:注册BeanDefinitions。
  parseBeanDefinitions:从根级(at the root level)开始解析XML数据("import", "alias", "bean".等)。
  parseDefaultElement:解析默认的元素。
  processBeanDefinition:加工BeanDefinition,加工后放到注册表中。再后面就是取出BeanDefinition进行装饰。在装饰的过程中报错了。之所以讲清楚每一步,就是让大家有个对过程的认知。我们来打开调试模式跟踪一下报错(我用的是spring的java源码,当然可以跟踪啦):下面是跟踪到日志的第10行进入的方法源码(decorateIfRequired):
1 private BeanDefinitionHolder decorateIfRequired(2             Node node, BeanDefinitionHolder originalDef, BeanDefinition containingBd) {3 4         String namespaceUri = getNamespaceURI(node);5         if (!isDefaultNamespace(namespaceUri)) {6             NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);7             if (handler != null) {8                 return handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));9             }
10             else if (namespaceUri != null && namespaceUri.startsWith("http://www.springframework.org/")) {
11                 error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node);
12             }
13             else {
14                 // A custom namespace, not to be handled by Spring - maybe "xml:...".
15                 if (logger.isDebugEnabled()) {
16                     logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]");
17                 }
18             }
19         }
20         return originalDef;
21     }

下面是调试的截图:

一开始解析几个默认命名空间的时候,总是进入不到"if"内部,循环了三四次,namespaceUri终于不是默认的命名空间了,于是程序开始进入"if"内部,这时我们发现这个命名空间是p规则空间。

继续往下跟踪:

我们发现,handler是null,这个问题导致error()方法的调用,于是第3行的报错日志就被打印出来了。

那么这个handler是何方神圣,为何此刻不灵了?

我们查看API看一下这个NameSpaceHandler的庐山真面目:

看清楚了没,这个handler是加工BeanDefinition的,而且文中说"它的实现者旨在返回一些实现了BeanDefinitionParser"的具体的类。那么也就是说,没找到这个NameSpaceHandler的实现者?为什么没找到?

真相原来是这样的:

XML命名空间(比如p规则等等,或者自定义的规则)都有以下步骤:

1.编写XSD文件。

2.编写NamespaceHandler使用XSD解析XML。

3.编写spring.handlers和spring.schemas串联起所有部件。

具体讲一下:XSD之前发帖讲过,是规则文件(命名空间),若有疑问请翻阅前帖查看。

那么一般情况下,如果不是系统默认命名空间,应该要写一个继承了NamespaceHandler的类来使用相应的XSD规则文件解析bean xml。

既然p规则是spring自己加的,那么特定的NamespaceHandler肯定是写了,不会为空。

而spring.handlers是NamespaceHandler和XSD文件的连接器(NamespaceHandler通过spring.handlers配置文件找到XSD文件)。

所以是spring.handlers出了问题。

一般情况下,spring.handlers和spring.schemas是写在环境路径的META-INF里面的。我们来看一下org.springframework.beans-3.0.5.RELEASE.jar包:

果不其然,用压缩工具打开该jar包,金屋藏娇,里面竟然藏了个META-INF文件夹。来看里面都有什么:

这就是上面提到的那两个文件。

下面是spring.handlers里面的内容:

1 http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler
2 http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler

以第一行为例,它表示:规则名为http\://www.springframework.org/schema/p的xsd对应对的解析类是org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler

下面是spring.schemas:

1 http\://www.springframework.org/schema/beans/spring-beans-2.0.xsd=org/springframework/beans/factory/xml/spring-beans-2.0.xsd2 http\://www.springframework.org/schema/beans/spring-beans-2.5.xsd=org/springframework/beans/factory/xml/spring-beans-2.5.xsd3 http\://www.springframework.org/schema/beans/spring-beans-3.0.xsd=org/springframework/beans/factory/xml/spring-beans-3.0.xsd4 http\://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans-3.0.xsd5 http\://www.springframework.org/schema/tool/spring-tool-2.0.xsd=org/springframework/beans/factory/xml/spring-tool-2.0.xsd6 http\://www.springframework.org/schema/tool/spring-tool-2.5.xsd=org/springframework/beans/factory/xml/spring-tool-2.5.xsd7 http\://www.springframework.org/schema/tool/spring-tool-3.0.xsd=org/springframework/beans/factory/xml/spring-tool-3.0.xsd8 http\://www.springframework.org/schema/tool/spring-tool.xsd=org/springframework/beans/factory/xml/spring-tool-3.0.xsd9 http\://www.springframework.org/schema/util/spring-util-2.0.xsd=org/springframework/beans/factory/xml/spring-util-2.0.xsd
10 http\://www.springframework.org/schema/util/spring-util-2.5.xsd=org/springframework/beans/factory/xml/spring-util-2.5.xsd
11 http\://www.springframework.org/schema/util/spring-util-3.0.xsd=org/springframework/beans/factory/xml/spring-util-3.0.xsd
12 http\://www.springframework.org/schema/util/spring-util.xsd=org/springframework/beans/factory/xml/spring-util-3.0.xsd

以第三行为例,它表示载入xsd文件的地址。

用一开始的栗子来说:

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:p="http://www.springframework.org/schema/p"5     xsi:schemaLocation="http://www.springframework.org/schema/beans 6        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">7    <bean id="car" class="com.mesopotamia.test1.Car" 8             p:name="汽车"9          p:brand="宝马"
10          p:maxSpeed="200"/>
11          
12    <bean id="car1" 
13            p:brand="宝马X5"
14         parent="car"
15         />
16 </beans>

第4行表示该beans使用p规则,结合spring.handlers找到p规则对应的解析类是SimplePropertyNamespaceHandler。

第6行表示p规则的地址,结合spring.schemas的第三行找到p规则对应的xsd文件的具体位置。

所以,这两个文件缺一不可。

回到一开始,我说我的spring是直接引入的源码,所以忘记加那两个文件了(jar包中是藏着的,而我用的是java文件夹,没有META-INF),因此系统找不到handler类而报错。

这时,我应该在 编译路径 下把META-INF放进去。(可能你自己的项目resources下面本身就有个META-INF,但是这个不管用,必须在编译路径下另外存放一个)。然后程序就能够完美运行了。

最后我们欣赏一下系统找到SimplePropertyNamespaceHandler后执行的解析方法:

1 public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) {2         log.info("调用了decorate方法之前的BeanDefinitionHolder:"+definition);3         if (node instanceof Attr) {4             Attr attr = (Attr) node;5             String propertyName = parserContext.getDelegate().getLocalName(attr);6             String propertyValue = attr.getValue();7             MutablePropertyValues pvs = definition.getBeanDefinition().getPropertyValues();8             if (pvs.contains(propertyName)) {9                 parserContext.getReaderContext().error("Property '" + propertyName + "' is already defined using " +
10                         "both <property> and inline syntax. Only one approach may be used per property.", attr);
11             }
12             if (propertyName.endsWith(REF_SUFFIX)) {
13                 propertyName = propertyName.substring(0, propertyName.length() - REF_SUFFIX.length());
14                 pvs.add(Conventions.attributeNameToPropertyName(propertyName), new RuntimeBeanReference(propertyValue));
15             }
16             else {
17                 pvs.add(Conventions.attributeNameToPropertyName(propertyName), propertyValue);
18             }
19         }
20         //added by mesopotamia on 2015.11.19
21         log.info("调用了decorate方法后的BeanDefinitionHolder:"+definition);
22         return definition;
23     }

这篇关于spring.schemas、spring.handlers的使用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/823250

相关文章

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Java判断多个时间段是否重合的方法小结

《Java判断多个时间段是否重合的方法小结》这篇文章主要为大家详细介绍了Java中判断多个时间段是否重合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录判断多个时间段是否有间隔判断时间段集合是否与某时间段重合判断多个时间段是否有间隔实体类内容public class D

Python使用国内镜像加速pip安装的方法讲解

《Python使用国内镜像加速pip安装的方法讲解》在Python开发中,pip是一个非常重要的工具,用于安装和管理Python的第三方库,然而,在国内使用pip安装依赖时,往往会因为网络问题而导致速... 目录一、pip 工具简介1. 什么是 pip?2. 什么是 -i 参数?二、国内镜像源的选择三、如何

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

Linux使用nload监控网络流量的方法

《Linux使用nload监控网络流量的方法》Linux中的nload命令是一个用于实时监控网络流量的工具,它提供了传入和传出流量的可视化表示,帮助用户一目了然地了解网络活动,本文给大家介绍了Linu... 目录简介安装示例用法基础用法指定网络接口限制显示特定流量类型指定刷新率设置流量速率的显示单位监控多个

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

Java中ArrayList和LinkedList有什么区别举例详解

《Java中ArrayList和LinkedList有什么区别举例详解》:本文主要介绍Java中ArrayList和LinkedList区别的相关资料,包括数据结构特性、核心操作性能、内存与GC影... 目录一、底层数据结构二、核心操作性能对比三、内存与 GC 影响四、扩容机制五、线程安全与并发方案六、工程