spring 2.0 sample 之 Jpetstore


首先还是先看看 web.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 的编写的。

你可能感兴趣的:(spring,Web,xml,ibatis,配置管理)