首先还是先看看 web.xml.
这个配置我们很熟悉了,定义了spring 要使用的配置文件。dataAccessContext-jta.xml 从名字上就可以看出来,
这是一个用于 jta 方式管理事务的配置文件。
这段配置也不陌生,就是定义了一些 servlet. 不过从 web.xml 里也可以知道,spring 注释掉了一些 structs 的配置,
也就是说如果你想整合 structs 和 spring, 那么只要开启那些注释就可以了。
看了一些 spring 的例子以后,我们对 spring 的配置习惯也有了一些了解。
首先,spring 肯定会有一个 applicationContext.xml , 这里定义的都是一些业务,事务控制的方面的内容。
其次,会有一个 servlet名字+"-servlet.xml" 的配置文件,如这里的 jpetstore-servlet.xml. 这里定义的都是请求的
处理的配置。如 controller , resolver 等。
最后,还有一个配置文件,要么是配置其它内容,如 imagedb 里的计划任务;要么是数据库连接及相关DAO类的配置,
例如本例的 dataAccessContext-local.xml 。
遵循 spring 的这些习惯,对于使用 spring 来创建项目,是十分有好处的。
那么下面我们就先看看 applicationContext.xml.
这个用法我们已经在 imagedb 例子里学过了,读取属性文件,用在配置文件里。
这些都是逻辑类的定义:2个校验类,1个逻辑处理类。
这个就是 spring 的最大特色之一了,利用 AOP 来管理事务。详细的信息可以察看
http://www.redsaga.com/spring_ref/2.0/html/aop.html
http://www.redsaga.com/spring_ref/2.0/html/transaction.html
好,接着去看看 dataAccessContext-local.xml 里的配置。
这里定义了一个 dataSource. ${jdbc.url} 不用说也知道怎么来的吧?
定义了一个事物管理的 bean.
这个是用来整个 ibatis 的配置,关于这个的详细信息,可以参考
http://www.redsaga.com/spring_ref/2.0/html/orm.html#orm-ibatis
这些都是对 DAO的类的定义。
到目前为止,我们应该可以看出来了,imagedb 例子里用的还是 spring 1.2 的配置方式,
而 jpetstore 里,用的则是 spring 2.0 的配置方式。
从这个例子里,两者最大的区别就是 事务的配置方式。spring2.0 用一种表达式的方式来配置事务,
这就使得事务的配置更加灵活。
好,下面继续看看 petstore-servlet.xml. 经验告诉我们,这个配置文件里定义的都是请求的处理,
以及视图的处理。
一开始,这个例子就给我们了一个新的视图处理方式。这个配置更容易懂,jsp 的文件放在 "/WEB-INF/jsp/spring/"里,
扩展名就是 “.jsp”。
这又是一个新的请求处理的定义。从这个名字“BeanNameUrlHandlerMapping”可以看出来,似乎是通过 bean 的名字来处理url请求。
果真不出所料。spring 直接定义了一个请求该由哪个类来处理。
这和 countries 和 imagedb 的例子都不同。
spring 果真是九九八十一招,但是到底哪一招更好?自己决定吧!
刚刚感叹完 spring 的招数之多,没想到下面的配置又给我们展示了一个新的招数。
从名字就可以看出来,这些操作都是属于安全级别的,所以,用了一个"interceptors"来做安全检查。
安全校验和业务逻辑,就好像零件一样非常容易的组装到到一起,想合并就合并,想拆卸就拆卸!
好了,配置文件看完了,下面来看看代码吧!
jetstore 是一个比较复杂的例子了,所以代码也比较多,有以下几个 package:
org.springframework.samples.jpetstore.dao
org.springframework.samples.jpetstore.dao.ibatis
org.springframework.samples.jpetstore.dao.ibatis.maps
org.springframework.samples.jpetstore.domain
org.springframework.samples.jpetstore.domain.logic
org.springframework.samples.jpetstore.service
org.springframework.samples.jpetstore.service.client
org.springframework.samples.jpetstore.web.spring
org.springframework.samples.jpetstore.web.structs
幸好 java 的世界一切都是那么有规有矩,看看这些package 的名字,也能猜到一二。
dao 里面应该都是数据库相关的。
domain, service 里,应该都是逻辑相关的
web 里,应该是 controller 相关的。
先从一个controller 看起吧。很多,挑 AccountFormController 看吧。
先看看构造函数。
这段代码作了4件事情:
第一件 设置这个controller 的 command object 保存在session 里。
第二件 设置 validateOnBinding = false. 为什么设置这个?为了能详细的解释,我们还是看看
SimpleFormController 的父类 AbstractFormController 的父类 BaseCommandController。
注意这个方法:
这段代码是调用 validator 的方法。注意这行:“onBindAndValidate(request, command, errors);”,
然后再看这个判断“if (this.validators != null && isValidateOnBinding() 。。。省略”
可见,这个例子重写了 onBindAndValidate 方法,同时设置 validateOnBinding = false,
就是不使用默认的校验方法。
其实在 BaseCommandController 里,onBindAndValidate 方法就是抽象方法。
第三件 设置了 commandName, 这是 sessionForm 所需要的。
第四件 设置显示的视图名称。
如果您用过 spring 1.2 的话,应该知道,对于这种会提交数据的controller来说,
会在配置文件里定义 command object (也就类似structs 里的form bean).
但是我们在配置文件里没有看到,看看代码里怎么写得吧!
原来这个controller 重载了 formBackingObject 来自己构造 command object.
之所以这么做,是因为 AccountForm 需要从 session 中拿数据。
这个 controller 里的 onSubmit 方法就是处理提交的请求了。
注意这个类中所使用的 “private PetStoreFacade petStore;”,是在配置文件里注入的,
并不是在 controller 里声称的。而且, petStore 里的方法都具有事务属性的。
比 EJB 简单,但是却做着EJB的工作 :)
再看看 AddItemToCartController 吧。
这个 controller 的父类是 Controller。
我想我们已经接触过3个controller了!只能说spring真的是灵活的framework,选择非常多,
目前接触的3种 controller 都各有特点,自己体会吧 :)
看看这个controller 的代码:
一行代码,却告诉我们,这个“WebUtils”类里肯定有不少好东西!快去看看吧,省得我们写重复的代码。
这个方法不用详细说,看名字就知道了:从 session 拿出一个变量,如果没有就创建它。
再来看看 OrderFormController 吧。
这个 controller 的父类是 AbstractWizardFormController。
我已经不再惊奇了。。。就是再看到5个controller 我也不惊奇了。
从这个名字可以看出来,这是一个类似向导功能的controller.
这部分内容你可以参考
http://www.redsaga.com/spring_ref/2.0/html/mvc.html#mvc-controller
的 “13.3.4. 命令控制器”的部分。
逐一看过这些controller 后,我们发现在这个例子中,
如果涉及到提交表单,就用 SimpleFormController,
如果有向导功能的,就用 AbstractWizardFormController,
否则,就直接继承 Controller.
逻辑部分和数据库部分代码就不说了,与 ibatis 的集成可以参考
http://www.redsaga.com/spring_ref/2.0/html/orm.html#orm-ibatis
看了这些例子,我们越来越觉得spring就是一个“大容器”,许多东西都可以轻易地整合到里面,
同时,spring 也十分灵活,好在目前看得这些例子,每个招数都是各有特点,也能让我们在
遇到问题时有所选择。
同时,这个例子的 annotation 目录下,提供了用 annotation 方式的例子。
但是,个人还是觉得 xml 更好一些--虽然有那么一点乱,但是管理起来很方便,而且,总会
有办法来简化 xml 的编写的。
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/dataAccessContext-local.xml /WEB-INF/applicationContext.xml
</param-value>
<!--
<param-value>
/WEB-INF/dataAccessContext-jta.xml /WEB-INF/applicationContext.xml
</param-value>
-->
</context-param>
这个配置我们很熟悉了,定义了spring 要使用的配置文件。dataAccessContext-jta.xml 从名字上就可以看出来,
这是一个用于 jta 方式管理事务的配置文件。
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>petstore</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<load-on-startup>3</load-on-startup>
</servlet>
<servlet>
<servlet-name>remoting</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>4</load-on-startup>
</servlet>
<servlet>
<servlet-name>axis</servlet-name>
<servlet-class>org.apache.axis.transport.http.AxisServlet</servlet-class>
<load-on-startup>5</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>petstore</servlet-name>
<!--
<servlet-name>action</servlet-name>
-->
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>remoting</servlet-name>
<url-pattern>/remoting/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>axis</servlet-name>
<url-pattern>/axis/*</url-pattern>
</servlet-mapping>
这段配置也不陌生,就是定义了一些 servlet. 不过从 web.xml 里也可以知道,spring 注释掉了一些 structs 的配置,
也就是说如果你想整合 structs 和 spring, 那么只要开启那些注释就可以了。
看了一些 spring 的例子以后,我们对 spring 的配置习惯也有了一些了解。
首先,spring 肯定会有一个 applicationContext.xml , 这里定义的都是一些业务,事务控制的方面的内容。
其次,会有一个 servlet名字+"-servlet.xml" 的配置文件,如这里的 jpetstore-servlet.xml. 这里定义的都是请求的
处理的配置。如 controller , resolver 等。
最后,还有一个配置文件,要么是配置其它内容,如 imagedb 里的计划任务;要么是数据库连接及相关DAO类的配置,
例如本例的 dataAccessContext-local.xml 。
遵循 spring 的这些习惯,对于使用 spring 来创建项目,是十分有好处的。
那么下面我们就先看看 applicationContext.xml.
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>WEB-INF/mail.properties</value>
<value>WEB-INF/jdbc.properties</value>
</list>
</property>
</bean>
这个用法我们已经在 imagedb 例子里学过了,读取属性文件,用在配置文件里。
<bean id="accountValidator" class="org.springframework.samples.jpetstore.domain.logic.AccountValidator"/>
<bean id="orderValidator" class="org.springframework.samples.jpetstore.domain.logic.OrderValidator"/>
<bean id="petStore" class="org.springframework.samples.jpetstore.domain.logic.PetStoreImpl">
<property name="accountDao" ref="accountDao"/>
<property name="categoryDao" ref="categoryDao"/>
<property name="productDao" ref="productDao"/>
<property name="itemDao" ref="itemDao"/>
<property name="orderDao" ref="orderDao"/>
</bean>
这些都是逻辑类的定义:2个校验类,1个逻辑处理类。
<aop:config>
<aop:advisor pointcut="execution(* *..PetStoreFacade.*(..))" advice-ref="txAdvice"/>
</aop:config>
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="insert*"/>
<tx:method name="update*"/>
<tx:method name="*" read-only="true"/>
</tx:attributes>
</tx:advice>
这个就是 spring 的最大特色之一了,利用 AOP 来管理事务。详细的信息可以察看
http://www.redsaga.com/spring_ref/2.0/html/aop.html
http://www.redsaga.com/spring_ref/2.0/html/transaction.html
好,接着去看看 dataAccessContext-local.xml 里的配置。
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
这里定义了一个 dataSource. ${jdbc.url} 不用说也知道怎么来的吧?
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
定义了一个事物管理的 bean.
<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="configLocation" value="WEB-INF/sql-map-config.xml"/>
<property name="dataSource" ref="dataSource"/>
</bean>
这个是用来整个 ibatis 的配置,关于这个的详细信息,可以参考
http://www.redsaga.com/spring_ref/2.0/html/orm.html#orm-ibatis
<bean id="accountDao" class="org.springframework.samples.jpetstore.dao.ibatis.SqlMapAccountDao">
<property name="sqlMapClient" ref="sqlMapClient"/>
</bean>
<bean id="categoryDao" class="org.springframework.samples.jpetstore.dao.ibatis.SqlMapCategoryDao">
<property name="sqlMapClient" ref="sqlMapClient"/>
</bean>
<bean id="productDao" class="org.springframework.samples.jpetstore.dao.ibatis.SqlMapProductDao">
<property name="sqlMapClient" ref="sqlMapClient"/>
</bean>
<bean id="itemDao" class="org.springframework.samples.jpetstore.dao.ibatis.SqlMapItemDao">
<property name="sqlMapClient" ref="sqlMapClient"/>
</bean>
<bean id="orderDao" class="org.springframework.samples.jpetstore.dao.ibatis.SqlMapOrderDao">
<property name="sqlMapClient" ref="sqlMapClient"/>
<property name="sequenceDao" ref="sequenceDao"/>
</bean>
<bean id="orderDao" class="org.springframework.samples.jpetstore.dao.ibatis.MsSqlOrderDao">
<property name="sqlMapClient" ref="sqlMapClient"/>
<property name="sequenceDao" ref="sequenceDao"/>
</bean>
<bean id="sequenceDao" class="org.springframework.samples.jpetstore.dao.ibatis.SqlMapSequenceDao">
<property name="sqlMapClient" ref="sqlMapClient"/>
</bean>
这些都是对 DAO的类的定义。
到目前为止,我们应该可以看出来了,imagedb 例子里用的还是 spring 1.2 的配置方式,
而 jpetstore 里,用的则是 spring 2.0 的配置方式。
从这个例子里,两者最大的区别就是 事务的配置方式。spring2.0 用一种表达式的方式来配置事务,
这就使得事务的配置更加灵活。
好,下面继续看看 petstore-servlet.xml. 经验告诉我们,这个配置文件里定义的都是请求的处理,
以及视图的处理。
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/spring/"/>
<property name="suffix" value=".jsp"/>
</bean>
一开始,这个例子就给我们了一个新的视图处理方式。这个配置更容易懂,jsp 的文件放在 "/WEB-INF/jsp/spring/"里,
扩展名就是 “.jsp”。
<bean id="defaultHandlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
这又是一个新的请求处理的定义。从这个名字“BeanNameUrlHandlerMapping”可以看出来,似乎是通过 bean 的名字来处理url请求。
<bean name="/shop/addItemToCart.do" class="org.springframework.samples.jpetstore.web.spring.AddItemToCartController">
<property name="petStore" ref="petStore"/>
</bean>
<bean name="/shop/checkout.do" class="org.springframework.samples.jpetstore.web.spring.ViewCartController">
<property name="successView" value="Checkout"/>
</bean>
.... 省略
果真不出所料。spring 直接定义了一个请求该由哪个类来处理。
这和 countries 和 imagedb 的例子都不同。
spring 果真是九九八十一招,但是到底哪一招更好?自己决定吧!
<bean id="secureHandlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="signonInterceptor"/>
</list>
</property>
<property name="urlMap">
<map>
<entry key="/shop/editAccount.do" value-ref="secure_editAccount"/>
<entry key="/shop/listOrders.do" value-ref="secure_listOrders"/>
<entry key="/shop/newOrder.do" value-ref="secure_newOrder"/>
<entry key="/shop/viewOrder.do" value-ref="secure_viewOrder"/>
</map>
</property>
</bean>
<bean id="signonInterceptor" class="org.springframework.samples.jpetstore.web.spring.SignonInterceptor"/>
<bean id="secure_editAccount" class="org.springframework.samples.jpetstore.web.spring.AccountFormController">
<property name="petStore" ref="petStore"/>
<property name="validator" ref="accountValidator"/>
<property name="successView" value="index"/>
</bean>
.... 省略
刚刚感叹完 spring 的招数之多,没想到下面的配置又给我们展示了一个新的招数。
从名字就可以看出来,这些操作都是属于安全级别的,所以,用了一个"interceptors"来做安全检查。
安全校验和业务逻辑,就好像零件一样非常容易的组装到到一起,想合并就合并,想拆卸就拆卸!
好了,配置文件看完了,下面来看看代码吧!
jetstore 是一个比较复杂的例子了,所以代码也比较多,有以下几个 package:
org.springframework.samples.jpetstore.dao
org.springframework.samples.jpetstore.dao.ibatis
org.springframework.samples.jpetstore.dao.ibatis.maps
org.springframework.samples.jpetstore.domain
org.springframework.samples.jpetstore.domain.logic
org.springframework.samples.jpetstore.service
org.springframework.samples.jpetstore.service.client
org.springframework.samples.jpetstore.web.spring
org.springframework.samples.jpetstore.web.structs
幸好 java 的世界一切都是那么有规有矩,看看这些package 的名字,也能猜到一二。
dao 里面应该都是数据库相关的。
domain, service 里,应该都是逻辑相关的
web 里,应该是 controller 相关的。
先从一个controller 看起吧。很多,挑 AccountFormController 看吧。
先看看构造函数。
public AccountFormController() {
setSessionForm(true);
setValidateOnBinding(false);
setCommandName("accountForm");
setFormView("EditAccountForm");
}
这段代码作了4件事情:
第一件 设置这个controller 的 command object 保存在session 里。
第二件 设置 validateOnBinding = false. 为什么设置这个?为了能详细的解释,我们还是看看
SimpleFormController 的父类 AbstractFormController 的父类 BaseCommandController。
注意这个方法:
protected final ServletRequestDataBinder bindAndValidate(HttpServletRequest request, Object command)
throws Exception {
ServletRequestDataBinder binder = createBinder(request, command);
BindException errors = new BindException(binder.getBindingResult());
if (!suppressBinding(request)) {
binder.bind(request);
onBind(request, command, errors);
if (this.validators != null && isValidateOnBinding() && !suppressValidation(request, command, errors)) {
for (int i = 0; i < this.validators.length; i++) {
ValidationUtils.invokeValidator(this.validators[i], command, errors);
}
}
onBindAndValidate(request, command, errors);
}
return binder;
}
这段代码是调用 validator 的方法。注意这行:“onBindAndValidate(request, command, errors);”,
然后再看这个判断“if (this.validators != null && isValidateOnBinding() 。。。省略”
可见,这个例子重写了 onBindAndValidate 方法,同时设置 validateOnBinding = false,
就是不使用默认的校验方法。
其实在 BaseCommandController 里,onBindAndValidate 方法就是抽象方法。
第三件 设置了 commandName, 这是 sessionForm 所需要的。
第四件 设置显示的视图名称。
如果您用过 spring 1.2 的话,应该知道,对于这种会提交数据的controller来说,
会在配置文件里定义 command object (也就类似structs 里的form bean).
但是我们在配置文件里没有看到,看看代码里怎么写得吧!
protected Object formBackingObject(HttpServletRequest request) throws Exception {
UserSession userSession = (UserSession) WebUtils.getSessionAttribute(request, "userSession");
if (userSession != null) {
return new AccountForm(this.petStore.getAccount(userSession.getAccount().getUsername()));
}
else {
return new AccountForm();
}
}
原来这个controller 重载了 formBackingObject 来自己构造 command object.
之所以这么做,是因为 AccountForm 需要从 session 中拿数据。
这个 controller 里的 onSubmit 方法就是处理提交的请求了。
注意这个类中所使用的 “private PetStoreFacade petStore;”,是在配置文件里注入的,
并不是在 controller 里声称的。而且, petStore 里的方法都具有事务属性的。
比 EJB 简单,但是却做着EJB的工作 :)
再看看 AddItemToCartController 吧。
这个 controller 的父类是 Controller。
我想我们已经接触过3个controller了!只能说spring真的是灵活的framework,选择非常多,
目前接触的3种 controller 都各有特点,自己体会吧 :)
看看这个controller 的代码:
Cart cart = (Cart) WebUtils.getOrCreateSessionAttribute(request.getSession(), "sessionCart", Cart.class);
一行代码,却告诉我们,这个“WebUtils”类里肯定有不少好东西!快去看看吧,省得我们写重复的代码。
这个方法不用详细说,看名字就知道了:从 session 拿出一个变量,如果没有就创建它。
再来看看 OrderFormController 吧。
这个 controller 的父类是 AbstractWizardFormController。
我已经不再惊奇了。。。就是再看到5个controller 我也不惊奇了。
从这个名字可以看出来,这是一个类似向导功能的controller.
这部分内容你可以参考
http://www.redsaga.com/spring_ref/2.0/html/mvc.html#mvc-controller
的 “13.3.4. 命令控制器”的部分。
逐一看过这些controller 后,我们发现在这个例子中,
如果涉及到提交表单,就用 SimpleFormController,
如果有向导功能的,就用 AbstractWizardFormController,
否则,就直接继承 Controller.
逻辑部分和数据库部分代码就不说了,与 ibatis 的集成可以参考
http://www.redsaga.com/spring_ref/2.0/html/orm.html#orm-ibatis
看了这些例子,我们越来越觉得spring就是一个“大容器”,许多东西都可以轻易地整合到里面,
同时,spring 也十分灵活,好在目前看得这些例子,每个招数都是各有特点,也能让我们在
遇到问题时有所选择。
同时,这个例子的 annotation 目录下,提供了用 annotation 方式的例子。
但是,个人还是觉得 xml 更好一些--虽然有那么一点乱,但是管理起来很方便,而且,总会
有办法来简化 xml 的编写的。