报错信息: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类,启动类):

配置文件:

 <?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="car" class="com.mesopotamia.test1.Car"
p:name="汽车"
p:brand="宝马"
p:maxSpeed="200"/> <bean id="car1"
p:brand="宝马X5"
parent="car"
/>
</beans>

bean类:

 package com.mesopotamia.test1;

 import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; public class Car {
private String name;
private String brand;
private double maxSpeed;
public double getMaxSpeed() {
return maxSpeed;
}
public void setMaxSpeed(double maxSpeed) {
this.maxSpeed = maxSpeed;
} private Log log=LogFactory.getLog(Car.class); public Car(){
//name="宝马";
log.info("调用了Car的构造函数,实例化了Car..");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
} public String toString(){
return "名字"+name+" 型号"+brand+" 速度:"+maxSpeed;
} }

启动:

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

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

 2016-12-07 21:21:43,901  INFO [main] (AbstractApplicationContext.java:456) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@45a877: startup date [Thu Dec 07 21:21:43 CST 2016]; root of context hierarchy
2016-12-07 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]
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]
Offending resource: file [C:\MySoftware\workspace\springtest2\resources\WEB-INF\classes\com\mesopotamia\test1\beans.xml] at org.springframework.beans.factory.parsing.FailFastProblemReporter.error(FailFastProblemReporter.java:68)
at org.springframework.beans.factory.parsing.ReaderContext.error(ReaderContext.java:85)
at org.springframework.beans.factory.parsing.ReaderContext.error(ReaderContext.java:80)
at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.error(BeanDefinitionParserDelegate.java:277)
at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.decorateIfRequired(BeanDefinitionParserDelegate.java:1375)
at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.decorateBeanDefinitionIfRequired(BeanDefinitionParserDelegate.java:1351)
at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.decorateBeanDefinitionIfRequired(BeanDefinitionParserDelegate.java:1339)
at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.processBeanDefinition(DefaultBeanDefinitionDocumentReader.java:260)
at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseDefaultElement(DefaultBeanDefinitionDocumentReader.java:153)
at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:132)
at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:93)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.registerBeanDefinitions(XmlBeanDefinitionReader.java:493)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:390)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:334)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:302)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:143)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:178)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:149)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:212)
at org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:126)
at org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:92)
at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:130)
at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:467)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:397)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
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):
 private BeanDefinitionHolder decorateIfRequired(
Node node, BeanDefinitionHolder originalDef, BeanDefinition containingBd) { String namespaceUri = getNamespaceURI(node);
if (!isDefaultNamespace(namespaceUri)) {
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler != null) {
return handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
}
else if (namespaceUri != null && namespaceUri.startsWith("http://www.springframework.org/")) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node);
}
else {
// A custom namespace, not to be handled by Spring - maybe "xml:...".
if (logger.isDebugEnabled()) {
logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]");
}
}
}
return originalDef;
}

下面是调试的截图:

一开始解析几个默认命名空间的时候,总是进入不到"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里面的内容:

 http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler
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:

 http\://www.springframework.org/schema/beans/spring-beans-2.0.xsd=org/springframework/beans/factory/xml/spring-beans-2.0.xsd
http\://www.springframework.org/schema/beans/spring-beans-2.5.xsd=org/springframework/beans/factory/xml/spring-beans-2.5.xsd
3 http\://www.springframework.org/schema/beans/spring-beans-3.0.xsd=org/springframework/beans/factory/xml/spring-beans-3.0.xsd
http\://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans-3.0.xsd
http\://www.springframework.org/schema/tool/spring-tool-2.0.xsd=org/springframework/beans/factory/xml/spring-tool-2.0.xsd
http\://www.springframework.org/schema/tool/spring-tool-2.5.xsd=org/springframework/beans/factory/xml/spring-tool-2.5.xsd
http\://www.springframework.org/schema/tool/spring-tool-3.0.xsd=org/springframework/beans/factory/xml/spring-tool-3.0.xsd
http\://www.springframework.org/schema/tool/spring-tool.xsd=org/springframework/beans/factory/xml/spring-tool-3.0.xsd
http\://www.springframework.org/schema/util/spring-util-2.0.xsd=org/springframework/beans/factory/xml/spring-util-2.0.xsd
http\://www.springframework.org/schema/util/spring-util-2.5.xsd=org/springframework/beans/factory/xml/spring-util-2.5.xsd
http\://www.springframework.org/schema/util/spring-util-3.0.xsd=org/springframework/beans/factory/xml/spring-util-3.0.xsd
http\://www.springframework.org/schema/util/spring-util.xsd=org/springframework/beans/factory/xml/spring-util-3.0.xsd

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

