Verwenden von Annotationen in Java zum Erstellen einer Strategie
Jan 10, 2025 pm 12:13 PMIch habe eine sehr interessante Situation bei der Arbeit erlebt und m?chte hier die L?sung mitteilen.
Stellen Sie sich vor, Sie müssen eine Reihe von Daten verarbeiten. Und um mit diesem Datensatz umzugehen, gibt es verschiedene Strategien. Ich musste beispielsweise Strategien erstellen, wie eine Datensammlung aus S3 oder Beispiele aus dem lokalen Repository abgerufen oder als Eingabe übergeben werden.
Und wer auch immer diese Strategie diktiert, ist derjenige, der die Anfrage stellt:
Ich m?chte die Daten in S3 erhalten. Nehmen Sie die am Tag X zwischen den Stunden H1 und H2 generierten Daten, die vom Abóbora-Client stammen. Holen Sie sich die letzten 3000 Daten, die dies erfüllen.
Oder:
Nehmen Sie die Beispieldaten, die Sie dort haben, und kopieren Sie sie 10.000 Mal, um den Stresstest durchzuführen.
Oder sogar:
Ich habe dieses Verzeichnis, Sie haben auch Zugriff darauf. Holen Sie sich alles in diesem Verzeichnis und rekursiv in die Unterverzeichnisse.
Und schlie?lich auch:
Nehmen Sie diese Dateneinheit, die sich in der Eingabe befindet, und verwenden Sie sie.
Wie umsetzen?
Mein erster Gedanke war: ?Wie kann ich die Form meiner Eingabe in Java definieren?“
Und ich kam zu der ersten Schlussfolgerung, die für das Projekt super wichtig ist: ?Wei?t du was? Ich werde keine Form definieren. Füge eine Map
Da ich au?erdem keine Formen in das DTO eingefügt habe, hatte ich v?llige Freiheit, mit der Eingabe zu experimentieren.
Nachdem wir einen Proof of Concept erstellt haben, kommen wir zu der Situation: Wir müssen aus dem Stress-POC herauskommen und zu etwas übergehen, das einem echten Nutzen nahekommt.
Der Dienst, den ich leistete, bestand darin, Regeln zu validieren. Grunds?tzlich musste ich beim ?ndern einer Regel diese Regel mit den Ereignissen vergleichen, die in der Produktionsanwendung aufgetreten sind. Oder wenn die Anwendung ge?ndert wurde und keine Fehler aufgetreten sind, wird erwartet, dass die Entscheidung für dieselbe Regel für dieselben Daten dieselbe bleibt; Wenn nun die Entscheidung für dieselbe Regel, die denselben Datensatz verwendet, ge?ndert wird ... nun, das kann zu Problemen führen.
Also brauchte ich diese Anwendung, um das Backtesting der Regeln durchzuführen. Ich muss die reale Anwendung aufrufen, die die Daten zur Auswertung und die betreffende Regel sendet. Die Verwendung hierfür ist sehr vielf?ltig:
- Validieren Sie m?gliche Abweichungen bei der Aktualisierung der Anwendung
- überprüfen Sie, ob die ge?nderten Regeln das gleiche Verhalten beibehalten
- zum Beispiel die Optimierung der Regelausführungszeit
- überprüfen Sie, ob die ?nderung der Regeln zu der erwarteten ?nderung der Entscheidungen geführt hat
- Best?tigen Sie, dass die Anwendung durch die ?nderung tats?chlich effizienter geworden ist
- Wenn ich beispielsweise die neue Version von GraalVM mit aktiviertem JVMCI verwende, erh?ht sich die Anzahl der Anfragen, die ich stellen kann?
Dafür brauche ich also einige Strategien für die Entstehung von Ereignissen:
- Holen Sie sich die echten Daten von S3
- Nehmen Sie die Daten, die sich als Beispiel im Repository befinden, und kopieren Sie sie mehrmals
- rufen Sie die Daten von einem bestimmten Ort auf meinem lokalen Computer ab
Und ich brauche auch Strategien, die von meinen Regeln abweichen:
- per Eingabe übergeben
- verwendet den schnell laufenden Stub
- verwendet eine Stichprobe basierend auf der Produktionsregel
- Verwenden Sie diesen Pfad hier auf meinem Computer
Wie gehe ich damit um? Nun, lassen Sie den Benutzer die Daten bereitstellen!
Die API für Strategie
Wissen Sie etwas, das meine Aufmerksamkeit immer auf JSON-Schema gelenkt hat? Das hier:
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://json-schema.org/draft/2020-12/schema", "$vocabulary": { //... } }
Diese Felder beginnen mit $. Meiner Meinung nach werden sie zur Angabe von Metadaten verwendet. Warum also nicht dies in der Dateneingabe verwenden, um die Metadaten der verwendeten Strategie anzugeben?
{ "dados": { "$strategy": "sample", "copias": 15000 }, //... }
Zum Beispiel kann ich 15000 Exemplare meiner Daten als Muster bestellen. Oder fordern Sie einige Dinge von S3 an, indem Sie eine Abfrage in Athena durchführen:
{ "dados": { "$strategy": "athena-query", "limit": 15000, "inicio": "2024-11-25", "fim": "2024-11-26", "cliente": "Abóbora" }, //... }
Oder im lokalen Pfad?
{ "dados": { "$strategy": "localpath", "cwd": "/home/jeffque/random-project-file", "dir": "../payloads/esses-daqui/top10-hard/" }, //... }
Und so kann ich die Auswahl der bevorstehenden Strategie delegieren.
Codeüberprüfung und die Fassade
Mein erster Ansatz im Umgang mit Strategien war dieser:
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); } }
Also stellte mein Architekt w?hrend der Codeüberprüfung zwei Fragen:
- ?Warum instanziieren Sie alles und lassen den Frühling nicht für sich arbeiten?“
- Er hat eine DataLoaderFacade im Code erstellt und sie halbfertig aufgegeben
Was habe ich daraus verstanden? Dass es eine gute Idee w?re, die Fassade zu nutzen, um die Verarbeitung an die richtige Ecke zu delegieren und... die manuelle Kontrolle aufzugeben?
Nun, durch den Frühling passiert viel Magie. Da wir in einem Java-Haus mit Java-Expertise sind, warum nicht das idiomatische Java/Spring verwenden, oder? Nur weil ich als Einzelperson manche Dinge schwer zu verstehen finde, hei?t das nicht zwangsl?ufig, dass sie kompliziert sind. Lassen Sie uns also in die Welt der Java-Abh?ngigkeitsinjektionsmagie eintauchen.
Erstellen des Objekts Fassade
Was früher war:
final var dataLoader = getDataLoader(inputDados) dataLoader.loadData(inputDados, workingPath);
Wurde:
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://json-schema.org/draft/2020-12/schema", "$vocabulary": { //... } }
Meine Controller-Ebene muss dies also nicht verwalten. überlassen Sie es der Fassade.
Also, wie machen wir die Fassade? Nun, um zu beginnen, muss ich alle Objekte hinein injizieren:
{ "dados": { "$strategy": "sample", "copias": 15000 }, //... }
Ok, für den Haupt-DataLoader schreibe ich ihn zus?tzlich zu @Service als @Primary. Den Rest schreibe ich einfach mit @Service auf.
Testen Sie dies hier, indem Sie getDataLoader so einstellen, dass es null zurückgibt, nur um auszuprobieren, wie Spring den Konstruktor aufruft, und ... es hat funktioniert. Jetzt muss ich notieren mit Metadaten für jeden Dienst, welche Strategie er verwendet...
Wie geht das...
Nun, schauen Sie! In Java haben wir Annotationen! Ich kann eine Laufzeit-Annotation erstellen, die enth?lt, welche Strategien von dieser Komponente verwendet werden!
Damit ich so etwas in AthenaQueryDataLoader haben kann:
{ "dados": { "$strategy": "athena-query", "limit": 15000, "inicio": "2024-11-25", "fim": "2024-11-26", "cliente": "Abóbora" }, //... }
Und ich kann auch Aliase haben, warum nicht?
{ "dados": { "$strategy": "localpath", "cwd": "/home/jeffque/random-project-file", "dir": "../payloads/esses-daqui/top10-hard/" }, //... }
Und zeigen!
Aber wie erstellt man diese Anmerkung? Nun, ich brauche ein Attribut, das ein Vektor aus Zeichenfolgen ist (der Java-Compiler kümmert sich bereits um die Bereitstellung einer einzelnen Zeichenfolge und deren Umwandlung in einen Vektor mit einer Position). Der Standardwert ist value. Es sieht so aus:
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); } }
Wenn das Anmerkungsfeld keinen Wert h?tte, müsste ich es explizit machen, und das würde h?sslich aussehen, wie in der EstrategiaFeia-Anmerkung:
final var dataLoader = getDataLoader(inputDados) dataLoader.loadData(inputDados, workingPath);
Meiner Meinung nach klingt es nicht so natürlich.
Okay, in Anbetracht dessen brauchen wir noch:
- Klassenanmerkungen aus übergebenen Objekten extrahieren
- Erstellen Sie eine String-Map → Datenlader (oder string → T)
Extrahieren der Anmerkung und Zusammenstellen der Karte
Um die Anmerkung zu extrahieren, muss ich Zugriff auf die Objektklasse haben:
dataLoaderFacade.loadData(inputDados, workingPath);
Darf ich au?erdem fragen, ob dieser Kurs mit einer Anmerkung wie ?Strategie:“ versehen wurde?
@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... } }
Erinnern Sie sich, dass es das Wertefeld gibt? Nun, dieses Feld gibt einen Vektor von Zeichenfolgen zurück:
@Service @Primary @Estrategia("athena-query") public class AthenaQueryDataLoader implements DataLoader { // ... }
Zeigen! Aber ich stehe vor einer Herausforderung, denn vorher hatte ich ein Objekt vom Typ T und jetzt m?chte ich dasselbe Objekt in (T, String)[] abbilden. In Streams ist die klassische Operation, die dies tut, flatMap. Und Java erlaubt mir auch nicht, so ein Tupel aus dem Nichts zurückzugeben, aber ich kann damit einen Datensatz erstellen.
Es würde ungef?hr so ??aussehen:
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://json-schema.org/draft/2020-12/schema", "$vocabulary": { //... } }
Was ist, wenn es ein Objekt gibt, das nicht mit einer Strategie versehen wurde? Wird es NPE geben? Besser nicht, lass es uns vor der NPE herausfiltern:
{ "dados": { "$strategy": "sample", "copias": 15000 }, //... }
Vor diesem Hintergrund muss ich noch eine Karte zusammenstellen. Und siehe da: Java stellt hierfür bereits einen Collector zur Verfügung! Collector.toMap(keyMapper, valueMapper)
{ "dados": { "$strategy": "athena-query", "limit": 15000, "inicio": "2024-11-25", "fim": "2024-11-26", "cliente": "Abóbora" }, //... }
Bis jetzt ok. Aber flatMap hat mich besonders gest?rt. Es gibt eine neue Java-API namens mapMulti, die dieses Potenzial zur Vervielf?ltigung bietet:
{ "dados": { "$strategy": "localpath", "cwd": "/home/jeffque/random-project-file", "dir": "../payloads/esses-daqui/top10-hard/" }, //... }
Sch?nheit. Ich habe es für DataLoader bekommen, aber ich muss das Gleiche auch für RuleLoader tun. Oder vielleicht auch nicht? Wie Sie bemerken, enth?lt dieser Code nichts, was spezifisch für DataLoader ist. Wir k?nnen diesen Code abstrahieren!!
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); } }
Unter der Fassade
Aus rein praktischen Gründen habe ich diesen Algorithmus in die Anmerkung eingefügt:
final var dataLoader = getDataLoader(inputDados) dataLoader.loadData(inputDados, workingPath);
Und für die Fassade? Nun, es ist gut, das Gleiche zu sagen. Ich habe beschlossen, Folgendes zu abstrahieren:
dataLoaderFacade.loadData(inputDados, workingPath);
Und die Fassade sieht so aus:
@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... } }
Das obige ist der detaillierte Inhalt vonVerwenden von Annotationen in Java zum Erstellen einer Strategie. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Hei?e KI -Werkzeuge

