この日記は私的なものであり所属会社の見解とは無関係です。 GitHub: takahashikzn

Spring-4.0.1リリース

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