国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
yanghuw的專欄,第一個(gè)Spring程序

概述
        你將會創(chuàng)建一個(gè)簡單的程序完成最基本的CRUD(Create, Retrieve, Update 和 Delete)操作。這個(gè)程序叫MyUsers,作為本書的樣例。這是一個(gè)3層架構(gòu)的web 程序,通過一個(gè)Action 來調(diào)用業(yè)務(wù)代理,再通過它來回調(diào) DAO類。下面的流程圖表示了MyUsers是如何工作的。數(shù)字表明了流程的先后順序,從web層(UserAction)到中間層(UserManager),再到數(shù)據(jù)層(UserDAO),然后返回。
        鑒于大多數(shù)讀者都比較熟悉struts,本程序采用它作為MVC 框架。Spring的魅力在于它宣稱式的事務(wù)處理,依懶性的綁定和持久性的支持。第4 章中將用 Spring框架對它進(jìn)行重構(gòu)。
接下來你會進(jìn)行以下幾個(gè)步驟:
1.下載Struts和Spring。
2.創(chuàng)建項(xiàng)目目錄和ant Build文件。
3.為持久層創(chuàng)建一個(gè)單元測試(unit tests)。
4.配置Hibernate和Spring。
5.編寫HIbernate DAO的實(shí)現(xiàn)。
6.進(jìn)行單元測試,通過DAO驗(yàn)證CRUD。
7.創(chuàng)建一個(gè)Manager來聲明事務(wù)處理。
8.為struts Action 編寫測試程序。
9.為web層創(chuàng)建一個(gè)Action 和model(DynaActionForm)。
10.進(jìn)行單元測試,通過Action驗(yàn)證CRUD。
11.創(chuàng)建JSP頁面,以通過瀏覽器來進(jìn)行CRUD操作。
12.通過瀏覽器來驗(yàn)證JSP頁面的功能。
13.用velocity模板替換JSP頁面。
14.使用Commons Validator添加驗(yàn)證。


下載Struts和Spring
1.下載安裝以下組件:
• JDK1.4.2(或以上)
• Tomcat5.0+
• Ant 1.6.1+
2.設(shè)置以下環(huán)境變量:
• JAVA_HOME
• ANT_HOME
• CATALINA_HOME
3.把以下路徑添加到PATH中:
• JAVA_HOME/bin
• ANT_HOME/bin
• CATALINA_HOME/bin
        為了開發(fā)基于java的web項(xiàng)目,開發(fā)人員必須事先下載必需的jars,建好開發(fā)目錄結(jié)構(gòu)和ant build文件。對于單一的struts項(xiàng)目,可以利用struts中現(xiàn)成的strutsblank.war。對于基于Spring MVC 框架的項(xiàng)目,可以用Spring中的webapp-minimal.war。這只為開發(fā)作準(zhǔn)備,兩者都沒有進(jìn)行struts-spring集成,也沒有考慮單元測試。為此,我們?yōu)樽x者準(zhǔn)備了Equinox。
        Equinox為開發(fā)Struts-spring式的程序提供一個(gè)基本框架。它已經(jīng)定義好了目錄結(jié)構(gòu),和ant build文件(針對compiling,deploying,testing),并且提供了struts, spring,Hibernate開發(fā)要用到的jars文件。Equinox中大部分目錄結(jié)構(gòu)和ant build文件來自我的開源項(xiàng)目──AppFuse。可以說,Equinox是一個(gè)簡化的AppFuse,它在最小配置情況下,為快速web開發(fā)提供了便利。由于Equinox源于AppFuse,所以在包名,數(shù)據(jù)庫名,及其它地方都找到它的影子。這是為讓你從基于Equinox的程序過渡到更為復(fù)雜的AppFuse。
http://sourcebeat.com/downloads上下載Equinox,解壓到一個(gè)合適的位置,開始準(zhǔn)備MyUsers的開發(fā)。


創(chuàng)建項(xiàng)目目錄和ant build文件
        為了設(shè)置初始的目錄結(jié)構(gòu),把下載的Equinox解壓到硬盤。建議windows用戶把項(xiàng)目放在C:\Source,Unix/Linux用戶放在~/dev(譯注:在當(dāng)前用戶目錄建一個(gè)dev目錄)中。windows用戶可以設(shè)置一個(gè)HOME環(huán)境變量,值為C:\Source。最簡單的方法是把Equinox解壓到你的喜歡的地方,進(jìn)入equinox目錄,運(yùn)行ant new -Dapp.anme=myusers。
        tips:在windows使用cygwin(www.cygwin.org)就可以像Unix/Linux系統(tǒng)一樣使用正斜杠,本書所有路徑均采用正斜杠。其它使用反斜杠系統(tǒng)(如windows中命令行窗口)的用戶請作相應(yīng)的調(diào)整。
       現(xiàn)在MyUsers程序已經(jīng)有如下的目錄結(jié)構(gòu):

       Equinox包含一個(gè)簡單而功能強(qiáng)大的build.xml,它可以用ant來進(jìn)行編譯,布署,和測試。ant中已經(jīng)定義好targets,在equinox運(yùn)行ant,將看到如下內(nèi)容:
[echo] Available targets are:
[echo] compile --> Compile all Java files
[echo] war --> Package as WAR file
[echo] deploy --> Deploy application as directory
[echo] deploywar --> Deploy application as a WAR file
[echo] install --> Install application in Tomcat
[echo] remove --> Remove application from Tomcat
[echo] reload --> Reload application in Tomcat
[echo] start --> Start Tomcat application
[echo] stop --> Stop Tomcat application
[echo] list --> List Tomcat applications
[echo] clean --> Deletes compiled classes and WAR
[echo] new --> Creates a new project
Equinox支持tomcat的ant tasks(任務(wù))。這些已經(jīng)集成在Equinox中,解講一下如何進(jìn)行集成的有助于理解它們的工作原理。


