Введение в Apache CXF Aegis Data Binding

Введение в Apache CXF Aegis Data Binding

1. обзор

Это руководство представляет собой введение в привязку данныхAegis, подсистему, которая может отображать объекты Java и документы XML, описанные схемами XML. Aegis позволяет детально контролировать процесс отображения, сводя к минимуму усилия по программированию.

Aegis является частьюApache CXF, но его использование не ограничивается только этой структурой. Вместо этого этот механизм привязки данных может использоваться где угодно, и поэтому в этом руководстве мы сосредоточимся на его использовании в качестве независимой подсистемы.

2. Maven Зависимости

Единственная зависимость, необходимая для активации привязки данных Aegis:


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

Последнюю версию этого артефакта можно найтиhere.

3. Определения типов

В этом разделе рассматриваются определения трех типов, используемые для иллюстрации Aegis.

3.1. Courseс

Это самый простой класс в нашем примере, который определяется как:

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

    // standard getters and setters
}

3.2. CourseRepoс

CourseRepo - тип верхнего уровня в нашей модели. Мы определяем его как интерфейс, а не как класс, чтобы продемонстрировать, как легко упорядочить интерфейс Java, что невозможно в JAXB без специального адаптера:

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

Обратите внимание, что мы объявляем методgetCourses с типом возвратаMap. Это намеренно, чтобы выразить еще одно преимущество Aegis над JAXB. Последний не может маршалировать карту без специального адаптера, в то время как первый может.

3.3. CourseRepoImplс

Этот класс обеспечивает реализацию интерфейсаCourseRepo:

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. Пользовательская привязка данных

Чтобы настройки вступили в силу, файлы сопоставления XML должны присутствовать в пути к классам. Требуется, чтобы эти файлы были помещены в каталог, структура которого соответствует иерархии пакетов связанных типов Java.

Например, если полностью определенное имя class называетсяpackage.ClassName, связанный с ним файл сопоставления должен находиться в подкаталогеpackage/ClassName в пути к классам. Имя файла сопоставления должно совпадать с соответствующим типом Java с добавленным к нему суффиксом.aegis.xml.

4.1. CourseRepo Отображение

ИнтерфейсCourseRepo принадлежит пакетуcom.example.cxf.aegis, поэтому соответствующий ему файл сопоставления называетсяCourseRepo.aegis.xml и помещается в каталогcom/example/cxf/aegis в пути к классам.

В файле сопоставленияCourseRepo мы меняем имя и пространство имен XML-элемента, связанного с интерфейсомCourseRepo, а также стиль его свойстваgreeting:


    
        
    

4.2. Карта курса

Подобно типуCourseRepo, файл сопоставления классаCourse называетсяCourse.aegis.xml и также находится в каталогеcom/example/cxf/aegis.

В этом файле сопоставления мы инструктируем Aegis игнорировать свойствоinstructor классаCourse при маршалинге, чтобы его значение не было доступно в объекте, воссозданном из выходного XML-документа:


    
        
    

Aegis' home page - это то место, где мы можем найти дополнительные параметры настройки.

5. тестирование

Этот раздел представляет собой пошаговое руководство по настройке и выполнению тестового примера, иллюстрирующего использование привязок данных Aegis.

Чтобы облегчить процесс тестирования, мы объявляем два поля в классе теста:

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

    // other methods
}

Эти поля были определены здесь для использования другими методами этого класса.

5.1. AegisContext Инициализация

Сначала необходимо создать объектAegisContext:

context = new AegisContext();

Затем этот экземплярAegisContext настраивается и инициализируется. Вот как мы устанавливаем корневые классы для контекста:

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

Aegis создает элемент сопоставления XML для каждогоType в объектеSet<Type>. В этом руководстве мы устанавливаем толькоCourseRepo в качестве корневого типа.

Теперь давайте настроим карту реализации для контекста, чтобы указать прокси-класс для интерфейсаCourseRepo:

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

Последняя конфигурация для контекста Aegis сообщает ему установить атрибутxsi:type в соответствующем XML-документе. Этот атрибут содержит фактическое имя типа связанного объекта Java, если он не переопределен файлом сопоставления:

context.setWriteXsiTypes(true);

Наш экземплярAegisContext теперь готов к инициализации:

context.initialize();

Чтобы сохранить код чистым, мы собираем все фрагменты кода из этого подраздела в один вспомогательный метод:

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

5.2. Простая настройка данных

Из-за простой природы этого учебника мы генерируем образцы данных прямо в памяти, а не полагаясь на постоянное решение. Давайте заполним репозиторий курса, используя приведенную ниже логику настройки:

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. Связывание объектов Java и элементов XML

Шаги, которые необходимо предпринять для упорядочения Java-объектов в XML-элементы, иллюстрируются следующим вспомогательным методом:

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();
}

Как мы видим, объектыAegisWriter иAegisType должны быть созданы из экземпляраAegisContext. Затем объектAegisWriter выполняет маршалинг данного экземпляра Java на указанный вывод.

В данном случае это объектXMLStreamWriter, связанный с файлом, названным в честь значения поля уровня классаfileName в файловой системе.

Следующий метод выполняет демаршализацию документа XML в объект Java данного типа:

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;
}

Здесь объектAegisReader создается из экземпляраAegisContext. Затем объектAegisReader создает объект Java из предоставленных входных данных. В этом примере входными данными является объектXMLStreamReader, поддерживаемый файлом, который мы создали с помощью методаmarshalCourseRepo, описанного выше.

5.4. Утверждения

Теперь пришло время объединить все вспомогательные методы, определенные в предыдущих подразделах, в тестовый метод:

@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
}

Сначала мы создаем экземплярCourseRepo, затем маршалируем его в XML-документ и, наконец, демаршалируем документ, чтобы воссоздать исходный объект. Давайте проверим, что воссозданный объект соответствует нашим ожиданиям:

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());

Понятно, что за исключением свойстваinstructor, все остальные имеют свои значения, включая свойствоenrolmentDate со значениями типаDate. Это именно то, что мы ожидаем, поскольку мы проинструктировали Aegis игнорировать свойствоinstructor при маршалинге объектовCourse.

5.5. Выходной XML-документ

Чтобы сделать эффект отображения файлов Aegis явным, мы покажем XML-документ без настройки ниже:


    
        
            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!

Сравните это со случаем, когда пользовательское сопоставление Aegis работает:


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

Вы можете найти эту XML-структуру вexample.xml, прямо внутри основного каталога проекта после выполнения теста, определенного в этом разделе.

Вы увидите, что атрибутtype и пространство имен элемента XML, соответствующего объектуCourseRepo, изменяются в соответствии с тем, что мы установили в файлеCourseRepo.aegis.xml. Свойствоgreeting также преобразуется в атрибут, а свойствоinstructor объектовCourse исчезает, как ожидалось.

Следует отметить, что по умолчанию Aegis преобразует базовый тип Java в наиболее подходящий тип схемы, например, от объектовDate до элементовxsd:dateTime, как показано в этом руководстве. Однако мы можем изменить эту конкретную привязку, установив конфигурацию в соответствующем файле сопоставления.

Пожалуйста, перейдите кAegis home page, если вы хотите получить дополнительную информацию.

6. Заключение

В этом руководстве показано использование привязки данных Apache CXF Aegis в качестве отдельной подсистемы. Он демонстрирует, как Aegis может использоваться для отображения объектов Java на элементы XML и наоборот.

Учебное пособие также посвящено тому, как настроить поведение привязки данных.

И, как всегда, реализацию всех этих примеров и фрагментов кода можно найти вthe GitHub project.