23.5  数据验证

在Spring MVC框架中,同样可以对表单数据进行验证,把错误信息返回页面,提示用户重新输入。Spring MVC框架提供了Validator接口,用户通过实现该接口来对数据进行研制,Validator的代码如下:

package org.springframework.validation;  

publicinterface Validator {  

boolean supports(Class clazz);  

void validate(Object obj, Errors errors);  

}

上面代码有两个方法,如下所述。

supports:用来判断参数clazz是否是要验证的Class类。

validate:用来对参数obj进行研制,并把错误信息存入errors对象。

下面通过一个例子来说明如何使用该接口。

【示例23-11】在示例23-6的登录页面中输入userName和password去实现登录。在该例子中,判断用户名是否包含”@”。如果不包含该字符,则提示用户重新输入。编写UserNameValidator.Java,代码如下:

package com.examp.ch23;  

import  org.springframework.validation.Validator;  

import  org.springframework.validation.ValidationUtils;  

import  org.springframework.validation.Errors;  

publicclass UserNameValidator implements Validator {  

publicboolean supports(Class clzz) {   //重写supports()方法

return LoginBean.class.isAssignableFrom(clzz);  

   }  

publicvoid validate(Object obj, Errors e) {  

       LoginBean p = (LoginBean)obj;  

boolean isvalid = false;  

for(int i=0;i<p.getUserName().length();i++){  

//遍历字符串,看有没有@字符

if (p.getUserName().charAt(i)==’@’)  

               isvalid = true;             //如果有,invalid为true

       }  

if (isvalid == false) {         //如果isvalid为false,则添加错误信息

           e.rejectValue(“userName”, “invalidFormat”,”invalid userName!”);  

       }  

   }  

}

可以看出,上面代码中UserNameValidator重写了validate()方法。在该方法中遍历所有的字符串,如果没有@字符,添加一条错误信息。接下来在Sping的配置文件中添加配置,如下所示。

<bean id=”userNameValidator”class=”com.examp.ch23.UserNameValidator”>  

</bean>  

<bean id=”loginController”class=”com.examp.ch23.LoginController”>  

   <property name=”formView”>  

       <value>welcomeView</value>  

   </property>  

   <property name=”commandClass”>  

       <value>com.examp.ch23.LoginBean</value>  

   </property>  

   <property name=”validator”>  

       <ref bean=”userNameValidator”/>  

   </property>  

</bean>

可以看出,上面代码中首先用<bean>标签定义UserNameValidator类,然后在loginController中通过<property name=”validator”>定义该类的验证器。使用<ref bean=”userNameValidator”/>引用前面定义的userNameValidator。在浏览器中输入http://localhost:8080/ch23/login.jsp,然后再输入一些错误信息查看验证器的执行效果。

23.6  Spring和Struts 2整合

Spring 和Struts 2都是应用非常广泛的J2EE应用程序框架,Struts 2主要注重的是将视图层和控制层分开,但是不涉及对模型层的优化设计;而Spring除了实现Struts 2的MVC功能外,还可以利用其控制反转的思想实现对模型层的优化,从更深层次去降低应用程序各个组件的耦合程度。本节将结合实例讲解如何实现Spring框架和Struts 2框架的集成使用。

23.6.1  整合原理

Struts 2在发布的时候在其插件包struts-2.1.2\lib中有struts2-spring-plugin-2.1.2.jar,正是它实现了Struts 2和Spring的整合。这个插件覆盖了Struts 2的ObjectFactory,所以在Struts 2创建一个对象的时候,例如Action类,它会先到Struts 2的配置文件去寻找类的名字,然后转到Spring配置文件中去查找名字找到该类。解压struts2-spring-plugin-2.1.2.jar,可以看到有一个struts-plugin.xml文件,其内容如下:

<!DOCTYPE struts PUBLIC  

“-//Apache Software Foundation//DTD Struts Configuration 2.0//EN”

“http://struts.apache.org/dtds/struts-2.0.dtd”>  