Tomcat和ant
tomcat中定義了一組任務(wù),可以通過Manager來安裝(install),刪除(remove),重載(reload)webapps。要使用這些任務(wù),可以把所有的定義寫在一個(gè)屬性文件中。在Eqinox的根目錄下,有一個(gè)名為tomcatTasks.properties包含如下內(nèi)容。
deploy=org.apache.catalina.ant.DeployTask
undeploy=org.apache.catalina.ant.UndeployTask
remove=org.apache.catalina.ant.RemoveTask
reload=org.apache.catalina.ant.ReloadTask
start=org.apache.catalina.ant.StartTask
stop=org.apache.catalina.ant.StopTask
list=org.apache.catalina.ant.ListTask
在build.xml定義一些任務(wù)來安裝,刪除,重新載入應(yīng)用程序。
<!-- Tomcat Ant Tasks -->
<taskdef file="tomcatTasks.properties">
<classpath>
<pathelement path="${tomcat.home}/server/lib/catalina-ant.jar"/>
</classpath>
</taskdef>
<target name="install" description="Install application in Tomcat"
depends="war">
<deploy url="${tomcat.manager.url}" username="${tomcat.manager.username}"
password="${tomcat.manager.password}" path="/${webapp.name}" war="file:$
{dist.dir}/${webapp.name}.war"/>
</target>
<target name="remove" description="Remove application from Tomcat">
<undeploy url="${tomcat.manager.url}" username="${tomcat.manager.username}"
password="${tomcat.manager.password}" path="/${webapp.name}"/>
</target>
<target name="reload" description="Reload application in Tomcat">
<reload url="${tomcat.manager.url}" username="${tomcat.manager.username}"
password="${tomcat.manager.password}" path="/${webapp.name}"/>
</target>
<target name="start" description="Start Tomcat application">
<start url="${tomcat.manager.url}" username="${tomcat.manager.username}"
password="${tomcat.manager.password}" path="/${webapp.name}"/>
</target> <target name="stop" description="Stop Tomcat application">
<stop url="${tomcat.manager.url}" username="${tomcat.manager.username}"
password="${tomcat.manager.password}" path="/${webapp.name}"/>
</target>
<target name="list" description="List Tomcat applications">
<list url="${tomcat.manager.url}"
username="${tomcat.manager.username}"
password="${tomcat.manager.password}"/>
</target>
在上面列出的target中,必須定義一些${tomcat.*}變量。在根目錄下有一個(gè)build.properties默認(rèn)定義如下:
# Properties for Tomcat Server
tomcat.manager.url=http://localhost:8080/manager
tomcat.manager.username=admin
tomcat.manager.password=admin
確保admin用戶可以訪問Manager應(yīng)用,打開$CATALINA_HOME/conf/tomcatusers.xml中是否存在下面一行。如果不存在,請自己添加。注意,roles屬性可能是一個(gè)以逗號(“,”)隔開的系列。
<user username="admin" password="admin" roles="manager"/>
為了測試所有修改,保存所有文件,啟動(dòng)tomcat。從命令行中進(jìn)行myusers目錄,運(yùn)行ant list,可以看到 tomcat server上運(yùn)行的應(yīng)用程序。
好了,現(xiàn)在運(yùn)行 ant deploy來安裝MyUsers。打開瀏覽器,在地址欄中輸入http://localhost:8080/myusers,出現(xiàn)如圖2.4的“Equinox Welcome”畫面。
下一節(jié),將寫一個(gè)User對象和一個(gè)維護(hù)其持久性的Hibernate DAO對象。用Sping來管理DAO 類及其依賴性。最會寫一個(gè)業(yè)務(wù)代理,用到AOP和聲明式事務(wù)處理。


為持久層編寫單元測試
在myUsers程序,使用Hibernat作為持久層。Hinbernate是一個(gè)O/R映像框架,用來關(guān)聯(lián)java對象和數(shù)據(jù)庫中的表(tables)。它使得對象的CRUD操作變得非常簡單,Spring結(jié)合了Hibernate變得更加容易。從Hibernate轉(zhuǎn)向Spring+Hibernate會減少75%的代碼。這主要是因?yàn)?,ServiceLocater和一些DAOFactory類的廢棄,spring的實(shí)時(shí)異常代替了Hibernate的檢測式的異常。
寫一個(gè)單元測試有助于規(guī)范 UserDAO接口。為UserDAO寫一個(gè)JUint測試程序,要完成以下幾步:
1.在test/org/appfuse/dao下新建一個(gè)UserDAOTest.java類。它繼承了同一個(gè)包中的BaseDAOTestCase,其父類初始化了Spring的ApplictionContext(來自web/WEBINF/applictionContext.xml),以下是JUnit測試的代碼。
package org.appfuse.dao;
// use your IDE to handle imports
public class UserDAOTest extends BaseDAOTestCase {
private User user = null;
private UserDAO dao = null;
protected void setUp() throws Exception {
log = LogFactory.getLog(UserDAOTest.class);
dao = (UserDAO) ctx.getBean("userDAO");
}
protected void tearDown() throws Exception {
dao = null;
}
public static void main(String[] args) {
junit.textui.TestRunner.run(UserDAOTest.class);
}
}
這個(gè)類還不能編譯,因?yàn)檫€沒有UserDAO 接口。在這之前,來寫一些來驗(yàn)證User的CRUD操作。
2.為UserDAOTest類添加testSave和testAddAndRemove方法,如下:
public void testSaveUser() throws Exception {
user = new User();
user.setFirstName("Rod");
user.setLastName("Johnson");
dao.saveUser(user);
assertTrue("primary key assigned", user.getId() != null);
log.info(user);
assertTrue(user.getFirstName() != null);
}
public void testAddAndRemoveUser() throws Exception {
user = new User();
user.setFirstName("Bill");
user.setLastName("Joy");
dao.saveUser(user);
assertTrue(user.getId() != null);
assertTrue(user.getFirstName().equals("Bill"));
if (log.isDebugEnabled()) {
log.debug("removing user...");
}
dao.removeUser(user.getId());
assertNull(dao.getUser(user.getId()));
}
從這些方法中可以看到,你需要在UserDAO創(chuàng)建以下方法:
saveUser(User)
removeUser(Long)
getUser(Long)
getUsers() (返回?cái)?shù)據(jù)庫的所有用戶)
3.在src/org/appfuse/dao目錄下建一個(gè)名為UserDAO.java類的,輸入以下代碼:
tips:如果你使用eclipse,idea 之類的IDE,左邊會出現(xiàn)在一個(gè)燈泡,提示類不存在,可以即時(shí)創(chuàng)建。
package org.appfuse.dao;
// use your IDE to handle imports
public interface UserDAO extends DAO {
public List getUsers();
public User getUser(Long userId);
public void saveUser(User user);
public void removeUser(Long userId);
}
為了UserDAO.java,UserDAOTest.java編譯通過,還要建一個(gè)User.java類。
4.在src/org/appfuse/model下建一個(gè)User.java文件,添加幾個(gè)成員變量:id,firstName,lastName,如下。
package org.appfuse.model;
public class User extends BaseObject {
private Long id;
private String firstName;
private String lastName;
/* 用你熟悉的IDE來生成getters和setters,Eclipse中右擊> Source -> Generate Gettersand Setters */
}
注意,你繼承了BaseObject 類,它包含幾個(gè)有用的方法:toString(),equlas(),hasCode(),后兩個(gè)是Hibernate必須的。建好User后,用IDE打開UserDAO和UserDAOTest兩個(gè)類,優(yōu)化導(dǎo)入。