用一开始的栗子来说:

 <?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="car" class="com.mesopotamia.test1.Car"
p:name="汽车"
p:brand="宝马"
p:maxSpeed=""/> <bean id="car1"
p:brand="宝马X5"
parent="car"
/>
</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后执行的解析方法:

 public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
log.info("调用了decorate方法之前的BeanDefinitionHolder:"+definition);
if (node instanceof Attr) {
Attr attr = (Attr) node;
String propertyName = parserContext.getDelegate().getLocalName(attr);
String propertyValue = attr.getValue();
MutablePropertyValues pvs = definition.getBeanDefinition().getPropertyValues();
if (pvs.contains(propertyName)) {
parserContext.getReaderContext().error("Property '" + propertyName + "' is already defined using " +
"both <property> and inline syntax. Only one approach may be used per property.", attr);
}
if (propertyName.endsWith(REF_SUFFIX)) {
propertyName = propertyName.substring(0, propertyName.length() - REF_SUFFIX.length());
pvs.add(Conventions.attributeNameToPropertyName(propertyName), new RuntimeBeanReference(propertyValue));
}
else {
pvs.add(Conventions.attributeNameToPropertyName(propertyName), propertyValue);
}
}
//added by mesopotamia on 2015.11.19
log.info("调用了decorate方法后的BeanDefinitionHolder:"+definition);
return definition;
}

由于时间与深度关系,暂时不作研究。

希望从这个问题中,我们不仅仅是知道了这个问题的答案。

(spring-第7回【IoC基础篇】)BeanDefinition的载入与解析&&spring.schemas、spring.handlers的使用的更多相关文章

  1. Spring IOC容器的初始化-(二)BeanDefinition的载入和解析

    前言 1.在讲BeanDefinition的载入和解析之前,我们先来看看什么是BeanDefinition. Bean对象在Spring中是以BeanDefinition来描述的,也就是说在Sprin ...

  2. Spring+SpringMVC+MyBatis+easyUI整合基础篇(一)项目简介

    很久之前就打算开始写一下自己的技术博客了,实在抽不出时间所以计划一直搁置了,最近项目进度渐渐缓了下来,不那么忙了,也因此开始筹备自己的博客.说到这次博客的主角,也是无心插柳找到的,来源于两年前自己写的 ...

  3. Spring+SpringMVC+MyBatis+easyUI整合基础篇(六)maven整合SSM

    写在前面的话   承接前文<Spring+SpringMVC+MyBatis+easyUI整合基础篇(五)讲一下maven>,本篇所讲述的是如何使用maven与原ssm项目整合,使得一个普 ...

  4. Spring+SpringMVC+MyBatis+easyUI整合基础篇(八)mysql中文查询bug修复

    写在前面的话 在测试搜索时出现的问题,mysql通过中文查询条件搜索不出数据,但是英文和数字可以搜索到记录,中文无返回记录.本文就是写一下发现问题的过程及解决方法.此bug在第一个项目中点这里还存在, ...

  5. Spring+SpringMVC+MyBatis+easyUI整合基础篇(十一)SVN服务器进阶

    日常啰嗦 上一篇文章<Spring+SpringMVC+MyBatis+easyUI整合基础篇(十)SVN搭建>简单的讲了一下SVN服务器的搭建,并没有详细的介绍配置文件及一些复杂的功能, ...

  6. Spring+SpringMVC+MyBatis+easyUI整合基础篇(十二)阶段总结

    不知不觉,已经到了基础篇的收尾阶段了,看着前面的十几篇文章,真的有点不敢相信,自己竟然真的坚持了下来,虽然过程中也有过懒散和焦虑,不过结果还是自己所希望的,克服了很多的问题,将自己的作品展现出来,也发 ...

  7. Spring+SpringMVC+MyBatis+easyUI整合基础篇

    基础篇 Spring+SpringMVC+MyBatis+easyUI整合基础篇(一)项目简介 Spring+SpringMVC+MyBatis+easyUI整合基础篇(二)牛刀小试 Spring+S ...

  8. Resource 定位、BeanDefinition 的载入和解析,BeanDefinition 注册。

    在前文提过,IOC 容器的初始化过程分为三步骤:Resource 定位.BeanDefinition 的载入和解析,BeanDefinition 注册. Resource 定位.我们一般用外部资源来描 ...

  9. (spring-第2回【IoC基础篇】)Spring的Schema,基于XML的配置

    要深入了解Spring机制,首先需要知道Spring是怎样在IoC容器中装配Bean的.而了解这一点的前提是,要搞清楚Spring基于Schema的Xml配置方案. 在深入了解之前,必须要先明白几个标 ...

  10. (spring-第3回【IoC基础篇】)spring的依赖注入-属性、构造函数、工厂方法等的注入(基于XML)

    Spring要把xml配置中bean的属性实例化为具体的bean,"依赖注入"是关卡.所谓的"依赖注入",就是把应用程序对bean的属性依赖都注入到spring ...

