Spring,NotReadablePropertyException和Glassfish版本 [英] Spring, NotReadablePropertyException and Glassfish version

查看:144
本文介绍了Spring,NotReadablePropertyException和Glassfish版本的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在研究一个使用Spring MVC的web应用程序。

它在Glassfish 3.0.1上工作良好,但在迁移到Glassfish 3.1时,它开始表现奇怪。有些页面只是部分显示,或者根本没有显示,并且在日志中有很多这种类型的消息:

  [#| 2012-08-30T11:50:17.582 + 0200 |警告| glassfish3.1 | javax.enterprise.system.container.web.com.sun.enterprise.web | _ThreadID = 69; _ThreadName =线程1; | StandardWrapperValve [SpringServlet]:PWC1406:用于Servlet的Servlet.service()SpringServlet抛出异常
org.springframework.beans.NotReadablePropertyException:bean类的无用属性'something'[com.something.Something]:Bean属性'something'不可读或具有无效的getter方法:getter的返回类型是否与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.AbstractDataBoundFormEl ementTag.getPropertyPath(AbstractDataBoundFormElementTag.java:198)
at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getName(AbstractDataBoundFormElementTag.java:164)
at org.springframework.web.servlet。在org.springframework.web.servlet.tags.form.AbstractHtmlElementTag.writeDefaultAttributes tags.form.AbstractDataBoundFormElementTag.writeDefaultAttributes(AbstractDataBoundFormElementTag.java:127)
(AbstractHtmlElementTag.java:421)
。在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(通过获取其值h构造函数)。但正如我所说,使用Glassfish 3.0.1时,这并不是一个问题,只有在Glassfish 3.1的新服务器上使用Glassfish 3.0.1时才有问题。



有谁知道是Glassfish版本中可能导致此问题的东西?或者在新服务器上缺少某种配置?



一些代码: b
$ b

控制器:

  @ModelAttribute 
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:

$ $ $ $ $ $ $ $ $ $ $ $私人最终的东西=新东西() ;
$ b $ public public getSomething(){
return something;
}
// no setter

public String getDescription(){
返回something.getDescription();
}

更新:



Res放屁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位
Java jdk 6版本31
Glassfish 3.1版本43
-xmx 1088m
-xms 1088m

我们使用的是Spring 3.1.0版本。

更新4:



我通过将jsp中的字段重命名为不存在的东西来重新创建错误在modelattribute中。但是,更重要的是,我注意到了一些事情:系统找不到getters的字段通常是modelattribute中引用的那些字段的超类。继续我的例子,SomthingContainer就是这样的:

  public class SuperSomethingContainer {
[...]
private私人事物;
public Something getSomething(){
return something;


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

控制器中的引用保持不变,因此引用的是相关对象的超类中的字段。



更新5:

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



(此外,错误字段的类型为someobject.something [0] .somethingelse [0],但是当somethingelse-list为空时,没有错误!对我来说,这意味着它以某种方式无法找到列表的获取方法(?))



更新6:



这个问题似乎与从jsps生成Java类有关。我们在部署时没有使用预编译的jsps,所以它们在第一次使用时被编译。第一次访问页面时发生问题,并且编译jsp。我也注意到,一旦问题发生,后面编译的jsps都会出错。我保留了一些生成java文件的问题,并且在下一次重新启动时,我会将它们与工作的文件进行比较。接近:)

更新7:



比较导致错误的编译后的jsp java文件与那没有,并没有区别。所以,有点遗漏了。



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



更新8:



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

  private Object getPropertyValue(PropertyTokenHolder tokens)throws BeansException {
String propertyName = tokens.canonicalName;
字符串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结果;
Object value = classCache.get(beanClass);
if(value instanceof Reference){
Reference ref =(Reference)value;
results =(CachedIntrospectionResults)ref.get();
}
else {
results =(CachedIntrospectionResults)value;
}
if(results == null){
//构建CachedIntrospectionResults,将其存储在classCache中并返回。

事实证明,返回的CachedIntrospectionResults是由classCache.get(beanClass)发现的。所以存储在classCache中的内容被破坏/不包含它应该包含的所有内容。我在classCache.get(beanClass)-line上放置了一个断点,并试着通过调试器运行它:
$ b


classCache.put(beanClass ,null);

当允许该方法完成并重建CachedIntrospectionResults时,事情再次开始工作。因此,如果允许重建它,那么存储在classCache中的内容与将创建的内容不同步。是否这是由于第一次构建时出现错误,或者classCache在我目前不知道的线路中的某处损坏了。



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

解决方案

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

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



解决方案似乎是从Spring 3.1.0.RELEASE升级到Spring 3.1.3.RELEASE。这里有一些变化,Spring在确定类的属性时不再包含软引用(软引用在罕见,特殊情况下而不是所有时间)。在升级Spring版本后,通过单元测试这个错误还没有被重现,它仍然是通过实际使用来查看是否是这种情况。

更新:已经过了几个星期,没有发现错误。所以更新Spring版本:)


I am working on a web-application that uses Spring MVC.

It has been working fine on Glassfish 3.0.1, but when migrating to Glassfish 3.1, it started acting strange. Some pages are only partially showing, or showing nothing at all, and in the log, a lot of messages of this type:

    [#|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)

The error message isn't incorrect, because the property in question does not have a setter-method (gets its value through the constructor). But like I said, this has not been a problem when using Glassfish 3.0.1, only when using it on the new server with Glassfish 3.1.

Does anyone know if there is something in the Glassfish version that might cause this? Or is it some kind of configuration that is missing on the new server?

Some code:

Controller:

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

Update:

Restarting Glassfish actually removes the problem - temporarily. I suspect that it might have something to do with the loading of the custom binders, we had some problems with out of memory errors, which I thought had something to do with it, but that has been fixed without fixing this problem.

Update 2:

On the 3.0.1 server, the one of the jvm arguments was -client. On the 3.1-server, it was -server. We changed it to -client, and this made the frequency of the error go down a lot, it was happening every other day with -server, took 2 weeks for it to happen with -client.

Update 3:

Some information about the servers (more can be added if requested..)

Server1 (the working one):

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

Server2 (the one with problems):

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

We are using Spring version 3.1.0.

Update 4:

I recreated the error by renaming a field in a jsp to something that does not exist in the modelattribute.

But, more importantly, I noticed something: The fields where the system can't find the getters are often fields of superclasses of the ones that are referenced in the modelattribute. To continue my example, the SomthingContainer is really like this:

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

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

The reference in the controller stays as is, so it's referencing a field that is in the superclass of the object in question.

Update 5:

I tried connecting to the production server with a debugger after the error occured. I put a breakpoint on the return statement of a controller-method returning the object with the error, and tried to see if I could access the field with problems at the time. And that I could, so the problem must lie within Spring MVC/the generated jsp-classes.

(Also, the field in error was of the type "someobject.something[0].somethingelse[0]", but when the somethingelse-list was empty, there was no error! To me, this implies that it somehow can't find the get-method of a list(?))

Update 6:

It seems that the problem has to do with the generation of Java-classes from the jsps. We have not used precompile jsps when deploying, so they are compiled when first used. The problem occurs the first time a page is visited, and the jsp compiled. I also noticed that once the problem has occured, jsps that are compiled after will all give errors. I've kept a few of the problem generated java files, and upon the next restart I will compare them to the working ones. Getting closer :)

Update 7:

Compared the compiled jsp java files that resulted in an error with ones that did not, and there was no difference. So that kinda leaves that out.

So, I now know that the Java object leaving the controller is fine (checked with debugger), and the java class generated from the jsp is fine. So it must be something in between, now I need to find out what...

Update 8:

Another round of debugging, and narrowed the problem down some more. It turns out that spring does some caching of the properties belonging to the various classes. In org.springframework.beans.BeanWrapperImpl, method getPropertyValue, there is the following:

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

The problem is that the cachedIntrospectionResults does not contain the property in question, it contains every other property of the class though. Will need to dig some more to try to find out why it is missing, if it's missing from the start or if it gets lost somewhere along the line.

Also, I've noticed that the missing properties are those that do not have setters, only getters. And, it seems to be context aware, as indicated by the stacktrace. So not finding a property when visiting one page does not mean that its not available when visiting another.

Update 9:

Another day, more debugging. Actually found some good stuff. The getCachedIntrospectionResults() call in the previous code block wounded up calling CachedIntrospectionResults#forClass(theClassInQuestion). This returned a CachedIntrospectionResults object, containing far from all of the properties expected (11 of 21). Going into the forClass-method, I found:

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.

It turned out that the CachedIntrospectionResults returned was found by classCache.get(beanClass). So what was stored in the classCache was corrupted/did not contain all that it should. I put a breakpoint on the classCache.get(beanClass)-line, and tried running this through the debugger:

classCache.put(beanClass, null);

When allowing the method to finish, and rebuild the CachedIntrospectionResults, things started working again. So, what is being stored in the classCache is out of sync with what would and should be created if it was allowed to rebuild it. Whether this is due to something going wrong the first time it is built, or if the classCache is corrupted somewhere along the line I do not currently know.

I'm starting to suspect that this has something to do with classloaders, as I've previously experienced problems due to changes in the way the classloader works when updating Glassfish..

解决方案

I got a colleague of mine investigate the error, and he was able to recreate it in a unit test. This was done by invoking the method that builds CachedIntroSpectionResults for a class, while at the same time stressing the jvm by adding strings to the memory, with very low memory settings. This approach made it fail 20/30000 times.

As to the cause of it, I only got an oral explanation, so I don't have all the details, but it was something like this: Java has its own introspection-results, and these are wrapped by Spring. The problem is that the java-results utilize soft references, which make them prone to garbage collections. So, when Spring was building its wrappers around these soft references at the exact same time that the garbage collector ran, it actually cleared some of the basis of what Spring was using, leading to properties being "lost".

The solution seems to be upgrading from Spring 3.1.0.RELEASE to Spring 3.1.3.RELEASE. Here, there are some changes, and Spring no longer wraps soft references when determining the properties of a class (soft reference are used in rare, special cases, instead of all the time). After upgrading the version of Spring, the error has not been reproducable through the unit test, it remains to see if this is the case through in practice use.

Update: It's been a few weeks, an no sign of the error. So updating Spring version worked :)

这篇关于Spring,NotReadablePropertyException和Glassfish版本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