配置Hibernate和Spring
現(xiàn)在已經(jīng)有了POJO(Plain Old Java Object),寫一個(gè)映像文件Hibernate就可能維護(hù)它。
1.在org/appfuse/model中新建一個(gè)名為User.hbm.xml文件,內(nèi)容如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"<hibernate-mapping>
<class name="org.appfuse.model.User" table="app_user">
<id name="id" column="id" unsaved-value="0">
<generator class="increment" />
</id>
<property name="firstName" column="first_name" not-null="true"/>
<property name="lastName" column="last_name" not-null="true"/>
</class>
</hibernate-mapping>
2.在web/WEB-INF/下的applictionContext.xml中添加映像。打開文件,找到<property name= mappingResources >,改成如下:
<property name="mappingResources">
<list>
<value>org/appfuse/model/User.hbm.xml</value>
</list>
</property>
在applictionContext.xml中,你可以看到數(shù)據(jù)庫是怎幺工作的,Hibernate和Spring是如何協(xié)作的。Eqinox會使用名為db/appfuse的HSQL數(shù)據(jù)庫。它將在你的ant “db”目錄下創(chuàng)建,詳細(xì)配置在“How Spring Is Configured in Equinox ”一節(jié)中描述。
3.運(yùn)行ant deploy reload(Tomcat正在運(yùn)行),在Tomcat控制臺的日志中可以看到,數(shù)據(jù)表正在創(chuàng)建。
INFO - SchemaExport.execute(98) | Running hbm2ddl schema export
INFO - SchemaExport.execute(117) | exporting generated schema to database
INFO - ConnectionProviderFactory.newConnectionProvider(53) | Initializing connection
provider: org.springframework.orm.hibernate.LocalDataSourceConnectionProvider
INFO - DriverManagerDataSource.getConnectionFromDriverManager(140) | Creating new
JDBC connection to [jdbc:hsqldb:db/appfuse]
INFO - SchemaExport.execute(160) | schema export complete
Tip: 如果你想看到更多或更少的日志,修改 web/WEB-INF/ classes/log4j.xml 的設(shè)置。
4.為了驗(yàn)證數(shù)據(jù)庫已經(jīng)建好,運(yùn)行 ant browser啟動(dòng)hsql console 。你會看到如的HSQL Database Manager。
Equinox 中spring是怎么配置的
使用Spring配置任何基于j2ee的web程序都很簡單。至少,你簡單的添加Spring的ContextLoaderListener到你的web.xml中。
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
這是一個(gè)ServletContextListener,它會在啟動(dòng)web應(yīng)用進(jìn)行初始化。默認(rèn)情況下,它會查找web/WEB-INF/applictionContext.xml文件,你可以指定名為contextConfigLocation的<context-param>元素來進(jìn)行修改,例如:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/sampleContext.xml</param-value>
</context-param>
<param-value>元素可以是以空格或是逗號隔開的一系列路徑。在 Equnox中,Spring的配置使用了這個(gè)Listener和默認(rèn)的 contextConfigLocation。那么,Spring怎幺知道Hibernate的存在?這就Spring的魅力所在,它讓依賴性的綁定變得非常簡單。請參閱applicationContext.xml的全部內(nèi)容:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"
<beans>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>org.hsqldb.jdbcDriver</value>
</property>
<property name="url">
<value>jdbc:hsqldb:db/appfuse</value>
</property>
<property name="username">
<value>sa</value>
</property>
<property name="password">
<value></value>
</property> </bean>
<!-- Hibernate SessionFactory -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
<property name="dataSource">
<ref local="dataSource"/>
</property>
<property name="mappingResources">
<list>
<value>org/appfuse/model/User.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect"> net.sf.hibernate.dialect.HSQLDialect </prop>
<prop key="hibernate.hbm2ddl.auto">create</prop>
</props>
</property>
</bean>
<!-- Transaction manager for a single Hibernate SessionFactory (alternative to JTA) -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate.HibernateTransactionManager">
<property name="sessionFactory">
<ref local="sessionFactory"/>
</property>
</bean>
</beans>
第一bean代表HSQL數(shù)據(jù)庫,Spring僅僅是調(diào)用LocalSessionFactoryBeanr的setDataSource(DataSource)使之工作。如果你想用JNDI DataSource替換,可以bean的定義改成類似下面的幾行:
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>java:comp/env/jdbc/appfuse</value>
</property>
</bean>
hibernate.hbm2ddl.auto 屬性在sessionFactory定義中,這個(gè)屬性是為了在應(yīng)用啟動(dòng)時(shí)自動(dòng)創(chuàng)建表,也可能是update或create-drop。
最后一個(gè)bean是transactionManager(你也可以使用JTA transaction),在處理跨越兩個(gè)數(shù)據(jù)庫的分布式的事務(wù)處理中尤其重要。如果你想使用jta transaction manager ,將此bean的class屬性改成
org.springframework.transaction.jta.JtaTransactionManager
下面實(shí)現(xiàn)UserDAO類。


