Einführung in Apache CXF Aegis Data Binding

Einführung in Apache CXF Aegis Data Binding

1. Überblick

Dieses Tutorial bietet eine Einführung in die Datenbindung vonAegis, ein Subsystem, das Java-Objekte und XML-Dokumente zuordnen kann, die durch XML-Schemas beschrieben werden. Aegis ermöglicht eine detaillierte Kontrolle über den Mapping-Prozess, während der Programmieraufwand auf ein Minimum beschränkt bleibt.

Aegis ist Teil vonApache CXF, darf jedoch nicht nur in diesem Rahmen verwendet werden. Stattdessen kann dieser Datenbindungsmechanismus überall verwendet werden. Daher konzentrieren wir uns in diesem Lernprogramm auf die Verwendung als unabhängiges Subsystem.

2. Maven-Abhängigkeiten

Die einzige Abhängigkeit, die zum Aktivieren der Aegis-Datenbindung erforderlich ist, ist:


    org.apache.cxf
    cxf-rt-databinding-aegis
    3.1.8

Die neueste Version dieses Artefakts befindet sich inhere.

3. Typdefinitionen

In diesem Abschnitt werden die Definitionen der drei Typen erläutert, die zur Veranschaulichung von Aegis verwendet werden.

3.1. Course

Dies ist die einfachste Klasse in unserem Beispiel, die wie folgt definiert ist:

public class Course {
    private int id;
    private String name;
    private String instructor;
    private Date enrolmentDate;

    // standard getters and setters
}

3.2. CourseRepo

CourseRepo ist der Top-Level-Typ in unserem Modell. Wir definieren es eher als eine Schnittstelle als als eine Klasse, um zu demonstrieren, wie einfach es ist, eine Java-Schnittstelle zu marshallen, was in JAXB ohne einen benutzerdefinierten Adapter unmöglich ist:

public interface CourseRepo {
    String getGreeting();
    void setGreeting(String greeting);
    Map getCourses();
    void setCourses(Map courses);
    void addCourse(Course course);
}

Beachten Sie, dass wir diegetCourses-Methode mit dem RückgabetypMap deklarieren. Damit soll ein weiterer Vorteil von Aegis gegenüber JAXB zum Ausdruck gebracht werden. Letzterer kann keine Karte ohne einen benutzerdefinierten Adapter marshallen, während ersterer kann.

3.3. CourseRepoImpl

Diese Klasse bietet eine Implementierung für dieCourseRepo-Schnittstelle:

public class CourseRepoImpl implements CourseRepo {
    private String greeting;
    private Map courses = new HashMap<>();

    // standard getters and setters

    @Override
    public void addCourse(Course course) {
        courses.put(course.getId(), course);
    }
}

4. Benutzerdefinierte Datenbindung

Damit die Anpassung wirksam wird, müssen XML-Zuordnungsdateien im Klassenpfad vorhanden sein. Es ist erforderlich, dass diese Dateien in einem Verzeichnis abgelegt werden, dessen Struktur der Pakethierarchie der zugeordneten Java-Typen entspricht.

Wenn beispielsweise eine vollständig qualifizierte Namensklassepackage.ClassName heißt, muss sich die zugehörige Zuordnungsdatei im Unterverzeichnispackage/ClassName im Klassenpfad befinden. Der Name einer Zuordnungsdatei muss dem zugeordneten Java-Typ entsprechen, an den das Suffix.aegis.xml angehängt ist.

4.1. CourseRepo Zuordnung

DieCourseRepo-Schnittstelle gehört zumcom.example.cxf.aegis-Paket, daher heißt die entsprechende ZuordnungsdateiCourseRepo.aegis.xml und wird im Verzeichniscom/example/cxf/aegis im Klassenpfad abgelegt.

In der ZuordnungsdateiCourseRepo ändern wir den Namen und den Namespace des XML-Elements, das derCourseRepo-Schnittstelle zugeordnet ist, sowie den Stil dergreeting-Eigenschaft:


    
        
    

4.2. Kurszuordnung

Ähnlich wie beim TypCourseRepo heißt die Zuordnungsdatei der KlasseCourseCourse.aegis.xml und befindet sich auch im Verzeichniscom/example/cxf/aegis.

In dieser Zuordnungsdatei weisen wir Aegis an, dieinstructor-Eigenschaft derCourse-Klasse beim Marshalling zu ignorieren, damit ihr Wert in dem aus dem XML-Ausgabedokument neu erstellten Objekt nicht verfügbar ist:


    
        
    

InAegis' home page finden wir weitere Anpassungsoptionen.

5. Testen

