Spring,NotReadablePropertyException和Glassfish版本

时间:2012-08-30 10:10:27

标签: java spring-mvc glassfish

我正在使用一个使用Spring MVC的Web应用程序。

它在Glassfish 3.0.1上运行良好,但是当迁移到Glassfish 3.1时,它开始表现得很奇怪。有些页面只是部分显示,或者根本没有显示任何内容,而且在日志中有很多这类消息:

    [#|2012-08-30T11:50:17.582+0200|WARNING|glassfish3.1|javax.enterprise.system.container.web.com.sun.enterprise.web|_ThreadID=69;_ThreadName=Thread-1;|StandardWrapperValve[SpringServlet]: PWC1406: Servlet.service() for servlet SpringServlet threw exception
    org.springframework.beans.NotReadablePropertyException: Invalid property 'something' of bean class [com.something.Something]: Bean property 'something' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
        at org.springframework.beans.BeanWrapperImpl.getPropertyValue(BeanWrapperImpl.java:729)
        at org.springframework.beans.BeanWrapperImpl.getNestedBeanWrapper(BeanWrapperImpl.java:576)
        at org.springframework.beans.BeanWrapperImpl.getBeanWrapperForPropertyPath(BeanWrapperImpl.java:553)
        at org.springframework.beans.BeanWrapperImpl.getPropertyValue(BeanWrapperImpl.java:719)
        at org.springframework.validation.AbstractPropertyBindingResult.getActualFieldValue(AbstractPropertyBindingResult.java:99)
        at org.springframework.validation.AbstractBindingResult.getFieldValue(AbstractBindingResult.java:226)
        at org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:120)
        at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getBindStatus(AbstractDataBoundFormElementTag.java:178)
        at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getPropertyPath(AbstractDataBoundFormElementTag.java:198)
        at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getName(AbstractDataBoundFormElementTag.java:164)
        at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.writeDefaultAttributes(AbstractDataBoundFormElementTag.java:127)
        at org.springframework.web.servlet.tags.form.AbstractHtmlElementTag.writeDefaultAttributes(AbstractHtmlElementTag.java:421)
        at org.springframework.web.servlet.tags.form.TextareaTag.writeTagContent(TextareaTag.java:95)
        at org.springframework.web.servlet.tags.form.AbstractFormTag.doStartTagInternal(AbstractFormTag.java:102)
        at org.springframework.web.servlet.tags.RequestContextAwareTag.doStartTag(RequestContextAwareTag.java:79)

错误消息不正确,因为有问题的属性没有setter-method(通过构造函数获取其值)。但就像我说的那样,使用Glassfish 3.0.1时,只有在使用Glassfish 3.1的新服务器上使用它时才会出现问题。

有没有人知道Glassfish版本中是否有可能导致此问题的内容?或者它是新服务器上缺少的某种配置?

一些代码:

控制器:

@ModelAttribute
public SomethingContainer retriveSomethingContainer(@PathVariable final long id {
    return somethingContainerDao.retrieveSomethingContainer(id);       
}

@InitBinder("somethingContainer")
public void initBinderForSomething(final WebDataBinder binder) {
    binder.setAllowedFields(new String[] {
        "something.title",
        "something.description",
    });
}

SomethingContainer:

@Embedded
private final Something something = new Something();

public Something getSomething() {
    return something;
}
//no setter

public String getDescription() {
    return something.getDescription();
}

更新

重新启动Glassfish实际上会暂时解决问题。我怀疑它可能与自定义绑定器的加载有关,我们遇到了内存错误的问题,我认为这与它有关,但是已经修复但没有解决这个问题。

更新2:

在3.0.1服务器上,其中一个jvm参数是-client。在3.1服务器上,它是-server。我们将它更改为-client,这使得错误的频率下降了很多,它每隔一天发生一次-server,花了2周的时间才发生-client。

更新3:

有关服务器的一些信息(如果请求,可以添加更多信息......)

Server1(工作的):

Windows Server 2003
Java jdk 6 build 35
Glassfish 3.0.1 build 22
-xmx 1024m

Server2(有问题的那个):

Windows Server 2008 64-bit
Java jdk 6 build 31
Glassfish 3.1 build 43
-xmx 1088m
-xms 1088m

我们使用的是Spring 3.1.0版。

更新4:

我通过将jsp中的字段重命名为modelattribute中不存在的内容来重新创建错误。

但是,更重要的是,我注意到了一些事情:系统无法找到getter的字段通常是modelattribute中引用的字段的超类。继续我的例子,SomthingContainer真的是这样的:

public class SuperSomethingContainer {
    [...]
    private Something something;
    public Something getSomething() {
        return something;
    }
}

public class SomethingContainer extends SuperSomethingContainer {
    [...]
}

控制器中的引用保持原样,因此它引用了相关对象的超类中的字段。

更新5:

我尝试在发生错误后使用调试器连接到生产服务器。我在一个控制器方法的return语句上放了一个断点,返回带有错误的对象,并试图查看当时是否可以访问该字段。我可以,所以问题必须在Spring MVC /生成的jsp-classes中。

(另外,错误的字段属于&#34; someobject.something [0] .somethingelse [0]&#34;,但是当somethingelse-list为空时,没有错误!对我来说,这意味着它无法以某种方式找到列表的get-method(?))

更新6:

似乎问题与jsps生成Java类有关。我们在部署时没有使用预编译jsps,因此在首次使用时会对它们进行编译。第一次访问页面时,会出现问题,并且编译了jsp。我也注意到一旦出现问题,之后编译的jsps都会出错。我保留了一些问题生成的java文件,在下次重启时我会将它们与工作文件进行比较。越来越近了:))

更新7:

将导致错误的已编译的jsp java文件与未导致错误的编译的jsp java文件进行比较,并且没有区别。所以有点离开了。

所以,我现在知道离开控制器的Java对象很好(用调试器检查),并且从jsp生成的java类很好。所以它必须介于两者之间,现在我需要找出...

更新8:

另一轮调试,并将问题缩小了一些。事实证明,spring会对属于各种类的属性进行一些缓存。在org.springframework.beans.BeanWrapperImpl,方法getPropertyValue中,有以下内容:

private Object getPropertyValue(PropertyTokenHolder tokens) throws BeansException {
    String propertyName = tokens.canonicalName;
    String actualName = tokens.actualName;
    PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName);
    if (pd == null || pd.getReadMethod() == null) {
        throw new NotReadablePropertyException(getRootClass(), this.nestedPath + propertyName);
    }

问题是cachedIntrospectionResults不包含有问题的属性,但它包含该类的所有其他属性。如果它从一开始就丢失了,或者它在某条线上丢失了,还需要挖掘更多,以试图找出它丢失的原因。

另外,我注意到缺少的属性是那些没有setter,只有getter的属性。并且,它似乎是上下文感知,如堆栈跟踪所示。因此,在访问一个页面时找不到属性并不意味着在访问另一个页面时它不可用。

更新9:

另一天,更多的调试。其实找到了一些好东西。前一个代码块中的getCachedIntrospectionResults()调用被调用CachedIntrospectionResults#forClass(theClassInQuestion)。这返回了一个CachedIntrospectionResults对象,该对象包含远离预期的所有属性(21个中的11个)。进入forClass方法,我找到了:

static CachedIntrospectionResults forClass(Class beanClass) throws BeansException {
    CachedIntrospectionResults results;
    Object value = classCache.get(beanClass);
    if (value instanceof Reference) {
        Reference ref = (Reference) value;
        results = (CachedIntrospectionResults) ref.get();
    }
    else {
        results = (CachedIntrospectionResults) value;
    }
    if (results == null) {
    //build the CachedIntrospectionResults, store it in classCache and return it.

事实证明,返回的CachedIntrospectionResults是由classCache.get(beanClass)找到的。因此,存储在classCache中的内容已损坏/不包含它应该包含的所有内容。我在classCache.get(beanClass)-line上放了一个断点,并尝试通过调试器运行它:

  

classCache.put(beanClass,null);

当允许方法完成并重建CachedIntrospectionResults时,事情又开始了。因此,如果允许重建它,那么存储在classCache中的内容与将要创建的内容不同步。这是否是由于第一次构建时出现问题,或者如果classCache在我当前不知道的某个地方被损坏了。

我开始怀疑这与类加载器有关,因为我之前遇到的问题是由于更新Glassfish时类加载器的工作方式发生了变化..

4 个答案:

答案 0 :(得分:2)

可能有多种可能的原因。我不确定实际情况,但我可以给你找出问题的方法

Glassfish 3.0.1 build 22上 服务器2 计算机部署应用程序

步骤1 ,现在如果它在服务器2机器上工作正常,这意味着Glass鱼库可能有问题,以下可能是导致此问题的原因

  1. Glassfish 3.0.1 build 22 Glassfish 3.1 build 43 中缺少的任何库>。您可以通过将所有库从Glassfish服务器复制到新服务器来解决。
  2. 我是Glassfish的库与spring版本相冲突。 [我在tomcat面临的Similliar问题,当我将我的春季图书馆从3.0.1替换为3.0.3时,它对我有用] ,所以用最新版本替换你的弹簧库。
  3. 第2步:,如果step1的结果是应用程序未在Glassfish 3.0.1 build 22上的服务器2上运行,则可能有以下原因

    1. 如果您在 java lib 上粘贴的任何库未包含在此服务器计算机中或具有不同版本。

    2. 在服务器1上 类路径 或使用任何 环境变量 设置的所有文件夹,服务器2上不存在或者没有jar或具有diff版本的jar

答案 1 :(得分:2)

我让我的同事调查错误,他能够在单元测试中重新创建它。这是通过调用为类构建CachedIntroSpectionResults的方法来完成的,同时通过向内存添加字符串来强调jvm,内存设置非常低。这种方法使其失败了20/30000次。

至于它的原因,我只得到一个口头解释,所以我没有所有的细节,但它是这样的:Java有自己的内省结果,这些都是由Spring包装的。问题是java结果使用软引用,这使得它们容易出现垃圾收集。因此,当Spring在垃圾收集器运行的同时围绕这些软引用构建包装器时,它实际上清除了Spring使用的一些基础,导致属性“丢失”。

解决方案似乎是从Spring 3.1.0.RELEASE升级到Spring 3.1.3.RELEASE。这里有一些更改,Spring在确定类的属性时不再包装软引用(软引用在罕见的特殊情况下使用,而不是一直使用)。升级版本的Spring后,错误无法通过单元测试重现,通过实际使用仍然可以看出是否是这种情况。

更新:已经过了几周,没有出现错误迹象。所以更新Spring版本起了作用:)

答案 2 :(得分:1)

我想我实际上找到了一个候选人。

在很短的时间内很少使用后,在其中一个测试服务器上收到错误之后,我们对原因进行了一些额外的检查。事实证明,测试服务器只有一半的可用内存,这使我们更加彻底地看待它。事实证明它没有耗尽所有内存,但是当使用JConsole调查堆上新生成空间的不同部分的内存使用情况时,结果发现其中一个冲突空间已经满了。我猜这会使它的一部分溢出,导致溢出的部分被GC编辑或无法通过它不应该到达的位置。

我们还没有验证这实际上是生产环境中的问题,但是一旦错误再次出现,我们将检查,如果是这种情况,我们将更改一些内存设置以允许更多空间新一代堆的生存区域。 (-XX:SurvivorRatio = 6或类似的东西)。

因此,较大的Spring MVC应用程序似乎需要大型幸存者空间,特别是在较新版本的Glassfish中。

答案 3 :(得分:0)

确实,Spring 3.1.0中新引入的ExtendedBeanInfo类存在问题,该类已在Spring 3.1.1中修复 - 请参阅(https://jira.spring.io/browse/SPR-8347)。