Статьи

Вы должны использовать сгенерированные JAXB классы для веб-сервисов RESTful

Я помню, что в конце 90-х / начале 2000-х все программирование было связано с XML. Все «эксперты» сказали, что вы должны использовать XML для обмена данными. У XML есть много преимуществ. Но часто при сегодняшнем программировании XML выглядит как «старая школа» или слишком ограничительный. XML наверняка может быть привередливым в работе. Многие разработчики, когда им предоставляется выбор (включая меня), будут использовать JSON поверх XML. JSON гораздо более простителен, чем XML, что упрощает работу с ним.

Хотя XML может быть старой школой, его все же можно использовать. Даже когда вы делаете основанные на JSON веб-службы Restful.

Веб-сервисы Restful весной

Я не собираюсь рассказывать о создании Restful Web Services в Spring в этом посте. Это тема будущего. Но когда вы используете Spring MVC для разработки Restful Web Services, вы можете настроить большую гибкость. Обычно я разрабатываю свои веб-сервисы, чтобы клиент мог использовать JSON или XML. Стандарты поддерживают это, и это довольно легко сделать.

Многие из примеров, которые вы увидите в Интернете, начнутся с использования аннотированных JAXB Java POJO. Это работает, но я также чувствую, что это ошибка. Контракт для ваших веб-сервисов теперь является классом Java. Который может работать для вас, но это не переносимо. Что если один из ваших клиентов пишет на PHP? JAXB-аннотированный Java-класс для них бесполезен.

XML-схема

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

Отличная особенность JAXB — вы можете генерировать аннотированные JAXB Java POJO из документа XML-схемы. И когда вы делаете это, у вас теперь есть переносной контракт на типы данных ваших веб-сервисов. Ваш «контракт» больше не привязан к языку программирования Java. Вы можете передать XML-схему кому-то, строящему клиент для вашего веб-сервиса на другом языке программирования, например, C #, и он может использовать XML-схему для генерации кода для клиента.

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

Сгенерированные JAXB классы для веб-служб Restful

В этой статье я покажу вам, как настроить проект Maven для создания Jar-файла Java-классов, сгенерированных JAXB из XML-схемы.

Создание проекта Maven в IntelliJ

Для целей этого примера я собираюсь использовать IntelliJ для создания проекта Maven. В реальных условиях вы захотите настроить его как независимый проект Maven или как модуль Maven. Это позволит сгенерированным JAXB классам связываться в JAR-файл и повторно использовать его в других проектах или модулях.

1. Создайте новый проект в IntelliJ.

Создать проект Maven в IntelliJ

2. Установите GroupId и ArtifactId в диалоговом окне «Новый проект» IntelliJ.

Диалог нового проекта в IntelliJ

3. Выберите, где хранить проект на вашем диске.

расположение пути в IntelliJ

4. IntelliJ подтвердит создание нового каталога. Нажмите ОК.

Каталог не существует в IntelliJ

5. В зависимости от ваших настроек в IntelliJ вас могут попросить импортировать изменения. Я часто отключаю эту функцию при работе с большими сложными проектами Maven по причинам производительности. Если вы видите это диалоговое окно, нажмите «Импортировать изменения».

импортировать изменения Maven в IntelliJ

6. На данный момент вы создали новый проект Maven в IntelliJ. Вы можете видеть, что стандартная структура каталогов Maven была создана.

Maven проект в IntelliJ

Создать XML-схему

Первый файл, который мы создадим, — это XML-схема, которую мы будем использовать. Допустим, мы создаем веб-сервис для добавления продукта, и нам нужен объект команды create product.

В нашей XML-схеме мы создаем класс Product, а также класс CreateProductRequest. Это позволит расширить ProductClass и добавить поле для ключа API. Этот файл находится в папке / main / resources. Это местоположение по умолчанию для файлов схемы XML.
jaxb.xsd

