2

Play 2.4.2 (最新バージョン) フレームワーク アプリケーションで ModelMapper 0.7.4 (最新バージョン) を使用しています。Play 2.4 には内部の Google Guice 依存性注入ソリューションが組み込まれており、Play 2.4 を Spring で動作させるために、アプリケーションは Guice から Spring Framework 依存性注入ソリューションに手動でブリッジされています。つまり、通信は Play から Guice、Spring へと流れます。

物事 (Spring による依存性注入) はうまく機能しているように見えますが、テスト開発環境でランダムな Java クラスが変更されると、Play はクラスまたは webapp を自動的にリロードします。このリロードは一般に問題なく動作しますが、この Play セットアップで ModelMapper が Spring Bean として使用されている場合、ModelMapper で問題が発生するようです。(ただし、セットアップ内で手動で Spring コンテナーを作成し、ModelMapper に Spring Bean として接続することによって、Guice-Spring ブリッジをバイパスしたときに問題を再現できませんでした。)

エラーは次のとおりです。

Caused by: org.modelmapper.ConfigurationException: ModelMapper configuration errors:

1) Failed to configure mappings

1 error
        at org.modelmapper.internal.Errors.throwConfigurationExceptionIfErrorsExist(Errors.java:241) ~[modelmapper-0.7.4.jar:na]
        at org.modelmapper.internal.ExplicitMappingBuilder.build(ExplicitMappingBuilder.java:207) ~[modelmapper-0.7.4.jar:na]
        at org.modelmapper.internal.TypeMapImpl.addMappings(TypeMapImpl.java:72) ~[modelmapper-0.7.4.jar:na]
        at org.modelmapper.internal.TypeMapStore.getOrCreate(TypeMapStore.java:101) ~[modelmapper-0.7.4.jar:na]
        at org.modelmapper.ModelMapper.addMappings(ModelMapper.java:93) ~[modelmapper-0.7.4.jar:na]
        at configs.AppConfig.modelMapper(AppConfig.java:109) ~[na:na]
        at configs.AppConfig$$EnhancerBySpringCGLIB$$b19a8688.CGLIB$modelMapper$2(<generated>) ~[na:na]
        at configs.AppConfig$$EnhancerBySpringCGLIB$$b19a8688$$FastClassBySpringCGLIB$$1f1c1728.invoke(<generated>) ~[na:na]
        at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228) ~[spring-core-4.2.0.RELEASE.jar:4.2.0.RELEASE]
        at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:318) ~[spring-context-4.2.0.RELEASE.jar:4.2.0.RELEASE]
        at configs.AppConfig$$EnhancerBySpringCGLIB$$b19a8688.modelMapper(<generated>) ~[na:na]
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_45]
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_45]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_45]
        at java.lang.reflect.Method.invoke(Unknown Source) ~[na:1.8.0_45]
        at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:162) ~[spring-beans-4.2.0.RELEASE.jar:4.2.0.RELEASE]
        ... 64 common frames omitted
Caused by: java.lang.ClassCastException: project.entities.User$$EnhancerByModelMapper$$f1b8f0f9 cannot be cast to project.entities.User
        at configs.AppConfig$1.configure(AppConfig.java:106) ~[na:na]
        at org.modelmapper.PropertyMap.configure(PropertyMap.java:383) ~[modelmapper-0.7.4.jar:na]
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_45]
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_45]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_45]
        at java.lang.reflect.Method.invoke(Unknown Source) ~[na:1.8.0_45]
        at org.modelmapper.internal.ExplicitMappingBuilder.build(ExplicitMappingBuilder.java:195) ~[modelmapper-0.7.4.jar:na]
        ... 78 common frames omitted

これは、クラスのリロード時にのみ発生し、リロードが行われていない場合には発生しません。また、modelMapper.addMappings(aPropertyMap) を使用しない場合も問題は発生しません。Spring AppConfig クラスは次のようになります。