Undress AI Tool
Ausziehbilder kostenlos

Undresser.AI Undress
KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover
Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Clothoff.io
KI-Kleiderentferner

Video Face Swap
Tauschen Sie Gesichter in jedem Video mühelos mit unserem v?llig kostenlosen KI-Gesichtstausch-Tool aus!

Hei?er Artikel

Hei?e Werkzeuge

Notepad++7.3.1
Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version
Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1
Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6
Visuelle Webentwicklungstools

SublimeText3 Mac-Version
Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Hei?e Themen

Der Unterschied zwischen HashMap und Hashtable spiegelt sich haupts?chlich in der Gewindesicherheit, der Nullwertunterstützung und der Leistung wider. 1. In Bezug auf die Gewindesicherheit ist Hashtable Thread-Safe, und seine Methoden sind haupts?chlich Synchronmethoden, w?hrend HashMap keine Synchronisationsverarbeitung durchführt, die nicht mit Thread-Safe ist. 2. In Bezug auf die Nullwertunterstützung erm?glicht HashMap einen Nullschlüssel und mehrere Nullwerte, w?hrend Hashtable keine Nullschlüssel oder -Werte zul?sst, sonst wird eine Nullpointerexception geworfen. 3. In Bezug auf die Leistung ist HashMap effizienter, da kein Synchronisationsmechanismus vorhanden ist und Hashtable für jeden Vorgang eine niedrige Verriegelungsleistung aufweist. Es wird empfohlen, stattdessen eine Concurrenthashmap zu verwenden.