Dieser Abschnitt ist eine schrittweise Anleitung zum Einrichten und Ausführen eines Testfalls, der die Verwendung von Aegis-Datenbindungen veranschaulicht.

Um den Testprozess zu vereinfachen, deklarieren wir zwei Felder innerhalb der Testklasse:

public class exampleTest {
    private AegisContext context;
    private String fileName = "example.xml";

    // other methods
}

Diese Felder wurden hier definiert, um von anderen Methoden dieser Klasse verwendet zu werden.

5.1. AegisContext Initialisierung

Zunächst muss einAegisContext-Objekt erstellt werden:

context = new AegisContext();

Die Instanz vonAegisContextwird dann konfiguriert und initialisiert. So legen wir Stammklassen für den Kontext fest:

Set rootClasses = new HashSet();
rootClasses.add(CourseRepo.class);
context.setRootClasses(rootClasses);

Aegis erstellt ein XML-Zuordnungselement für jedesType innerhalb desSet<Type>-Objekts. In diesem Tutorial legen wir nurCourseRepo als Root-Typ fest.

Legen Sie nun die Implementierungszuordnung für den Kontext fest, um die Proxy-Klasse für dieCourseRepo-Schnittstelle anzugeben:

Map, String> beanImplementationMap = new HashMap<>();
beanImplementationMap.put(CourseRepoImpl.class, "CourseRepo");
context.setBeanImplementationMap(beanImplementationMap);

Die letzte Konfiguration für den Aegis-Kontext weist ihn an, das Attributxsi:type im entsprechenden XML-Dokument festzulegen. Dieses Attribut enthält den tatsächlichen Typnamen des zugeordneten Java-Objekts, sofern er nicht von der Zuordnungsdatei überschrieben wird:

context.setWriteXsiTypes(true);

Die Instanz vonAegisContextkann jetzt initialisiert werden:

context.initialize();

Um den Code sauber zu halten, sammeln wir alle Codeausschnitte aus diesem Unterabschnitt in einer Hilfsmethode:

private void initializeContext() {
    // ...
}

5.2. Einfache Dateneinrichtung

Aufgrund der Einfachheit dieses Lernprogramms generieren wir Beispieldaten direkt im Speicher, anstatt auf eine dauerhafte Lösung zu setzen. Füllen Sie das Kurs-Repo mit der folgenden Setup-Logik:

private CourseRepoImpl initCourseRepo() {
    Course restCourse = new Course();
    restCourse.setId(1);
    restCourse.setName("REST with Spring");
    restCourse.setInstructor("Eugen");
    restCourse.setEnrolmentDate(new Date(1234567890000L));

    Course securityCourse = new Course();
    securityCourse.setId(2);
    securityCourse.setName("Learn Spring Security");
    securityCourse.setInstructor("Eugen");
    securityCourse.setEnrolmentDate(new Date(1456789000000L));

    CourseRepoImpl courseRepo = new CourseRepoImpl();
    courseRepo.setGreeting("Welcome to Beldung!");
    courseRepo.addCourse(restCourse);
    courseRepo.addCourse(securityCourse);
    return courseRepo;
}

5.3. Binden von Java-Objekten und XML-Elementen

Die Schritte, die zum Marshallen von Java-Objekten in XML-Elemente ausgeführt werden müssen, werden mit der folgenden Hilfsmethode veranschaulicht:

private void marshalCourseRepo(CourseRepo courseRepo) throws Exception {
    AegisWriter writer = context.createXMLStreamWriter();
    AegisType aegisType = context.getTypeMapping().getType(CourseRepo.class);
    XMLStreamWriter xmlWriter = XMLOutputFactory.newInstance()
      .createXMLStreamWriter(new FileOutputStream(fileName));

    writer.write(courseRepo,
      new QName("http://aegis.cxf.example.com", "example"), false, xmlWriter, aegisType);

    xmlWriter.close();
}

Wie wir sehen können, müssen die ObjekteAegisWriter undAegisType aus der InstanzAegisContexterstellt werden. DasAegisWriter-Objekt marshallt dann die angegebene Java-Instanz für die angegebene Ausgabe.

In diesem Fall ist dies einXMLStreamWriter-Objekt, das einer Datei zugeordnet ist, die nach dem Wert des Felds auf KlassenebenefileNameim Dateisystem benannt ist.

Die folgende Methode dekomprimiert ein XML-Dokument für ein Java-Objekt des angegebenen Typs:

private CourseRepo unmarshalCourseRepo() throws Exception {
    AegisReader reader = context.createXMLStreamReader();
    XMLStreamReader xmlReader = XMLInputFactory.newInstance()
      .createXMLStreamReader(new FileInputStream(fileName));

    CourseRepo courseRepo = (CourseRepo) reader.read(
      xmlReader, context.getTypeMapping().getType(CourseRepo.class));

    xmlReader.close();
    return courseRepo;
}

