简体   繁体   English

当结果类型为Struts2中的重定向操作时,prepare()方法将被调用两次

[英]The prepare() method is called twice, when the result type is redirect action in Struts2

I have the following action class: 我有以下动作课:

@Namespace("/admin_side")
@ResultPath("/WEB-INF/content")
@ParentPackage(value="struts-default")
public final class FabricAction extends ActionSupport implements Serializable, ValidationAware, Preparable, ModelDriven<Fabric>
{
    @Autowired
    private final transient FabricService fabricService=null;
    private static final long serialVersionUID = 1L;

    private int pageSize=5;
    private Long id;
    private Boolean deleteOneRow;
    private Boolean deleteMultipleRows;
    private String message;
    private List<Long>chk;
    private Long deleteId;

    private Long begin;
    private Long end;
    private Long currentPage=1L;
    private Long rowCount;
    private Long totalPages;
    private Integer status;

    private Fabric entity=new Fabric();
    private List<Fabric>fabrics=new ArrayList<Fabric>();

    //Getters & Setters.

    @Action(value = "Fabric",
            results = {
                @Result(name=ActionSupport.SUCCESS, location="Fabric.jsp"),
                @Result(name = ActionSupport.INPUT, location = "Fabric.jsp")},
            interceptorRefs={
                @InterceptorRef(value="paramsPrepareParamsStack", params={"params.acceptParamNames", "id, currentPage, rowCount, totalPages, message, status", "validation.validateAnnotatedMethodOnly", "true", "validation.excludeMethods", "load"})})
    public String load() throws Exception
    {
        //Invokes, when the page is loaded.
        return ActionSupport.SUCCESS;
    }

    @Action(value = "FabricPage",
        results = {@Result(name=ActionSupport.SUCCESS, location="Fabric.jsp", params={"namespace", "/admin_side", "actionName", "Fabric", "currentPage", "${currentPage}"}),
        @Result(name = ActionSupport.INPUT, location = "Fabric.jsp")},
        interceptorRefs={
            @InterceptorRef(value="conversionError"),
            @InterceptorRef(value="paramsPrepareParamsStack", params={"params.acceptParamNames", "currentPage", "validation.validateAnnotatedMethodOnly", "true"})})
    public String page()
    {
        //Invokes, when a page link is clicked.
        return ActionSupport.SUCCESS;
    }

    @Validations(
            requiredStrings={
                @RequiredStringValidator(fieldName="fabricName", type= ValidatorType.FIELD, key = "fabric.name.required")},
            stringLengthFields={
                @StringLengthFieldValidator(fieldName="fabricName", type= ValidatorType.FIELD, minLength="2", maxLength="45", key="fabric.name.length", messageParams={"2", "45"})})
    @Action(value = "AddFabric",
        results = {
            @Result(name=ActionSupport.SUCCESS, type="redirectAction", location="Fabric.jsp", params={"namespace", "/admin_side", "actionName", "Fabric", "currentPage", "${currentPage}", "message", "${message}", "id", "${id}", "status", "${status}"}),
            @Result(name = ActionSupport.INPUT, location = "Fabric.jsp")},
        interceptorRefs={
            @InterceptorRef(value="conversionError"),
            @InterceptorRef(value="paramsPrepareParamsStack", params={"params.acceptParamNames", "id, fabricId, fabricName, currentPage, rowCount, totalPages, status", "validation.validateAnnotatedMethodOnly", "true"})
        })
    public String insert()
    {
        //Handles insert and update operations.
        return ActionSupport.SUCCESS;
    }

    @Action(value = "EditFabric",
            results = {
                @Result(name=ActionSupport.SUCCESS, location="Fabric.jsp"),
                @Result(name = ActionSupport.INPUT, location = "Fabric.jsp")},
            interceptorRefs={
                @InterceptorRef(value="paramsPrepareParamsStack", params={"params.acceptParamNames", "id, fabricId, fabricName, currentPage", "validation.validateAnnotatedMethodOnly", "true"}),
                @InterceptorRef(value="conversionError")})
    public String edit()
    {
        //Invokes, when an edit link is clicked.
        return ActionSupport.SUCCESS;
    }

    @Validations(
            fieldExpressions={@FieldExpressionValidator(fieldName="deleteOneRow", expression="deleteOneRow==true", shortCircuit=true, key="delete.row.reject")})
    @Action(value = "DeleteFabric",
            results = {
                @Result(name=ActionSupport.SUCCESS, type="redirectAction", location="Fabric.action", params={"currentPage", "${currentPage}", "message", "${message}", "status", "${status}"}),
                @Result(name = ActionSupport.INPUT, location = "Fabric.jsp")},
            interceptorRefs={
                @InterceptorRef(value="paramsPrepareParamsStack", params={"params.acceptParamNames", "deleteId, deleteOneRow, currentPage, status", "validation.validateAnnotatedMethodOnly", "true"}),
                @InterceptorRef(value="conversionError")})
    public String deleteSingleRow()
    {
        //Handles deletion of a single row.
        return ActionSupport.SUCCESS;
    }

    @Validations(
            requiredFields={
                @RequiredFieldValidator(type= ValidatorType.FIELD, fieldName="chk", key="delete.multiple.alert"),
                @RequiredFieldValidator(type= ValidatorType.FIELD, fieldName="deleteMultipleRows", key="delete.multiple.confirm")})
    @Action(value = "DeleteFabrics",
            results = {
                @Result(name=ActionSupport.SUCCESS, type="redirectAction", location="Fabric.jsp", params={"namespace", "/admin_side", "actionName", "Fabric", "currentPage", "${currentPage}", "message", "${message}", "status", "${status}"}),
                @Result(name = ActionSupport.INPUT, location = "Fabric.jsp")},
            interceptorRefs={
                @InterceptorRef(value="paramsPrepareParamsStack", params={"params.acceptParamNames", "deleteMultipleRows, chk, currentPage, rowCount, totalPages", "validation.validateAnnotatedMethodOnly", "true"}),
                @InterceptorRef(value="conversionError")})
    public String deleteMultipleRows()
    {
        //Handles deletion of multiple rows.
        return ActionSupport.SUCCESS;
    }

