Java Beanプロパティへの静的に検証されたリンク

ツールを長期間、真剣に使用する場合、必然的に不満があります。最初は我慢しなければならない不便ですが、ある時点で、常に苦しむよりも一度修正する方が簡単だと気づきます。 自分で「仕上げる」ことができる優れたツール。

Javaは優れたツールなので、このような不便さと修正方法について説明します。

だから不便


Javaには、Beanプロパティを参照する構文はありません。 例で説明する方が簡単です。 タイプCustomer customerプロパティを持つAccountがあり、 nameプロパティを持つAccountがあるとします。 言い換えれば:

 public class Account { public Customer getCustomer() { ... } } public class Customer { public String getName() { ... } } 

また、インターフェイス上にタブレットを作成してBeanのリストを表示できるTableBuilderがあります。表示するプロパティ(ネストされている可能性があります)を指定するだけで、すべてのルーチン作業が既に実行されます。

name customerAccountname customerを表示したいと言うにはどうすればいいですか? 通常、文字列リテラルを使用します。

 TableBuilder<Account> tableBuilder = TableBuilder.of(Account.class); ... tableBuilder.addColumn("customer.name"); 

この方法の欠点は、これが単なる文字列ではないことをコンパイラが認識せず、その正確性を検証できないことです。つまり、すべての入力ミスは実行時にのみエラーになります。 同じ理由で、開発環境はCustomerどのプロパティをCustomerいるかを知ることができません。 そして、すべてをチェックしてデバッグしても、最初のリファクタリングは努力を破壊します。

それほど明らかではない別の不便な点がありますaddColumn(String)入力しても、このメソッドが文字列だけでなく、一連のプロパティを期待していることはまったくわかりません。 コンパイラーにすべてをチェックさせ、環境にプロンプ​​トを表示させ、リファクタリングは中断しませんでした。 それほど多くはありませんが、これに必要なすべての情報は既にそこにあります。

ソリューションに向けて


タスクは解決できないように見えます。Javaでは、クラスのメンバーにアクセスせずに参照する構文構造は実際にはありません。 ただし、これにより、モックフレームワークがエレガントかつ厳密に「メソッドが呼び出されるタイミング」を表現できなくなりました。たとえば、Mockitoでは次のことが可能です。

 Account account = mock(Account.class); when(account.getCustomer()).thenReturn(...); 

mock()メソッドは、 Accountように見えるプロキシを作成して返しますが、まったく異なる方法で動作します。呼び出されたメソッドに関する情報をThreadLocal変数に保存し、それを取得してwhen()を使用when()ます。 同じトリックを使用して問題を解決できます。

 Account account = root(Account.class); tableBuilder.addColumn( $( account.gertCustomer().getName() ) ); 

root()は、呼び出されたメソッドをThreadLocal変数に格納するプロキシを返し、次のプロキシを返します。これにより、プロパティのチェーンになる呼び出しのチェーンを作成できます。

$()は文字列ではなく、オブジェクト指向の方法でプロパティのチェーンを表すBeanPath型のオブジェクトをBeanPathます。 このチェーンの個々の要素をナビゲートする(各要素について、名前とタイプが保存される)か、すでにわかっている文字列に変換することができます。

 $( account.gertCustomer().getName() ).toDotDelimitedString() => "customer.name" 

$() 、その主な機能に加えて、チェーンのタイプ(チェーンの最後のプロパティ)をTableBuilderます。 TableBuilderTableBuilder別のタイプのドロップを追加できます。

 public <T> ColumnBuilder<T> addColumn(BeanPath<T> path) {...} 

ここでは、CUSTISでこのような小さなフレームワークを作成し、それを自分で使用して、GitHubに投稿しました

使用面


動的プロキシを介した実装には、次の制限があります。 第一に、チェーンの「ルート」プロパティと非終了プロパティは、最終クラス( enum 、string、 jlIntegerなどを含む)にすることはできません。 フレームワークはそれらをプロキシできず、 nullを返しnull

 $( account.getCustomer().getName().length() ) // => NPX! 

それにもかかわらず、どのタイプのプロパティでもチェーンを閉じることができます:最終クラスとプリミティブ(チェーンの途中では無意味で不可能です)。

第二に、ゲッターはフレームワークから見えるようにする必要があります。つまり、ゲッターはprivateまたはpackage-localであってはなりません。 ただし、一般にデフォルトのコンストラクターとパブリックコンストラクターは存在しない場合があります。プロキシーは、コンストラクターをバイパスしてインスタンス化されます。 これを正当な方法で行うことはできないため、HotSpot JVMの組み込みsun.misc.Unsafe.allocateObject()組み込みが使用され、フレームワークが他のJVMに耐えられなくなります。 Rutsは再利用可能であり、再利用する必要がありますが、状態は含まれていません。

 Account account = root(Account.class); tableBuilder.addColumn( $( account.getCustomer().getName() ) ); tableBuilder.addColumn( $( account.getNumber() ) ); tableBuilder.addColumn( $( account.getOpenDate() ) ); 

root()および$()メソッドは、単なる静的メソッドであるため、エイリアスを作成できます。

 public class BeanPathMagicAlias { public static <T> BeanPath<T> path(T callChain) { return BeanPathMagic.$(callChain); } } 

これを使用して、好みに合わせてメソッドの名前を変更したり、便利なショートカットを作成したりできます。 特に、そのようなものはbeanpathですでに宣言されていbeanpath

 public static String $$(Object callChain) { return $(callChain).toDotDelimitedString(); } 

文字列リテラルを期待するコードでbeanpathを使用するのに役立ちます。 BeanPath BeanPath手動で構築することもできBeanPath -その動作は、構築中に設定される状態によって完全に決定されます。 だから:

 BeanPath<String> bp1 = $( account.getCustomer().getName()); BeanPath<String> bp2 = BeanPath.root(Account.class) .append("customer", Customer.class) .append("name", String.class); bp1.equals(bp2) // => true 

これは、上記の制限を回避するのに役立ちます(チェーンに最終クラスがあるか、パブリックゲッターがない場合)。 同時に、チェーンの正確性は開発者の良心に残ります。

今後の計画


beanpathはソースコードでのみ使用できるようになりました。 したがって、まず、Maven Centralで完全なアセンブリと展開を確立したいと思います。 次に、 sun.misc.UnsafeをObjnesisに置き換えて、Beanpathを移植可能にします。 まあ、かなりの見通し-別のエッジから問題の解決策にアプローチする:JPA静的メタモデルで静的コード生成を使用します。
このオプションにはいくつかの利点があります。

  1. ゼロのランタイムオーバーヘッド。
  2. チェーンの「ルート」の類型化をキャプチャする機能。
  3. 生成されたクラスAPIでは、不要なメソッド(プロパティに関連しない)を除外できます。

PS Querydslには同様の機能があり、同じ方法で実装されますが、Querydslインフラストラクチャに強く結び付けられています。

Source: https://habr.com/ru/post/J243803/


All Articles