<?xml version="1.0"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" jaxb:version="2.0">

    <!--This tells JAXB what package to create the Java classes in-->
    <xsd:annotation>
        <xsd:appinfo>
            <jaxb:schemaBindings>
                <jaxb:package name="guru.springframework.domain"/>
            </jaxb:schemaBindings>
        </xsd:appinfo>
    </xsd:annotation>

    <xsd:complexType name="Product">
        <xsd:sequence>
            <xsd:element name="productId" type="xsd:integer"/>
            <xsd:element name="productDescription" type="xsd:string"/>
            <xsd:element name="productPrice" type="xsd:decimal"/>
        </xsd:sequence>
    </xsd:complexType>

    <xsd:complexType name="CreateProductRequest">
        <xsd:complexContent>
            <xsd:extension base="Product">
                <xsd:attribute name="apikey" type="xsd:string"/>
            </xsd:extension>
        </xsd:complexContent>
    </xsd:complexType>

</xsd:schema>

Настроить Maven

Создание проекта в IntelliJ дало нам очень простой файл POM Maven. Вот Maven POM, созданный для нас.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>guru.springframework</groupId>
    <artifactId>jaxb-xsd-example</artifactId>
    <version>1.0-SNAPSHOT</version>

</project>

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

Нам нужно добавить три зависимости в наше Maven POM. Первый — это JAXB API, второй — реализация JAXB, и, наконец, третий — для плагина Maven JAXB.

    <dependencies>
        <!--jaxb support-->
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.2.12</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.2.11</version>
        </dependency>
    </dependencies>

Maven JAXB Плагин

Далее нам нужно настроить плагин JAXB для Maven для POM Maven.

            <plugin>
                <groupId>org.jvnet.jaxb2.maven2</groupId>
                <artifactId>maven-jaxb2-plugin</artifactId>
                <version>0.12.3</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

Полное Maven POM

Вот последний Maven POM.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>guru.springframework</groupId>
    <artifactId>jaxb-xsd-example</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!--jaxb support-->
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.2.12</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.2.11</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.jvnet.jaxb2.maven2</groupId>
                <artifactId>maven-jaxb2-plugin</artifactId>
                <version>0.12.3</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Создание нашего проекта JAXB Maven

Выполнение Maven Package Goal

IntelliJ делает работу с Maven очень простой. В правой части IDE вы увидите кнопку «Проекты Maven», щелкнув по которой откроется диалоговое окно «Проекты Maven». Чтобы построить наш проект, в разделе «Жизненный цикл» дважды щелкните по цели «пакета».

Maven Проекты в IntelliJ

Вы должны увидеть, как Maven работает и строит успешно.

[INFO] 
[INFO] --- maven-jar-plugin:2.3.2:jar (default-jar) @ jaxb-xsd-example ---
[INFO] Building jar: /Users/jt/src/springframework.guru/blog/jaxb-xsd-example/target/jaxb-xsd-example-1.0-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.410s
[INFO] Finished at: Fri Aug 07 07:23:44 EDT 2015
[INFO] Final Memory: 16M/207M
[INFO] ------------------------------------------------------------------------

Maven Build Artifacts

Maven будет встроен в каталог ‘Target’. Вы можете увидеть классы Java, созданные в IntelliJ.

JAXB Maven Артефакты

JAXB генерируемые классы

Мы ожидали два класса, сгенерированных из XML-схемы, которую мы определили.

Product.java

// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.11 
// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
// Any modifications to this file will be lost upon recompilation of the source schema. 
// Generated on: 2015.08.07 at 07:23:43 AM EDT 
//


package guru.springframework.domain;

import java.math.BigDecimal;
import java.math.BigInteger;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlType;


