Unable to locate Spring NamespaceHandler for XML schema产生的原因及解决方法

结论

原因有两个:
  • context、aop及其他spring相关的xml命名空间需要特殊Jar包才能解析。必须确保pom文件中依赖了对应的spring jar包。context对应spring-context.jar包。aop对应spring-aop.jar包。
  • 如果配置文件中定义了2个及以上的spring xml命名空间,那么要防止jar包中配置文件相互覆盖。spring系列jar包中配置文件的命名及路径完全相同,默认情况下这些配置文件会相互覆盖。使用maven-shade-plugin来修改同名配置文件的处理方法,将覆盖改为合并。

场景还原

这是一个maven项目,项目框架采用的是Spring。项目的结构如下所示。

+- performance
   +- pom.xml //maven配置文件
   +- target  //编译生成文件
   |  +- performance-1.0.0-SNAPSHOT.jar //可运行的jar文件
   |  +- original-performance-1.0.0-SNAPSHOT.jar
   |  \- classes //编译项目源文件生成class文件
   +- src     //源码文件
      +- test
      +- main
         +- java
         |  \- concert
         |     +- Audience.java
         |     +- Main.java
         |     +- Performance.java
         |     \- PerformanceImpl.java
         +- resources
            \- performance.xml

  • 在项目根目录performance中执行命令`mvn clean package`就可以生成可执行的jar文件`performance-1.0.0-SNAPSHOT.jar`。
  • 生成jar文件后,执行命令`jar -jar target/performance-1.0.0-SNAPSHOT.jar`就可以运行jar文件。

Spring配置文件

  • xml文件中定义了两个命名空间context及aop。
  • context命名空间用于设定自动扫描包名,用于发现bean。
  • aop用于配置aspectJ注解的解析。
<?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:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:component-scan base-package="concert" />

    <aop:aspectj-autoproxy />

    <bean class="concert.Audience" />
 
    <!--<bean class="concert.PerformanceImpl" />-->
</beans>

pom配置文件

  • aop命名空间需要org.springframework:spring-aop:4.3.14.RELEASE解析。
  • context命名空间需要org.springframework:spring-context:4.3.14.RELEASE解析。
<!-- ... -->
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>4.3.14.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.3.14.RELEASE</version>
    </dependency>

    <!-- ... -->
    </dependencies>
<!-- ... -->

程序入口源码文件

public class Main {
    public static void main(String[] args) {
       ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("performance.xml");
       Performance p = context.getBean(Performance.class);
       p.perform();
       context.close();
    }
}

生成可运行的jar文件

mvn clean package

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.059 s
[INFO] Finished at: 2018-06-25T16:04:51+08:00
[INFO] ------------------------------------------------------------------------

运行jar文件

java -jar target/performance-1.0.0-SNAPSHOT.jar

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/context]
Offending resource: class path resource [performance.xml]

	at org.springframework.beans.factory.parsing.FailFastProblemReporter.error(FailFastProblemReporter.java:70)
	at org.springframework.beans.factory.parsing.ReaderContext.error(ReaderContext.java:118)
	at org.springframework.beans.factory.parsing.ReaderContext.error(ReaderContext.java:110)
	at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.error(BeanDefinitionParserDelegate.java:301)
	at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1407)
	at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1400)
	at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:172)
	at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:142)
	at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:94)
	at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.registerBeanDefinitions(XmlBeanDefinitionReader.java:508)
	at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:392)
	at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:336)
	at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:304)
	at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:181)
	at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:217)
	at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:188)
	at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:252)
	at org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:127)
	at org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:93)
	at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:129)
	at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:614)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:515)
	at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
	at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
	at concert.Main.main(Main.java:7)

问题分析方法

  • 百度问题Unable to locate Spring NamespaceHandler for XML schem原因。
有一种解释说,context及aop都属于自定义命名空间。这些命名空间的规范描述需要特殊的方法来解析。context规范描述需要spring-context来解析,aop规范描述需要spring-aop来解析。如果没有将解析代码spring-context和spring-aop打包进最终生成的performance-1.0.0-SNAPSHOT.jar文件,那么运行时就会出现这个问题。
  • 检查是否将spring-context及spring-aop添加到依赖列表。
  • 检查是否正确书写了context及aop的规范描述URL。
  • 对比正常运行项目
正常项目
错误项目
pom配置文件
依赖spring-context及spring-aop。
依赖spring-context及spring-aop。
spring配置文件
只引用了一个自定义命名空间aop
引用了两个命名空间,aop和context。
取消使用context命名空间。context命名空间用于设定自动扫描目录,自动识别bean对象。可以通过显式声命bean来取消context命名空间。即使用<bean id="" class="" />声明bean文件。
重新运行mvn clean package生成jar文件,执行jar文件,可以正常运行,不再抛出异常。
初步结论:spring配置文件不能定义2个及2个以上的命名空间。
  • 自定义classPath运行concert.Main
还原context命名空间,使用IntelliJ运行concert.Main,竟然正常运行!
IntelliJ本质是自定义classPath运行程序的。在terminal中自定义classpath运行程序,是可以正确运行程序的,并未抛出BeanDefinitionParsingException异常。详情如下所示。
around--before
Silencing cell phones
Taking seats
performanceImpl perform...
around--after
CLAP CLAP CLAP!!!
推翻之前结论:“spring配置文件不能定义2个及2个以上的命名空间
  • google无法解析Spring命名空间问题
一种解释是,spring-context及spring-aop中有同名的配置文件,这些文件在最后打包进performance-1.0.0-SNAPSHOT.jar时,会出现覆盖问题。因此,就会有一个模块(aop或者context)无法正常发挥作用。
如下图所示。

解决方法

在maven-shade-plugin插件中通过Resource Transformers将所有的spring.schemas文件中的内容都合并到一起。
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>1.2.1</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.AppendingTransformer">
                        <resource>META-INF/spring.tooling</resource>
                    </transformer>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <mainClass>concert.Main</mainClass>
                    </transformer>
                </transformers>
            </configuration>
        </execution>
    </executions>
</plugin>
重新打包运行,可以正常运行。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值