Hier wird aus der Instanz vonAegisContexteinAegisReader-Objekt generiert. Das ObjektAegisReadererstellt dann aus der bereitgestellten Eingabe ein Java-Objekt. In diesem Beispiel ist diese Eingabe einXMLStreamReader-Objekt, das von der Datei unterstützt wird, die wir mit der oben beschriebenenmarshalCourseRepo-Methode generiert haben.

5.4. Behauptungen

Jetzt ist es an der Zeit, alle in den vorherigen Unterabschnitten definierten Hilfsmethoden zu einer Testmethode zu kombinieren:

@Test
public void whenMarshalingAndUnmarshalingCourseRepo_thenCorrect()
  throws Exception {
    initializeContext();
    CourseRepo inputRepo = initCourseRepo();
    marshalCourseRepo(inputRepo);
    CourseRepo outputRepo = unmarshalCourseRepo();
    Course restCourse = outputRepo.getCourses().get(1);
    Course securityCourse = outputRepo.getCourses().get(2);

    // JUnit assertions
}

Wir erstellen zuerst eineCourseRepo-Instanz, stellen sie dann in einem XML-Dokument zusammen und entfernen schließlich das Dokument, um das ursprüngliche Objekt neu zu erstellen. Lassen Sie uns überprüfen, ob das neu erstellte Objekt unseren Erwartungen entspricht:

assertEquals("Welcome to Beldung!", outputRepo.getGreeting());
assertEquals("REST with Spring", restCourse.getName());
assertEquals(new Date(1234567890000L), restCourse.getEnrolmentDate());
assertNull(restCourse.getInstructor());
assertEquals("Learn Spring Security", securityCourse.getName());
assertEquals(new Date(1456789000000L), securityCourse.getEnrolmentDate());
assertNull(securityCourse.getInstructor());

Es ist klar, dass mit Ausnahme der Eigenschaftinstructoralle anderen Werte wiederhergestellt werden, einschließlich der EigenschaftenrolmentDatemit Werten vom TypDate. Dies ist genau das, was wir erwarten, da wir Aegis angewiesen haben, die Eigenschaftinstructorbeim Marshalling vonCourse-Objekten zu ignorieren.

5.5. XML-Dokument ausgeben

Um den Effekt von Aegis-Mapping-Dateien zu verdeutlichen, zeigen wir das XML-Dokument ohne Anpassung an:


    
        
            1
            
                2009-02-14T06:31:30+07:00
                
                1
                Eugen
                REST with Spring
            
        
        
            2
            
                2016-03-01T06:36:40+07:00
                
                2
                Eugen
                Learn Spring Security
            
        
    
    Welcome to Beldung!

Vergleichen Sie dies mit dem Fall, in dem die benutzerdefinierte Zuordnung von Aegis in Aktion ist:


    
        
            1
            
                2009-02-14T06:31:30+07:00
                
                1
                REST with Spring
            
        
        
            2
            
                2016-03-01T06:36:40+07:00
                
                2
                Learn Spring Security
            
        
    

Sie finden diese XML-Struktur inexample.xml direkt im Hauptverzeichnis des Projekts, nachdem Sie den in diesem Abschnitt definierten Test ausgeführt haben.

Sie werden sehen, dass sich dastype-Attribut und der Namespace des XML-Elements, die demCourseRepo-Objekt entsprechen, entsprechend den Angaben in derCourseRepo.aegis.xml-Datei ändern. Die Eigenschaftgreetingwird ebenfalls in ein Attribut umgewandelt, und die Eigenschaftinstructorder ObjekteCourseverschwindet erwartungsgemäß.

Es ist zu beachten, dass Aegis standardmäßig einen grundlegenden Java-Typ in den am besten passenden Schematyp konvertiert, z. vonDate Objekten zuxsd:dateTime Elementen, wie in diesem Tutorial gezeigt. Wir können diese bestimmte Bindung jedoch ändern, indem Sie die Konfiguration in der entsprechenden Zuordnungsdatei festlegen.

Bitte navigieren Sie zuAegis home page, wenn Sie weitere Informationen wünschen.

6. Fazit

Dieses Tutorial zeigt die Verwendung der Apache CXF Aegis-Datenbindung als eigenständiges Subsystem. Es zeigt, wie Aegis verwendet werden kann, um Java-Objekte XML-Elementen zuzuordnen und umgekehrt.

Das Lernprogramm befasst sich auch mit dem Anpassen des Datenbindungsverhaltens.

Und wie immer finden Sie die Implementierung all dieser Beispiele und Codefragmente inthe GitHub project.