旉Q?006-06-08 作者:Dejan Bosanac 览ơ数Q? 7201 本文关键字:Spring, configuration, source control, CVS, subversion, 配置, 文章工具 | ![]() |
本文ؓ您提供关?a target="_blank">Spring MVC框架的配|技巧,以帮助管理基于Spring的web应用E序的多个实例。本配置理主题常被学术界所忽略Q但是,q对于现实的web开发尤为重要。本主题q不直接兌M具体的技术,因此Q我们将从最基本的概念开始对q个问题q行说明。下面,我们根据Spring MVC框架QؓZ本技术开发的目提供一pd的解x案?/p>
Zl常会在一C上的L上配|一UWeb应用E序。例如,在生产中Q一个网站可能只有一个实例。除了此实例外,开发h员可以在用于开发的机器上配|其他的Q开发)实例。也可以在公司(机构Q内部的本地开发服务器上维护其他应用程序装|,q将让您受益匪浅。该实例的目的是使Web设计者可以获得有质量保证的材料,qؓ需要ؓ应用E序提供文g资料的h提供准入?/p>
大家都知道,即是最单的场景Q也需要安装、配|和l护三个实例。而对于位于不同地理位|的团队来说Q要从事q样的项目便更加困难。对于Q何不是特别简单的Web应用E序目Q都需要多名开发h员来安装目装置和本地设|以及运行单元测试的装置{?/p>
很多l织都将自己开发的产品作ؓWeb应用E序。我们可以在很多产品中发现这U情况,例如电子商务pȝ?a target="_blank">内容理pȝQCMSQ,以及博客发布q_{。这cM品可在多个服务器中进行部|Ӏ对于成功的多用途Web应用E序来说Q他们的开发h员必要保证他们的应用程序便于安装,q且能够与其他Web应用E序完美集成。经q上q讨Z后,我们应该明了Q作为本文主题的应用E序配置是通用Web应用E序目开发h员所需要解决的重要问题之一?/p>
诸如CVS或Subversion之类的版本控制系l是开发组l用的一U标准工兗这U工具代表了一些组l的中心源代码版本库Q它们被用于保持源代码的有序。用户可以跟t应用程序源代码的变化,昄不同版本的区别,q可以确定项目分支。而且Q它们得在应用E序部v中进行部分更新成为可能?/p>
很明显,版本控制pȝ软g是跟t源代码所必需的,它对于解军_用程序配|问题有非常大的帮助。在本文中,我们不会把重点攑֜版本控制pȝ上,因ؓq方面已l有很多相关的材料了。在此,我们关注版本控刉题中的一个小话题Q如何Web应用E序的配|更加便P其是用Spring MVC框架~写的Web应用E序Q?/p>
问题是:我们在此讨论的是一U什么样的配|?MWeb应用E序都需要一些资源,q些资源通常都是其所q行的服务器所Ҏ的,例如数据库URL、发送电子邮件的SMTP服务器,以及包含专用软g文g的文件夹{。这L讄应该集中Q从而应用E序配置更加单?/p>
但是Q这只是q个问题最单的一U版本。有时候,在应用程序开发中需要更加复杂的配置。这意味着Q必d各次部v中的不同Beanq接hQ而这会问题更加复杂?/p>
q些应用E序配置问题的解x案有诸多优势Q包括:化应用程序的安装和配|过E,使源代码版本控制更加便,减少源代码版本库中的冲突现象。下面,我们通过CZ详细讨论q个话题?/p>
我们首先来演CZ下上文所提到的最单的版本。在q一场景中,我们希望在应用程序部|中改变的是单的配置参数Q例如链接、密码等。如果您曄使用Spring MVC框架开发过Web应用E序Q那么您应该知道q里用到的两个配置文gQ?/p>
那么问题在哪儿呢Q问题就出在applicationContext.xml中将包括一些特定于L的Bean定义。其中,最明显的一个示例就是包含了JDBCq接信息的beanQ但是Q何一U稍微复杂些的应用程序都有十几个cM的Bean。看一下下面的CZQ?/p>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>org.postgresql.Driver</value>
</property>
<property name="url">
<value>jdbc:postgresql://localhost/test</value>
</property>
<property name="username">
<value>postgres</value>
</property>
<property name="password">
<value></value>
</property>
</bean>
q个解决Ҏ的问题在于对applicationContext.xml文g的维护。对于初学者来_设想一下,目攑֜源代码版本控制系l中Q例如CVS。下面,假设您希望在|站中添加新的功能,那么需要在应用E序上下文定义中d额外的Bean定义。问题是如何在生产服务器上体现这些改变?/p>
通常情况下,应用E序的本地实例不会与zd站点使用同样的数据库Q因此applicationContext.xml文g包括让您能够访问本地数据库的设|。当您想提交在源代码版本库中的改变时Q就需要注意这些特定于L属性的同步性。版本库中的文g最l可能用本地设|中的配|。如果想在生产服务器上更新配|,必L动同步这些属性的倹{这是非常枯燥的dQ而且q非常容易出错?/p>
对于应用E序的每个实例来_q个问题更加重要。假如有三位开发h员正在用代码段基址Q而且他们使用的是本地的数据库。当您提交更改的时候,他们每个人在本地服务器上更新源代码的时候都必须非常谨慎。他们会手动同步q些更改Q然后提交他们的工作。这样一来,版本控制pȝ对于q些配置文g来说已经毫无用处。如果曾l用过Spring MVCQ那么您应该知道applicationContext.xml是应用程序中的关键组Ӟ因ؓ是它所有的东西_合在一赗所以,我们需要一U机制来帮助使应用程序中各项保持有序Q这炚w帔R要?/p>
正如前面所提到的,q是您可能遇到的较简单的配置问题。更隄问题出现在当需要在不同服务器中q行不同的Beanq接的时候。这c问题常会出现在日常软g开发Q务中。例如,假如您的产品有一个客戯n份验证模块,可以Ҏ自关pL据库或LDAP服务器中的用戯行n份验证。自Ӟq一w䆾验证模块可以使用抽象了特定版本库的Beanq行配置。如果您x变不同应用程序部|中验证用户的方式,需要在applicationContext.xml文g中进行不同的Beanq接。这U配|问题常见于在部|中有可配置Ҏ的所有应用程序?/p>
在下文中Q我们将讨论q两U配|问题。首先我们会x同步的Bean属性问题及其解x案,接下来,我们会讨论更加复杂的同步Beanq接问题?/p>
q个问题的一U可行的解决Ҏ是将所有特定于L的参数都攑ֈ普通的Java属性文件中Q用Spring的PropertyPlaceHolderConfigurerc,这些参数写入Bean属性中?/p>
使用q一解决ҎQ我们可以生成如下的属性文Ӟ/WEB-INF/jdbc.propertiesQ:
jdbc.driver=org.postgresql.Driver
jdbc.url=jdbc:postgresql://localhost/test
jdbc.user=postgres
jdbc.password=
我们的Bean配置如下Q?/p>
<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>
如上所qͼ我们定义了一个PropertyPlaceholderConfigurercȝ实例Qƈ其位置属性设|ؓ我们的属性文件。该c被实现为Bean工厂的后处理器,q将使用定义在文件中的属性来代替所有的占位W(${...}valueQ?/p>
利用q种技术,我们可以从applicationContext.xml中移除所有特定于L的配|属性。通过q种方式Q我们可以自由地文gd新的BeanQ而不必担心特定于L属性的同步性。这样可以简化生产部|和l护?/p>
上面的技术解决了W一个问题,可是如果您计划修改不同应用程序部|之间的Beanq接Q这一技术便不很适合。针对这一问题的一个解x案便是额外创Z个名为applicationContext-[hostname].xml 的XML定义文g。其?strong>[hostname]是部|应用程序的L的名U。例如,在本地的机器上,q个文g通常名ؓapplicationContext-localhost.xmlQ而在部vӞ它可能更名ؓapplicationContext-somehost.com.xml?/p>
可以猜测Q这一文g必须包括特定于某一L的所有配|Bean。在本文中,我们假设dataSource bean定义位于这cL件中Q而不是通用的applicationContext.xml定义。当Ӟq种机制与前者ƈ非冲H,但是Z更加单明了,我们只xq种Ҏ?/p>
既然我们已经有了特定的配|,下面我们来讨论一下如何将其整合到整个Spring MVC配置概念中。要辑ֈq一目的Q可以有许多ҎQ我们将详细C一说明。但首先Q我们应该注意到Q由于有些Bean可能位于独立的配|文件中Q因此在applicationContext.xml中,所有对它们的局部引用都必须更换成全局名称?/p>
例如Q如下引用:
<property name="someProperty">
<ref local="someBean"/>
</property>
应更改ؓQ?/p>
<property name="someProperty">
<ref bean="someBean"/>
</property>
在这之后Q我们有很多可以d额外的资源以用于配置的方式。其中最明显的就是?lt;import>标签这一额外资源包含在applicationContext.xml配置文g中。用时Q要该标签攑֜applicationContext.xml文g开头。例如:
<import resource="applicationContext-somehost.com.xml"/>
现在Q在独立的XML定义文g和普通的应用E序上下文定义文件中的所有通用Bean定义都有了特定于L的连接。由于大多数的Bean都不是特定于L的,因此我们可以像处理Web应用E序中的其他资源一栯由地处理applicationContext.xml文gQƈ可以通过合适的版本控制pȝ与其q行同步?/p>
但是Q上q方法也有一定的弊端。如果您想保留不同XML文g的不同配|,׃然必L心applicationContext.xml的同步性,因ؓ资源的名U必L据不同服务器q行更改。虽然与原有的解x案相比有了很大提高,只需更改文g名,但是q还是需要开发h员的手动协助?/p>
׃与applicationContext.xml相比Q主机配|不需如此频繁地进行更改,因此下一步便是将L配置Ud到web.xml文g中(如果可能的话Q。幸q的是,我们有一个可用的解决Ҏ。看一下下面关于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文g中常有的ContextLoaderListener之外Q我们还d了contextConfigLocation上下文参数配|。这一参数用于指示框架查找q些配置文g的位|。如果这一参数被省略,则Spring只能到applicationContext.xml中查找。这里我们也定义了特定于L的配|文件来使用?/p>
利用q种ҎQ我们将所有特定于L的配|从applicationContext.xml文g中移除,q样便减M其在不同应用E序部v中的同步性?/p>
如果q种Ҏ成ؓ您的C惯,您还可以使其更加灉|。通过遵守下列指oQ也可以特定于L的配|从web.xml文g中移除?/p>
为此Q需要创建特定于我们的应用程序上下文的类Q?/p>
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};
}
}
}
q个cL展了Spring中常被作为默认g用的XmlWebApplicationContext。XmlWebApplicationContextcdWeb应用E序的配|从XML定义文g中复制过来。默认情况下Q它可以配置来自applicationContext.xml?strong>[servlet-name]-servlet.xml文g中的应用E序。这个类执行的惟一一w外Q务便是获取它所在的L名称QƈapplicationContext-[hostname].xml文gd到配|文件列表中?/p>
Z使用q个c,我们需要对其进行编译,其包含在类途径中,q指CSpring框架使用它。前两步非常单,我们׃在此赘述。我们可以指CSping通过contextClass上下文参数来使用它。除了web.xml文g中的原有配置Q我们还可以d下列内容Q?/p>
<context-param>
<param-name>contextClass</param-name>
<param-value>
net.nighttale.spring.util.PerHostXmlWebApplicationContext
</param-value>
</context-param>
如果我们使用q一配置片断Q将会有三个文g被用于初始化q个框架Q?strong>[servlet-name]-servlet.xml、applicationContext-[hostname].xml以及applicationContext.xml?/p>
正如您所看到的,applicationContext.xml和web.xml文g已经完全摆脱了Q何特定的配置l节Q而且您也不必担心会在更新应用E序时破坏配|?/p>
但是Q这U方法有一个不之处。因为,不论是否会用,都需要在应用E序部v中有W三个配|文件。在q种情况下,便不需要特定于L的配|。例如:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans></beans>
最后,需要知道应用程序上下文c需要查扄特定L名。检查主机名U的最单的Ҏ是在机器上运行下列代码:
System.out.println(InetAddress.getLocalHost().getHostName())
可以其作ؓJava代码执行Q也可在喜欢使用的脚本语aQ如BeanShell或GroovyQ中作ؓ一个具有Java风格语法的脚本执行。在获取了主机的名称之后Q应该创Z个默认的/WEB-INF/applicationContext-[hostname].xmlI文件夹Q如我们上面所定义的)Q然后便可以开始了?/p>
在本文中Q我们提供了一pd的配|技巧,让您在用Spring MVC框架完成日常工作的时候更加轻松。如果您希望知道如何l护各种Web应用E序部vQ可以试着扑և最适合您的开发过E的解决Ҏ。您的生zM更ؓL?/p>
原文出处:http://www.onjava.com/pub/a/onjava/2006/03/22/advanced-spring-configuration.html
作者简?/span> | |
Dejan Bosanac 是一位Y件开发h员、技术顾问和作者,他主要关注不同技术的整合和协作,其是与Java和Web相关的技?/td> |
?/font>
SpringSide
里翻C个好东西Q?
jodd:form
"
Jodd Form的用极其简单,因ؓ太简单了Q才会名不见l传而被我们选用Q让我们|顾各大Framework的Form TagQ?/font>
他只要把<form>的头以<jodd:form bean= "mybean">包住卛_Q就会自动绑定mybean的所有同名属性到普通html标记--input, selectbox, checkbox,radiobox.....在这些input框里不用再写M代码
可见Q它的好处一是节U代码,二是保留了html版的form tag, 不需要用一套taglib来代?/strong>
而且支持内嵌对象的绑定,如book.category.name?/font>
比那些需要用非Uhtml标记?lt;ww:input>Q逐个l定input框,select框的Form TagQ虽减了些许灉|性,但实在方便得太厉実?/font>
如果属性来自于requestQ而不是某个java beanQ写<jodd:form bean= "request">
jodd采用的是普通的bean反射Q调用所有属性的toString()函数?/font>
注意Qjodd在html tag不含value="xxx"属性时才会自动l定。如果某个input框你不希望jodd自动l定Q或者默认的toString()不能满要求Ӟ可以手工input框写?value="xxxx"Qjodd看到你已l定义了属性|׃自动l开?br />
"
实好用Q?/p>
Jodd 是一个开源项目, http://jodd.sourceforge.net Q?/span> 有一个好用的 jsp 标签Q可以大大简化有表单输入?/span> controller ?/span>
使用
Jodd
的优点:
<!--[if !supportLists]-->1.<!--[endif]-->化和l一controllerQ抛?/span>extends SimpleFormControllerQ统一使用implements Controller的方式?/span>
<!--[if !supportLists]-->2.<!--[endif]-->?/span>JSP面?/span>bind,不需要一个字D一个字D늚l定?/span>
<!--[if !supportLists]-->3.<!--[endif]-->?/span>bean没有M要求Q可以用Q意的bean做ؓformBean.
使用Ҏ介:
<!--[if !supportLists]-->Q.<!--[endif]-->?/span>jodd.jar攑ֈweb-inf->lib下,?/span>web.xml里声名标{:
<!--[if !supportLists]-->Q.<!--[endif]-->L的一?/span>javaBean做ؓFormBean
<!--[if !supportLists]-->Q.<!--[endif]-->?/span>JSP面使用jodd tag:,比如对应用户d面?/span>
<!--[if !supportLists]-->Q.<!--[endif]-->Dispatch-servelt.xml中对controller的配|?/span>
<bean id="myController" class="caike.MyController">
</bean>
不再需要这U方式:
<!--
<bean id="myController" class="caike.MyFormController">
<property name="commandClass" value="caike" />
<property name="formView" value="userForm" />
</bean>
-->
<!--[if !supportLists]-->Q.<!--[endif]-->?/span>controller中取?/span>user
需要注意的地方Q?/span>
表单中对应的名字 name ?/span> javaBean 里对应的属性名要相同?/span>
<input type="text" name="userName"
class="input" size="20">
public class User {
private String userName;
.......
http://www.aygfsteel.com/calvin/archive/2005/08/24/10914.html