用hibernate 實(shí)現(xiàn)UserDAO
為了實(shí)現(xiàn)Hibernate UserDAO,需要完成以下幾步:
1.在src/org/appfuse/dao/hibernate(你要新建這個(gè)目錄/包) 新建一個(gè)文件UserDAOHibernate.ava。這個(gè)類繼承了HibernatDaoSupport類,并實(shí)現(xiàn)了UserDAO接口。
package org.appfuse.dao.hibernate;
// use your IDE to handle imports
public class UserDAOHibernate extends HibernateDaoSupport implements UserDAO {
private Log log = LogFactory.getLog(UserDAOHibernate.class);
public List getUsers() {
return getHibernateTemplate().find("from User");
}
public User getUser(Long id) {
return (User) getHibernateTemplate().get(User.class, id);
}
public void saveUser(User user) {
getHibernateTemplate().saveOrUpdate(user);
if (log.isDebugEnabled()) {
log.debug( userId set to: + user.getID());
}
}
public void removeUser(Long id) {
Object user = getHibernateTemplate().load(User.class, id);
getHibernateTemplate().delete(user);
}
}
Spring的HibernateDaoSupport類是一個(gè)方便實(shí)現(xiàn)Hibernate DAO的超類,你可以它的一些有用的方法,來獲得Hibernate DAO或是SessionFactory。最方便的方法是getHibernateTemplate(),它返回一個(gè)HibernateTempalte對象。這個(gè)模板把檢測式異常(checked exception)包裝成實(shí)時(shí)式異常(runtime exception),這使得你的DAO 接口無需拋出Hibernate異常。
程序還沒有把UserDAO綁定到 UserDAOHibernate上,必須創(chuàng)建它們之間的關(guān)聯(lián)。
2.在Spring配置文件(web/WEB-INF/applictionContext.xml)中添加以下內(nèi)容:
<bean id="userDAO" class="org.appfuse.dao.hibernate.UserDAOHibernate">
<property name="sessionFactory">
<ref local="sessionFactory"/>
</property>
</bean>
這樣就在你的UserDAOHibernate(從HibernateDaoSupport的setSessionFactory繼承)中建了一個(gè)Hibernate Session Factory。Spring會檢測一個(gè)Session(也就是,它在web層是開放的)是否已經(jīng)存在,并且直接使用它,而不是新建一個(gè)。這樣你可以使用Spring流行的“Open Session in View ”模式來載入collections。


進(jìn)行單元測試,來驗(yàn)證DAO的CRUD操作
在進(jìn)行第一個(gè)測試之前,把你的日志級別從“INFO”調(diào)到“WARN”。
1.把log4j.xml(在web/WEB-INF/classes目錄下)中<level value="INFO"/>改為<level value="WARN"/>。
2.鍵入ant test來運(yùn)行UserDAOTest。如果你有多個(gè)測試,你必須用ant test -Dtestcase=UserDAOTest來指定要運(yùn)行的測試。運(yùn)行之后,你會如下類似的一些日志信息。


