【问题标题】:The prepare() method is called twice, when the result type is redirect action in Struts2prepare() 方法被调用两次,当结果类型是 Struts2 中的重定向动作时
【发布时间】:2014-01-07 15:41:58
【问题描述】:

我有以下动作类:

@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);
    }
}

在执行除与insert() 方法相关联的插入和更新之外的所有操作(它们都与映射到&lt;s:submit&gt; 操作的insert() 相关联)时,将执行prepare() 方法只有一次。

在执行插入或更新时,prepare() 方法被调用了两次。为什么会这样?

如果@Result() 中的type="redirectAction"prepare() 方法会执行两次。当@Resulttype设置为redirectAction时,有没有办法防止prepare()方法被执行两次?

【问题讨论】:

  • 您能在prepare() 中打印一个动作名称吗?
  • 如何显示动作名称?
  • 类似System.out.println("actionName="+ActionContext.getContext().getName())
  • 它在页面加载时显示动作名称Fabric,在编辑时EditFabric,在更新时,首先显示AddFabric,然后再次显示Fabric,插入AddFabric时相同,然后Fabric.

标签: java configuration struts2 interceptor prepare


【解决方案1】:

您在选择结果typelocation 时犯了一个错误。因为"Fabric.jsp""Fabric.action" 不是有效的操作名称。

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

应该是dispatcher 结果

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

redirectAction结果

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

如果您使用redirectAction 结果类型,则默认创建新的操作类实例,其中在操作之前执行prepare 方法。如果您打印的hashCode() 应该不同,即使操作名称不同,您也可以检查这一点。因此,没有prepare 方法被调用两次,因为它只有prepare 拦截器在每个操作的堆栈中。另一方面,如果您使用paramsPrepareParamsStack,则params 拦截器会被调用两次:在prepare 拦截器之前和之后。

【讨论】:

  • 我做了这么多更改,但没有什么能阻止prepare() 方法在type="redirectAction" 的情况下被执行两次,这对于防止在发出post 请求和@987654340 后重复提交表单是必要的@ 被评估以响应请求。
  • 没有调用两次的准备方法,正如我在回答中所说的那样。
  • 在使用paramsPrepareParamsStacktype="redirectAction" 的情况下,首先调用AddFabric 操作,然后再次执行问题中的Fabric 操作,导致prepare() 成为再次执行,这反过来会导致在数据库上触发不应该发生的代价高昂的查询。否则根本没有使用prepare()方法的意义。
  • 换句话说,给定一个&lt;s:submit action="AddFabric" value="Submit"/&gt;,其操作映射到insert()方法,结果类型为redirectAction。当按下此按钮时,应该调用的唯一操作是AddFabric。不应触发其他操作,但在redirectAction 的情况下,与load() 方法关联的Fabric 操作将再次执行,如您所提到的,如果您使用redirectAction 结果类型,则新的操作类实例为默认情况下创建,其中在操作之前执行准备方法prepare() 将被执行两次
  • 我根本没有声明,但我可以看到prepare() 方法在这种情况下根本没有用。
【解决方案2】:

我遇到了同样的问题,我最终发现我在多个地方引用了一个拦截器堆栈。在您的情况下,您似乎在班级级别:

@ParentPackage(value="struts-default"),我相信它使用默认堆栈。

然后在@Act​​ion 级别,您可以引用paramsPrepareParamsStack

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

在我的具体情况下,我的 Action 类扩展了 BaseAction 类。我在两个地方都声明了@InterceptorRef 注释。 BaseAction(引用的第一个拦截器堆栈):

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

然后,在动作类中,我再次使用了拦截器 ref ("myCustomStack")。在我的 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 {
...
}

最后,这是我的 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>

【讨论】:

    猜你喜欢
    • 2012-03-05
    • 1970-01-01
    • 1970-01-01
    • 2020-11-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多