随机推荐

  1. 图像处理JPEGCodec类错误问题 毕业设计遇到的问题

     图像处理JPEGCodec类已经从Jdk1.7移除 2014-06-16 20:01:26 分类: 架构设计与优化 著名测试工具jira在使用图像处理JPEGCodec类会报告以下信息: 我是这样用 ...

  2. 老生常谈的Hibernate二级缓存

    理解缓存的定义: 缓存(Cache): 计算机领域非常通用的概念.它介于应用程序和永久性数据存储源(如硬盘上的文件或者数据库)之间,其作用是降低应用程序直接读写永久性数据存储源的频率,从而提高应用的运 ...

  3. HDU-----(1083)Courses(最大匹配)

    Courses Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total S ...

  4. mint上部署lamp环境

    不得不说现在在linux mint上部署lamp很方便,比windows服务器上的asp.net的部署升级都简单. 1 安装MySql sudo apt-get install mysql-serve ...

  5. C++数据类型范围

    C++中有很多基本的数据类型,我们在使用过程中需要根据所需要存储数据的范围的不同而选择恰当的数据类型. Visual C++ 32 位和 64 位编译器可识别本文后面的表中的类型. int (unsi ...

  6. 179. Largest Number -- 数字字符串比较大小

    Given a list of non negative integers, arrange them such that they form the largest number. For exam ...

  7. RestSharp .net 轻量级rest客户端

    RestSharp Simple REST and HTTP API Client for .NET 官网:http://restsharp.org/ GiHub: https://github.co ...

  8. python中数据的变量和字符串的常用使用方法

    1.查看变量类型: a=2 print(a,type(a)) print的用法:在print后面跟多个输出,可以用逗号分隔. 回收变量名,如把a存储不同的数据,你不需要删除原有变量就可以直接赋值 2. ...

  9. if条件语句

    第四天 XMind 思维导图复习之前知识 数据类型-变量常量-运算符(表达式)-语句(顺序.分支.循环)-数组-函数 1.if语句格式 if(表达式) { 语句 } 注意: 1.如果,表达式成立,只执 ...

  10. journal

    dec 5 rpt prep exam dec 4 lie to me dec 3 exam dec 2 preparation for exam dec 1 preparation for exam ...