Java verwendet Wrapper-Klassen, da grundlegende Datentypen nicht direkt an objektorientierten Operationen teilnehmen k?nnen und Objektformen h?ufig in den tats?chlichen Bedürfnissen erforderlich sind. 1. Sammelklassen k?nnen nur Objekte speichern, z. B. Listen verwenden automatische Boxen, um numerische Werte zu speichern. 2. Generika unterstützen keine Grundtypen, und Verpackungsklassen müssen als Typparameter verwendet werden. 3.. Verpackungsklassen k?nnen Nullwerte darstellen, um nicht festgelegte oder fehlende Daten zu unterscheiden. 4. Verpackungsklassen bieten praktische Methoden wie String -Conversion, um die Analyse und Verarbeitung von Daten zu erleichtern. In Szenarien, in denen diese Eigenschaften ben?tigt werden, sind Verpackungsklassen unverzichtbar.

Der JIT -Compiler optimiert den Code durch vier Methoden: Methode Inline, Hotspot -Erkennung und -vergleich, Typespekulation und Devirtualisation sowie die Eliminierung des redundanten Betriebs. 1. Methode Inline reduziert den Anrufaufwand und fügt h?ufig kleine Methoden direkt in den Anruf ein. 2. Erkennung und Hochfrequenzcodeausführung und zentral optimieren, um Ressourcen zu sparen. 3. Typ Spekulation sammelt Informationen zum Laufzeittyp, um Devirtualisation -Anrufe zu erzielen und die Effizienz zu verbessern. 4. Redundante Operationen beseitigen nutzlose Berechnungen und Inspektionen basierend auf den Betriebsdaten, wodurch die Leistung verbessert wird.

