中国Struts空间
百度空间 | 百度首页 
               
 
文章列表
 
2008-06-17 16:19
在Struts2中,默认的struts-default package中有一个defaultStack的拦截器堆栈,据我推断如果不配置则Struts2会默认执行这个堆栈里的拦截器。最近在做项目的时候遇到了这么一个问题,如果想向这个defaultStack中添加一个自定义的interceptor,那么针对两种情况会有不同的配置注意事项。
1. 使用经典的配置方式。
2. 使用annotation的配置方式,也就是所谓的'0'配置。
这两种情况在配置stack的时候有一点不同,第1种,可以配置如下,
<package name="base-package" extends="struts-default">
    <interceptors>
        <interceptor name="ajaxInterceptor" class="interceptors.AjaxInterceptor"></interceptor>
        <interceptor-stack name="defaultStack">
            ...
            <interceptor-ref name="ajaxInterceptor" />
            ...
        </interceptor-stack>
    </interceptors>
</package>
<package name="demo" extends="base-package" namespace="/demo">
    <action name="BizAction_*" method="{1}"
        class="demo.BizAction">
        <result name="input">/demo/BizEdit.jsp</result>
        <result name="list">/demo/BizList.jsp</result>
    </action>
</package>
这个时候是不会出现问题的,在BizAction_*的调用过程中会调用到ajaxInterceptor。注意这里是直接更改defaultStack,而没有另外起名字。
第2种,如果根据如上的配置,是有问题的struts永远也不会调用到ajaxInterceptor,刚开始不明白为什么,其实现在也没太搞清楚原理,感觉无法自定义拦截器了,几乎崩溃,后来发现需要新增一个stack而这个stack的名字不能等于defaultStack,定义如下:
<package name="base-package" extends="struts-default">
    <interceptors>
        <interceptor name="ajaxInterceptor" class="interceptors.AjaxInterceptor"></interceptor>
        <interceptor-stack name="myStack">
            ...
            <interceptor-ref name="ajaxInterceptor" />
            ...
        </interceptor-stack>
    </interceptors>
    <default-interceptor-ref name="myStack"/>
</package>
Action的annotation配置:
@ParentPackage("base-package")
public class WLogAction{
}
这样等于是显式的告诉struts用一个全新定义的package,这样配置后就可以调用到ajaxInterceptor了。
 
2008-05-12 10:43
前一段做开发过程中想取得Struts的constant配置,找了半天也没找到,javaeye也没有给我答案,只好去Nabble去寻找,结果给一位达人发了个Mail,解决了,解决方法如下:
public final static String ACTION_EXTENSION;
static
       {
          String ext = null;
          try
          {
             ext = DefaultSettings.get("struts.action.extension");
          }
         catch ( Exception ex )
          {
          }

         ACTION_EXTENSION = ext;
       }
这样就可以取到了。
JavaEye贴:www.javaeye.com/topic/185815
 
2008-04-20 21:57
问题的提出:
在Struts2中的某些Plugin的使用是需要继承Plugin中的struts-plugin.xml中定义的package的,比如在JsonPlugin中,如果使

用就需要继承如下定义的package:json-default,如果使用AjaxFileUpload的plugin的话就需要继承如下的

package:ajaxfileupload-default。
具体问题在:http://www.javaeye.com/topic/183692
问题的解决办法和源代码分析:
1 寻找Struts2处理此处的源代码位置
众所周知Struts2为WebWork的衍生物,在tomcat启动后我们可以发现如下的log:
com.opensymphony.xwork2.config.providers.XmlConfigrationProvider register
信息:Parsing configuration file [struts.xml]
因此可以基本判断XmlConfigrationProvider应该是处理package加载的处理类。
2 分析源代码
处理流程如下:
(1)XmlConfigrationProvider的loadPackages方法
在此方法中,我们可以看到当Xml文件的Node的名字为"package"的时候就执行addPackage方法。
此处还留有一个问题,就是loadPackages方法是谁来调用的?我会研究后给出结果。
(2)XmlConfigrationProvider的addPackage方法
此方法第一句:
PackageConfig newPackage = buildPackageContext(packageElement);
(2)XmlConfigrationProvider的buildPackageContext方法
String parent = packageElement.getAttribute("extends");//获取extends的字符串
...
if(!TextUtil.stringSet(TextUtils.noNull(parent))){//no parents
...
}else{//has parents,let's look it up 如果存在父package处理
List parents = ConfigurationUtil.buildParentsFromString(configuration,parent);//获取父package的数量,注意此处返回为List
...
}
(3)ConfigurationUtil的buildParentsFromString方法
此处就是Struts2处理父包(parent package)的核心判断方法
此处Struts2使用了StringTokenizer类
可以看到源代码:
StringTokenizer tokenizer = new StringTokenizer(parent,",");//使用逗号为分隔符

就此源代码就分析完了,那么我们也就有了对于问题的解决办法,我们还可以看到buildParentsFromString在处理类中多次调用,说明还有一些其他其他处理情况,可以根据此深入分析。
3 解决办法
在package的extends用逗号分隔你想继承的parent package即可。
 
