Spring 4.3の新機能
1. 概要
Spring 4.3リリースでは、コアコンテナ、キャッシング、JMS、Web MVC、およびフレームワークのテストサブモジュールにいくつかの素晴らしい改良が加えられました。
この投稿では、以下を含むこれらの改善点のいくつかについて説明します。
-
暗黙的なコンストラクター注入
-
Java 8デフォルトインターフェイスメソッドのサポート
-
依存関係の解決の改善
-
キャッシュ抽象化の改良
-
構成された@RequestMappingバリアント
-
@ Requestscope、@ Sessionscope、@ Applicationscopeアノテーション
-
@RequestAttributeおよび@SessionAttributeアノテーション
-
Libraries/Application Servers Versions Support
-
InjectionPointクラス
2. 暗黙的なコンストラクター注入
次のサービスクラスを検討してください。
@Service
public class FooService {
private final FooRepository repository;
@Autowired
public FooService(FooRepository repository) {
this.repository = repository
}
}
非常に一般的な使用例ですが、コンストラクターの@Autowiredアノテーションを忘れると、明示的に配線を行わない限り、コンテナーはデフォルトのコンストラクターを探す例外をスローします。
4.3の時点で、このような単一コンストラクターのシナリオで明示的な注入注釈を指定する必要がなくなりました。 これは、注釈をまったく持たないクラスにとって特にエレガントです。
public class FooService {
private final FooRepository repository;
public FooService(FooRepository repository) {
this.repository = repository
}
}
Spring 4.2以下では、SpringがFooServiceのデフォルトコンストラクターを見つけることができないため、このBeanの次の構成は機能しません。 Spring 4.3はよりスマートで、コンストラクターを自動的に自動配線します。
同様に、@Configurationクラスは歴史的にコンストラクターインジェクションをサポートしていなかったことにお気づきかもしれません。 4.3の時点では、それらは可能であり、単一コンストラクターのシナリオでも@Autowiredを省略できるのは当然です。
@Configuration
public class FooConfiguration {
private final FooRepository repository;
public FooConfiguration(FooRepository repository) {
this.repository = repository;
}
@Bean
public FooService fooService() {
return new FooService(this.repository);
}
}
3. Java 8デフォルトインターフェイスメソッドのサポート
Spring 4.3以前では、デフォルトのインターフェースメソッドはサポートされていませんでした。
JDKのJavaBeanイントロスペクターでさえ、デフォルトのメソッドをアクセサーとして検出しなかったため、これを実装するのは簡単ではありませんでした。 Spring 4.3以降、デフォルトのインターフェイスメソッドとして実装されたゲッターとセッターはインジェクション中に識別されます。これにより、たとえば、次の例のように、アクセスされたプロパティの一般的なプリプロセッサとして使用できます。
public interface IDateHolder {
void setLocalDate(LocalDate localDate);
LocalDate getLocalDate();
default void setStringDate(String stringDate) {
setLocalDate(LocalDate.parse(stringDate,
DateTimeFormatter.ofPattern("dd.MM.yyyy")));
}
}
このBeanには、stringDateプロパティが注入されている可能性があります。
デフォルトのインターフェイスメソッドで@BeforeTransactionや@AfterTransactionなどのテストアノテーションを使用する場合も同様です。 JUnit 5は、デフォルトのインターフェイスメソッドでのテストアノテーションを既にサポートしており、Spring 4.3が先導しています。 これで、インターフェイスで一般的なテストロジックを抽象化し、テストクラスで実装できます。 テストトランザクションの前後にメッセージを記録するテストケースのインターフェイスを次に示します。
public interface ITransactionalTest {
Logger log = LoggerFactory.getLogger(ITransactionalTest.class);
@BeforeTransaction
default void beforeTransaction() {
log.info("Before opening transaction");
}
@AfterTransaction
default void afterTransaction() {
log.info("After closing transaction");
}
}
アノテーション@BeforeTransaction,@AfterTransactionおよび@Transactionalに関するもう1つの改善点は、アノテーション付きメソッドがpublicである必要があるという要件の緩和です。これで、任意の可視性レベルを持つことができます。
4. 依存関係の解決の改善
最新バージョンでは、ObjectProviderも導入されています。これは、既存のObjectFactoryインターフェースの拡張であり、getIfAvailableやgetIfUniqueなどの便利な署名を使用して、Beanが存在する場合、または単一の候補を決定できます(特に、一致するBeanが複数ある場合の主要な候補)。
@Service
public class FooService {
private final FooRepository repository;
public FooService(ObjectProvider repositoryProvider) {
this.repository = repositoryProvider.getIfUnique();
}
}
上記のように、初期化中にカスタム解決の目的でこのようなObjectProviderハンドルを使用するか、(通常ObjectFactoryで行うように)後期オンデマンド解決のフィールドにハンドルを格納できます。
5. キャッシュ抽象化の改良
キャッシュの抽象化は、主にCPUとIOを消費する値をキャッシュするために使用されます。 特定のユースケースでは、特定のキーが複数のスレッドによって要求される場合があります(つまり、 クライアント)並行して、特に起動時に。 同期キャッシュのサポートは、長い間リクエストされていた機能で、現在実装されています。 次のように仮定します。
@Service
public class FooService {
@Cacheable(cacheNames = "foos", sync = true)
public Foo getFoo(String id) { ... }
}
値の計算中に同時スレッドをブロックするようにフレームワークに指示するsync = true属性に注意してください。 これにより、同時アクセスの場合に、この集中的な操作が1回だけ呼び出されるようになります。
Spring 4.3では、次のようにキャッシュの抽象化も改善されています。
-
キャッシュ関連のアノテーションのSpEL式は、Beanを参照できるようになりました(つまり、 @beanName.method())。
-
ConcurrentMapCacheManagerおよびConcurrentMapCacheは、新しいstoreByValue属性を介したキャッシュエントリのシリアル化をサポートするようになりました。
-
@Cacheable、@CacheEvict、@CachePut、および@Cachingをメタ注釈として使用して、属性のオーバーライドを使用してカスタム構成の注釈を作成できるようになりました。
6. 構成された@RequestMappingバリアント
Spring Framework 4.3では、@RequestMappingアノテーションの次のメソッドレベルの合成バリアントが導入されています。これは、一般的なHTTPメソッドのマッピングを簡素化し、アノテーション付きハンドラーメソッドのセマンティクスをより適切に表現するのに役立ちます。
-
@GetMapping
-
@PostMapping
-
@PutMapping
-
@DeleteMapping
-
@PatchMapping
たとえば、@GetMappingは、@RequestMapping(method = RequestMethod.GET)の短縮形です。 次の例は、合成された@GetMappingアノテーションで簡略化されたMVCコントローラーを示しています。
@Controller
@RequestMapping("/appointments")
public class AppointmentsController {
private final AppointmentBook appointmentBook;
@Autowired
public AppointmentsController(AppointmentBook appointmentBook) {
this.appointmentBook = appointmentBook;
}
@GetMapping
public Map get() {
return appointmentBook.getAppointmentsForToday();
}
}
7. @RequestScope、@SessionScope、@ApplicationScopeアノテーション
アノテーション駆動型コンポーネントまたはJavaConfigを使用する場合、@RequestScope、@SessionScope、および@ApplicationScopeアノテーションを使用して、コンポーネントを必要なスコープに割り当てることができます。 これらのアノテーションは、Beanのスコープを設定するだけでなく、スコープ付きプロキシモードをScopedProxyMode.TARGET_CLASS.に設定します。
TARGET_CLASSモードは、CGLIBプロキシがこのBeanのプロキシに使用され、より広いスコープであっても、他のBeanに注入できることを保証することを意味します。 TARGET_CLASSモードでは、インターフェイスだけでなくクラスのプロキシも可能です.
@RequestScope
@Component
public class LoginAction {
// ...
}
@SessionScope
@Component
public class UserPreferences {
// ...
}
@ApplicationScope
@Component
public class AppPreferences {
// ...
}
8. @RequestAttributeおよび@SessionAttributeアノテーション
HTTPリクエストのパラメータをControllerメソッドに挿入するためのさらに2つのアノテーション、つまり@RequestAttributeと@SessionAttributeが表示されました。 グローバルに管理されている既存の属性にアクセスできます(つまり、 Controllerの外側)。 これらの属性の値は、たとえば、javax.servlet.Filterまたはorg.springframework.web.servlet.HandlerInterceptorの登録済みインスタンスによって提供される場合があります。
リクエストを解析し、セッションにloginパラメータを追加し、リクエストに別のqueryパラメータを追加する次のHandlerInterceptor実装を登録したとします。
public class ParamInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
request.getSession().setAttribute("login", "john");
request.setAttribute("query", "invoices");
return super.preHandle(request, response, handler);
}
}
このようなパラメータは、メソッド引数に対応するアノテーションを付けてControllerインスタンスに挿入できます。
@GetMapping
public String get(@SessionAttribute String login,
@RequestAttribute String query) {
return String.format("login = %s, query = %s", login, query);
}
9. Libraries/Application Servers Versions Support
Spring 4.3は、以下のライブラリバージョンとサーバー世代をサポートしています。
-
Hibernate ORM 5.2(4.2 / 4.3および5.0 / 5.1も引き続きサポート、3.6は非推奨)
-
ジャクソン2.8(Spring 4.3の時点で、ジャクソン2.6以降に最小レイズ)
-
OkHttp 3.x(OkHttp 2.xを並べてサポート)
-
Netty 4.1
-
Undertow 1.4
-
Tomcat 8.5.2および9.0 M6
さらに、Spring 4.3は、更新されたASM5.1とObjenesis2.4をspring-core.jarに埋め込みます。
10. InjectionPoint
InjectionPointクラスは、Spring 4.3で導入された新しいクラスであり、メソッド/コンストラクターのパラメーターであるかフィールドであるかにかかわらず、provides information about places where a particular bean gets injectedです。
このクラスを使用して見つけることができる情報の種類は次のとおりです。
-
Fieldオブジェクト– Beanがフィールドに注入される場合、getField()メソッドを使用して、Fieldオブジェクトとしてラップされた注入ポイントを取得できます。
-
MethodParameter – Beanがパラメーターに注入された場合、getMethodParameter()メソッドを呼び出して、MethodParameterオブジェクトとしてラップされた注入ポイントを取得できます。
-
Member –getMember()メソッドを呼び出すと、Memberオブジェクトにラップされた注入されたBeanを含むエンティティが返されます
-
Class<?> –getDeclaredType()を使用して、Beanが注入されたパラメーターまたはフィールドの宣言されたタイプを取得します
-
Annotation[] –getAnnotations()メソッドを使用すると、フィールドまたはパラメーターに関連付けられた注釈を表す注釈オブジェクトの配列を取得できます。
-
AnnotatedElement –getAnnotatedElement()を呼び出して、注入ポイントをAnnotatedElementオブジェクトとしてラップします。
このクラスが非常に役立つのは、それらが属するクラスに基づいてLoggerBeanを作成する場合です。
@Bean
@Scope("prototype")
public Logger logger(InjectionPoint injectionPoint) {
return Logger.getLogger(
injectionPoint.getMethodParameter().getContainingClass());
}
クラスごとに異なるロガーが作成されるように、Beanはprototypeスコープで定義する必要があります。 singleton Beanを作成し、複数の場所に注入する場合、Springは最初に検出された注入ポイントを返します。
次に、BeanをAppointmentsControllerに注入できます。
@Autowired
private Logger logger;
11. 結論
この記事では、Spring 4.3で導入されたいくつかの新機能について説明しました。
ボイラープレートを排除する便利なアノテーション、依存関係のルックアップとインジェクションの新しい便利な方法、およびWebとキャッシュ機能内のいくつかの大幅な改善について説明しました。
記事on GitHubのソースコードを見つけることができます。