創(chuàng)建Manager,聲明事務(wù)處理(create manager and declare transactions)
J2EE開發(fā)中建議將各層進(jìn)行分離。換言之,不要把數(shù)據(jù)層(DAOs)和web層(servlets)混在一起。使用Spring,很容易做到這一點(diǎn),但使用“業(yè)務(wù)代理”(business delegate)模式, 可以對這些層進(jìn)一步分離。
使用業(yè)務(wù)代理模式的主要原因是:
● 大多數(shù)持久層組件執(zhí)行一個(gè)業(yè)務(wù)邏輯單元,把邏輯放在一非web類中的最大好處是,web service或是豐富平臺客戶端(rich platform client)可以像使用servlet一樣來用同一API。
● 大多數(shù)業(yè)務(wù)邏輯都在同一方法中完成,當(dāng)然可能多個(gè)DAO。使用業(yè)務(wù)代理,使得你可以高級業(yè)務(wù)代理層(level)使用Spring的聲明式業(yè)務(wù)代理特性。
MyUsers應(yīng)用中UserManager和UserDAO擁有相同的一個(gè)方法。主要不同的是Manager對于web更為友好(web-friendly),它可以接受Strings,而UserDAO只能接受Longs, 并且它可以在saveUser方法中返回一個(gè)User對象。這在插入一個(gè)新用戶比如,要獲得主鍵,是非常方便的。Manager(或稱為業(yè)務(wù)代理)中也可以添加一些應(yīng)用中所需要的其它業(yè)務(wù)邏輯。
1.啟動(dòng)“service”層,在test/org/appfuse/service(你必須先建好這個(gè)目錄)中新建一個(gè)UserManagerTest類,這個(gè)類繼承了JUnit 的 TestCase類,代碼如下:
package org.appfuse.service;
// use your IDE to handle imports
public class UserManagerTest extends TestCase {
private static Log log = LogFactory.getLog(UserManagerTest.class);
private ApplicationContext ctx;
private User user;
private UserManager mgr;
protected void setUp() throws Exception {
String[] paths = {"/WEB-INF/applicationContext.xml"};
ctx = new ClassPathXmlApplicationContext(paths);
mgr = (UserManager) ctx.getBean("userManager");
}
protected void tearDown() throws Exception {
user = null;
mgr = null;
}
// add testXXX methods here
public static void main(String[] args) {
junit.textui.TestRunner.run(UserDAOTest.class);
}
在setup方法中,使用ClassPathXmlApplicationContext把a(bǔ)pplicationContext.xml載入變量ApplicationContext中。載入ApplictionContext有幾種途徑,從classpath中,文件系統(tǒng),或web 應(yīng)用內(nèi)。   2.輸入第一個(gè)測試方法的代碼,來驗(yàn)證Manager成功完成添加或是刪除User對象。
public void testAddAndRemoveUser() throws Exception {
user = new User();
user.setFirstName("Easter");
user.setLastName("Bunny");
user = mgr.saveUser(user);
assertTrue(user.getId() != null);
if (log.isDebugEnabled()) {
log.debug("removing user...");
}
String userId = user.getId().toString();
mgr.removeUser(userId);
user = mgr.getUser(userId);
if (user != null) {
fail("User object found in database!");
}
}
這個(gè)測試實(shí)際上是一個(gè)集成測試(integration test),而不是單元測試(unit test)。為了更接近單元測試,可以使用EasyMock或是類似工具來“偽裝”(fake) DAO。這樣,就不必關(guān)心ApplictionContext和任何依賴Spring API 的東西。建議在測試項(xiàng)目依賴(Hibernate,Spring,自己的類)的內(nèi)部構(gòu)件,包括數(shù)據(jù)庫。                             3.為了編譯UserManagerTest,在src/org/appfuse/service中新建一個(gè)接口──UserManager。在org.appfuse.service包中創(chuàng)建這個(gè)類,代碼如下:
package org.appfuse.service;
// use your IDE to handle imports
public interface UserManager {
public List getUsers();
public User getUser(String userId);
public User saveUser(User user);
public void removeUser(String userId);
}
4.建一個(gè)名為org.appfuse.service.impl的子包,新建一個(gè)類實(shí)現(xiàn)UserManager 接口的。
package org.appfuse.service.impl;
// use your IDE to handle imports
public class UserManagerImpl implements UserManager {
private static Log log = LogFactory.getLog(UserManagerImpl.class);
private UserDAO dao;
public void setUserDAO(UserDAO dao) {
this.dao = dao;
}
public List getUsers() {
return dao.getUsers();
}
public User getUser(String userId) {
User user = dao.getUser(Long.valueOf(userId));
if (user == null) {
log.warn("UserId ‘" + userId + "‘ not found in database.");
}
return user;
}
public User saveUser(User user) {
dao.saveUser(user);
return user;
}
public void removeUser(String userId) {
dao.removeUser(Long.valueOf(userId));
}
}
這個(gè)類看不出你在使用Hibernate。當(dāng)你打算把持久層轉(zhuǎn)向一種不同的技術(shù)時(shí),這樣做很重要。
這個(gè)類提供一個(gè)私有dao成員變量,和setUserDAO方法一樣。這樣能夠讓Spring能夠表演“依賴性綁定”魔術(shù)(perform “dependency binding” magic),把這些對象扎在一起。在使用mock重構(gòu)這個(gè)類時(shí),你必須在UserManager接口中添加serUserDAO 方法。
5.在進(jìn)行測試之前,配置Spring,讓getBeans返回一個(gè)UserManagerImpl類。在
web/WEB-INF/applicationContext.xml文件中,添加以下幾行:
<bean id="userManager" class="org.appfuse.service.UserManagerImpl">
<property name="userDAO">
<ref local="userDAO"/>
</property>
</bean>
唯一的問題,你還沒有使Spring的AOP,特別是聲明式的事務(wù)處理發(fā)揮作用。
6.為了達(dá)到目的,使用ProxyFactoryBean代替userManager。ProxyFactoryBean是一個(gè)類的不同的實(shí)現(xiàn),這樣AOP 能夠解釋和覆蓋調(diào)用的方法。在事務(wù)處理中,使用TransactionProxyFactoryBean代替UserManagerImpl 類。在context文件中添加下面bean的定義:
<bean id="userManager"
class="org.springframework.transaction.interceptor.TransactionProxy
FactoryBean">
<property name="transactionManager">
<ref local="transactionManager"/>
</property>
<property name="target">
<ref local="userManagerTarget"/>
</property>
<property name="transactionAttributes">
<props>
<prop key="save*">PROPAGATION_REQUIRED</prop>
<prop key="remove*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
從這個(gè)xml代碼片斷中可以看出,TransactionProxyFactoryBean必須有一個(gè)transactionManager 屬性設(shè)置和transactionAttributes 定義。
7.讓事務(wù)處理代理服務(wù)器(Transaction Proxy)知道你要模仿的對象:
userManagerTarget。作為新bean的一部分,把原來的userManager bean改成擁有一個(gè)userManagerTarget的id屬性。
編輯applictionContext.xml添加 userManager 和 userManagerTarget 的定義后,運(yùn)行ant test -Dtestcase=UserManager ,看看終端輸出:
8.如果你想看看事務(wù)處理的執(zhí)行和提交情況,在log4j.xml中添加:
<logger name="org.springframework.transaction">
<level value="DEBUG"/>
<!-- INFO does nothing -->
</logger>
重新運(yùn)行測試,將看到大量日志信息,如它相關(guān)的對象,事務(wù)的創(chuàng)建和提交等。測試完畢,最好刪除上面的日志定義(logger)。
祝賀你!你已經(jīng)實(shí)現(xiàn)了一個(gè)web應(yīng)用的Spring/Hibernate后端解決方案。并且你已經(jīng)用AOP和聲明式業(yè)務(wù)處理配置好了業(yè)務(wù)代理。了不起,自我鼓勵(lì)一下!(This is no small feat; give yourself a pat on the back!)


對象struts Action進(jìn)行單元測試
業(yè)務(wù)代理和DAO都起作用,我們看看MVC框架吸盤(sucker)的上部。停!不止這些吧,你可以進(jìn)行C(Controller),不是V(View)。為了管理用戶建一個(gè)Struts Action,繼續(xù)進(jìn)行驅(qū)動(dòng)測試(Test-Driven)開發(fā)。
Equinox是為Struts配置的。配置Struts需要在web.xml 中進(jìn)行一些設(shè)置,并在web/WEB-INF下定義一個(gè)struts-config.xml文件。由于Struts開發(fā)人員比較多,這里先使用Struts。
在test/org/appfuse/web目錄下新建一個(gè)文件UserActionTest.java,開發(fā)你的第一個(gè)Struts Aciton單元測試。文件內(nèi)容如下:
package org.appfuse.web;
// use your IDE to handle imports
public class UserActionTest extends MockStrutsTestCase {
public UserActionTest(String testName) {
super(testName);
}
public void testExecute() {
setRequestPathInfo("/user");
addRequestParameter("id", "1");
actionPerform();
verifyForward("success");
verifyNoActionErrors();
}
}
為web層創(chuàng)建Action和Model(DynaActionForm)
1.在src/org/appfuse/web下新建一個(gè)文件UserAction.java。這個(gè)擴(kuò)展了DispatchAction,你可以花幾分鐘,在這個(gè)類中,創(chuàng)建CRUD方法。
package org.appfuse.web;
// use your IDE to handle imports
public class UserAction extends DispatchAction {
private static Log log = LogFactory.getLog(UserAction.class);
public ActionForward execute(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception{
request.getSession().setAttribute("test", "succeeded!");
log.debug("looking up userId: " + request.getParameter("id"));
return mapping.findForward("success");
}
}
2.為了配置Struts,使”/user”這個(gè)請求路徑代表其它。在web/WEB-INF/strutsconfig.xml中加入一個(gè)action-mapping。打開文件加入:
<action path="/user" type="org.appfuse.web.UserAction">
<forward name="success" path="/index.jsp"/>
</action>
3.執(zhí)行命令 ant test -Dtestcase=UserAction ,你會看到友好的“BUILD SUCCESSFUL” 信息。
4.在struts-config.xml中添加form-bean定義。對于Struts ActionForm,使用DynaActionForm,這是一個(gè)javabean,可以從XML定義中動(dòng)態(tài)的創(chuàng)建。
<form-bean name="userForm" type="org.apache.struts.action.DynaActionForm">
<form-property name="user" type="org.appfuse.model.User"/>
</form-bean>
這里沒有使用具體的ActionForm,因?yàn)橹皇褂靡粋€(gè)User 對象的包裝器。理想情況下,你可以User 對象,但會失去Struts環(huán)境下的一些特性:驗(yàn)證屬性(validate properties),checkbox復(fù)位(reset checkboxs)。后面,將演示用Spring怎幺會更加簡單,它可以讓你在web層使用 User對象。                                                                 5.修改action定義,在request中使用這個(gè)form。
<action path="/user" type="org.appfuse.web.UserAction" name="userForm"
scope="request">
<forward name="success" path="/index.jsp"/>
</action>
6.修改UserActionTest,測試不同的 CRUD方法。
public class UserActionTest extends MockStrutsTestCase {
public UserActionTest(String testName) {
super(testName);
}
// Adding a new user is required between tests because HSQL creates
// an in-memory database that goes away during tests.
public void addUser() {
setRequestPathInfo("/user");
addRequestParameter("method", "save");
addRequestParameter("user.firstName", "Juergen");
addRequestParameter("user.lastName", "Hoeller");
actionPerform();
verifyForward("list");
verifyNoActionErrors();
}
public void testAddAndEdit() {
addUser();
// edit newly added user
addRequestParameter("method", "edit");
addRequestParameter("id", "1");
actionPerform();
verifyForward("edit");
verifyNoActionErrors();
}
public void testAddAndDelete() {
addUser();
// delete new user
setRequestPathInfo("/user");
addRequestParameter("method", "delete");
addRequestParameter("user.id", "1");
actionPerform();
verifyForward("list");
verifyNoActionErrors();
}
public void testList() {
addUser();
setRequestPathInfo("/user");
addRequestParameter("method", "list");
actionPerform();
verifyForward("list");
verifyNoActionErrors();
List users = (List) getRequest().getAttribute("users");
assertNotNull(users);
assertTrue(users.size() == 1);
}
}
7.修改UserAction,這樣測試程序才能通過,并能處理(客戶端)請求。最簡單的方法是添加edit,save和delete方法,請確保你事先已經(jīng)刪除了execute方法。下面是修改過的UserAction.java文件。
public class UserAction extends DispatchAction {
private static Log log = LogFactory.getLog(UserAction.class);
private UserManager mgr = null;
public void setUserManager(UserManager userManager) {
this.mgr = userManager;
}
public ActionForward delete(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception{
if (log.isDebugEnabled()) {
log.debug("entering ‘delete‘ method...");
}
mgr.removeUser(request.getParameter("user.id"));
ActionMessages messages = new ActionMessages();
messages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage
("user.deleted"));
saveMessages(request, messages);
return list(mapping, form, request, response);
}
public ActionForward edit(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception {
if (log.isDebugEnabled()) {
log.debug("entering ‘edit‘ method...");
}
DynaActionForm userForm = (DynaActionForm) form;
String userId = request.getParameter("id");
// null userId indicates an add
if (userId != null) {
User user = mgr.getUser(userId);
if (user == null) {
ActionMessages errors = new ActionMessages();
errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage
("user.missing"));
saveErrors(request, errors);
return mapping.findForward("list");
}
userForm.set("user", user);
}
return mapping.findForward("edit");
}
public ActionForward list(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception {
if (log.isDebugEnabled()) {
log.debug("entering ‘list‘ method...");
}
request.setAttribute("users", mgr.getUsers());
return mapping.findForward("list");
}
public ActionForward save(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception {
if (log.isDebugEnabled()) {
log.debug("entering ‘save‘ method...");
}
DynaActionForm userForm = (DynaActionForm) form;
mgr.saveUser((User)userForm.get("user"));
ActionMessages messages = new ActionMessages();
messages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage
("user.saved"));
saveMessages(request, messages);
return list(mapping, form, request, response);
}
}
接下來,你可以修改這個(gè)類的CRUD方法。
8.修改struts-config.xml,使用ContextLoaderPlugin來配置Spring的UserManager設(shè)置。要配置ContextLoaderPlugin,把下面內(nèi)容添加到你的struts-config.xml中。
<plug-in className= org.springframework.web.struts.ContextLoaderPlugIn >
<set-property property= contextConfigLocation value= /WEBINF/
applicationContext.xml, /WEB-INF/action-servlet.xml />
</plug-in>
默認(rèn)情況下這個(gè)插件會載入action-servlet.xml文件。要讓Test Action 知道你的Manager,必須配置這個(gè)插件,如同載入applicationContext。
9.對每個(gè)使用Spring的action,定義一個(gè)type=
org.springframework.web.struts.DelegatingActionProxy 的action-mapping,為每個(gè)
Spring action 聲明一個(gè)配對的Spring bean。這樣修改一下你的action mapping就能使用
這個(gè)新類。
10.為DispatchAction修改action mapping。
為了讓DispatchAction運(yùn)行,在mapping中添加參數(shù)parameter= “method” , 它表示(在一個(gè)URL 或是隱藏字段hidden field)要調(diào)用的方法,同時(shí)轉(zhuǎn)向(forwards)edit和list forwards(參考能進(jìn)行CRUD操作的UserAction類).
<action path="/user"
type="org.springframework.web.struts.DelegatingActionProxy" name="userForm"
scope="request" parameter="method">
<forward name="list" path="/userList.jsp"/>
<forward name="edit" path="/userForm.jsp"/>
</action>
確保web目錄下已經(jīng)建好userList.jsp和userForm.jsp兩個(gè)文件。暫時(shí)不必在文件中寫入內(nèi)容。
11.作為插件的一部分,配置Spring,將/user bean設(shè)置成“UserManager”。在
we/WEB-INF/action-servlet.xml中添加以下定義。
<bean name="/user" class="org.appfuse.web.UserAction" singleton="false">
<property name="userManager">
<ref bean="userManager"/>
</property>
</bean>
定義中,使用singleton= false 。這樣就為每個(gè)請求,新建一個(gè)Action,減少線程安全的Action需求。不管是Manager還是DAO都有成員變量,可能不需要這些屬性(默認(rèn)singleton= true )。
12.在message.properties上配置資源綁定。
在userAction類中,在完成一些操作后,會顯示“成功”或是“錯(cuò)誤”頁面,這些信息的鍵可以存放在這個(gè)應(yīng)用的ResourceBundle(或messages.properties文件中)。特別是:
● user.saved
● user.missing
● user.deleted
把這些鍵存入web/WEB-INF/下的messages.properties文件中。例如:
user.saved=User has been saved successfully.
user.missing=No user found with this id.
user.deleted=User successfully deleted.
這個(gè)文件通過struts-config.xml中的<message-resources>元素進(jìn)行加載。
<message-resources parameter="messages"/>
運(yùn)行 ant test -Dtestcase=UserAction. 輸出結(jié)果如下:


填充JSP文件,這樣可以通過瀏覽器來進(jìn)行CRUD操作
1.在你的jsp文件(userFrom.jsp 和userList.jsp)中添加代碼,這樣它們可以表示actions的結(jié)果。如果還事先準(zhǔn)備,在web目錄下建一個(gè)文件 userList.jsp。添加一些代碼你就可以看到數(shù)據(jù)庫中所有的用戶資料。在下面代碼中,第一行包含(include)了一個(gè)文件taglibs.jsp。這個(gè)文件包含了應(yīng)用所有JSP Tag Library的聲明。大部分是Struts Tag,JSTL和SiteMesh(用來美化JSP頁面)。
<%@ include file="/taglibs.jsp"%>
<title>MyUsers ~ User List</title>
<button onclick="location.href=‘user.do?method=edit‘">Add User</button>
<table class="list">
<thead>
<tr>
<th>User Id</th>
<th>First Name</th>
<th>Last Name</th>
</tr>
</thead>
<tbody>
<c:forEach var="user" items="${users}" varStatus="status">
<c:choose>
<c:when test="${status.count % 2 == 0}">
<tr class="even">
</c:when>
<c:otherwise>
<tr class="odd">
</c:otherwise>
</c:choose>
<td><a href="user.do?method=edit&id=${user.id}">${user.id}</a></ td>
<td>${user.firstName}</td>
<td>${user.lastName}</td>
</tr>
</c:forEach>
</tbody>
</table>
你可以有一行“標(biāo)題頭”(headings)(在<thead>中)。JSTL 的 <c:forEach>進(jìn)行結(jié)果迭代,顯示所有的用戶。
2.向數(shù)據(jù)庫添加一些數(shù)據(jù),你就會看到一些真實(shí)(actual)的用戶(users)。你可以選擇一種方法,手工添加,使用ant browse,或是在build.xml中添加如下的target:
<target name="populate">
<echo message="Loading sample data..."/>
<sql driver="org.hsqldb.jdbcDriver" url="jdbc:hsqldb:db/appfuse"
userid="sa" password="">
<classpath refid="classpath"/>
INSERT INTO app_user (id, first_name, last_name) values (5, ‘Julie‘,
‘Raible‘);
INSERT INTO app_user (id, first_name, last_name) values (6, ‘Abbie‘,
‘Raible‘);
</sql>
</target>
警告:為了使內(nèi)置的HSQLDB正常工作,從能運(yùn)行ant的目錄下啟動(dòng)tomcat。在Unix/Linux鍵入 $CATALINA_HOME/bin/startup.sh ,在win 上 %CATALINA_HOME%\bin\startup.bat 。


通過瀏覽器驗(yàn)證JSP的功能
1.有了這個(gè)JSP文件和里面的樣例數(shù)據(jù),就可以通過瀏覽器來查看這個(gè)頁面。運(yùn)行antdeploy reload,轉(zhuǎn)到地址
http://localhost:8080/myusers/user.do?method=list。出現(xiàn)以下畫面:
2.這個(gè)樣例中,缺少國際化的頁面標(biāo)題頭,和列標(biāo)題頭(column headings)。在web/WEB-INF/classes中messages.properties中加入一些鍵:
user.id=User Id
user.firstName=First Name
user.lastName=Last Name
修改過的國際化的標(biāo)題頭如下:
<thead>
<tr>
<th><bean:message key= user.id /></th>
<th><bean:message key= user.firstName /></th>
<th><bean:message key= user.lastName /></th>
</tr>
</thead>
注意同樣可以使用JSTL的<fmt:message key= ... >標(biāo)簽。如果想為表添加排序和分布功能,可以使用 Display Tag (http://displaytag.sf.net)。下面是使用這個(gè)標(biāo)簽的一個(gè)樣例:
<display:table name="users" pagesize="10" styleClass="list"
requestURI="user.do?method=list">
<display:column property="id" paramId="id" paramProperty="id"
href="user.do?method=edit" sort="true"/>
<display:column property="firstName" sort="true"/>
<display:column property="lastName" sort="true"/>
</display:table>
請參考display tag文檔中有關(guān)的列標(biāo)題頭國際化的部分。
3.你已經(jīng)建好了顯示(list),創(chuàng)建form就可以添加/編輯(add/edit)數(shù)據(jù)。如果事先沒有準(zhǔn)備,可以在web目錄下新建一個(gè)userForm.jsp文件。向文件中添加以下代碼:
<%@ include file="/taglibs.jsp"%>
<title>MyUsers ~ User Details</title>
<p>Please fill in user‘s information below:</p>
<html:form action="/user" focus="user.firstName">
<input type="hidden" name="method" value="save"/>
<html:hidden property="user.id"/>
<table>
<tr>
<th><bean:message key="user.firstName"/>: </th>
<td><html:text property="user.firstName"/></td>
</tr>
<tr>
<th><bean:message key="user.lastName"/>: </th>
<td><html:text property="user.lastName"/></td>
</tr>
<tr>
<td></td>
<td> <html:submit styleClass="button">Save</html:submit>
<c:if test="${not empty param.id}">
<html:submit styleClass="button" onclick="this.form.method.value=‘delete‘">
Delete</html:submit>
</c:if>
</td>
</table>
</html:form>
注意:如果你正在開發(fā)一個(gè)國際化的應(yīng)用,把上面的信息和按鈕標(biāo)簽替換成
<bean:message> 或是 <fmt:message> 標(biāo)簽。這是一個(gè)很好的練習(xí)。對于信息message,建議把key 名稱寫成”pageName.message”(例如:userForm.message )的形式,按鈕名字寫成“button.name”(例如button.save)。
4.運(yùn)行ant deploy ,通過瀏覽器頁面的user form來進(jìn)行 CRUD 操作。
最后大部分web應(yīng)用都需要驗(yàn)證。下一節(jié)中,配置struts validator,要求用戶的last name 是必填的。


用commons Validator添加驗(yàn)證
為了在Struts中使用驗(yàn)證,執(zhí)行以下幾步:
1.在struts-config.xml中添加ValidatorPlugin。
2.創(chuàng)建validation.xml,指定lastName為必填字段。
3.僅為save()方法設(shè)置驗(yàn)證(validation)。
4.在message.properties中添加validation errors。
在struts-config.xml中添加ValidatorPlugin
配置Validatorp plugins,添加以下片斷到struts-config.xml(緊接著Spring plugin):
<plug-in className="org.apache.struts.validator.ValidatorPlugIn">
<set-property property="pathnames" value="/WEB-INF/validatorrules.
xml, /WEB-INF/validation.xml"/>
</plug-in>
從這里你可以看出,Validator會查找WEB-INF下的兩個(gè)文件validator-ruls.xml和validation.xml。第一個(gè)文件,validator-rules.xml,是一個(gè)標(biāo)準(zhǔn)文件,作為Struts的一部分發(fā)布,它定義了所有可用的驗(yàn)證器(validators),功能和客戶端的javascript類似。
第二個(gè)文件,包含針對每個(gè) form的驗(yàn)證規(guī)則。
創(chuàng)建validation.xml,指定lastName為必填字段
validation.xml文件中包含很多DTD定義的標(biāo)準(zhǔn)元素。但你只需要如下所示的<form>和<field>,更多信息請參閱Validator的文檔。在web/WEB-INF/validation.xml中的formvalidation標(biāo)簽之間添加form-set元素。
<formset>
<form name="userForm">
<field property="user.lastName" depends="required">
</form>
</formset>
把 DynaActionForm 改為 DynaValidatorForm
把struts-config.xml中的DynaActionForm 改為 DynaValidatorForm。
<form-bean name="userForm"
type="org.apache.struts.validator.DynaValidatorForm">
為save()方法設(shè)置驗(yàn)證(validation)
使用Struts DispatchAction 弊端是,驗(yàn)證會在映射層(mapping level)激活。為了在list和edit頁面關(guān)閉驗(yàn)證。你必須單獨(dú)建一個(gè)”validate=false”的映射。例如,AppFuse的UserAction有兩個(gè)映射:”/editUser” 和”/listUser”。然而有一個(gè)更簡單的方法,可以減少xml,只是多了一些java代碼。
1.在/user映射中,添加validate=false 。
2.修改UserAction中的save()方法,調(diào)用form.validate()方法,如果發(fā)現(xiàn)錯(cuò)誤,返回編輯頁面。
if (log.isDebugEnabled()) {
log.debug("entering ‘save‘ method...");
}
// run validation rules on this form
ActionMessages errors = form.validate(mapping, request);
if (!errors.isEmpty()) {
saveErrors(request, errors);
return mapping.findForward("edit");
}
DynaActionForm userForm = (DynaActionForm) form;
當(dāng)dispatchAction運(yùn)行時(shí),與附帶一個(gè)屬性的兩個(gè)映射相比,這樣更加簡潔。但用兩個(gè)映射也有一些優(yōu)點(diǎn):
● 驗(yàn)證失敗時(shí),可以指定轉(zhuǎn)向”input”屬性。
● 在映射中可以添加“role”屬性,可以指定誰有訪問權(quán)限。例如,任何人都可以看到編輯(edit)頁面,但只有管理員可以保存(save)。
運(yùn)行ant deploy重新載入(reload),嘗試添加一個(gè)新用戶,不要填寫lastName。你會看到一個(gè)驗(yàn)證錯(cuò)誤,表明lastName是必填字段,如下所示:
Struts Validator的另一種比較好的特性是客戶端驗(yàn)證(client-side validation)。
4.在form標(biāo)簽(web/userForm.jsp)中添加”onsubmit”屬性,在form末尾添加
<html:javascript>。
<html:form action="/user" focus="user.firstName" onsubmit="return
validateUserForm(this)">
...
</html:form>
<html:javascript formName="userForm"/>
現(xiàn)在如果運(yùn)行ant deploy,試圖保存一個(gè)lastname為空的用戶,會彈出一個(gè)JavaScript提示:“Last Name is required”。這里有一個(gè)問題,這個(gè)帶JavaScript的form 把validator的JavaScript功能都載入了頁面。



Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=575085

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
使用 AppFuse 快速構(gòu)建 J2EE 應(yīng)用
使用AppFuse進(jìn)行開發(fā)的總結(jié)
Appfuse框架
struts2.2.+spring2.5+hibernate3.2實(shí)踐整合成功
Seven simple reasons to use AppFuse
Appfuse的Ant任務(wù)列表
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服