【问题标题】:Unexpected Exception caught setting 'xxx' on 'class xxx: Error setting expression 'xxx' with value ['x', ]在'class xxx上设置'xxx'时出现意外异常:错误设置表达式'xxx'的值['x',]
【发布时间】:2014-01-09 10:45:40
【问题描述】:

我通过查询字符串将一些参数传递给实现ModelDriven<Transporter> 的动作类。

<s:form namespace="/admin_side" action="Test" id="dataForm" name="dataForm">
    <s:url id="editURL" action="EditTest" escapeAmp="false">
        <s:param name="transporterId" value="1"/>
        <s:param name="transporterName" value="'DHL'"/>
    </s:url>
    <s:a href="%{editURL}">Click</s:a>
</s:form>

动作类如下。

@Namespace("/admin_side")
@ResultPath("/WEB-INF/content")
@ParentPackage(value = "struts-default")
public final class TestAction extends ActionSupport 
                           implements Serializable, Preparable, ModelDriven<Transporter>
{
    private static final long serialVersionUID = 1L;
    private Transporter transporter = new Transporter();

    @Action(value = "Test",
    results = {
        @Result(name = ActionSupport.SUCCESS, location = "Test.jsp"),
        @Result(name = ActionSupport.INPUT, location = "Test.jsp")},
    interceptorRefs = {
        @InterceptorRef(value = "paramsPrepareParamsStack", 
                 params = {"params.acceptParamNames", "transporterId, transporterName"})})
    public String load() throws Exception {
        return ActionSupport.SUCCESS;
    }

    @Action(value = "EditTest",
    results = {
        @Result(name = ActionSupport.SUCCESS, location = "Test.jsp"),
        @Result(name = ActionSupport.INPUT, location = "Test.jsp")},
    interceptorRefs = {
        @InterceptorRef(value = "paramsPrepareParamsStack", 
                 params = {"params.acceptParamNames", "transporterId, transporterName"})})
    public String edit() {
        System.out.println(transporter.getTransporterId() 
                         + " : " + transporter.getTransporterName());
        return ActionSupport.SUCCESS;
    }

    @Override
    public Transporter getModel() {
        return transporter;
    }

    @Override
    public void prepare() throws Exception {}
}

服务器终端显示以下消息。

Jan 09, 2014 4:06:32 PM com.opensymphony.xwork2.interceptor.ParametersInterceptor error
SEVERE: Developer Notification (set struts.devMode to false to disable this message):
Unexpected Exception caught setting 'transporterId' on 'class actions.TestAction: Error setting expression 'transporterId' with value ['1', ]
Jan 09, 2014 4:06:32 PM com.opensymphony.xwork2.interceptor.ParametersInterceptor error
SEVERE: Developer Notification (set struts.devMode to false to disable this message):
Unexpected Exception caught setting 'transporterName' on 'class actions.TestAction: Error setting expression 'transporterName' with value ['DHL', ]

即使日志级别是SEVERE,这些参数的值在操作类中也可以作为

System.out.println(transporter.getTransporterId() 
                 + " : " + transporter.getTransporterName());

edit() 方法中。

如果paramsPrepareParamsStackdefaultStack 替换,那么这些消息就会消失。

['DHL', ] 这样的表达式表示一个数组。模型中的transporterIdtransporterName 分别是LongString 类型。

我做错了什么?

