Wir haben einen Microservice, der einige JSON-Daten aus der Queue bekommt, ein wenig verarbeitet und das Ergebnis der Verarbeitung weiter - wieder über die Queue - sendet. Im Microservice arbeiten wir nicht direkt mit JSONObject
an likes, wir ordnen JSON Java-Klassen mit Jackson zu.
Bei der Bearbeitung interessiert sich der Microservice nur für einige Eigenschaften der eingehenden Nachricht, nicht für alle. Stellen Sie sich vor, dass es nur
empfängt %Vor%Und sendet:
%Vor%Wie kann ich andere Eigenschaften der Nachricht tunnellieren oder weiterleiten, die für diesen Dienst nicht von Interesse sind, ohne sie explizit in meinen Klassen zuzuordnen?
Für die Zwecke dieses Microservice würde es genügen, eine Struktur wie folgt zu haben:
%Vor%Wenn ich jedoch nicht alle anderen Eigenschaften abbilde, gehen sie verloren.
Wenn ich sie abbilde, muss ich das Modell dieses Microservice jedes Mal aktualisieren, wenn sich die Struktur der Nachricht ändert, was Aufwand erfordert und fehleranfällig ist.
Der einfachste Weg ist die Verwendung von Map anstelle von benutzerdefinierten POJO im Falle einer agilen Struktur:
Es ist leicht, von JSON zu lesen, z.B. mit JsonParser parser
(java docs hier ) ):
Es ist einfach, mit BasicDBObject
(java docs hier ):
Sie können es sogar erreichen, indem Sie Map
mit Task
wie folgt umschließen:
Es ist auch möglich, das benutzerdefinierte JsonDeserializer
zu verwenden, wenn es erforderlich ist ( Task
muss in diesem Fall mit @JsonDeserialize(using = TaskDeserializer.class)
kommentiert werden):
Wie bereits erwähnt, können @JsonAnyGetter
und @JsonAnySetter
die beste Wahl für Sie sein.
Ich denke, dass Sie es so flexibel wie typsicher machen können.
Das erste, was mir in den Sinn kommt, ist die genaue Trennung der Eigenschaften, die Sie brauchen, und des ganzen Rests.
Ein einfaches unveränderbares "Berechnungs" -Objekt. Natürlich kann es auf andere Weise gestaltet werden, aber die Unveränderlichkeit macht es einfacher und zuverlässiger, glaube ich.
%Vor%Eine einfache Berechnungsstrategie, die in einer Aufzählung definiert wird, da Jackson mit Aufzählungen wirklich gut arbeitet.
%Vor% Beachten Sie, dass diese Klasse einen Wert liefern soll, aber sammeln Sie den Rest in einer Satellitenkarte, die von den mit @JsonAnySetter
und @JsonAnyGetter
annotierten Methoden verwaltet wird.
Die Karte und die Methoden können sicher deklariert werden private
, da Jackson das Schutzniveau nicht wirklich interessiert (und das ist großartig).
Außerdem ist es in unveränderlicher Weise entworfen, mit Ausnahme der zugrunde liegenden Karte, die einfach auf einen neuen Wert kopiert werden kann.
Hier ist eine Klasse, die eine konkrete Berechnungsaufgabe definiert.
Auch hier arbeitet Jackson perfekt mit privaten Feldern und Methoden zusammen, so dass die gesamte Komplexität gekapselt werden kann.
Ein Nachteil, den ich sehen kann, ist, dass JSON-Eigenschaften sowohl für das Serialisieren als auch für das Deserialisieren deklariert sind, aber auch als Vorteil betrachtet werden können.
Beachten Sie, dass @JsonGetter
Argumente hier nicht notwendig sind, aber ich habe die Eigenschaftsnamen sowohl für Ein- als auch für Aus-Operationen verdoppelt.
Keine Aufgaben sollen manuell instanziiert werden - lass es nur Jackson tun.
Hier ist ein einfacher GET / PUT / DELETE-Controller für Integrationstests oder einfach nur manuell mit curl
getestet werden.
Da die get
und delete
-Methoden, die unten in der DAO-Klasse deklariert werden, NoSuchElementException
werfen, kann die Ausnahme leicht auf HTTP 404 abgebildet werden.
Nur ein einfacher Dienst, der eine gewisse "Geschäftslogik" enthält.
%Vor%Nur eine Inhaberklasse, um mit MongoDB-Repositorys in Spring Data zu arbeiten, die den Ziel-MongoDB-Dokumentauflistungsnamen angeben.
%Vor% Ein Spring Data MongoDB CRUD-Repository für die Klasse CalculationMapping
.
Dieses Repository wird unten verwendet.
Die DAO-Komponente macht in der Demo selbst nicht viel Arbeit, und es geht eher darum, den Persistenzjob an seine Superklasse zu delegieren und von Spring Framework leicht zu finden.
%Vor% Dies ist das Herz, das ganze ursprüngliche Objekt zu erhalten.
Die ObjectMapper
-Instanz wird verwendet, um Aufgaben in ihre jeweiligen Aufgabenzuordnungen (siehe convertValue
-Methode) gemäß den mit den Jackson-Anmerkungen angegebenen Serialisierungsregeln zu konvertieren.
Da die Demo Spring Data MongoDB verwendet, sind die Mapping-Klassen effektiv Map<String, Object>
und erben die Klasse Document
.
Leider scheinen Map
-orientierte Zuordnungen nicht mit Spring Data MongoDB-Annotationen wie @Id
, @Field
usw. zu funktionieren (mehr dazu unter Wie kombiniere ich java.util.Map-basierte Mappings mit den Spring Data MongoDB Annotationen (@ Id, @Field, ...)? ).
Es kann jedoch gerechtfertigt sein, solange Sie keine beliebigen Dokumente zuordnen möchten.
Und eine Spring Boot-Demo, die alles als eine einzige HTTP-Anwendung ausführt, die Port 9000 hört.
%Vor%curl
%Vor%wurde zu db local
gewechselt
%Vor%& gt; GET / foo HTTP / 1.1
& gt; Benutzer-Agent: curl / 7.35.0
& gt; Host: localhost: 9000
& gt; Akzeptieren: /
& gt;
& lt; HTTP / 1.1 404
& lt; Inhaltslänge: 0
& lt; Datum: Do, 15 Dec 2016 10:07:40 GMT
& lt;
(leer)
%Vor%%Vor%& gt; PUT / foo HTTP / 1.1
& gt; Benutzer-Agent: curl / 7.35.0
& gt; Host: localhost: 9000
& gt; Akzeptieren: /
& gt; Inhaltstyp: application / json
& gt; Inhaltslänge: 72
& gt;
& lt; HTTP / 1.1 204
& lt; Datum: Do, 15 Dec 2016 10:11:13 GMT
& lt;
%Vor%{"_id": "foo", "_class": "q41036545.CalculationTaskMapping", "a": 3, "b": 4, "Vorgang": "MULTIPLY", "Ergebnis": 12, "foo ":" FOO "," bar ":" BAR "}
%Vor%& gt; GET / foo HTTP / 1.1
& gt; Benutzeragent: curl / 7.35.0
& gt; Host: localhost: 9000
& gt; Akzeptieren: /
& gt;
& lt; HTTP / 1.1 200
& lt; Content-Type: application / json; charset = UTF-8
& lt; Transfer-Encoding: Chunked
& lt; Datum: Do, 15 Dec 2016 10:16:33 GMT
& lt;
{"a": 3.0, "b": 4.0, "Operation": "MULTIPLY", "Ergebnis": 12.0, "foo": "FOO", "bar": "BAR"}
%Vor%& gt; LÖSCHEN / foo HTTP / 1.1
& gt; Benutzer-Agent: curl / 7.35.0
& gt; Host: localhost: 9000
& gt; Akzeptieren: /
& gt;
& lt; HTTP / 1.1 204
& lt; Datum: Do, 15 Dec 2016 11:51:26 GMT
& lt;
(leer)
Der Quellcode kann unter Ссылка
gefunden werden Keine von beiden ist sehr schön, aber was Sie tun könnten, ist die Struktur in zwei Teile aufzuteilen - ein strukturiertes Teil, das von Ihrem Microservice deserialisiert wird, und ein separates Feld 'additionalFields', das Ihren anderen JSON enthält, dann können Sie Ändern Sie den JSON in diesem Feld, ohne Task
zu ändern. Sie könnten verschachtelte JSON-Dateien entweder als String
hinzufügen.
Oder Sie könnten ein Map<String, Object>
hinzufügen, mit dem Sie Schlüssel-Wert-Paare hinzufügen können, aber Sie würden wiederum den Typ Sicherheit verlieren:
Eine weitere Möglichkeit: Verwenden Sie @JsonAnySetter
und / oder @JsonAnyGetter
, um eingebettete "dynamische" Eigenschaften zu erstellen. Ein Beispiel hier:
aber die Grundidee ist, dass es möglich ist, "irgendetwas anderes" von JSON über @JsonAnySetter
annotierte Methode (oder Feld in neueren Versionen) zu binden; sowie "zusätzliche Eigenschaften" für die Serialisierung durch ähnliche @JsonAnyGetter
Methode / Feld mit Map
Wert.
Fügen Sie @JsonView zu Ihrem POJO hinzu, so:
%Vor% und senden Sie es dann mit com.fasterxml.jackson.databind.ObjectMapper
an Ihren Dienst wie folgt:
Konfigurieren Sie eine ObjectMapper-Bean in Spring / Javaconfig und legen Sie die Eigenschaft so fest, dass fehlende Eigenschaften ignoriert werden. Injizieren Sie dieselbe Bohne wo nötig.
%Vor%Wenn ich also verstehe, was Sie wollen:
Service A - & gt; sende json string - & gt; Service B - & gt; Sende die gleiche JSON-Zeichenfolge - & gt; Service C
Wenn dies das ist, was Sie wollen, würde ich ein Design vorschlagen, bei dem Sie die gesamte json-Zeichenkette speichern und diese einfach übergeben. Mit IgnoreUnknownProperties in Service B und C können Sie Ihre Objekte nach Belieben analysieren lassen. Dies erfordert jedoch, dass Sie es selbst analysieren, aber das ist leicht mit ObjectMapper, Sie können es erstellen und in Ihren Frühling Kontext und verwenden Sie es überall.
Beispiel:
%Vor%Sie können die Zeichenfolge + MyClass einfach in ein Objekt einfügen oder eine Variable in MyClass erstellen, die die Zeichenfolge für die zukünftige Verwendung enthält (wie in meinem Beispiel gezeigt).
Dies ist der einfachste Weg, den ich sehe, dass Sie die Daten behalten können und Ihre Objekte nicht ändern müssen, um sie zu enthalten.
Noch eine Anmerkung, mache nicht jedes Mal einen neuen ObjectMapper (), erstelle ihn einmal, setze ihn in den Spring-Kontext und hol ihn dann durch Autowired, wo du ihn benutzen willst.
Tags und Links java spring json jackson microservices