@Configuration
public class AppConfig {
    @Bean
    public ModelMapper modelMapper() {
        ModelMapper modelMapper = new ModelMapper();

        // BEGIN: WITHOUT THE FOLLWOING CODE, it works fine
        PropertyMap<CreateUserFormDTO, User> userMap = new PropertyMap<CreateUserFormDTO, User>() {
            @Override
            public void configure() {
                map().setPassword(source.getPassword1());
            }
        };
        modelMapper.addMappings(userMap);
        // END


        return modelMapper;
    }
}

ModelMapper には、プレーンな Spring @Autowire インジェクションを使用してアクセスします。User および CreateUserFormDTO クラスは単なる POJO です。

問題は何でしょうか?

4

2 に答える 2

4

問題はまさにジャンが言ったことです。Spring-Dev Tools によると:

クラスパス上のファイルが変更されるたびに、spring-boot-devtools を使用するアプリケーションは自動的に再起動します。これは、コードの変更に対して非常に高速なフィードバック ループを提供するため、IDE で作業する場合に便利な機能です。

コードの変更が発生すると、ModelMapper Library は基本クラスローダーによりクラスパスに既にロードされています。Spring-Dev ツールによると:

Spring Boot が提供する再起動テクノロジーは、2 つのクラスローダーを使用して機能します。変更されないクラス (たとえば、サードパーティの jar からのクラス) は、基本クラスローダーにロードされます。積極的に開発しているクラスは、再起動クラスローダーにロードされます。アプリケーションが再起動されると、再起動クラスローダーが破棄され、新しいクラスローダーが作成されます。このアプローチは、基本クラスローダがすでに利用可能であり、データが取り込まれているため、通常、アプリケーションの再起動は「コールド スタート」よりもはるかに高速であることを意味します。

デフォルトでは、IDE で開いているプロジェクトはすべて「再起動」クラスローダーでロードされ、通常の .jar ファイルは「ベース」クラスローダーでロードされます。

そのため、再起動クラスローダーをカスタマイズし、modelmapper.jar を基本クラスローダーの代わりに再起動クラスローダーに配置する必要があります。

そうするために、

  1. プロジェクトにファイルを作成して、META-INF/spring-devtools.propertiesプロジェクトのクラスパスにロードします。

  2. この行を spring-devtools.properties ファイルに追加します

    restart.include.modelmapper=/modelmapper-.*.jar
    
  3. プロジェクト全体をクリーンアップしてビルドします。ファイルが変更されるたびに、modelmapper ライブラリが常に再ロードされるようになりました。

リンク:

  1. ModelMapper 問題: 254
  2. 再起動クラスローダーのカスタマイズ
于 2018-05-15T10:16:17.967 に答える
3

ここでの問題は、ModelMapper がマッパーのメモリ内キャッシュを作成し (「 」を参照TypeMapStore.getOrCreate)、モデルと互換性のあるクラスを返すビルダー クラスを作成する ( User$$EnhancerByModelMapper$$f1b8f0f9extends User)ことです。

リロードしない限り、すべて正常に動作します。ただし、再生アプリをリロードすると、アプリ クラス ローダーが破棄されるため、クラスの新しいインスタンスUser新しいクラスローダーに読み込まれます。User$$EnhancerByModelMapper$$f1b8f0f9前のクラスを引き続き拡張し、新しいクラスへのキャストに失敗します。

ModelMapper を使用したソリューションがありません。キャッシュされたマッパーによって ClassCastException がスローされたときに TypeMapstore がそれを破棄し、新しいマッパーを再作成しようとするように、ModelMapper を修正する必要があると思います。

最終的に、ModelMapper のクラスロードの問題についてコメントしたときに私が取り組んでいたプロジェクトは、ModelMapper を Selma ( http://www.selma-java.org/ ) に置き換えました。これは、テストで問題なく動作しました (私の契約はその時点で終了しました)。彼らが最終的にセルマを維持したかどうかはわかりません)

于 2015-09-15T07:50:28.070 に答える