    public Fabric getEntity() {
        return entity;
    }

    public void setEntity(Fabric entity) {
        this.entity = entity;
    }

    public List<Fabric> getFabrics()
    {
        return fabrics;
    }

    @Override
    public Fabric getModel()
    {
        return entity;
    }

    @Override
    public void prepare() throws Exception
    {
        fabrics= fabricService.getList((int)(currentPage-1)*pageSize, pageSize);
    }
}

While performing all the operations except insert and update associated with the insert() method (both of them are associated with insert() which is mapped to an <s:submit> action), the prepare() method is executed only once. 在执行除与insert()方法关联的insert和update以外的所有操作(它们都与被映射到<s:submit>动作的insert()关联)时, prepare()方法仅执行一次。

While performing either insert or update, the prepare() method is seen to be invoked twice. 在执行插入或更新时,可以看到prepare()方法被调用了两次。 Why does this happen? 为什么会这样?

In case of type="redirectAction" in @Result() , the prepare() method is executed twice. 如果@Result()type="redirectAction" ,则prepare()方法将执行两次。 Is there a way to prevent the prepare() method from being executed twice, when type of @Result is set to redirectAction ? @Result type设置为redirectAction时,是否有一种方法可以防止prepare()方法执行两次?

You have made a mistake either choosing a result type or location . 选择结果typelocation出错。 Because "Fabric.jsp" or "Fabric.action" aren't valid action names. 因为"Fabric.jsp""Fabric.action"不是有效的动作名称。

@Result(name=ActionSupport.SUCCESS, type="redirectAction", location="Fabric.jsp",

should be a dispatcher result 应该是dispatcher结果

@Result(name=ActionSupport.SUCCESS, location="Fabric.jsp",

or redirectAction result redirectAction结果

@Result(name=ActionSupport.SUCCESS, type="redirectAction", location="Fabric"

If you use redirectAction result type the new action class instance is created by default in which a prepare method is executed before the action. 如果使用redirectAction结果类型,则默认情况下会创建新的动作类实例,在该实例中,在动作之前执行prepare方法。 You can check this if you print hashCode() should be different even if different action names. 如果打印的hashCode()应该不同(即使动作名称不同hashCode()也可以进行检查。 So, no prepare method called twice as it only prepare interceptor in the stack of each action. 因此,没有调用两次的prepare方法,因为它仅在每个动作的堆栈中prepare拦截器。 On the other hand if you use paramsPrepareParamsStack the params interceptor is called twice: before and after prepare interceptor. 另一方面,如果使用paramsPrepareParamsStack ,则两次调用params拦截器:在prepare拦截器之前和之后。

I've had the same issue and what I eventually found is that I was referencing an interceptor stack in multiple places. 我遇到了同样的问题,最终发现我在多个地方引用了一个拦截器堆栈。 In your case, it appears you have at the class level: 就您而言,您似乎在课程级别上:

@ParentPackage(value="struts-default") , which I believe uses the default stack. @ParentPackage(value="struts-default") ,我相信它使用默认堆栈。

And then at the @Action level, you have a reference to the paramsPrepareParamsStack : 然后在@Act​​ion级别,您可以引用paramsPrepareParamsStack

interceptorRefs={
            @InterceptorRef(value="paramsPrepareParamsStack"...)})

In my specific case, my Action classes extended a BaseAction class. 在我的特定情况下,我的Action类扩展了BaseAction类。 I was declaring the @InterceptorRef annotation in both places. 我在两个地方都声明了@InterceptorRef批注。 The BaseAction (first place interceptor stack referenced): BaseAction(已引用第一个拦截器堆栈):

@Namespace("/")
@InterceptorRef("myCustomStack")
@Results({ @Result(name = "cancel", location = "${previousPageLink}", type = "redirectAction", params = {
 }) })
public class BaseAction extends ActionSupport implements ServletRequestAware, ParameterAware,
SessionAware { 
...
}

Then, in the action class, I had the interceptor ref ("myCustomStack") again. 然后,在动作类中,我再次有了拦截器ref(“ myCustomStack”)。 Removing the interceptor ref stack in my HomeAction class solved the issue for me. 在我的HomeAction类中删除拦截器引用堆栈对我来说解决了这个问题。

@Namespace("/")
@InterceptorRefs({
@InterceptorRef(value = "store", params = {"operationMode", "RETRIEVE"}) })
/*@InterceptorRef("myCustomStack") })*/
@Results({ @Result(name = "success", location = "home.def", type = "tiles")
})
public class HomeAction extends BaseAction implements Preparable {
...
}

And finally, here is my struts.xml that has the default.parent.package set to 'default': 最后,这是我的struts.xml,其default.parent.package设置为“ default”:

<struts>
  <constant name="struts.convention.action.packages" value="com.myapp.action" />
  <constant name="struts.convention.default.parent.package" value="default" />
  <constant name="struts.action.extension" value="action" />
  <constant name="struts.enable.DynamicMethodInvocation" value="false" />
  <constant name="struts.ognl.allowStaticMethodAccess" value="true" />
  <package name="default" namespace="/" extends="tiles-default,json-default">
    <interceptors>
      <interceptor-stack name="myCustomStack">
        <interceptor-ref name="basicStack" />
        <interceptor-ref name="staticParams" />
        <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-stack>
    </interceptors>
  </package>
</struts>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM