7

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)

問題のプロパティにはセッター メソッドがない (コンストラクターを介して値を取得する) ため、エラー メッセージは正しくありません。しかし、私が言ったように、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",
    });
}

何かコンテナ:

@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 引数の 1 つが -client でした。3.1 サーバーでは、-server でした。-client に変更したところ、エラーの頻度が大幅に減少しました。-server では 1 日おきに発生していましたが、-client では発生するのに 2 週間かかりました。

更新 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 のフィールドの名前を変更して、エラーを再現しました。

しかし、もっと重要なことに、システムがゲッターを見つけられないフィールドは、多くの場合、モデル属性で参照されているもののスーパークラスのフィールドであることに気付きました。私の例を続けると、SomthingContainer は実際には次のようになります。

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

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

コントローラーの参照はそのままなので、問題のオブジェクトのスーパークラスにあるフィールドを参照しています。

更新 5:

エラーが発生した後、デバッガーで本番サーバーに接続しようとしました。エラーのあるオブジェクトを返すコントローラーメソッドの return ステートメントにブレークポイントを置き、その時点で問題のあるフィールドにアクセスできるかどうかを確認しようとしました。そして、私はそれができたので、問題はSpring MVC /生成されたjspクラス内にあるに違いありません。

(また、エラーのフィールドは「someobject.something[0].somethingelse[0]」型でしたが、somethingelse-list が空の場合、エラーはありませんでした!私にとって、これはどういうわけかできないことを意味しますリストのgetメソッドを見つける(?))

更新 6:

この問題は、jsps からの Java クラスの生成に関係しているようです。デプロイ時にプリコンパイルjspを使用していないため、最初に使用するときにコンパイルされます。この問題は、ページに初めてアクセスし、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;
    String actualName = tokens.actualName;
    PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName);
    if (pd == null || pd.getReadMethod() == null) {
        throw new NotReadablePropertyException(getRootClass(), this.nestedPath + propertyName);
    }

問題は、 cachedIntrospectionResults に問題のプロパティが含まれていないことですが、クラスの他のすべてのプロパティが含まれています。最初から欠落している場合、または行のどこかで失われている場合は、欠落している理由を突き止めるために、さらに掘り下げる必要があります。

また、欠落しているプロパティは、セッターを持たず、ゲッターのみであることに気付きました。また、スタックトレースが示すように、コンテキストに対応しているようです。したがって、あるページにアクセスしたときにプロパティが見つからないということは、別のページにアクセスしたときにそのプロパティが利用できないという意味ではありません。

更新 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) 行にブレークポイントを設定し、これをデバッガーで実行してみました。

classCache.put(beanClass, null);

メソッドを終了させ、CachedIntrospectionResults を再構築できるようにすると、再び機能し始めました。そのため、classCache に格納されているものは、再構築が許可されている場合に作成されるものと同期されていません。これが最初にビルドされたときに何か問題が発生したためなのか、それとも classCache が途中で壊れたのか、現在のところわかりません。

Glassfish の更新時にクラスローダーが動作する方法の変更による問題を以前に経験したため、これはクラスローダーと関係があるのではないかと疑い始めています..

4

4 に答える 4

2

複数の理由が考えられます。実際のことはわかりませんが、問題を見つける方法を教えてください

ステップ 1:サーバー 2マシンで、 Glassfish 3.0.1 ビルド 22にアプリケーションをデプロイします。サーバー 2 マシンで正常に動作する場合は、Glass fish のライブラリに問題がある可能性があることを意味します。この問題の原因は次のとおりです。

  1. Glassfish 3.0.1 ビルド 22にあるGlassfish 3.1 ビルド 43にないライブラリ。すべてのライブラリを作業中の Glassfish サーバーから新しいサーバーにコピーすることで解決できます。
  2. 私はGlassfishのライブラリが春のバージョンと競合しています。[ Tomcatで直面した同様の問題と、春のライブラリを3.0.1から3.0.3に置き換えたときにうまくいった]ので、春のライブラリを最新のものに置き換えてください。

ステップ 2: ステップ 1 の結果、Glassfish 3.0.1 ビルド 22 のサーバー 2 マシンでアプリケーションが実行されていない場合、次の理由が考えられます。

  1. Java libに貼り付けたライブラリが、このサーバー マシンに含まれていないか、バージョンが異なる場合。

  2. クラスパスに設定されているフォルダー、またはサーバー 1 の環境変数を使用しているフォルダーは、サーバー 2 に存在しないか、jar を持っていないか、差分バージョンの jar を持っています。

于 2012-09-24T10:16:02.457 に答える
2

同僚にエラーを調査してもらい、単体テストでエラーを再現することができました。これは、クラスの CachedIntroSpectionResults を構築するメソッドを呼び出すと同時に、非常に低いメモリ設定で文字列をメモリに追加することで jvm に負荷をかけることによって行われました。このアプローチにより、20/30000回失敗しました。

原因については、口頭での説明しか受けていないので詳しくはわかりませんが、Java には独自のイントロスペクション結果があり、Spring によってラップされているというようなものでした。問題は、java-results がソフト参照を利用しているため、ガベージ コレクションが発生しやすいことです。そのため、Spring がこれらのソフト参照のラッパーをガベージ コレクターの実行とまったく同時に構築していたとき、Spring が使用していたものの基礎の一部が実際にクリアされ、プロパティが「失われる」ことになりました。

ソリューションは、Spring 3.1.0.RELEASE から Spring 3.1.3.RELEASE にアップグレードしているようです。ここにはいくつかの変更があり、Spring はクラスのプロパティを決定するときにソフト参照をラップしなくなりました (ソフト参照は常にではなく、まれな特殊なケースで使用されます)。Spring のバージョンをアップグレードした後、単体テストでエラーが再現されていないため、実際の使用でこれが当てはまるかどうかを確認する必要があります。

更新: 数週間経ちましたが、エラーの兆候はありません。したがって、Springバージョンの更新は機能しました:)

于 2013-03-08T11:44:48.630 に答える
1

この原因の候補を実際に見つけたと思います。

テスト サーバーの 1 つでエラーが発生した後、非常に短期間でほとんど使用しなかったため、原因について追加のチェックを行いました。テスト サーバーの使用可能なメモリが半分しかないことが判明したため、もう少し詳しく調べてみました。すべてのメモリを使い切っていないことが判明しましたが、JConsole を使用して、ヒープ上の新しい世代領域の別の部分のメモリ使用量を調査すると、残存領域の 1 つがいっぱいになっていることがわかりました。これにより一部がオーバーフローし、オーバーフローした部分がGCされたり、本来あるべき場所にないために到達不能になったりしたと推測しています。

これが実際に本番環境の問題であることはまだ確認していませんが、エラーが再び発生した場合は確認し、その場合はメモリ設定を変更してサバイバル領域のためにより多くのスペースを確保します新しい世代のヒープ。(-XX:SurvivorRatio=6 など)。

そのため、Spring MVC アプリケーションが大きくなると、特に新しいバージョンの Glassfish では、大きなサバイバー スペースが必要になるようです。

于 2012-10-09T09:12:49.157 に答える
0

実際、Spring 3.1.0 で新しく導入されたクラスには問題がありましたがExtendedBeanInfo、これは Spring 3.1.1 で修正されました - ( https://jira.spring.io/browse/SPR-8347を参照)。

于 2014-11-12T09:44:30.533 に答える