Spring-4.0.1がリリースされています。
以前の日記で書いた不具合はきちんと修正されているようです。
とある業務アプリケーションに対して、長いSeleniumシナリオテストを一通り流してみましたが、大きなトラブルも無く完走です。
全く問題がなかったわけではなく…
今度は不具合ではなく、破壊的仕様変更が入った箇所を一つ見つけました。
僕はSpringのorg.springframework.core.GenericTypeResolverというクラスを使っていたのですが、
こいつのresolveTypeArgumentsの仕様が大きく変わってしまいました。
どのように変わったかというと、Spring3の当該メソッドのJavadocに答えが。
/**
* Resolve the type arguments of the given generic interface against the given
* target class which is assumed to implement the generic interface and possibly
* declare concrete types for its type variables.
*
*Note: In Spring 3.2, this method doesn't return {@code null} in all scenarios
* where it should. To be fixed in Spring 4.0; for client code, this just means it
* might see {@code null} in a few more cases then where it now sees an array with
* a single {@link Object} type.
*
* @param clazz the target class to check against
* @param genericIfc the generic interface or superclass to resolve the type argument from
* @return the resolved type of each argument, with the array size matching the
* number of actual type arguments, or {@code null} if not resolvable
*/
与えられたクラスに対し、そのクラスがジェネリクスなインタフェースを実装しており、かつ具体的な型を型引数として宣言しているとみなし、型引数を解決する。
注:Spring3.2ではこのメソッドは決してnullを返すことは無いが、Spring4.0では、解決結果がObjectになるケースでnullを返すようになる。
実際に試してみました。
public interface Foo<T1, T2> { } public static class Bar<T> implements Foo<String, T> { } public void test() { // メンドクサイのでワンライナーで書く System.out.println( Arrays.asList( Objects.firstNonNull( GenericTypeResolver.resolveTypeArguments(Bar.class, Foo.class), new Object[0]))); }
このコードをSpring-3.2.6で動かすと
[class java.lang.String, class java.lang.Object]
という結果ですが、Spring-4.0.1で動かすと
[]
です。具体的にはnullが返ってきます。
Springのコードはどうなっているか
Spring4では、当該メソッドはこんな感じです。
/** * Resolve the type arguments of the given generic interface against the given * target class which is assumed to implement the generic interface and possibly * declare concrete types for its type variables. * * @param clazz the target class to check against * @param genericIfc the generic interface or superclass to resolve the type argument from * @return the resolved type of each argument, with the array size matching the * number of actual type arguments, or {@code null} if not resolvable */ public static Class<?>[] resolveTypeArguments(Class<?> clazz, Class<?> genericIfc) { ResolvableType type = ResolvableType.forClass(clazz).as(genericIfc); if (!type.hasGenerics() || type.hasUnresolvableGenerics()) { return null; } return type.resolveGenerics(); }
見ての通りですが、type.hasUnresolvableGenerics()
がtrueならnullを返すという実装です。
『それじゃ困るんですけど!!』というアナタへ
これまで通りの動作じゃないと困るというアナタ(僕含む)。この通りにすればOK。
ResolvableType type = ResolvableType.forClass(clazz).as(genericIfc);
return type.resolveGenerics();