首先还是来看看 web.xml.
这个和我们之前看得例子不同,这里制定了spring 的配置文件的名称,就是 applicationContext-jdbc.xml。
如果不指定的话,默认就是 applicationContext.xml 了。
不过这个例子所体现的习惯和我们之前看过的例子不同,之前的例子都是这样:数据库相关的配置,会额外
放到另一个配置文件里。
我想这里应该是为了举这个可以改变默认配置文件的名称的例子而这么做的吧!
这部分很熟悉了,就是配置 servlet.
好,接下来我们就看看 applicationContext-jdbc.xml 。
这些配置应该是相当熟悉了:定义属性编辑器,定义数据源,定义事务管理。
不过这里的事务管理里,用了一个 “preInterceptors” 拦截器。
再往下的配置就都是 JMX 的配置。可以参考以下资料:
http://www.redsaga.com/spring_ref/2.0/html/jmx.html
接下来我们来看看 petclinic-servlet.xml.
这个不陌生了,这是spring 国际化的方式。
这是处理 url 的定义。
只是这里的命名有些别扭,处理一个url的名字竟然是 "xxxForm",和以往的习惯的不太相同。
定义了一个处理exception的 bean.
注意,exception 首先还是由容器来处理的,只有容易不处理的exception,才会由这个bean来处理。
这又是一个spring 灵活的配置用法。当你用 /welcome.htm 访问时,如果你不在controller里制定显示哪个
视图,那么 spring 就给你显示 welcomeView 这个视图。
又见到这个配置了!在 countries 例子里我们已经学过了。只是这里没有定义一个“父类”配置。
简单看看 /classes/views.properties 吧!
[quote]
welcomeView.(class)=org.springframework.web.servlet.view.JstlView
welcomeView.url=/WEB-INF/jsp/welcome.jsp
vetsView.(class)=org.springframework.web.servlet.view.JstlView
vetsView.url=/WEB-INF/jsp/vets.jsp
[/quote]
想象一下流程:如果访问了 /welcome.htm, 那么spring 会默认使用 welcomeView 视图,而通过这个配置
文件我们知道,welcomeView 视图会由 JstlView 来处理,并且显示的页面是 /WEB-INF/jsp/welcome.jsp。
好了,这个 viewResolver 到此为止,下面继续往下看。
这是定义一个 controller. 没什么特殊的。
这个配置我们太熟悉了,clinicControllerResolver 肯定是一个 MultiActionController 的子类。
用了这么多次 MultiActionController,我们是不是有一个疑问:MultiActionController如何使用
command object 呢?因为到目前为止,都没有提供这样的例子。
最简单的办法就是直接去看 MultiActionController 的代码!
我们看到这样一行代码。意思就是如果方法的最后一个参数不是 HttpSession 类型,那么就会认为是command object.
明白了吧,配置文件里其实不需要改动,只要在写方法的时候,再多加一个参数就可以了!
例如 原来是:
改成
就可以了!
回归正题。
好了,下面的那些配置都很普通,就是名字不太习惯 :)
配置文件已经看完了,只是奇怪为什么这个例子还是使用spring 1.2 的风格?
从已经学得这几个例子来看,spring 的特色就是注入,我们到目前为止还没看到有 “new”在代码里出现。
都是通过配置文件来注入变量的。
好,下面来看看代码。
代码倒也没什么难的,和以往不同的是这个例子定义了一个 AbstractClinicForm,这是一些其他的controller
的父类。
看了这些例子,发现 session form 的使用是频繁的。归根到底,session form 也是保存在session里,
如此频繁的将变量放到session 并不是好的方式吧!或许只是为了写例子方便吧!
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext-jdbc.xml</param-value>
</context-param>
这个和我们之前看得例子不同,这里制定了spring 的配置文件的名称,就是 applicationContext-jdbc.xml。
如果不指定的话,默认就是 applicationContext.xml 了。
不过这个例子所体现的习惯和我们之前看过的例子不同,之前的例子都是这样:数据库相关的配置,会额外
放到另一个配置文件里。
我想这里应该是为了举这个可以改变默认配置文件的名称的例子而这么做的吧!
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>petclinic</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>petclinic</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
这部分很熟悉了,就是配置 servlet.
好,接下来我们就看看 applicationContext-jdbc.xml 。
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="/WEB-INF/jdbc.properties"/>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<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>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="hsqlClinic" class="org.springframework.samples.petclinic.jdbc.HsqlJdbcClinic" lazy-init="true">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="mysqlClinic" class="org.springframework.samples.petclinic.jdbc.MySQLJdbcClinic" lazy-init="true">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="clinic" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManager"/>
<property name="target" ref="${petclinic.jdbcImplBeanName}"/>
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
<property name="preInterceptors" ref="callMonitor"/>
</bean>
<bean id="callMonitor" class="org.springframework.samples.petclinic.jmx.CallMonitoringInterceptor"/>
这些配置应该是相当熟悉了:定义属性编辑器,定义数据源,定义事务管理。
不过这里的事务管理里,用了一个 “preInterceptors” 拦截器。
再往下的配置就都是 JMX 的配置。可以参考以下资料:
http://www.redsaga.com/spring_ref/2.0/html/jmx.html
接下来我们来看看 petclinic-servlet.xml.
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="messages"/>
</bean>
这个不陌生了,这是spring 国际化的方式。
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/welcome.htm">clinicController</prop>
<prop key="/vets.htm">clinicController</prop>
<prop key="/findOwners.htm">findOwnersForm</prop>
<prop key="/owner.htm">clinicController</prop>
<prop key="/addOwner.htm">addOwnerForm</prop>
<prop key="/editOwner.htm">editOwnerForm</prop>
<prop key="/addPet.htm">addPetForm</prop>
<prop key="/editPet.htm">editPetForm</prop>
<prop key="/addVisit.htm">addVisitForm</prop>
</props>
</property>
</bean>
这是处理 url 的定义。
只是这里的命名有些别扭,处理一个url的名字竟然是 "xxxForm",和以往的习惯的不太相同。
<bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="org.springframework.dao.DataAccessException">dataAccessFailure</prop>
<prop key="org.springframework.transaction.TransactionException">dataAccessFailure</prop>
</props>
</property>
</bean>
定义了一个处理exception的 bean.
注意,exception 首先还是由容器来处理的,只有容易不处理的exception,才会由这个bean来处理。
<bean id="viewNameTranslator" class="org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator">
<property name="suffix" value="View"/>
</bean>
这又是一个spring 灵活的配置用法。当你用 /welcome.htm 访问时,如果你不在controller里制定显示哪个
视图,那么 spring 就给你显示 welcomeView 这个视图。
<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="basename" value="views"/>
</bean>
又见到这个配置了!在 countries 例子里我们已经学过了。只是这里没有定义一个“父类”配置。
简单看看 /classes/views.properties 吧!
[quote]
welcomeView.(class)=org.springframework.web.servlet.view.JstlView
welcomeView.url=/WEB-INF/jsp/welcome.jsp
vetsView.(class)=org.springframework.web.servlet.view.JstlView
vetsView.url=/WEB-INF/jsp/vets.jsp
[/quote]
想象一下流程:如果访问了 /welcome.htm, 那么spring 会默认使用 welcomeView 视图,而通过这个配置
文件我们知道,welcomeView 视图会由 JstlView 来处理,并且显示的页面是 /WEB-INF/jsp/welcome.jsp。
好了,这个 viewResolver 到此为止,下面继续往下看。
<bean id="clinicController" class="org.springframework.samples.petclinic.web.ClinicController">
<property name="methodNameResolver" ref="clinicControllerResolver"/>
<property name="clinic" ref="clinic"/>
</bean>
这是定义一个 controller. 没什么特殊的。
<bean id="clinicControllerResolver" class="org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver">
<property name="mappings">
<props>
<prop key="/welcome.htm">welcomeHandler</prop>
<prop key="/vets.htm">vetsHandler</prop>
<prop key="/owner.htm">ownerHandler</prop>
</props>
</property>
</bean>
这个配置我们太熟悉了,clinicControllerResolver 肯定是一个 MultiActionController 的子类。
用了这么多次 MultiActionController,我们是不是有一个疑问:MultiActionController如何使用
command object 呢?因为到目前为止,都没有提供这样的例子。
最简单的办法就是直接去看 MultiActionController 的代码!
// If last parameter isn't of HttpSession type, it's a command.
if (method.getParameterTypes().length >= 3 &&
!method.getParameterTypes()[method.getParameterTypes().length - 1].equals(HttpSession.class)) {
Object command = newCommandObject(method.getParameterTypes()[method.getParameterTypes().length - 1]);
params.add(command);
bind(request, command);
}
我们看到这样一行代码。意思就是如果方法的最后一个参数不是 HttpSession 类型,那么就会认为是command object.
明白了吧,配置文件里其实不需要改动,只要在写方法的时候,再多加一个参数就可以了!
例如 原来是:
public ModelAndView welcomeHandler(HttpServletRequest request, HttpServletResponse response) {
....
}
改成
public ModelAndView welcomeHandler(HttpServletRequest request, HttpServletResponse response, Account account) {
....
}
就可以了!
回归正题。
好了,下面的那些配置都很普通,就是名字不太习惯 :)
配置文件已经看完了,只是奇怪为什么这个例子还是使用spring 1.2 的风格?
从已经学得这几个例子来看,spring 的特色就是注入,我们到目前为止还没看到有 “new”在代码里出现。
都是通过配置文件来注入变量的。
好,下面来看看代码。
代码倒也没什么难的,和以往不同的是这个例子定义了一个 AbstractClinicForm,这是一些其他的controller
的父类。
看了这些例子,发现 session form 的使用是频繁的。归根到底,session form 也是保存在session里,
如此频繁的将变量放到session 并不是好的方式吧!或许只是为了写例子方便吧!