ホームページ >Java >&#&チュートリアル >Java でアノテーションを使用して戦略を立てる
仕事で非常に興味深い状況に遭遇したので、ここで解決策を共有したいと思いました。
一連のデータを処理する必要があると想像してください。そして、このデータセットを扱うには、いくつかの異なる戦略があります。たとえば、S3 からデータのコレクション、ローカル リポジトリ内のサンプル、または入力として渡されるサンプルを取得する方法に関する戦略を作成する必要がありました。
そして、この戦略を決定するのは、リクエストを行う人です:
S3のデータを取得したい。 X 日目の H1 時間と H2 時間の間に生成された、Abobora クライアントからのデータを取得します。これを満たす最後の 3000 データを取得します。
または:
そこにあるサンプル データを 10,000 回コピーして、ストレス テストを実行します。
または:
私はこのディレクトリを持っています。あなたもそれにアクセスできます。そのディレクトリ内のすべてを取得し、サブディレクトリに再帰的に入れます。
そして最後に:
入力にあるこのデータユニットを取得して使用します。
最初に考えたのは、「Java で入力の形状を定義するにはどうすればよいでしょうか?」
そして私は、プロジェクトにとって非常に重要な最初の結論に達しました: 「ご存知ですか? 形状を定義するつもりはありません。それを処理できる Map
その上、DTO にシェイプを入れていないので、入力を完全に自由に試すことができました。
したがって、概念実証を確立した後、ストレスの多い POC から抜け出し、実際の使用に近いものに進む必要があるという状況に到達します。
私が行っていたサービスはルールを検証することでした。基本的に、ルールを変更するときは、そのルールを取得し、本番アプリケーションで発生したイベントと照合する必要がありました。または、アプリケーションが変更され、バグがなかった場合、同じデータに対する同じルールの決定が同じままであることが期待されます。ここで、同じデータセットを使用する同じルールの決定が変更された場合...そうですね、それは潜在的な問題です。
そこで、ルールのバックテストを実行するにはこのアプリケーションが必要でした。評価用のデータと問題のルールを送信する実際のアプリケーションを実行する必要があります。これの使用法は非常に多様です:
そのためには、イベントの起源に関するいくつかの戦略が必要です。
そして、私のルールとは異なる戦略も必要です:
これにどう対処しますか?そうですね、ユーザーにデータを提供してもらいましょう!
json スキーマについて私がいつも注意を引いていたことを知っていますか?これはこちらです:
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://json-schema.org/draft/2020-12/schema", "$vocabulary": { //... } }
これらのフィールドは $ で始まります。私の意見では、これらはメタデータを示すために使用されます。では、どの戦略が使用されているかのメタデータを示すために、これをデータ入力で使用してみてはいかがでしょうか?
{ "dados": { "$strategy": "sample", "copias": 15000 }, //... }
たとえば、持っているデータをサンプルとして 15,000 部注文できます。または、Athena でクエリを作成して、S3 に何かをリクエストします:
{ "dados": { "$strategy": "athena-query", "limit": 15000, "inicio": "2024-11-25", "fim": "2024-11-26", "cliente": "Abóbora" }, //... }
それともローカルパスにありますか?
{ "dados": { "$strategy": "localpath", "cwd": "/home/jeffque/random-project-file", "dir": "../payloads/esses-daqui/top10-hard/" }, //... }
それで、今後の戦略の選択を私に任せることができます。
戦略に対処するための私の最初のアプローチは次のとおりでした:
public DataLoader getDataLoader(Map<String, Object> inputDados) { final var strategy = (String) inputDados.get("$strategy"); return switch (strategy) { case "localpath" -> new LocalpathDataLoader(); case "sample" -> new SampleDataLoader(resourcePatternResolver_spring); case "athena-query" -> new AthenaQueryDataLoader(athenaClient, s3Client); default -> new AthenaQueryDataLoader(athenaClient, s3Client); } }
そこで、私のアーキテクトはコードレビュー中に 2 つの質問をしました。
このことから何が分かりましたか?ファサードを使用することは、処理を正しいコーナーに委任し、手動制御を放棄することをお勧めします?
そうですね、春のせいでたくさんの魔法が起こります。私たちは Java の専門知識を備えた Java ハウスにいるのですから、慣用的な Java/Spring を使用してみてはいかがでしょうか? 私個人として、いくつかの事柄を理解するのが難しいと感じるからといって、必ずしもそれが複雑であることを意味するわけではありません。それでは、Java 依存関係注入の魔法の世界を受け入れてみましょう。
以前は:
final var dataLoader = getDataLoader(inputDados) dataLoader.loadData(inputDados, workingPath);
次のようになりました:
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://json-schema.org/draft/2020-12/schema", "$vocabulary": { //... } }
したがって、私の controller 層はこれを管理する必要はありません。ファサードまでお任せください
それでは、ファサードをどうするか?まず、すべてのオブジェクトをそれに注入する必要があります:
{ "dados": { "$strategy": "sample", "copias": 15000 }, //... }
メインの DataLoader については、@Service に加えて @Primary として記述します。残りは @Service で書き留めるだけです。
ここでこれをテストし、Spring がコンストラクターをどのように呼び出しているかを試すためだけに getDataLoader を null を返すように設定して、うまくいきました。ここで、各サービスがどのような戦略を使用しているかをメタデータでメモする必要があります...
これを行うには...
まあ、見てください! Java には アノテーション があります。そのコンポーネントで使用される戦略を含む ランタイム アノテーションを作成できます!
AthenaQueryDataLoader では次のようなものを作成できます。
{ "dados": { "$strategy": "athena-query", "limit": 15000, "inicio": "2024-11-25", "fim": "2024-11-26", "cliente": "Abóbora" }, //... }
エイリアスも持つことができます。そうしないのはなぜですか?
{ "dados": { "$strategy": "localpath", "cwd": "/home/jeffque/random-project-file", "dir": "../payloads/esses-daqui/top10-hard/" }, //... }
そして見せてください!
しかし、この注釈はどうやって作成するのでしょうか?そうですね、文字列のベクトルである属性を持たせる必要があります (Java コンパイラは、単独の文字列を提供し、それを位置 1 のベクトルに変換する処理をすでに処理しています)。デフォルト値は value です。次のようになります:
public DataLoader getDataLoader(Map<String, Object> inputDados) { final var strategy = (String) inputDados.get("$strategy"); return switch (strategy) { case "localpath" -> new LocalpathDataLoader(); case "sample" -> new SampleDataLoader(resourcePatternResolver_spring); case "athena-query" -> new AthenaQueryDataLoader(athenaClient, s3Client); default -> new AthenaQueryDataLoader(athenaClient, s3Client); } }
注釈フィールドが値でない場合は、それを明示的にする必要があり、EstrategiaFeia 注釈のように見苦しくなります。
final var dataLoader = getDataLoader(inputDados) dataLoader.loadData(inputDados, workingPath);
私の意見では、それはあまり自然ではありません。
それでは、次のものがまだ必要です。
注釈を抽出するには、オブジェクト クラスにアクセスする必要があります。
dataLoaderFacade.loadData(inputDados, workingPath);
それに加えて、このクラスに Strategy:
のようなアノテーションが付けられているかどうかを尋ねてもいいですか?
@Service // para o Spring gerenciar esse componente como um serviço public class DataLoaderFacade implements DataLoader { public DataLoaderFacade(DataLoader primaryDataLoader, List<DataLoader> dataLoaderWithStrategies) { // armazena de algum modo } @Override public CompletableFuture<Void> loadData(Map<String, Object> input, Path workingPath) { return getDataLoader(input).loadData(input, workingPath); } private DataLoader getDataLoader(Map<String, Object> input) { final var strategy = input.get("$strategy"); // magia... } }
値フィールドがあることを覚えていますか?さて、このフィールドは文字列のベクトルを返します:
@Service @Primary @Estrategia("athena-query") public class AthenaQueryDataLoader implements DataLoader { // ... }
ショー!しかし、私には課題があります。以前は T 型のオブジェクトを持っていましたが、今はその同じオブジェクトを (T, String)[] にマップしたいと考えているからです。ストリームでは、これを行う古典的な操作は flatMap です。また、Java では、そのようなタプルを突然返すことはできませんが、それを使用してレコードを作成することはできます。
次のようになります:
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://json-schema.org/draft/2020-12/schema", "$vocabulary": { //... } }
戦略の注釈が付けられていないオブジェクトがある場合はどうなりますか?それはNPEを与えるでしょうか?それはやめた方が良いので、NPE の前にフィルタリングして除外しましょう:
{ "dados": { "$strategy": "sample", "copias": 15000 }, //... }
そう考えると、まだ地図をまとめる必要があります。そして、見てください。Java はすでにこのためのコレクターを提供しています。 Collector.toMap(keyMapper, valueMapper)
{ "dados": { "$strategy": "athena-query", "limit": 15000, "inicio": "2024-11-25", "fim": "2024-11-26", "cliente": "Abóbora" }, //... }
ここまではOK。しかし、私は flatMap が特に気になりました。 MapMulti と呼ばれる新しい Java API があり、これには次のような可能性が秘められています。
{ "dados": { "$strategy": "localpath", "cwd": "/home/jeffque/random-project-file", "dir": "../payloads/esses-daqui/top10-hard/" }, //... }
美しさ。 DataLoader 用に取得しましたが、RuleLoader でも同じことを行う必要があります。それともそうではないでしょうか?お気づきかと思いますが、このコードには DataLoader に固有のものは何もありません。このコードを抽象化できます!!
public DataLoader getDataLoader(Map<String, Object> inputDados) { final var strategy = (String) inputDados.get("$strategy"); return switch (strategy) { case "localpath" -> new LocalpathDataLoader(); case "sample" -> new SampleDataLoader(resourcePatternResolver_spring); case "athena-query" -> new AthenaQueryDataLoader(athenaClient, s3Client); default -> new AthenaQueryDataLoader(athenaClient, s3Client); } }
純粋に実用的な理由から、私はこのアルゴリズムを注釈内に配置しました:
final var dataLoader = getDataLoader(inputDados) dataLoader.loadData(inputDados, workingPath);
そしてファサードについては?まあ、仕事も同じことを言うのが良いです。私はこれを抽象化することにしました:
dataLoaderFacade.loadData(inputDados, workingPath);
ファサードは次のようになります:
@Service // para o Spring gerenciar esse componente como um serviço public class DataLoaderFacade implements DataLoader { public DataLoaderFacade(DataLoader primaryDataLoader, List<DataLoader> dataLoaderWithStrategies) { // armazena de algum modo } @Override public CompletableFuture<Void> loadData(Map<String, Object> input, Path workingPath) { return getDataLoader(input).loadData(input, workingPath); } private DataLoader getDataLoader(Map<String, Object> input) { final var strategy = input.get("$strategy"); // magia... } }
以上がJava でアノテーションを使用して戦略を立てるの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。