[JAVA] Exemple multi-module d'API RESTful avec IntelliJ + Jersey + Spring Framework

Ceci est une suite de l'article suivant.

Exemple de configuration d'API RESTful minimum avec Jersey + Spring Framework https://qiita.com/kasa_le/items/59ebd6b5490945dd5580

Configuration multi-modules avec Maven (Jersey RESTful) https://qiita.com/kasa_le/items/db0d84e3e868ff14bc2b

Cette fois, c'est comme fusionner les projets créés dans les deux articles ci-dessus. Cependant, je vais vous expliquer en créant un projet à partir de zéro.

Objectif

Implémentez l'API RESTful avec ** Jersey ** et ** Spring Framework ** (pas Boot).

objectif

--Jersey + Spring Framework fonctionne avec l'API RESTful (CRUD). --DI peut être fait avec une configuration multi-module.

Environnement etc.

Outils etc. Version etc.
MacbookPro macOS Mojave 10.14.5
IntelliJ IDEA Ultimate 2019.3.3
Java AdoptOpenJDK 11
apache maven 3.6.3
Jersey 2.30.1
JUnit 5.6.0
Tomcat apache-tomcat-8.5.51
Postman 7.19.1
Spring Framework 5.2.4-RELEASE

Paramètres du projet

Dans IntelliJ, créez un nouveau projet Maven.

1. Pompon de racine

Fondamentalement, vous pouvez copier les paramètres de dépendance et de plug-in à partir du projet Configuration minimale de Jersey + Spring Framework.

pom.xml


