上文介绍Spring MVC框架配置时遇到的问题,以下是解决方案。
解决方案
同步Bean属性
这个问题的一种可行的解决方案是将所有特定于主机的参数都放到普通的Java属性文件中,使用Spring的PropertyPlaceHolderConfigurer类,将这些参数写入Bean属性中。
使用这一解决方案,我们可以生成如下的属性文件(/Web-INF/JDBC.properties):
- jdbc.driver=org.postgresql.Driver
- jdbcjdbc.url=jdbc:postgresql://localhost/test
- jdbc.user=postgres
- jdbc.password=
我们的Bean配置如下:
- <bean id="propertyConfigurer"
- class="org.springFramework.beans.factory.config.PropertyPlaceholderConfigurer">
- <property name="location">
- <value>/WEB-INF/jdbc.properties</value>
- </property>
- </bean>
- <bean id="dataSource"
- class="org.springframework.jdbc.datasource.DriverManagerDataSource">
- <property name="driverClassName">
- <value>${jdbc.driver}</value>
- </property>
- <property name="url">
- <value>${jdbc.url}</value>
- </property>
- <property name="username">
- <value>${jdbc.user}</value>
- </property>
- <property name="password">
- <value>${jdbc.password}</value>
- </property>
- </bean>
如上所述,我们定义了一个PropertyPlaceholderConfigurer类的实例,并将其位置属性设置为我们的属性文件。该类被实现为Bean工厂的后处理器,并将使用定义在文件中的属性来代替所有的占位符(${...}value)。
利用这种技术,我们可以从applicationContext.XML中移除所有特定于主机的配置属性。通过这种方式,我们可以自由地为该文件添加新的Bean,而不必担心特定于主机属性的同步性。这样可以简化生产部署和维护。
同步性连接
上面的技术解决了***个问题,可是如果您计划修改不同应用程序部署之间的Bean连接,这一技术便不很适合。针对这一问题的一个解决方案便是额外创建一个名为applicationContext-[hostname].xml 的XML定义文件。其中[hostname]是部署应用程序的主机的名称。例如,在本地的机器上,这个文件通常名为 applicationContext-localhost.xml,而在部署时,它可能更名为applicationContext- somehost.com.xml。
可以猜测,这一文件必须包括特定于某一主机的所有配置Bean。在本文中,我们将假设dataSource bean定义将位于这类文件中,而不是通用的applicationContext.xml定义。当然,这种机制与前者并非冲突,但是为了更加简单明了,我们将只关注这种方法。
既然我们已经有了特定的配置,下面我们就来讨论一下如何将其整合到整个Spring MVC框架配置概念中。要达到这一目的,可以有许多方法,我们将详细地一一说明。但首先,我们应该注意到,由于有些Bean可能位于独立的配置文件中,因此在 applicationContext.xml中,所有对它们的局部引用都必须更换成全局名称。
例如,如下引用:
- <property name="someProperty">
- <ref local="someBean"/>
- </property>
应更改为:
- <property name="someProperty">
- <ref bean="someBean"/>
- </property>
在这之后,我们有很多可以添加额外的资源以用于配置的方式。其中最明显的就是使用<import>标签将这一额外资源包含在 applicationContext.xml配置文件中。使用时,要将该标签放在applicationContext.xml文件开头。例如:
- <import resource="applicationContext-somehost.com.xml"/>
现在,在独立的XML定义文件和普通的应用程序上下文定义文件中的所有通用Bean定义都有了特定于主机的连接。由于大多数的Bean都不是特定于主机的,因此我们可以像处理Web应用程序中的其他资源一样自由地处理applicationContext.xml文件,并可以通过合适的版本控制系统与其进行同步。
但是,上述方法也有一定的弊端。如果您想保留不同XML文件的不同配置,就仍然必须担心applicationContext.xml的同步性,因为资源的名称必须根据不同服务器进行更改。虽然与原有的解决方案相比有了很大提高,只需更改文件名,但是这还是需要开发人员的手动协助。
由于与applicationContext.xml相比,主机配置不需如此频繁地进行更改,因此下一步便是将主机配置移动到web.xml文件中(如果可能的话)。幸运的是,我们有一个可用的解决方案。看一下下面关于web.xml配置的片断:
- <listener>
- <listener-class> org.springFramework.web.context.ContextLoaderListener
- </listener-class>
- </listener>
- <context-param>
- <param-name>contextConfigLocation
- </param-name>
- <param-value> /WEB-INF/applicationContext.xml /WEB-INF/ applicationContext-somehost.com.xml
- </param-value>
- </context-param>
正如您所看到的,除了web.xml文件中常有的ContextLoaderListener之外,我们还添加了 contextConfigLocation上下文参数配置。这一参数用于指示框架查找这些配置文件的位置。如果这一参数被省略,则Spring就只能到 applicationContext.xml中查找。这里我们也定义了特定于主机的配置文件来使用。
利用这种方法,我们将所有特定于主机的配置从applicationContext.xml文件中移除,这样便减轻了其在不同应用程序部署中的同步性。
如果这种方法成为您的新习惯,您还可以使其更加灵活。通过遵守下列指令,也可以将特定于主机的配置从web.xml文件中移除。
为此,需要创建特定于我们的应用程序上下文的类:
- package net.nighttale.spring.util;
- import Java.net.InetAddress;
- import org.springframework.web.context.support.XmlWebApplicationContext;
- public class PerHostXmlWebApplicationContext
- extends XmlWebApplicationContext ...{
- protected String[] getDefaultConfigLocations() ...{
- String hostname = "localhost";
- try ...{
- hostname = InetAddress.getLocalHost().getHostName();
- } catch (Exception e) ...{
- }
- String perHostConfiguration = DEFAULT_CONFIG_LOCATION_PREFIX
- + "applicationContext-"
- + hostname
- + DEFAULT_CONFIG_LOCATION_SUFFIX
- ;
- logger.debug(
- "Adding per host configuration file: "
- + perHostConfiguration
- );
- if (getNamespace() != null) ...{
- return new String[] ...{
- DEFAULT_CONFIG_LOCATION_PREFIX
- + getNamespace()
- + DEFAULT_CONFIG_LOCATION_SUFFIX
- , perHostConfiguration};
- }
- else ...{
- return new String[] ...{
- DEFAULT_CONFIG_LOCATION
- , perHostConfiguration};
- }
- }
- }
这个类拓展了Spring中常被作为默认值使用的XmlWebApplicationContext。 XmlWebApplicationContext类将Web应用程序的配置从XML定义文件中复制过来。默认情况下,它可以配置来自 applicationContext.xml和[Servlet-name]-servlet.xml文件中的应用程序。这个类执行的惟一一项额外任务便是获取它所在的主机名称,并将applicationContext-[hostname].xml文件添加到配置文件列表中。
为了使用这个类,我们需要对其进行编译,将其包含在类途径中,并指示Spring框架使用它。前两步非常简单,我们就不在此赘述。我们可以指示Sping通过contextClass上下文参数来使用它。除了web.xml文件中的原有配置,我们还可以添加下列内容:
- <context-param>
- <param-name>contextClass</param-name>
- <param-value> net.nighttale.spring.util.PerHostXmlWebApplicationContext
- </param-value>
- </context-param>
如果我们使用这一配置片断,将会有三个文件被用于初始化这个框架:[servlet-name]-servlet.xml、applicationContext-[hostname].xml以及applicationContext.xml。
正如您所看到的,applicationContext.xml和web.xml文件已经完全摆脱了任何特定的配置细节,而且您也不必担心会在更新应用程序时破坏配置。
但是,这种方法有一个不足之处。因为,不论是否会使用,都需要在应用程序部署中有第三个配置文件。在这种情况下,便不需要特定于主机的配置。例如:
- <?xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
- <beans>
- </beans>
***,需要知道应用程序上下文类需要查找的特定主机名。检查主机名称的最简单的方法是在机器上运行下列代码:
- System.out.println(InetAddress.getLocalHost().getHostName());
可以将其作为Java代码执行,也可在喜欢使用的脚本语言(如BeanShell或Groovy)中作为一个具有Java风格语法的脚本执行。在获取了主机的名称之后,应该创建一个默认的/WEB-INF/applicationContext-[hostname].xml空文件夹(如我们上面所定义的),然后便可以开始了。
结束语
在本文中,我们提供了一系列的配置技巧,让您在使用Spring MVC框架完成日常工作的时候更加轻松。如果您希望知道如何维护各种Web应用程序部署,可以试着找出最适合您的开发过程的解决方案。您的生活会更为轻松。
【编辑推荐】