Перейти к основному содержимому

Введение в привязку данных Apache CXF Aegis

· 7 мин. чтения

1. Обзор

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

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

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

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

<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-databinding-aegis</artifactId>
<version>3.1.8</version>
</dependency>

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

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

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

3.1. Курс

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

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

// standard getters and setters
}

3.2. КурсРепо

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

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

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

3.3. CourseRepoImpl

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

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

// standard getters and setters

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

4. Пользовательская привязка данных

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

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

4.1. Сопоставление репозитория курса

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

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

<mappings xmlns:ns="http://courserepo.foreach.com">
<mapping name="ns:ForEach">
<property name="greeting" style="attribute"/>
</mapping>
</mappings>

4.2. Картографирование курса

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

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

<mappings>
<mapping>
<property name="instructor" ignore="true"/>
</mapping>
</mappings>

На домашней странице Aegis мы можем найти дополнительные параметры настройки.

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

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

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

public class ForEachTest {
private AegisContext context;
private String fileName = "foreach.xml";

// other methods
}

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

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

Во- первых, необходимо создать объект AegisContext :

context = new AegisContext();

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

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

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

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

Map<Class<?>, 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("ForEach");
restCourse.setEnrolmentDate(new Date(1234567890000L));

Course securityCourse = new Course();
securityCourse.setId(2);
securityCourse.setName("Learn Spring Security");
securityCourse.setInstructor("ForEach");
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<XMLStreamWriter> 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.foreach.com", "foreach"), false, xmlWriter, aegisType);

xmlWriter.close();
}

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

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

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

private CourseRepo unmarshalCourseRepo() throws Exception {       
AegisReader<XMLStreamReader> 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());

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

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

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

<ns1:foreach xmlns:ns1="http://aegis.cxf.foreach.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="ns1:CourseRepo">
<ns1:courses>
<ns2:entry xmlns:ns2="urn:org.apache.cxf.aegis.types">
<ns2:key>1</ns2:key>
<ns2:value xsi:type="ns1:Course">
<ns1:enrolmentDate>2009-02-14T06:31:30+07:00
</ns1:enrolmentDate>
<ns1:id>1</ns1:id>
<ns1:instructor>ForEach</ns1:instructor>
<ns1:name>REST with Spring</ns1:name>
</ns2:value>
</ns2:entry>
<ns2:entry xmlns:ns2="urn:org.apache.cxf.aegis.types">
<ns2:key>2</ns2:key>
<ns2:value xsi:type="ns1:Course">
<ns1:enrolmentDate>2016-03-01T06:36:40+07:00
</ns1:enrolmentDate>
<ns1:id>2</ns1:id>
<ns1:instructor>ForEach</ns1:instructor>
<ns1:name>Learn Spring Security</ns1:name>
</ns2:value>
</ns2:entry>
</ns1:courses>
<ns1:greeting>Welcome to Beldung!</ns1:greeting>
</ns1:foreach>

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

<ns1:foreach xmlns:ns1="http://aegis.cxf.foreach.com"
xmlns:ns="http://courserepo.foreach.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="ns:ForEach" greeting="Welcome to Beldung!">
<ns:courses>
<ns2:entry xmlns:ns2="urn:org.apache.cxf.aegis.types">
<ns2:key>1</ns2:key>
<ns2:value xsi:type="ns1:Course">
<ns1:enrolmentDate>2009-02-14T06:31:30+07:00
</ns1:enrolmentDate>
<ns1:id>1</ns1:id>
<ns1:name>REST with Spring</ns1:name>
</ns2:value>
</ns2:entry>
<ns2:entry xmlns:ns2="urn:org.apache.cxf.aegis.types">
<ns2:key>2</ns2:key>
<ns2:value xsi:type="ns1:Course">
<ns1:enrolmentDate>2016-03-01T06:36:40+07:00
</ns1:enrolmentDate>
<ns1:id>2</ns1:id>
<ns1:name>Learn Spring Security</ns1:name>
</ns2:value>
</ns2:entry>
</ns:courses>
</ns1:foreach>

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

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

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

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

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

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

В этом руководстве также рассматривается, как настроить поведение привязки данных.

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