前些天在进行项目部署时发现启动不成功,看了日志是 applicationContext.xml 配置文件解析 namespace 时失败。 因此对 spring 的 xml 加载机制做一下分享。
我们项目中的配置文件有如下配置:
Copy <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:kiev="http://www.meizu.com/kiev/schema/service"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.meizu.com/kiev/schema/service
http://www.meizu.com/kiev/schema/service.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-4.0.xsd"> 从中我们看下这个配置
其实我们也可以这样写
其中的区别也看到了,就是一个有版本号,一个没有,这其中有什么区别?
xml 的 schema 里有 namespace,可以给它起个别名。比如常见的 spring 的 namespace:
xsi:schemaLocation 配置的是 namespace 与 xsd 地址的映射,所以 xsi:schemaLocation 的配置都是成对出现。 前面的是 namespace 的 URI,后面的是 xsd 文件的 URI。
spring 如何加载 xml
从 xsd 的 URI 可以看出这是一个网络连接地址,需要连接网络才能访问。 但是在断网的情况下我们依然可以正常访问,因为 Spring 会默认先从本地加载 xsd 文件。 打开 spring-context-3.2.0.RELEASE.jar,可以看到里面有两个特别的文件:
spring.handler
spring.schema
再看 xsd 文件
很明显,spring 把 xsd 文件都放在了本地,而且以前的版本也在里面。 而没有配置版本号呢?我们也可以看到其实使用了当前的版本:
我们经常对所引用的版本号不太在意,有时候从别的地方拷贝过来就直接使用了,这就有可能带来问题。 比较好的做法就是不写版本号,这样比较安全。
其实写一个自己的 xsd 很简单。首先实现自己的 NamespaceHandler,再配置 spring.handler 和 spring.schema 就可以了。 魅族有个自己的项目叫 Kiev,看下这个项目的 jar 包:
之前项目中有出现某个 bean 加载不到,或者事务失效的情况。这些主要是因为 spring 与 spring mvc 容器之间可见性的问题导致的。 Spring 容器作为父容器是看不到 spring mvc 子容器加载的配置的。由于个人喜好问题,父子容器的加载每个项目配置也都不一样。为了省事,减少麻烦,可以只加载子容器而不加载父容器,这样就可以解决可见性问题,避免父容器的配置被子容器覆盖而导致相关问题。
可是有特殊情况,如 Shiro 的配置必须由父容器来进行加载,这样还是不能逃避这个问题。
原因: 子容器加载了父容器的 @Service,而事务或者一些属性注入是在父容器中进行的,这样子容器加载的 @Service 就覆盖了父容器中已经增强过了的 bean。此时的 bean 是原生态的,因此会导致事务及属性注入的失效。 怎么办: 那就是各做各事,父容器不加载 Controller,子容器只加载 Controller,不多做事情。这里又体现了少做事情的好处了。 具体配置如下:applicationContext.xml
springmvc-servlet.xml
这里最后一个注意点是第 2 行的 use-default-filters="false",这个值默认为 true 的。如果不改成 false,容器还会加载 Controller 的相关子注解 @Service、@Reposity