StaticMethodsinInterfaces -reisEtroducucuedInjava8toalloytilityFunctionSwitHinTheInterfaceItEp.beejava8, solche Funktionen, dieseparatehelperklassen, führendemTodisorganizedCode.Now, StaticMetheSprovidreefits: 1) theeneNableable -theenableaby

Instanzinitialisierungsbl?cke werden in Java verwendet, um die Initialisierungslogik beim Erstellen von Objekten auszuführen, die vor dem Konstruktor ausgeführt werden. Es ist für Szenarien geeignet, in denen mehrere Konstruktoren Initialisierungscode, komplexe Feldinitialisierung oder anonyme Szenarien der Klasseninitialisierung teilen. Im Gegensatz zu statischen Initialisierungsbl?cken wird es jedes Mal ausgeführt, wenn es instanziiert wird, w?hrend statische Initialisierungsbl?cke nur einmal ausgeführt werden, wenn die Klasse geladen wird.

InvaVa, theFinalKeywordPreventsAvariable von ValueFromBeingumedAfterasssignment, ButitsBehaviordiffersForprimitive und ANSPRIMITIVEVARIABLE, FinalMakesthevalueconstant, AsinfinalIntmax_speed = 100; WhirerastsignmentcausaSesSaSesSaSesSaSaSesSaSesSaSaSesSaSaSesSaSesSesirror

Der Werksmodus wird verwendet, um die Logik der Objekterstellung zusammenzufassen, wodurch der Code flexibler, einfach zu pflegen und locker gekoppelt ist. Die Kernantwort lautet: Durch zentrales Verwalten von Logik der Objekterstellung, das Ausblenden von Implementierungsdetails und die Unterstützung der Erstellung mehrerer verwandter Objekte. Die spezifische Beschreibung lautet wie folgt: Der Fabrikmodus gibt Objekterstellung an eine spezielle Fabrikklasse oder -methode zur Verarbeitung und vermeidet die Verwendung von NewClass () direkt; Es ist für Szenarien geeignet, in denen mehrere Arten von verwandten Objekten erstellt werden, die Erstellungslogik sich ?ndern und Implementierungsdetails versteckt werden müssen. Zum Beispiel werden im Zahlungsabwickler Stripe, PayPal und andere Instanzen durch Fabriken erstellt. Die Implementierung umfasst das von der Fabrikklasse zurückgegebene Objekt basierend auf Eingabeparametern, und alle Objekte erkennen eine gemeinsame Schnittstelle. Gemeinsame Varianten umfassen einfache Fabriken, Fabrikmethoden und abstrakte Fabriken, die für unterschiedliche Komplexit?ten geeignet sind.

Es gibt zwei Arten von Konvertierung: implizit und explizit. 1. Die implizite Umwandlung erfolgt automatisch, wie z. B. das Konvertieren in INT in Doppel; 2. Explizite Konvertierung erfordert einen manuellen Betrieb, z. B. die Verwendung (int) MyDouble. Ein Fall, in dem die Typ -Konvertierung erforderlich ist, umfasst die Verarbeitung von Benutzereingaben, mathematische Operationen oder das übergeben verschiedener Werte zwischen Funktionen. Probleme, die beachtet werden müssen, sind: Umdrehung von Gleitpunktzahlen in Ganzzahlen wird der fraktionale Teil abschneiden, gro?e Typen in kleine Typen zu einem Datenverlust führen, und einige Sprachen erm?glichen keine direkte Konvertierung bestimmter Typen. Ein ordnungsgem??es Verst?ndnis der Regeln der Sprachkonvertierung hilft, Fehler zu vermeiden.
