Spring取得*.properties文件属性

来源:互联网 发布:网上卖的淘宝教程没用 编辑:程序博客网 时间:2024/06/02 03:37

在web项目中我们有时候会想要,从xx.properties中拿取某一些固定的数据作为全局的属性于是我们运用了<context:property-placeholder>标签但一个奇怪的问题是,假如我们要加载两个xx.properties文件的,Spring会在运行时报如下的错误:

org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition with name 'dataSource' defined in file [D:\apache-tomcat-8.0.47\webapps\ROOT\WEB-INF\classes\spring\applicationContext-dao.xml]: Could not resolve placeholder 'jdbc.driver' in string value "${jdbc.driver}"; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'jdbc.driver' in string value "${jdbc.driver}"at org.springframework.beans.factory.config.PlaceholderConfigurerSupport.doProcessProperties(PlaceholderConfigurerSupport.java:211)at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.processProperties(PropertySourcesPlaceholderConfigurer.java:180)at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.postProcessBeanFactory(PropertySourcesPlaceholderConfigurer.java:155)at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:265)at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:162)at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:609)at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:464)at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:403)at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:306)at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:106)at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4853)at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5314)at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:753)at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:729)at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:717)at org.apache.catalina.startup.HostConfig.manageApp(HostConfig.java:1733)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:498)at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:300)at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:484)at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:433)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:498)at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:300)at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1468)at javax.management.remote.rmi.RMIConnectionImpl.access$300(RMIConnectionImpl.java:76)at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1309)at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1401)at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:829)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:498)at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:357)at sun.rmi.transport.Transport$1.run(Transport.java:200)at sun.rmi.transport.Transport$1.run(Transport.java:197)at java.security.AccessController.doPrivileged(Native Method)at sun.rmi.transport.Transport.serviceCall(Transport.java:196)at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:568)at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:826)at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:683)at java.security.AccessController.doPrivileged(Native Method)at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:682)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)at java.lang.Thread.run(Thread.java:748)Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'jdbc.driver' in string value "${jdbc.driver}"at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:174)at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126)at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:194)at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:158)at org.springframework.context.support.PropertySourcesPlaceholderConfigurer$2.resolveStringValue(PropertySourcesPlaceholderConfigurer.java:175)at org.springframework.beans.factory.config.BeanDefinitionVisitor.resolveStringValue(BeanDefinitionVisitor.java:282)at org.springframework.beans.factory.config.BeanDefinitionVisitor.resolveValue(BeanDefinitionVisitor.java:204)at org.springframework.beans.factory.config.BeanDefinitionVisitor.visitPropertyValues(BeanDefinitionVisitor.java:141)at org.springframework.beans.factory.config.BeanDefinitionVisitor.visitBeanDefinition(BeanDefinitionVisitor.java:82)at org.springframework.beans.factory.config.PlaceholderConfigurerSupport.doProcessProperties(PlaceholderConfigurerSupport.java:208)... 54 more
到底出了啥问题 


那究竟是什么问题呢?也想了很久哦....终于回想起来了(写书时读过Spring源码),原来是Spring容器采用反射扫描的发现机制,在探测到Spring容器中有一个org.springframework.beans.factory.config.PropertyPlaceholderConfigurer的Bean就会停止对剩余PropertyPlaceholderConfigurer的扫描(Spring 3.1已经使用PropertySourcesPlaceholderConfigurer替代PropertyPlaceholderConfigurer了)。 

而<context:property-placeholder/>这个基于命名空间的配置,其实内部就是创建一个PropertyPlaceholderConfigurer Bean而已。换句话说,即Spring容器仅允许最多定义一个PropertyPlaceholderConfigurer(或<context:property-placeholder/>),其余的会被Spring忽略掉(其实Spring如果提供一个警告就好了)。 

拿上来的例子来说,如果A和B模块是单独运行的,由于Spring容器都只有一个PropertyPlaceholderConfigurer,因此属性文件会被正常加载并替换掉。如果A和B两模块集成后运行,Spring容器中就有两个PropertyPlaceholderConfigurer Bean了,这时就看谁先谁后了, 先的保留,后的忽略!因此,只加载到了一个属性文件,因而造成无法正确进行属性替换的问题。 

解决的办法很简单使用通配符加载文件:

<context:property-placeholder location="classpath:properties/*.properties" />
或者采用如下的配置:

<context:property-placeholder location="classpath*:conf/conf*.properties"/> 

但是下面又会出现一个问题,我是applicationContext.xml中配置在标签的,当我需要在Controller中使用xx.propertiest的时候,结果发现Controller中居然无法得到改属性,这是为什么呢?其实这涉及到了Spring和SpringMVC父子容器的概念,两个容器都能加载java类注册成bean,于是就存在两个查找值的空间了,Controller默认是在子容器中查找PropertyPlaceholderConfigurer Bean的但是子容器压根就没加载这个东西,当然也就找不到该属性了,解决的办法有两个:

1.在applicationContext-mvc.xml中在配置一次该标签,这种方法最为简单,但是不太被大家认同,毕竟都是在一个项目当中,为何要浪费资源加载两份相同的文件呢。(目前就掌握这一种解法)



原创粉丝点击