Struts2系统学习(15)Struts2校验框架基本原理分析
来源:互联网 发布:淘宝上买什么 编辑:程序博客网 时间:2024/05/17 06:57
15 Struts2校验框架基本原理分析
Struts2提供的验证框架,功能强大而且简单易用。那么一个好的验证框架需要考虑哪些因素呢?
1. 验证功能的复用性
比如都是对一个int数据类型的验证,验证的是它的数据范围,如果验证功能抽象的好,就可以复用同样的验证功能,省去重复开发的麻烦。
2. 验证功能的可扩展性
是不是可以自己扩展验证功能,并保证扩展功能和原有的框架功能一样使用。
3. 验证与业务逻辑分离
在业务开发时,可能需要在业务逻辑不变的情况下修改验证逻辑,比如某个网站要求大于18周岁的公民才能注册,随着业务的开展,要修改为大于15岁的公民才能注册,很显然,这个时候,注册逻辑本身没有改变,但是验证逻辑发生了变化,那么,分离的验证逻辑可以保证在修改验证逻辑的时候,不会为业务逻辑带来麻烦。
针对上一节Struts2系统学习(14)输入校验-基于XML配置方式实现校验的案例,来了解下Struts2校验框架。
首先要明确输入验证也是通过拦截器interceptor实现的。打开struts2-core-xxx.jar下的struts-default.xml,查看默人拦截器栈defaultStack的内容:
<interceptor-stack name="defaultStack"> <interceptor-ref name="exception"/> <interceptor-ref name="alias"/> <interceptor-ref name="servletConfig"/> <interceptor-ref name="i18n"/> <interceptor-ref name="prepare"/> <interceptor-ref name="chain"/> <interceptor-ref name="scopedModelDriven"/> <interceptor-ref name="modelDriven"/> <interceptor-ref name="fileUpload"/> <interceptor-ref name="checkbox"/> <interceptor-ref name="datetime"/> <interceptor-ref name="multiselect"/> <interceptor-ref name="staticParams"/> <interceptor-ref name="actionMappingParams"/> <!-- 将HTTP请求中包含的参数值设置到Action中 --> <interceptor-ref name="params"/> <!-- 从ActionContext中将转化类型时候发生的错误添加到Action的值域错误中,在校验时候 经常被使用到来显示类型转化错误的信息。 --> <interceptor-ref name="conversionError"/> <interceptor-ref name="validation"> <param name="excludeMethods">input,back,cancel,browse</param> </interceptor-ref> <interceptor-ref name="workflow"> <param name="excludeMethods">input,back,cancel,browse</param> </interceptor-ref> <interceptor-ref name="debugging"/> <interceptor-ref name="deprecation"/></interceptor-stack>
params拦截器和conversionError拦截器在validation拦截器的前面。params拦截器将请求的参数反射注入到Action的属性;
conversionError拦截器验证Action的属性是否符合条件。
validation拦截器会根据配置文件的配置进行验证,其实,validation验证的是值栈中的内容(params注入),而值栈中的内容则来源于请求的参数部分。
验证框架运行流程:
再看下xwork-validator-1.0.3.dtd
<?xml version="1.0" encoding="UTF-8"?><!-- XWork Validators DTD. Used the following DOCTYPE. <!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0.3//EN" "http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">--><!-- 元素validators中可以包含一到多个field或validator元素,即有两种验证类型 --><!ELEMENT validators (field|validator)+><!-- field中至少包含一个field-validator元素 --><!ELEMENT field (field-validator+)><!-- field属性name是必须的,类型是字符串 --><!ATTLIST field name CDATA #REQUIRED><!-- 元素field-validator包含零个或多个param、每个param之后必须有一个message属元素(如果有param,必须在message之前) --><!ELEMENT field-validator (param*, message)><!-- field-validator有两个属性值,第一个是type必须存在,第二个属性是short-circuit,值为true或者false。默认值为false --><!ATTLIST field-validator type CDATA #REQUIRED short-circuit (true|false) "false"><!-- 同上 --><!ELEMENT validator (param*, message)><!ATTLIST validator type CDATA #REQUIRED short-circuit (true|false) "false"><!-- 表示只有 PCDATA 的元素,PCDATA表示会被解析的字符,比如param代表年龄,<param name='age'>18</param>那么这个18会被解析为整型 --><!ELEMENT param (#PCDATA)><!-- param中的属性name必须存在,且是字符型 --><!ATTLIST param name CDATA #REQUIRED ><!ELEMENT message (#PCDATA|param)*><!-- message中有key属性,可选,key代表国际化信息的表示。--><!ATTLIST message key CDATA #IMPLIED >
分析完dtd文件,就清楚了xml文件中为什么要这样写。校验框架可能存在的两种情况:一种是字段优先校验,另一种是校验器优先校验
<!ELEMENT validators (field|validator)+>
struts2字段优先校验与校验器优先校验。
字段校验器配置格式:
<field name="被校验的字段"> <field-validator type="校验器名"> <!--此处需要为不同校验器指定数量不等的校验规则--> <param name="参数名">参数值</param> .................... <!--校验失败后的提示信息,其中key指定国际化信息的key--> <message key="I18Nkey">校验失败后的提示信息</message> <!--校验失败后的提示信息:建议用getText("I18Nkey"),否则可能出现Freemarker template Error--> </field-vallidator> <!-- 如果校验字段满足多个规则,下面可以配置多个校验器--></field>
非字段校验器配置格式:
<validator type="校验器名"> <param name="fieldName">需要被校验的字段</param> <!--此处需要为不同校验器指定数量不等的校验规则--> <param name="参数名">参数值</param> <!--校验失败后的提示信息,其中key指定国际化信息的key--> <message key="I18Nkey">校验失败后的提示信息</message> <!--校验失败后的提示信息:建议用getText("I18Nkey"),否则可能出现Freemarker template Error--></validator>
在Struts2应用中,如果对action的输入验证出现错误,应用会自动转发到input视图中,这是如何做到的呢?
(1)通过struts2-core-2.3.24.jar中的struts-default.xml文件我们可以看到:
<package name="struts-default" abstract="true"> ... <interceptor-stack name="defaultStack"> ... <!-- 验证框架Interceptor --> <interceptor-ref name="validation"> <param name="excludeMethods">input,back,cancel,browse</param> </interceptor-ref> <!-- 工作流Interceptor --> <interceptor-ref name="workflow"> <param name="excludeMethods">input,back,cancel,browse</param> </interceptor-ref> </interceptor-stack> ...
此处的指出的两个Interceptor和我们讨论的问题有关系。打开struts-default.xml,找到两个拦截器所对应的类:
<interceptor name="validation" class="org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor"/><interceptor name="workflow" class="com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor"/>
(2)分析AnnotationValidationInterceptor:
AnnotationValidationInterceptor继承自ValidationInterceptor(com.opensymphony.xwork2.validator),这两个类我们抖需要分析。在AnnotationValidationInterceptor中的doIntercept方法中我们看到:
protected String doIntercept(ActionInvocation invocation) throws Exception { Object action = invocation.getAction(); //此处框架只是用了Annotation的方式来查看,现在触发Action方式是否不需要验证的,如果不需要就直接触发,否则使用父类的方法,注意下面的代码简写了 if (action != null) { Method method = getActionMethod(action.getClass(), invocation.getProxy().getMethod()); SkipValidation skip = (SkipValidation)method.getAnnotation(SkipValidation.class); if (skip != null) { return invocation.invoke();//直接触发 } } return super.doIntercept(invocation);//使用父类的方法}
(3)分析父类ValidationInterceptor的方法:
protected String doIntercept(ActionInvocation invocation) throws Exception { doBeforeInvocation(invocation); return invocation.invoke();}
此处有个核心方法doBeforeInvocation(invocation);
,注意是在触发Action之前执行的方法,这也是为什么验证框架不通过就进不了Action方法的原因所在。
protected void doBeforeInvocation(ActionInvocation invocation) throws Exception { Object action = invocation.getAction(); ActionProxy proxy = invocation.getProxy(); //the action name has to be from the url, otherwise validators that use aliases, like //MyActio-someaction-validator.xml will not be found String context = this.getValidationContext(proxy); String method = proxy.getMethod(); // 省略了log if (declarative) { // 根据已经配置好的验证规则(XML)来对action指定的方法进行验证,如果出现错误就会addFieldError if (validateAnnotatedMethodOnly) { actionValidatorManager.validate(action, context, method); } else { // 对action的所有方法进行验证 actionValidatorManager.validate(action, context); } } // 如果action本身也实现类Validateable接口,则还需要执行action中的validate方法 if (action instanceof Validateable && programmatic) { // keep exception that might occured in validateXXX or validateDoXXX Exception exception = null; Validateable validateable = (Validateable) action; // log ... try { PrefixMethodInvocationUtil.invokePrefixMethod( invocation, new String[] { VALIDATE_PREFIX, ALT_VALIDATE_PREFIX }); } catch(Exception e) { // If any exception occurred while doing reflection, we want // validate() to be executed if (LOG.isWarnEnabled()) { LOG.warn("an exception occured while executing the prefix method", e); } exception = e; } // 默认alwaysInvokeValidate = true; if (alwaysInvokeValidate) { // 此处调用action的validate()方法 validateable.validate(); } if (exception != null) { // rethrow if something is wrong while doing validateXXX / validateDoXXX throw exception; } } }
(4)到此为止我们已经知道验证器是如何工作的,如何添加验证错误的了,以及验证的顺序。梳理下工作原理:
Struts2默认的defaultStack中包含验证validation拦截器,拦截请求后,在doIntercept(invocation)
中先判断action是否需要进行验证,如果不需要,则调用invocation.invoke()
直接触发;如果需要验证,则调用该父类的doIntercept(invocation)
方法,父类的doIntercept方法中,先执行doBeforeInvocation(invocation)
,在其中根据已经配置好的验证规则(XML)来对action进行验证,如果出现错误就会addFieldError,最后判断如果action本身也实现类验证接口Validateable,则执行action中的validate方法,完成最后的验证,如果以上验证成功,此时doBeforeInvocation(invocation)完成调用,最后调用invocation.invoke(),进行下一步的处理。
那么回到我们的问题,Struts2是怎么回到原有页面(input视图)的?这就是DefaultWorkflowInterceptor的功能,来看代码:
public class DefaultWorkflowInterceptor extends MethodFilterInterceptor { ... // 默认验证失败防护"input"视图 private String resultName = Action.INPUT; ... protected String doIntercept(ActionInvocation invocation) throws Exception { Object action = invocation.getAction(); /* 如果需要修改验证失败返回的视图,action需要实现ValidationAware接口,实际应用中,可直接继承 * ActionSupport类,它已经实现了该接口,由此可见:需要对action进行输入校验,必须要将此action * 实现ValidationAware接口,这样才能获取到验证中出现的错误信息! */ if (action instanceof ValidationAware) { ValidationAware validationAwareAction = (ValidationAware) action; if (validationAwareAction.hasErrors()) { // log... String resultName = inputResultName; /* * action可以实现了相关接口或在方法中利用annotion,可以改变验证失败返回的视图 * 例如: * @InputConfig(resultName="errorValidate") * public String execute() {} * 所以此处需要重新获取返回的视图名称。如果没有设置,则返回的还是"input" */ resultName = processValidationWorkflowAware(action, resultName); resultName = processInputConfig(action, invocation.getProxy().getMethod(), resultName); resultName = processValidationErrorAware(action, resultName); return resultName; } } // 如果action没有实现ValidationAware接口,或者实现了该接口但校验没有错误,则进行下一步处理 return invocation.invoke();}
总结下返回到验证错误的视图的流程:
在workflow拦截器中,如果被拦截的action实现类ValidationAware接口,则去判断是否存在action errors 或 field errors,如果存在,会根据action实现的相关接口或annotion的设置获取返回的视图名称,据此转发到错误视图。
由于自己学习struts2不久,因此源代码的分析不能太深入(随着学习的深入,会重新分析),可能会有错误,还希望大家予以指正,谢谢!
参考:
1. Struts2 校验框架
2. struts2字段校验器与非字段校验器的区别
转载请注明出处:http://blog.csdn.net/mark_lq/article/details/49837507
- Struts2系统学习(15)Struts2校验框架基本原理分析
- Struts2系统学习(16)OGNL表达式及基本原理分析
- Struts2学习笔记(六)校验框架
- Struts2 校验框架学习笔记
- Struts2 校验框架学习笔记
- Struts2 校验框架学习笔记
- Struts2 校验框架学习笔记
- struts2校验框架学习笔记
- struts2框架实现基本原理
- struts2学习笔记(7)——validate校验框架
- struts2学习笔记(四)基于xml的框架校验
- Struts2学习笔记9:Struts2的校验框架
- struts2 校验框架总结
- Struts2中的校验框架
- struts2 校验框架
- Struts2中的校验框架
- Struts2中的校验框架
- Struts2中的校验框架
- 桶排序
- 网络设备将网口切换到业务板以后为什么会报各种不正确的信息?
- 1029. Median (25)
- Unity编程笔录--Unity Android加密dll
- VS2010远程调试 (2)
- Struts2系统学习(15)Struts2校验框架基本原理分析
- 变长数据项排序
- Gym 100796K Profact
- 创建型模式:工厂方法模式(Factory Method)--分析优缺点
- 在Win7上安装VS2015 RC(候选发布版)失败完美解决方法
- java调度器(重试机制)实现
- 编程师札记第一卷序语
- HDU 1421
- android hal 学习——数据结构整理