<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/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>my.example.jerseyspring</groupId>
    <artifactId>jersey-spring-restful</artifactId>
    <packaging>war</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>Jersey and Spring Framework RESTfulAPI Sample</name>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <inherited>true</inherited>
                <configuration>
                    <source>11</source>
                    <target>11</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.2</version>
                <configuration>
                    <additionalClasspathElements>
                        <additionalClasspathElement>src/test/java/</additionalClasspathElement>
                    </additionalClasspathElements>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-site-plugin</artifactId>
                <version>3.7.1</version>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-report-plugin</artifactId>
                <version>3.0.0-M4</version>
            </plugin>
        </plugins>
    </build>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.glassfish.jersey</groupId>
                <artifactId>jersey-bom</artifactId>
                <version>${jersey.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <!-- Jersey -->
        <!-- https://mvnrepository.com/artifact/org.glassfish.jersey.core/jersey-server -->
        <dependency>
            <groupId>org.glassfish.jersey.core</groupId>
            <artifactId>jersey-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.inject</groupId>
            <artifactId>jersey-hk2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.media</groupId>
            <artifactId>jersey-media-json-binding</artifactId>
        </dependency>

        <!-- Spring dependencies -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!-- Jersey + Spring -->
        <!-- https://mvnrepository.com/artifact/org.glassfish.jersey.ext/jersey-spring5 -->
        <dependency>
            <groupId>org.glassfish.jersey.ext</groupId>
            <artifactId>jersey-spring5</artifactId>
            <version>${jersey.version}</version>
        </dependency>

        <!--JAXB supprimé de JDK9-->
        <!-- https://mvnrepository.com/artifact/javax.xml.bind/jaxb-api -->
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.1</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/javax.activation/activation -->
        <dependency>
            <groupId>javax.activation</groupId>
            <artifactId>activation</artifactId>
            <version>1.1.1</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.glassfish.jaxb/jaxb-runtime -->
        <dependency>
            <groupId>org.glassfish.jaxb</groupId>
            <artifactId>jaxb-runtime</artifactId>
            <version>2.3.2</version>
        </dependency>

        <!--tester-->
        <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>${junit.jupiter.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>${junit.jupiter.version}</version>
            <scope>test</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.glassfish.jersey.test-framework.providers/jersey-test-framework-provider-grizzly2 -->
        <dependency>
            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
            <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
            <version>2.30.1</version>
            <scope>test</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.assertj/assertj-core -->
        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-core</artifactId>
            <version>3.15.0</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-params</artifactId>
            <version>${junit.jupiter.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <properties>
        <spring.version>5.2.4.RELEASE</spring.version>
        <jersey.version>2.30.1</jersey.version>
        <junit.jupiter.version>5.6.0</junit.jupiter.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
</project>

2. Création de sous-modules

(1) Supprimez le dossier racine src

Supprimez-le manuellement.

(2) Ajout d'un sous-module

Dans le menu IntelliJ [Fichier] - [Nouveau] - [Module ...], ajoutez les deux suivants dans le projet Maven.

Le nom du sous-module est arbitraire. N'oubliez pas de définir ** Parent **. (Veuillez sélectionner le projet racine.)

Ensuite, le «» du parent «pom.xml» devrait se changer en «pom». S'il n'a pas changé, corrigez-le manuellement.

root/pom.xml


    <groupId>my.example.jerseyspring</groupId>
    <artifactId>jersey-spring-restful</artifactId>
    <packaging>pom</packaging>

Chaque pom.xml du sous-module devrait ressembler à ceci:

repository/pom.xml


<?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">
    <parent>
        <artifactId>jersey-spring-restful</artifactId>
        <groupId>my.example.jerseyspring</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>repository</artifactId>
    <packaging>jar</packaging>
</project>

serverapi/pom.xml


<?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">
    <parent>
        <artifactId>jersey-spring-restful</artifactId>
        <groupId>my.example.jerseyspring</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>serverapi</artifactId>
    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>my.example.jerseyspring</groupId>
            <artifactId>repository</artifactId>
            <version>${project.version}</version>
        </dependency>

        <!-- for spring test-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

Le module serverapi est inclus dans la portée <test> car le Spring Test est requis pour le test Junit. Cependant, si vous en avez besoin dans d'autres sous-modules, vous pouvez le mettre à la racine pom.xml.

Aucun n'a spécifié sa propre version. Je souhaite utiliser la version parente telle quelle. C'est une politique de ne pas gérer la version pour chaque sous-module. Si nécessaire, spécifiez la version pour chacun avec la balise <version>.

(3) Structure des dossiers

La structure actuelle des dossiers devrait ressembler à ceci.

$ tree
.
├── SpringJerseyRest.iml
├── pom.xml
├── repository
│   ├── pom.xml
│   └── src
│       ├── main
│       │   ├── java
│       │   └── resources
│       └── test
│           └── java
└── serverapi
    ├── pom.xml
    └── src
        ├── main
        │   ├── java
        │   └── resources
        └── test
            └── java

2. Paramètres du module de référentiel

(1) Classe de modèle

Créez les classes suivantes dans le package my.example.jerseyspring.repository.models.

Employee.java


package my.example.jerseyspring.repository.models;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Employee {
    private int id;
    private String firstName;

    public Employee() {
    }

    public Employee(int id, String firstName) {
        this.id = id;
        this.firstName = firstName;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
}

(2) Classe de référentiel

Créez la classe ʻEmployeeRepository dans le package my.example.jerseyspring.repository`.

EmployeeRepository.java


package my.example.jerseyspring.repository;


import my.example.jerseyspring.repository.exceptions.DuplicateIdException;
import my.example.jerseyspring.repository.exceptions.EmployeeNameNotFoundException;
import my.example.jerseyspring.repository.exceptions.EmployeeNotFoundException;
import my.example.jerseyspring.repository.models.Employee;
import org.springframework.stereotype.Repository;

import java.util.ArrayList;
import java.util.List;

@Repository
public class EmployeeRepository {

    private List<Employee> employeeList;

    public EmployeeRepository() {
        employeeList = new ArrayList<>();
        employeeList.add(new Employee(3, "Cupcake"));
        employeeList.add(new Employee(4, "Donuts"));
        employeeList.add(new Employee(5, "Eclair"));
        employeeList.add(new Employee(8, "Froyo"));
        employeeList.add(new Employee(9, "Gingerbread"));
    }

    public List<Employee> selectAll() {
        return employeeList;
    }

    public Employee select(int id) {
        for (Employee employee : employeeList) {
            if (employee.getId() == id) {
                return employee;
            }
        }
        throw new EmployeeNotFoundException();
    }

    public synchronized void insert(int id, String firstName) {
        try {
            select(id);
        } catch (EmployeeNotFoundException e) {
            //Sinon, vous pouvez ajouter
            employeeList.add(new Employee(id, firstName));
            return;
        }
        //Ne peut pas être ajouté si le même ID existe
        throw new DuplicateIdException();
    }

    public synchronized void update(int id, String firstName) {
        Employee employee = select(id);
        employee.setFirstName(firstName);
    }

    public synchronized void delete(int id) {
        Employee employee = select(id);
        employeeList.remove(employee);
    }

    public List<Employee> search(String name) {
        List<Employee> list = new ArrayList<>();
        for (Employee employee : employeeList) {
            if (employee.getFirstName().contains(name)) {
                list.add(employee);
            }
        }
        if (list.size() > 0) return list;
        throw new EmployeeNameNotFoundException(name);
    }
}

Dans le précédent exemple multi-module, j'ai mis l'implémentation singleton par moi-même, mais la DI de Spring Framework est fondamentalement singleton , Suppression de sa mise en œuvre.

(3) Classe d'exception

ʻCréez chaque classe d'exception utilisée dans la classe EmployeeRepository. Le paquet est repository.exceptions`.

Exemple) Classe ʻEmployeeNotFoundException`

EmployeeNotFoundException.java


package my.example.jerseyspring.repository.exceptions;

public class EmployeeNotFoundException extends RuntimeException {
    public EmployeeNotFoundException() {
        super("L'employé avec cet ID est introuvable.");
    }
}

Créez d'autres classes de la même manière.

Aussi, j'apporterai le paquet transacation utilisé dans le projet Configuration minimale de Jersey + Spring. Le package repository.transaction est très bien.

(4) Classe de test Junit

Créez une classe de test pour la classe d'exception.

A titre d'exemple, prenons le cas de ʻEmployeeNotFoundException`.

EmployeeNotFoundExceptionTest.java


package my.example.jerseyspring.repository.exceptions;

import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

class EmployeeNotFoundExceptionTest {

    @Test
    void getMessage() {
        EmployeeNotFoundException e = new EmployeeNotFoundException();
        assertThat(e.getMessage()).isEqualTo("L'employé avec cet ID est introuvable.");
    }
}

Depuis IntelliJ, exécutons le test Junit et vérifions qu'il réussit.

(5) Configuration finale

En fin de compte, vous devriez avoir une structure de dossiers comme celle-ci:

$ tree
.
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── my
    │   │       └── example
    │   │           └── jerseyspring
    │   │               └── repository
    │   │                   ├── EmployeeRepository.java
    │   │                   ├── exceptions
    │   │                   │   ├── DuplicateIdException.java
    │   │                   │   ├── EmployeeNameNotFoundException.java
    │   │                   │   └── EmployeeNotFoundException.java
    │   │                   ├── models
    │   │                   │   └── Employee.java
    │   │                   └── transaction
    │   │                       ├── TransactionBo.java
    │   │                       └── impl
    │   │                           └── TransactionBoImpl.java
    │   └── resources
    └── test
        └── java
            └── my
                └── example
                    └── jerseyspring
                        └── repository
                            └── exceptions
                                ├── DuplicateIdExceptionTest.java
                                ├── EmployeeNameNotFoundExceptionTest.java
                                └── EmployeeNotFoundExceptionTest.java

19 directories, 11 files

3. Paramètres du module Serverapi

(1) Classe de service

Créez la classe ʻEmployeeService dans le package my.example.jerseyspring.rest`.

EmployeeService.java


package my.example.jerseyspring.rest;

import my.example.jerseyspring.repository.EmployeeRepository;
import my.example.jerseyspring.repository.models.Employee;
import org.springframework.stereotype.Service;

import javax.ws.rs.*;
import javax.ws.rs.core.*;
import java.util.List;

@Service
@Path("/employees")
public class EmployeeService {

    final EmployeeRepository employeeRepository;

    public EmployeeService(EmployeeRepository employeeRepository) {
        this.employeeRepository = employeeRepository;
    }

    @Context
    UriInfo uriInfo;

    @GET
    @Path("/all")
    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    public List<Employee> getAll() {
        return employeeRepository.selectAll();
    }

    @GET
    @Path("/{id}")
    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    public Employee getEmployee(@PathParam("id") int id) {
        return employeeRepository.select(id);
    }

    @GET
    @Path("/search")
    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    public List<Employee> searchEmployee(@QueryParam("name") String name) {
        return employeeRepository.search(name);
    }

    @POST
    @Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    public Response addEmployee(Employee employee) {
        employeeRepository.insert(employee.getId(), employee.getFirstName());

        UriBuilder builder = uriInfo.getAbsolutePathBuilder();
        builder.path(String.valueOf(employee.getId()));
        return Response.created(builder.build()).build();
    }

    @PUT
    @Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    public Response updateEmployee(Employee employee) {
        employeeRepository.update(employee.getId(), employee.getFirstName());
        //Si vous en créez un nouveau, vous devez renvoyer créé, mais dans cet exemple, il s'agit d'une erreur, donc il renvoie toujours ok.
        return Response.ok().build();
    }

    @DELETE
    @Path("/{id}")
    @Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    public Response deleteEmployee(@PathParam("id") int id) {
        employeeRepository.delete(id);
        //Pour renvoyer le statut de l'entité, renvoyez ok.
        //S'il est accepté mais que le processus n'est pas terminé(Je viens juste de suivre, etc.)Retours acceptés
        //Dans cet exemple, seule la suppression est terminée et le contenu correspondant est perdu.
        return Response.noContent().build();
    }
}

Aussi, j'apporterai la classe PaymentService utilisée dans le projet Configuration minimale de Jersey + Spring.

(2) Classe de gestionnaire d'exceptions

Créez une classe pour le mappage d'exceptions dans le package rest.handlers. Requis pour chaque classe d'exception créée par le module de référentiel. Pour le mappage des exceptions, cliquez ici (https://qiita.com/kasa_le/items/3c7e7a426acf846ee64e#%E4%BE%8B%E5%A4%96%E3%83%8F%E3%83%B3%E3 Veuillez vous référer à% 83% 89% E3% 83% AA% E3% 83% B3% E3% 82% B0).

Exemple) mappeur de classe ʻEmployeeNotFoundException`

NotFoundExceptionHandler.java


package my.example.jerseyspring.rest.handler;


import my.example.jerseyspring.repository.exceptions.EmployeeNotFoundException;

import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

@Provider
public class NotFoundExceptionHandler implements ExceptionMapper<EmployeeNotFoundException> {

    public Response toResponse(EmployeeNotFoundException ex) {
        return Response.status(Response.Status.NOT_FOUND).build();
    }
}

(3) Paramètres Bean

Créez ʻapplicationContext.xml sous src / main / resources` et faites-le comme suit.

applicationContext.xml


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <context:component-scan base-package="my.example.jerseyspring"/>

    <bean id="transactionBo" class="my.example.jerseyspring.repository.transaction.impl.TransactionBoImpl"/>

    <bean id="employeeRepository" class="my.example.jerseyspring.repository.EmployeeRepository"/>

</beans>

(4) Paramètres du dossier webapp

Créez un dossier src / main / webapp.

--Créez web.xml sous webapp / WEB-INF et faites-en le contenu suivant

web.xml


<?xml version="1.0" encoding="UTF-8"?>
<!-- This web.xml file is not required when using Servlet 3.0 container,
     see implementation details http://jersey.java.net/nonav/documentation/latest/jax-rs.html -->
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <display-name>Restful Web Application</display-name>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <servlet>
        <servlet-name>jersey-servlet</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>my.example.jerseyspring</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>jersey-servlet</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>
</web-app>

--Créez ʻindex.jsp sous webapp / ʻet faites-le comme suit

index.jsp


<html>

<body>
<h2>Jersey + Spring RESTful Web Application!</h2>
<p><a href="rest/payment/mkyong">Jersey resource</a>
    <br>
<p><a href="rest/employees/all">All Employee List</a>
<p><a href="rest/employees/3">get id=3 employee</a>

</body>

</html>

(5) Classe de test Junit

Créez une classe EmployeeServiceTest et une classe PaymentServiceTest pour écrire un test.

EmployeeServiceTest.java


package my.example.jerseyspring.rest;

import my.example.jerseyspring.repository.EmployeeRepository;
import my.example.jerseyspring.repository.models.Employee;
import my.example.jerseyspring.rest.handler.DuplicateExceptionHandler;
import my.example.jerseyspring.rest.handler.NameNotFoundExceptionHandler;
import my.example.jerseyspring.rest.handler.NotFoundExceptionHandler;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.List;
import java.util.stream.Stream;

import static org.assertj.core.api.Assertions.assertThat;

@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:/applicationContext.xml")
public class EmployeeServiceTest extends JerseyTest {

    @Autowired
    EmployeeRepository employeeRepository;

    @Override
    protected Application configure() {
        return new ResourceConfig(EmployeeService.class)
                .register(DuplicateExceptionHandler.class)
                .register(NameNotFoundExceptionHandler.class)
                .register(NotFoundExceptionHandler.class)
                .register(this);
    }

    @BeforeEach
    @Override
    public void setUp() throws Exception {
        super.setUp();
    }

    @AfterEach
    @Override
    public void tearDown() throws Exception {
        super.tearDown();
    }

    @ParameterizedTest
    @ValueSource(strings = {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    public void getAll(String mediaType) {
        final Response response = target("/employees/all").request().accept(mediaType).get();
        assertThat(response.getHeaderString("Content-Type"))
                .isEqualTo(mediaType);

        List<Employee> content = response.readEntity(new GenericType<>() {
        });
        assertThat(content.size()).isEqualTo(5);
        assertThat(content.get(0)).isEqualToComparingFieldByField(new Employee(3, "Cupcake"));
        assertThat(content.get(1)).isEqualToComparingFieldByField(new Employee(4, "Donuts"));
        assertThat(content.get(2)).isEqualToComparingFieldByField(new Employee(5, "Eclair"));
        assertThat(content.get(3)).isEqualToComparingFieldByField(new Employee(8, "Froyo"));
        assertThat(content.get(4)).isEqualToComparingFieldByField(new Employee(9, "Gingerbread"));
    }

    @ParameterizedTest
    @MethodSource("getParamProvider")
    public void getEmployee(int id, String mediaType) {
        String urlPath = String.format("/employees/%d", id);
        final Response response = target(urlPath).request().accept(mediaType).get();
        assertThat(response.getHeaderString("Content-Type"))
                .isEqualTo(mediaType);

        Employee employee = response.readEntity(Employee.class);
        Employee expect = employeeRepository.select(id);
        assertThat(employee).isEqualToComparingFieldByField(expect);
    }

    static Stream<Arguments> getParamProvider() {
        return Stream.of(
                Arguments.of(3, MediaType.APPLICATION_JSON),
                Arguments.of(4, MediaType.APPLICATION_JSON),
                Arguments.of(5, MediaType.APPLICATION_JSON),
                Arguments.of(8, MediaType.APPLICATION_JSON),
                Arguments.of(9, MediaType.APPLICATION_JSON),
                Arguments.of(3, MediaType.APPLICATION_XML),
                Arguments.of(4, MediaType.APPLICATION_XML),
                Arguments.of(5, MediaType.APPLICATION_XML),
                Arguments.of(8, MediaType.APPLICATION_XML),
                Arguments.of(9, MediaType.APPLICATION_XML)
        );
    }

    @ParameterizedTest
    @ValueSource(strings = {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    public void searchEmployee(String mediaType) {
        final Response response = target("/employees/search")
                .queryParam("name", "a")
                .request()
                .accept(mediaType)
                .get();
        assertThat(response.getHeaderString("Content-Type"))
                .isEqualTo(mediaType);

        List<Employee> content = response.readEntity(new GenericType<>() {
        });
        assertThat(content.size()).isEqualTo(3);
        assertThat(content.get(0)).isEqualToComparingFieldByField(new Employee(3, "Cupcake"));
        assertThat(content.get(1)).isEqualToComparingFieldByField(new Employee(5, "Eclair"));
        assertThat(content.get(2)).isEqualToComparingFieldByField(new Employee(9, "Gingerbread"));
    }

    @ParameterizedTest
    @MethodSource("postRawProvider")
    public void addEmployee(int id, String bodyRaw, String mediaType) {

        final Response response = target("/employees").request()
                .post(Entity.entity(bodyRaw, mediaType));
        assertThat(response.getStatus()).isEqualTo(201);
        assertThat(response.getHeaderString("Location"))
                .isEqualTo("http://localhost:9998/employees/" + id);
    }

    static Stream<Arguments> postRawProvider() {
        final String json = "{\"firstName\":\"Honeycomb\",\"id\":11}";
        final String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
                "<employee><firstName>KitKat</firstName><id>19</id></employee>";
        return Stream.of(
                Arguments.of(11, json, MediaType.APPLICATION_JSON),
                Arguments.of(19, xml, MediaType.APPLICATION_XML)
        );
    }

    @ParameterizedTest
    @MethodSource("putRawProvider")
    public void updateEmployee(int id, String bodyRaw, String mediaType) {
        final Response response = target("/employees").request()
                .put(Entity.entity(bodyRaw, mediaType));
        assertThat(response.getStatus()).isEqualTo(200);

        Employee employee = target("/employees/" + id).request().get(Employee.class);
        Employee expected = employeeRepository.select(id);
        assertThat(employee).isEqualToComparingFieldByField(expected);
    }

    static Stream<Arguments> putRawProvider() {
        final String json = "{\"firstName\":\"Frozen yogurt\",\"id\":8}";
        final String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
                "<employee><firstName>Cup Cake</firstName><id>3</id></employee>";
        return Stream.of(
                Arguments.of(8, json, MediaType.APPLICATION_JSON),
                Arguments.of(3, xml, MediaType.APPLICATION_XML)
        );
    }

    @Test
    public void deleteEmployee() {
        final Response response = target("/employees/9")
                .request().delete();
        assertThat(response.getStatus()).isEqualTo(204);
    }

    @Test
    public void exception_selectEmployee() {
        final Response response = target("/employees/1").request().get();
        assertThat(response.getStatus()).isEqualTo(404);
    }

    @Test
    public void exception_searchEmployee() {
        final Response response = target("/employees/search?name=android").request().get();
        assertThat(response.getStatus()).isEqualTo(404);
    }

    @ParameterizedTest
    @MethodSource("putRawProvider")
    public void exception_addEmployee(int id, String bodyRaw, String mediaType) {
        final Response response = target("/employees").request()
                .post(Entity.entity(bodyRaw, mediaType));
        assertThat(response.getStatus()).isEqualTo(409);
    }

    @ParameterizedTest
    @MethodSource("putExceptionProvider")
    public void exception_updateEmployee(int id, String bodyRaw, String mediaType) {
        final Response response = target("/employees").request()
                .put(Entity.entity(bodyRaw, mediaType));
        assertThat(response.getStatus()).isEqualTo(404);
    }

    static Stream<Arguments> putExceptionProvider() {
        final String json = "{\"firstName\":\"Lollipop\",\"id\":21}";
        final String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
                "<employee><firstName>Jelly Bean</firstName><id>17</id></employee>";
        return Stream.of(
                Arguments.of(21, json, MediaType.APPLICATION_JSON),
                Arguments.of(3, xml, MediaType.APPLICATION_XML)
        );
    }

    @Test
    public void exception_deleteEmployee() {
        final Response response = target("/employees/1").request().get();
        assertThat(response.getStatus()).isEqualTo(404);
    }
}

Dans Exemple multi-module, la partie qui obtenait l'objet Sington en tant que ʻEmployeeRepository.getInstance est à la variable membre déclarée par @ Autowired. Accès et changement. Avec l'annotation @ Autowired, Spring va DI pour vous, mais en fait, il semble que différentes instances seront créées pour la personne qui travaille réellement avec le service et la personne qui teste (cela devrait être un singleton, mais probablement le test). J'ai vu un article disant qu'il est normal de faire ResourceConfig # register (this)` (car le contexte est différent du contexte de l'application). Cependant, n'est-ce pas un bug qui fonctionne avec cela? Parce qu'il y a un message d'avertissement? Il y a aussi une opinion, donc je ne sais pas si elle est correcte. https://stackoverflow.com/questions/34453448/how-to-access-spring-bean-from-jerseytest-subclass

Cette fois, la classe de référentiel n'est pas le sujet principal en premier lieu, elle fournit juste un accès temporaire aux données, donc je suis content de cela une fois. Cependant, sachez que cela peut cesser de fonctionner à mesure que la version de Spring ou de Jersey augmente.

La classe PaymentServiceTest est également importée à partir de l'exemple de configuration minimum (https://qiita.com/kasa_le/items/59ebd6b5490945dd5580). Apportez également la classe TransactionBoMock créée pour le test de remplacement simulé DI.

Ensuite, placez ʻapplicationContext.xml dans le dossier resourcesdu dossiertest` afin de pouvoir remplacer le bean, et procédez comme suit.

test/resources/applicationContext.xml


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <context:component-scan base-package="my.example.jerseyspring"/>

    <bean id="transactionBo" class="my.example.jerseyspring.repository.transaction.impl.TransactionBoMock"/>
    <bean id="employeeRepository" class="my.example.jerseyspring.repository.EmployeeRepository"/>

</beans>

Je n'ai pas créé d'interface pour EmployeeRepository, donc je ne la remplacerai pas cette fois, mais normalement, je devrais utiliser une classe Mocked pour tester par rapport à l'implémentation d'origine qui récupère les données de DB, etc.

(6) Configuration finale

La structure du dossier serverapi devrait ressembler à ceci:

.
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── my
    │   │       └── example
    │   │           └── jerseyspring
    │   │               └── rest
    │   │                   ├── EmployeeService.java
    │   │                   ├── PaymentService.java
    │   │                   └── handler
    │   │                       ├── DuplicateExceptionHandler.java
    │   │                       ├── NameNotFoundExceptionHandler.java
    │   │                       └── NotFoundExceptionHandler.java
    │   ├── resources
    │   │   └── applicationContext.xml
    │   └── webapp
    │       ├── WEB-INF
    │       │   └── web.xml
    │       └── index.jsp
    └── test
        ├── java
        │   └── my
        │       └── example
        │           └── jerseyspring
        │               ├── repository
        │               │   └── transaction
        │               │       └── impl
        │               │           └── TransactionBoMock.java
        │               └── rest
        │                   ├── EmployeeServiceTest.java
        │                   └── PaymentServiceTest.java
        └── resources
            └── applicationContext.xml

21 directories, 13 files

4. Exécuter, tester

(1)JUnitTest Exécutez JUnitTest pour voir si tous les tests réussissent.

$ mvn clean test

(2) Contrôle de fonctionnement

Configurez et déployez Tomcat.

Vous devriez voir une page comme celle ci-dessous, cliquez sur le lien et vous devriez voir la valeur de retour de l'API.

jersey-spring-restful.png

Si vous souhaitez vérifier l'entrée / la sortie du système POST ou de Json / Xml, veuillez vérifier en utilisant Curl ou Postman.

Les projets à ce jour sont téléchargés ci-dessous. https://github.com/le-kamba/spring-jersey-sample/tree/spring_jersey

Impressions

J'ai pu le faire en douceur sans devenir accro, sauf pour la partie singleton.

À propos, la structure des dossiers a été mise en place en mettant la commande ** tree ** dans brew sur mac. Je devais le faire dès l'installation de Homebrew en premier lieu. (Vous ne l'avez pas mis?)

#Installation Homebrew
$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"

#installation d'arbre
$ brew install tree

référence

Quelle est la différence entre Spring DI et new? https://qiita.com/uqichi/items/5f59817beb3dff9c0c1e

Recommended Posts

Exemple multi-module d'API RESTful avec IntelliJ + Jersey + Spring Framework
Exemple de configuration d'API RESTful minimum avec Jersey + Spring Framework
Spring Boot: exemple de projet d'API Restful
API RESTful d'autorisation d'accès au démarrage de printemps
Implémenter l'API REST avec Spring Boot
Lancer un (ancien) projet Spring Boot avec IntelliJ
Créer un projet Java Spring Boot avec IntelliJ
Changements majeurs dans la fonctionnalité de base de Spring Framework 5.0
Exemple de code pour appeler l'API Yahoo! Shopping Product Search (v3) avec Spring RestTemplate
J'ai créé une fonction pour enregistrer des images avec l'API dans Spring Framework. Partie 1 (édition API)