同じクラスにキャストするときのClassCastException
-
05-07-2019 - |
質問
2つの異なるJavaプロジェクトがあり、1つにはdynamicbeans.DynamicBean2
とdynamic.Validator
の2つのクラスがあります。
他のプロジェクトでは、これらのクラスの両方を動的にロードし、Object
class Form {
Class beanClass;
Class validatorClass;
Validator validator;
}
次に、Validator
を使用してvalidatorClass.newInstance()
オブジェクトを作成し、validator
に保存してから、beanClass.newInstance()
を使用してBeanオブジェクトも作成し、セッションに追加します。
portletRequest.setAttribute("DynamicBean2", bean);
Form
プロジェクトのライフサイクル中に、以前に作成したBeanオブジェクトをセッションからロードするvalidator.validate()
を呼び出します(Websphere Portal Serverを実行しています)。このオブジェクトをDynamicBean2
にキャストしようとすると、ClassCastExceptionで失敗します。
を使用してセッションからオブジェクトを戻すとき
faces.getApplication().createValueBinding("#{DynamicBean2}").getValue(faces);
そして.getClass()
を使用してそのクラスを確認します<=>を取得します。これは、キャストしたいクラスですが、ClassCastExceptionを取得しようとすると、
これを取得する理由は何ですか?
解決
プログラムフローの説明にはあまり従いませんが、通常ClassCastExceptionsを受け取ったとき、あるクラスローダーでクラスをロードしたことを説明できず、別のクラスローダーによってロードされた同じクラスにキャストしようとします。これは機能しません。これらはJVM内の2つの異なるClassオブジェクトによって表され、キャストは失敗します。
WebSphereでのクラスローディングに関する記事。私はそれがあなたのアプリケーションにどのように適用されるか言うことはできませんが、いくつかの可能な解決策があります。少なくとも考えられます:
-
コンテキストクラスローダーを手動で変更します。適切なクラスローダーへの参照を実際に取得できる必要がありますが、これは場合によっては不可能な場合があります。
Thread.currentThread().setContextClassLoader(...);
-
クラスが階層の上位のクラスローダーによってロードされていることを確認します。
-
オブジェクトをシリアル化および逆シリアル化します。 (うん!)
ただし、特定の状況により適した方法がおそらくあります。
他のヒント
クラスオブジェクトは異なるクラスローダーにロードされたため、各クラスで作成されたインスタンスは「互換性がない」と見なされます。これは、さまざまなクラスローダーが使用され、オブジェクトが渡される環境での一般的な問題です。これらの問題は、Java EEおよびポータル環境で簡単に発生する可能性があります。
クラスのインスタンスをキャストするには、キャストされるオブジェクトにリンクされたクラスが、現在のスレッドコンテキストクラスローダーによってロードされたものと同じである必要があります。
Apache Commons Digesterを使用してXMLからオブジェクトのリストを作成しようとすると、A2AClassCastException問題が発生しました。
List<MyTemplate> templates = new ArrayList<MyTemplate>();
Digester digester = new Digester();
digester.addObjectCreate("/path/to/template", MyTemplate.class);
digester.addSetNext("/path/to/template", "add");
// Set more rules...
digester.parse(f); // f is a pre-defined File
for(MyTemplate t : templates) { // ClassCastException: Cannot cast mypackage.MyTemplate to mypackage.MyTemplate
// Do stuff
}
前述のように、原因は、ダイジェスターがプログラムの残りの部分と同じClassLoaderを使用していないことです。これをJBossで実行したところ、commons-digester.jarはJBossのlibディレクトリではなく、webappのlibディレクトリにあることが判明しました。 jarをmywebapp / WEB-INF / libにコピーすることでも問題は解決しました。別の解決策は、casll digester.setClassLoader(MyTemplate.class.getClassLoader())でしたが、このコンテキストでは非常にい解決策のように感じます。
異なるマシンで複数のJBossインスタンスを使用しているときに同じ問題が発生しました。残念なことに、私は以前この投稿に出くわしませんでした。
異なるマシンにデプロイされたアーティファクトがあり、そのうちの2つは同じ名前のクラスローダーを宣言しました。コピーに注意してください<!> amp; Paste!
なぜClassCastExceptionがスローされ、関連するクラスローダーが言及されないのですか? -それは非常に役立つ情報だと思います。
将来このようなものが利用可能になるかどうかは誰にも分かりますか? 20-30のアーティファクトのクラスローダーを確認する必要はありません。または、例外テキストで見逃したものがありますか?
編集:META-INF / jboss-app.xmlファイルを編集し、ローダーの名前を変更しました。アイデアは一意の名前にすることです。職場では、ビルド中にmaven({$ version})によって挿入されたバージョンと組み合わせたアーティファクトid(unique)を使用します。
動的フィールドの使用はオプションですが、同じアプリケーションの異なるバージョンをデプロイする場合に役立ちます。
<jboss-app>
<loader-repository>
com.example:archive=unique-archive-name-{$version}
</loader-repository>
</jboss-app>
ここでいくつかの情報を見つけることができます: https://community.jboss.org/wiki/ClassLoadingConfiguration
同じ問題があり、ついにjava.netで回避策を見つけました:
すべてのorg.eclipse.persistence jar
ファイルをglassfish4/glassfish/modules
からWEB-INF/lib
にコピーします。次に、glassfish-web.xml
に移動し、class-delegate
をfalse
に設定します。
私のために働いた!
JAXBおよびJBoss AS 7.1でも同様の問題がありました。問題と解決策は次のとおりです: javax.xml.bind.JAXBException:クラス***もそのスーパークラスもこのコンテキストで認識されています。与えられた例外はorg.foo.bar.ValueSetをorg.foo.bar.ValueSetにキャストできないことでした
ワイルドフライEJBでも同じ問題が発生しました。EJBはオブジェクトのリストを返し、リモートおよびローカルインターフェイスを持っています。リスト内のオブジェクトをキャストしようとする時点までうまく機能していたものを、誤ってLocalインターフェイスを使用しました。
ローカル/リモートインターフェース:
public interface DocumentStoreService {
@javax.ejb.Remote
interface Remote extends DocumentStoreService {
}
@javax.ejb.Local
interface Local extends DocumentStoreService {
}
EJB Bean:
@Stateless
public class DocumentStoreServiceImpl implements DocumentStoreService.Local, DocumentStoreService.Remote {
EJBの正しいスプリングラッパー:
<bean id="documentStoreService" class="org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean">
<property name="jndiName" value="java:global/dpc/dpc-ejb/DocumentStoreServiceImpl!santam.apps.dpc.service.DocumentStoreService$Remote"/>
<property name="businessInterface" value="santam.apps.dpc.service.DocumentStoreService$Remote"/>
<property name="resourceRef" value="true" />
</bean>
$ Remoteに注意してください。これを$ Localに変更すると、Localインターフェースが問題なく見つかり、問題なく(同じコンテナの別のアプリケーションから)メソッドが実行されますが、モデルオブジェクトはそうではありません。誤ってローカルインターフェイスを使用した場合、マーシャリングされ、別のクラスローダーからのものです。
別のオプション:
weblogicで私に起こりましたが、私は他のサーバーでも同様に起こり得ると思います-もしあなたが(ちょうど)<!そのため、一部のクラスが再ロードされます。代わりに<!> quot; Clean <!> quot;すべてのクラスが一緒に再ロードされます。
別のEJBからのEJBルックアップでも同じ問題がありました。 EJBクラス構成に@Remote(MyInterface.class)を追加することを解決しました
同じ my.package.MyClassをWildFly 10.1のmy.package.MyClass
にキャストできなかったため、@ Emil Lundbergが彼の答えで説明したこととは反対のことを理解しました。
my.war / WEB-INF / jboss-deployment-structure.xmlにモジュール(
依存関係として my.package.MyClass
を含む)を追加しました
<dependencies>
...
<module name="my.package"/>
</dependencies>
および対応する jar を my.war / WEB-INF / lib
から削除し、WARを再デプロイすると、コードは期待どおりに機能しました。
したがって、問題を確実に解決しました。ここで、たとえば WAR の更新バージョンがアセンブルおよびデプロイされる場合など、問題が再発しないことを確認する必要があります。
このため、それらの WAR のソースでは、 jar に&lt; scope&gt; provided&lt; / scope&gt;
を追加する必要があります> pom.xml
で、次回 my.war
が修正/拡張コードを挿入して再アセンブルされるときに、この jar を my.war / WEB-INF / lib
に追加します。
Springbootプロジェクトで spring-boot-devtools
に依存関係を追加すると、この問題が発生しました。依存関係を削除すると、問題はなくなりました。この時点での最良の推測は、 spring-boot-devtools
が新しいクラスローダーを導入し、新しいクラスローダーが一部のユーザーによって使用されていない特定のケースで異なるクラスローダー間のクラスキャスト問題の問題を引き起こすことですスレッド。
リファレンス: Springブートdevtoolsに関連するドーザーマップの例外