2008-04-09 09:10
标签:Struts2 token 重复提交
<s:token>是Struts2中为了防止表单重复提交的标签,这个标签的实现类是org.apache.struts2.views.jsp.ui.TokenTag,这个类是关键的父类是org.apache.struts2.views.jsp.ComponentTagSupport。
第一、
TokenTag ComponentTagSupport.doStartTag 将控件对象化
TokenTag ComponentTagSupport.doEndTag component.end 调用component(Token)
Token 继承自org.apache.struts2.components.UIBean,因此首先调用UIBean.end方法,在UIBean.end方法中最后一句调用定义为protected的方法evaluateExtraParams,这个方法是提供给UIBean的子类扩展使用的,在Token的evaluateExtraParams方法中
String token = buildToken(tokenName);
其中buildToken方法实际上调用的是 TokenHelper.setToken方法,在setToken方法中值得注意的是两点,
1 generateGUID()方法,此方法是生成Token值得算法所在
2 session.put(tokenName, token)可以看到此处将生成的值存储在session中,等待以后比对。
第二、
到此<s:token>已经完成了他的任务,之后的比对来判断是否为重复提交,Struts2是通过interceptor来完成的TokenInterceptor,调用TokenHelper.validToken方法,此方法从Parameters中取出token值来和session里比较,如果不一样return false
 
2008-04-02 21:43
问题:为什么在Struts2中,验证不通过后能够回到原来页面?
详细:在Struts2应用中,我们发现,如果通过验证框架验证录入后,如果出现错误,应用会自动回到录入页面,这是为什么?在Struts2中,Action、Interceptor 需要返回String类型的result,框架才能通过配置好的转向来决定下一个视图是哪个页面。那么验证框架是怎么做到的呢?
源代码分析:
第一、
通过struts2-core-2.0.11.jar中的struts-default.xml文件我们可以看到
<package name="struts-default" abstract="true">
<interceptor-stack name="defaultStack">
...
<interceptor-ref name="validation"/> 验证框架Interceptor
<interceptor-ref name="workflow"/> 工作流Interceptor
</interceptor-stack>
此处的指出的两个Interceptor是和我们讨论的问题有关系的两个Interceptor。
<interceptor name="validation" class="org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor"/>
<interceptor name="workflow" class="com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor"/>
由此我们知道了需要看的源代码的路径。
第二、
分析AnnotationValidationInterceptor
AnnotationValidationInterceptor继承自ValidationInterceptor(package 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);//使用父类的方法
}
那么我们继续来看父类的方法
在ValidationInterceptor中
protected String doIntercept(ActionInvocation invocation) throws Exception {
        doBeforeInvocation(invocation);
       
        return invocation.invoke();
}
此处有个核心方法doBeforeInvocation,注意是在触发Action之前执行的方法,这也是为什么验证框架不通过就进不了Action方法的原因所在。
    protected void doBeforeInvocation(ActionInvocation invocation) throws Exception {
        Object action = invocation.getAction();
        String context = invocation.getProxy().getActionName();
        String method = invocation.getProxy().getMethod();

        if (log.isDebugEnabled()) {
            log.debug("Validating "
                    + invocation.getProxy().getNamespace() + "/" + invocation.getProxy().getActionName() + " with method "+ method +".");

        }
    //此处是初始化AnnotationActionValidatorManager类(静态初始化)
    //通过这个类的validate方法来验证的
    //此方法不具体解释,主要是根据已经配置好的验证规则(XML)来验证,如果出现错误就会addFieldError

        if (validateAnnotatedMethodOnly) {
            ActionValidatorManagerFactory.getInstance().validate(action, context, method);
        } else {
            ActionValidatorManagerFactory.getInstance().validate(action, context);
        }

    }
第三、
    好,到此为止我们已经知道验证器是如何工作的,如何添加验证错误的了,那么回到我们的问题,Struts2是怎么回到原有页面的?我们还有一个类没有解释,就是DefaultWorkflowInterceptor,来看代码:
    public class DefaultWorkflowInterceptor extends MethodFilterInterceptor {
    ...
        private String inputResultName = Action.INPUT;//"input"
    ...
        protected String doIntercept(ActionInvocation invocation) throws Exception {
        Object action = invocation.getAction();
        if (action instanceof Validateable) {
            //这里会触发Action中validate和validateDo开头的方法,来进行验证
        ...
        }
       

        if (action instanceof ValidationAware) {
            ValidationAware validationAwareAction = (ValidationAware) action;

            if (validationAwareAction.hasErrors()) {
            //如果之前addFieldError后,这里就会捕捉到
                if (_log.isDebugEnabled()) {
                    _log.debug("Errors on action "+validationAwareAction+", returning result name 'input'");
                }
                return inputResultName;//关键在此处
            }
        }

        return invocation.invoke();
    }
    }
应该看到了吧,其实回到原来页面只是一种假象,Struts2中默认input是录入页面,这样这里就默认回到了“原来的页面”,如果我们的录入页面不是input所指的页面,恐怕就会有问题了。因此在做Struts2应用的时候,最好一个action只有一个录入页面,否则会有些麻烦了。因此如果想做一个业务异常的拦截器的话,最好也是返回input,从这里也能看出Struts2也在向ROR学习,约定高过配置的思想。
其中的分析可能有不对的地方,希望大家来指点。
 
     
 
 
个人档案
 
chinastruts

上次登录:
2008年 9月
加为好友
 
   
 
文章分类
 
 
 
 
 
 
     
 
最新评论
 
文章评论|照片评论

 
     
 
好友最新文章
 
     
 
最近访客
 
 

宋光辉1

bangsen

kuangjiu198911

crabboy

edilyxin

lixw_du

chenshenjack

lhwork
     
 
背景音乐
 
 
其它
 
已有人次访问本空间
 
订阅RSS  什么是RSS?

您也想拥有这样的空间?请点此申请。
     


©2009 Baidu