基于tomcat与Spring的实现差异化配置方案

起因

在实际开发过程中经常需要加载各种各样的配置文件..比如数据库的用户名密码,要加载的组件,bean等等..但是这种配置在各个环境中经常是不一样的....比如开发环境和测试环境,真实的生产环境..

这种配置不可能写死在代码里.很有可能会写在XML或者properties文件里..

这些文件可以在spring里去加载. 比如

1     <bean id="propertyPlaceholderConfigurer"
2         class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
3         <property name="locations">
4             <list>
5                 <value>classpath*:mysql.properties</value>
6             </list>
7         </property>
8     </bean>
1     <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
2         destroy-method="close">
3         <property name="driverClassName" value="${jdbc.driverClassName}" />
4         <property name="url" value="${jdbc.url}" />
5         <property name="username" value="${jdbc.username}" />
6         <property name="password" value="${jdbc.password}" />
7     </bean>

比如不同环境下的数据库账号密码就可以通过配置不同的mysql.properties来解决.不同环境下需要加载的bean也可以通过这种方式解决.

 

然而这里有一个关键问题没有解决:

打包的结果是得到一个war包.mysql.properties会被打包到classpath的目录里去.

打包一般都是从一个环境里下统一打出来的.怎么让它把不同的配置打进去?

 

解决办法

其实我们并不需要把mysql.properties打包进去.可以通过设置maven的war plugin过滤掉mysql.properties.

既然不是打包进去的.那我们肯定要让Spring能够读取到这个配置.

直接将配置的路径写死在程序里显然不是一个好方法.因为不同机器上的mysql.properties差异文件的路径绝大多数都是不同的..

这里采用相对路径是个不错的办法...

 

我们先来看看Spring是怎么解析我们在XML中配置的路径(classpath*:mysql.properties)的.

在创建applicationContext的时候会根据xml的配置加载bean的配置

我们在PropertyPlaceholderConfigurer这个bean里配置了要加载mysql.properties.所以applicationContext会读取这个properties的地址.

在XML中这只是一个字符串...所以Spring还需要将它解析成具体的Resource才行....

这里用到了PathMatchingResourcePatternResolver类.

第323行可以发现怎么字符串mysql.properties这是一个相对路径,它要转化成绝对路径,再转化成Resource才行.转化成绝对路径最后靠的是ClassLoader的getResources方法.

 1     public Enumeration<URL> getResources(String name) throws IOException {
 2         Enumeration[] tmp = new Enumeration[2];
 3         if (parent != null) {
 4             tmp[0] = parent.getResources(name);
 5         } else {
 6             tmp[0] = getBootstrapResources(name);
 7         }
 8         tmp[1] = findResources(name);
 9 
10         return new CompoundEnumeration<>(tmp);
11     }

我没有tomcat的源码..但是从java的ClassLoader类中也可以看到会循环向上级ClassLoader查找资源,然后返回.

那么前面的问题就可以解决了..

我可以把mysql.properties放到这个webapp的classloader或者它父ClassLoader的classpath下.那Spring就能找到这个资源.

然后我们来看一下资料..

http://www.tuicool.com/articles/I3iqee

从资料(文章及图片)中我们可以得知:

webapp的classpath是WEB-INF下的lib和classes

我们的mysql.properties不可能放到这个里面.因为这个目录是被打到war包里面的...

那我们可以将mysql.properties放到sharedLoader或者commonLoader对应的classpath里即可...

那怎么得到那2种ClassLoader的classpath呢...

从 http://www.cnblogs.com/penzz/p/3241870.html 资料中我们可以得知.可以修改shared.loader去修改共享的class目录.

所以我们修改catalina.properties下的shared.loader.然后把mysql.properties放到shared.loader指定的目录下即可.

 

思考

配置当然也可以放到commonLoader的classpath下...但是显然是不需要的.因为Catalina服务器不需要用到我们的配置文件....

即使把配置放到shared.loader的classpath下也是多个web项目共享的...每个webapp项目都能够获取这个配置文件...

但是因为实际上可以通过一个tomcat只部署一个项目来解决这样的问题...多个webapp为了不相互干扰也不会用同1个tomcat启动的..不然1个webapp要重启tomcat也会影响其他webapp....所以sharedloader也不失为一种不错的解决办法...

当然最佳的解决办法还是让webappClassLoader能够读取到自己的配置文件.多个webappClassLoader不共享配置....但是貌似我的tomcat6不支持...不知道高版本的tomcat是不是有什么更好的方法....

 

总结

最后再来总结下实现差异化配置的方法(基于tomcat):

1.把差异化的东西放到特定的properties中(不一定真要是properties.只是它比较简单...或者把所有配置都放到XML中,但是把启动哪种配置的开关选项放到properties里),在打包的时候过滤掉这些文件.

2.设置catalina.properties.修改shared.loader的值为一个指定的目录

3.把差异化的properties文件放到shared.loader指定的目录下..

这样不同的环境下就可以有不同的配置文件了.而且每次打包也不需要关注那些差异的配置了..

 

另外不同的web容器肯定有不同的方法去设置classpath.不管怎么设置,只要让它能找到我们的配置文件就可以了..比如公司用的是weblogic...做法就比较简单粗暴...通过修改sh脚本直接在原本的classpath目录下再增加了一个classpath.......或许tomcat也可以....只是我一直实验失败.....

 

posted @ 2016-02-20 15:20  abcwt112  阅读(885)  评论(0编辑  收藏  举报