<struts>  

   <!–定义一个bean:spring,其实现类为StrutsSpringObjectFactory –>  

   <bean type=”com.opensymphony.xwork2.ObjectFactory” name=”spring”class=  

“org.apache.struts2.spring.StrutsSpringObjectFactory” />  

   <!–使Spring object factory的值为上面定义的bean:spring –>  

   <constant name=”struts.objectFactory” value=”spring” />  

   <!–创建spring-default 包,其中包含两个拦截器–>  

   <package name=”spring-default”>  

       <interceptors>  

           <interceptor name=”autowiring”class=”com.opensymphony.xwork2.  

           spring.interceptor.ActionAutowiringInterceptor”/>  

           <interceptor name=”sessionAutowiring”class=”org.apache.struts2.  

           spring.interceptor.SessionContextAutowiringInterceptor”/>  

       </interceptors>  

   </package>      

</struts>

可以看到,上面代码中用<bean>标签定义了一个name为StrutsSpringObjectFactory的bean对象,用它去替换struts.objectFactory:<constant name=”struts.objectFactory” value=”spring” />。并且定义了一个spring-default包,该包有两个拦截器,如果Action类不是由ObjectFactory创建的话,则这两个插件会自动装配Action类。

23.6.2  整合步骤

Spring和Struts 2的整合其实很简单,下面是整合步骤。

(1)复制jar文件。把struts 2-spring-plugin-2.1.2.jar和spring.jar复制到Web工程的WEB-INF/lib目录下,并且还需要复制commons-logging.jar。

(2)配置struts.objectFactory属性。在文件struts.properties中设置struts.objectFactory属性,使其使用StrutsSpringObjectFactory,代码如下:

struts.objectFactory=spring

当然也可以在struts.xml文件中配置,其中spring就是struts-plugin.xml中定义的bean:spring,代码如下所示。

<struts>  

   <constant name=”struts.objectFactory” value=”spring”>  

</struts>

(3)配置Spring监听器。在web.xml文件中增加如下内容:

<listener>  

<listener-class>org.springframework.web.context.ContextLoaderListener</-  

listener-class>  

</listener>

(4)编写Spring配置文件。默认情况下Spring配置文件的名字为applicationContext.xml,该文件需要保存到Web工程的WEB-INF/目录下。内容实例如下:

<?xml version=”1.0″ encoding=”UTF-8″?>  

<!DOCTYPE beans PUBLIC  

“-//SPRING//DTD BEAN//EN”

“http://www.springframework.org/dtd/spring-beans.dtd”>  

<beans>  

   <!–创建一个bean:login,其实现类为LoginAction –>  

   <bean id=”login”class=”com.examp.ch23.LoginAction”>  

       <property name=”loginCheck” ref=”loginCheck”/>  

   </bean>  

   …  

</beans>

在这个文件中定义要加载的类对象。

(5)编写Struts 2配置文件。将Struts 2和Spring集成,其配置文件也有变化,如下所示。

<?xml version=”1.0″ encoding=”UTF-8″ ?>  

<!DOCTYPE struts PUBLIC  

“-//Apache Software Foundation//DTD Struts Configuration 2.0//EN”

“http://struts.apache.org/dtds/struts-2.0.dtd”>  

<struts>  

   <package name=”ch23″extends=”struts-default” >  

       <!–class=”login”指向applicationContext.xml文件中定义的bean:login–>  

       <action name=”LoginAction”class=”login”>  

           <result name=”input”>/login.jsp</result>  

           <result name=”success”>/success.jsp</result>  

       </action>  

       …  

   </package>  

</struts>

可以看出上面代码中的LoginAction其class属性并没有给出完整路径名,因而objectFactory是找不到该类的,所以就会到applicationContext.xml中去寻找bean的名字为login的类对象。

23.7  一个Spring+Struts 2的开发实例

下面通过一个实例来讲解如何实现Spring和Struts 2的集成使用。

【示例23-12】这个例子通过一个简单的用户登录程序说明如何实现二者的集成。首先创建一个login.jsp用于显示用户登录界面,代码如下:

<%@ page contentType=”text/html;charset=UTF-8″ language=”Java” %>  

<%@ taglib prefix=”s” uri=”/struts-tags” %>  

<html>  

<head>  

<title>用户登录界面</title>  

<s:head />  

</head>  

<body>  

<table>  

<s:form id=”id” action=”login”>             <!–将请求提交给login去处理–>  

   <s:textfield name=”username” key=”username”/>  

   <s:password name=”password” key=”password1″/>  

   <s:submit key=”submit”/>  

</s:form>  

</table>  

</body>  

</html>

接着,在struts 2.xml文件中定义一个业务控制器loginAction,它用来接收请求并对请求进行处理,配置的代码如下所示。

<action name=”LoginAction”class=”login”>  

   <result name=”success”>success.jsp</result>  

   <result name=”error”>login.jsp</result>  

</action>

上面的class属性中login并不是一个完整的类名,这个类通过applicationContext.xml的bean标签去配置,代码如下所示。

<bean id=”logincheck”class=”com.examp.ch25.UserCheck”></bean>  

<bean id=”login”class=”com.examp.ch25.LoginAction”>  

   <property name=”ck” ref=” logincheck “></property>  

</bean>

可以看出,该文件定义了两个bean,class为实现类,login就是Struts2配置文件中loginAction指向的class属性值,通过property实现名字为check的类:loginCheck的注入。接下来创建一个实现登录检查的接口Check.Java,代码如下所示。

package com.examp.ch25;  

publicinterface Check {  

boolean checkok(String username,String

password);   //声明checkok()方法

}

创建接口的实现类LoginCheck.Java,代码如下所示。

package com.examp.ch25;  

publicclass UserCheck implements Check {  

publicboolean checkok(String username, String password) {  

//重写接口中的checkok()方法

if (username != null && username.equals(“sa”) && password != null

               && password.equals(“sa”)) {  

returntrue;  

       } else {  

returnfalse;  

       }  

   }  

}

下面是业务控制器LoginAction.Java的代码,如下所示。

package com.examp.ch25;  

import com.opensymphony.xwork2.ActionSupport;  

publicclass LoginAction extends ActionSupport {  

private String username;        //用于接收表单参数:userName

private String password;        /用于/接收表单参数:password  

private String msg;  

private Check ck;               //Check是一个接口类型,属性ck通过Spring的

                                     配置文件注入  

publicvoid setCk(Check ck) {  

this.ck = ck;  

   }  

public String getUsername() {  

return username;  

   }  

publicvoid setUsername(String username) {  

this.username = username;  

   }  

public String getPassword() {  

return password;  

   }  

publicvoid setPassword(String password) {  

this.password = password;  

   }  

public String getMsg() {  

return msg;  

   }  

publicvoid setMsg(String msg) {  

this.msg = msg;  

   }  

public String execute() throws Exception {  

if(ck.checkok(username, password)){     //调用ckek的checkok()方法进

                                                 行验证  

           msg=”整合成功,调用Spring容器中的Action”;  

return SUCCESS;  

       }else{  

return INPUT;  

       }  

   }  

}

从上面代码中可以看出,在LoginAction类中并没有用硬编码的形式把属性loginCheck写死,而是定义该属性为Check接口类型,并通过配置文件applicationContext.xml实现LoginCheck的注入。这样可以很方便地实现插拔操作,这也正是Spring AOP思想的灵魂之所在。

运行实例,在浏览器中输入http://localhost:8080/ ch23/login.jsp,用户名和密码都输入sa,显示的结果如图23-6所示。

图23-6  login.jsp运行结果示意图

说明:通过这个示例可以看出,Spring和Struts 2框架的整合主要是使用Struts 2 MVC框架的控制器和视图,使用Spring去定义控制器,并且通过Spring AOP编程的思想进行模型层的优化。在第25章中将会讲解Spring+Struts 2+Hibernate开发,读者将会看到,使用Spring可以很好地优化模型层。