【问题讨论】:

    标签: java struts2 model-driven struts2-interceptors interceptorstack


    【解决方案1】:

    这里不涉及数组问题(即使看起来像那样):这种异常意味着Struts无法为您的参数找到Setter:

    来自ParametersInterceptor documentation

    缺少参数的警告

    当给定参数名称没有设置器时,会出现警告消息 像下面这样会在 devMode 下登录:

    SEVERE: Developer Notification (set struts.devMode to false to disable this 
    message):
    Unexpected Exception caught setting 'search' on 'class demo.ItemSearchAction: 
    Error setting expression 'search' with value ['search', ]
    Error setting expression 'search' with value ['search', ] - [unknown location] 
      at com.opensymphony.xwork2.ognl.OgnlValueStack.handleRuntimeException(OgnlValueStack.java:201)
      at com.opensymphony.xwork2.ognl.OgnlValueStack.setValue(OgnlValueStack.java:178)
      at com.opensymphony.xwork2.ognl.OgnlValueStack.setParameter(OgnlValueStack.java:152)
    

    因此是允许开发人员发现缺少的 setter 的预期行为 或参数名称或设置器中的拼写错误。

    您可以通过在 JSP 中放入 Action 中不存在的元素来轻松重现此错误。

    由于模型中存在您的属性(及其设置器),并且您使用的是ModelDrivenparamsPrepareParamsStack,我认为正在发生的事情是:

    • ModelDriven Interceptor 被委托处理模型对象;
    • 第一次调用Parameters InterceptorModelDriven Interceptor还没有运行;
    • 那么您的 Action 对 Model 对象一无所知,并尝试在 Action 中而不是在 Model 中查找参数的 Setter。
    • 第二个拦截器改为在 ModelDriven 拦截器之后运行,并且确切地知道在哪里设置参数。这就是您在 Action 方法中正确设置参数的原因。

    但如果这是真的,那么您应该能够在 prepare() 方法中检索这些参数(这就是您使用此堆栈的原因...):
    请尝试,并在此处发布结果。

    我想到解决这个问题的第一件事是将ModelDriven Interceptor放在第一个Parameters Interceptor之前(通过复制或移动它,我不确定哪种副作用,如果有的话,这两种情况都可能产生,您应该再次尝试并在此处报告)。

    然后定义下面的堆栈,并使用它。

    <interceptor-stack name="modelParamsPrepareParamsStack">
        <interceptor-ref name="exception"/>
        <interceptor-ref name="alias"/>
        <interceptor-ref name="i18n"/>
        <interceptor-ref name="checkbox"/>
        <interceptor-ref name="multiselect"/>
    
        <!-- NEW ModelDriven Position -->
        <interceptor-ref name="modelDriven"/>
    
        <interceptor-ref name="params">
            <param name="excludeParams">^dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,^parameters\..*,^action:.*,^method:.*</param>
        </interceptor-ref>
        <interceptor-ref name="servletConfig"/>
        <interceptor-ref name="prepare"/>
        <interceptor-ref name="chain"/>
    
        <!-- OLD ModelDriven Position -->
        <!--interceptor-ref name="modelDriven"/-->
    
        <interceptor-ref name="fileUpload"/>
        <interceptor-ref name="staticParams"/>
        <interceptor-ref name="actionMappingParams"/>
        <interceptor-ref name="params">
            <param name="excludeParams">^dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,^parameters\..*,^action:.*,^method:.*</param>
        </interceptor-ref>
        <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-stack>
    

    希望对您有所帮助。

    【讨论】:

    • 使用paramsPrepareParamsStackModelDriven&lt;T extends Object&gt;,模型中的字段/属性值在prepare() 方法中不可用。使用此堆栈,这些消息消失得无影无踪,模型值也可在 prepare() 方法中使用。
    【解决方案2】:

    在您提供的代码中,我找不到 Transporter 类的声明。

    所以我猜这可能是因为您的 Transpoter 类的参数多于 2 个,而不仅仅是 id 和 name。

    其实这个错误信息一直出现在我提到的这种情况上。

    为了解决这个问题,你可以定义一个数据传输对象(DTO),它只有两个属性,id 和 name。使用这个 DTO 接受来自 jsp 的参数,然后将属性值传递给 Transporter 对象。

    我在2019年看到了这个问题,并提供了一个解决方案,希望它可以在未来被其他人使用。

    【讨论】:

      猜你喜欢
      • 2023-03-10
      • 2015-04-18
      • 1970-01-01
      • 2021-12-25
      • 1970-01-01
      • 2021-01-08
      • 2014-05-13
      • 2016-06-07
      • 2016-09-14
      相关资源
      最近更新 更多