/**
 * <p>Java class for Product complex type.
 * 
 * <p>The following schema fragment specifies the expected content contained within this class.
 * 
 * <pre>
 * &lt;complexType name="Product"&gt;
 *   &lt;complexContent&gt;
 *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&gt;
 *       &lt;sequence&gt;
 *         &lt;element name="productId" type="{http://www.w3.org/2001/XMLSchema}integer"/&gt;
 *         &lt;element name="productDescription" type="{http://www.w3.org/2001/XMLSchema}string"/&gt;
 *         &lt;element name="productPrice" type="{http://www.w3.org/2001/XMLSchema}decimal"/&gt;
 *       &lt;/sequence&gt;
 *     &lt;/restriction&gt;
 *   &lt;/complexContent&gt;
 * &lt;/complexType&gt;
 * </pre>
 * 
 * 
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Product", propOrder = {
    "productId",
    "productDescription",
    "productPrice"
})
@XmlSeeAlso({
    CreateProductRequest.class
})
public class Product {

    @XmlElement(required = true)
    protected BigInteger productId;
    @XmlElement(required = true)
    protected String productDescription;
    @XmlElement(required = true)
    protected BigDecimal productPrice;

    /**
     * Gets the value of the productId property.
     * 
     * @return
     *     possible object is
     *     {@link BigInteger }
     *     
     */
    public BigInteger getProductId() {
        return productId;
    }

    /**
     * Sets the value of the productId property.
     * 
     * @param value
     *     allowed object is
     *     {@link BigInteger }
     *     
     */
    public void setProductId(BigInteger value) {
        this.productId = value;
    }

    /**
     * Gets the value of the productDescription property.
     * 
     * @return
     *     possible object is
     *     {@link String }
     *     
     */
    public String getProductDescription() {
        return productDescription;
    }

    /**
     * Sets the value of the productDescription property.
     * 
     * @param value
     *     allowed object is
     *     {@link String }
     *     
     */
    public void setProductDescription(String value) {
        this.productDescription = value;
    }

    /**
     * Gets the value of the productPrice property.
     * 
     * @return
     *     possible object is
     *     {@link BigDecimal }
     *     
     */
    public BigDecimal getProductPrice() {
        return productPrice;
    }

    /**
     * Sets the value of the productPrice property.
     * 
     * @param value
     *     allowed object is
     *     {@link BigDecimal }
     *     
     */
    public void setProductPrice(BigDecimal value) {
        this.productPrice = value;
    }

}

CreateProductRequest.java

Обратите внимание, как этот класс фактически расширяет класс Product.

// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.11 
// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
// Any modifications to this file will be lost upon recompilation of the source schema. 
// Generated on: 2015.08.07 at 07:23:43 AM EDT 
//


package guru.springframework.domain;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlType;


/**
 * <p>Java class for CreateProductRequest complex type.
 * 
 * <p>The following schema fragment specifies the expected content contained within this class.
 * 
 * <pre>
 * &lt;complexType name="CreateProductRequest"&gt;
 *   &lt;complexContent&gt;
 *     &lt;extension base="{}Product"&gt;
 *       &lt;attribute name="apikey" type="{http://www.w3.org/2001/XMLSchema}string" /&gt;
 *     &lt;/extension&gt;
 *   &lt;/complexContent&gt;
 * &lt;/complexType&gt;
 * </pre>
 * 
 * 
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "CreateProductRequest")
public class CreateProductRequest
    extends Product
{

    @XmlAttribute(name = "apikey")
    protected String apikey;

    /**
     * Gets the value of the apikey property.
     * 
     * @return
     *     possible object is
     *     {@link String }
     *     
     */
    public String getApikey() {
        return apikey;
    }

    /**
     * Sets the value of the apikey property.
     * 
     * @param value
     *     allowed object is
     *     {@link String }
     *     
     */
    public void setApikey(String value) {
        this.apikey = value;
    }

}

Вывод

Это просто очень простой пример генерации классификации из XML-схемы с использованием JAXB и Maven. В этом посте я показал вам шаг за шагом, как использовать XML-схему и JAXB для генерации классов Java. Сгенерированные классы объединены в файл JAR, который является переносимым и может использоваться совместно с другими проектами Java.

Будучи консультантом Spring Source, я работал в крупной компании, которая разработала несколько API Restful. Команда разработчиков API не использовала JAXB для генерации классов из XML-схемы. Скорее они создавали классы JAXB вручную и не могли предложить своим клиентам XML-схему. Как потребитель их API, это был трудоемкий процесс настройки и устранения неполадок моих типов данных.

Я также консультировался в организациях, где команда использовала XML-схемы для создания своих API. Наличие схемы XML сделало написание клиентского кода несложным.

Ресурсы

JAXB

Вы можете найти документацию по проекту JAXB здесь .

JAXB Maven Плагин

Я использовал настройки по умолчанию для плагина JAXB Maven. Дополнительные опции доступны. Документация для плагина JAXB Maven находится здесь .

Исходный код проекта

Исходный код, используемый в этом руководстве, доступен на Github здесь .