[JAVA] RESTful API-Beispiel für mehrere Module mit IntelliJ + Jersey + Spring Framework

Dies ist eine Fortsetzung des folgenden Artikels.

Beispiel für eine minimale RESTful-API-Konfiguration mit Jersey + Spring Framework https://qiita.com/kasa_le/items/59ebd6b5490945dd5580

Konfiguration mit mehreren Modulen mit Maven (Jersey RESTful) https://qiita.com/kasa_le/items/db0d84e3e868ff14bc2b

Diesmal ist es so, als würden Sie die in den beiden oben genannten Artikeln erstellten Projekte zusammenführen. Ich werde es jedoch erklären, indem ich ein Projekt von Grund auf neu erstelle.

Zweck

Implementieren Sie die RESTful-API mit ** Jersey ** und ** Spring Framework ** (nicht Boot).

Tor

--Jersey + Spring Framework funktioniert mit RESTful API (CRUD). --DI kann mit einer Konfiguration mit mehreren Modulen durchgeführt werden.

Umwelt etc.

Werkzeuge 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

Projekt Einstellungen

Erstellen Sie in IntelliJ ein neues "Maven-Projekt".

1. Wurzel Pom

Grundsätzlich können Sie die Abhängigkeits- und Plug-In-Einstellungen aus dem Projekt Jersey + Spring Framework-Mindestkonfiguration kopieren.

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 aus JDK9 entfernt-->
        <!-- 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>

        <!--Prüfung-->
        <!-- 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. Submodule erstellen

(1) Löschen Sie den Stammordner "src"

Löschen Sie es manuell.

(2) Zugabe eines Submoduls

Fügen Sie im IntelliJ-Menü [Datei] - [Neu] - [Modul ...] die folgenden beiden im Maven-Projekt hinzu.

Der Name des Submoduls ist beliebig. Vergessen Sie nicht, ** Parent ** einzustellen. (Bitte wählen Sie das Root-Projekt aus.)

Dann sollte sich das der übergeordneten pom.xml in pom ändern. Wenn es sich nicht geändert hat, beheben Sie es manuell.

root/pom.xml


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

Jede pom.xml des Submoduls sollte folgendermaßen aussehen:

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>

Das Serverapi-Modul ist im Bereich "" enthalten, da der Frühlingstest für den Junit-Test erforderlich ist. Wenn Sie es jedoch für andere Submodule benötigen, können Sie es im Stammverzeichnis "pom.xml" ablegen.

Weder hat ihre eigene Version angegeben. Ich möchte die übergeordnete Version so verwenden, wie sie ist. Es ist eine Richtlinie, die Version nicht für jedes Submodul zu verwalten. Geben Sie bei Bedarf die jeweilige Version mit dem Tag "" an.

(3) Ordnerstruktur

Die aktuelle Ordnerstruktur sollte so aussehen.

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

2. Einstellungen des Repository-Moduls

(1) Modellklasse

Erstellen Sie die folgenden Klassen im Paket "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) Repository-Klasse

Erstellen Sie eine "EmployeeRepository" -Klasse im Paket "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) {
            //Wenn nicht, können Sie hinzufügen
            employeeList.add(new Employee(id, firstName));
            return;
        }
        //Kann nicht hinzugefügt werden, wenn dieselbe ID vorhanden ist
        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);
    }
}

Im vorherigen Multi-Modul-Beispiel war die Singleton-Implementierung selbst enthalten, aber der DI des Spring Framework ist im Grunde genommen Singleton. , Entfernen seiner Implementierung.

(3) Ausnahmeklasse

Erstellen Sie jede Ausnahmeklasse, die in der Klasse "EmployeeRepository" verwendet wird. Das Paket ist "repository.exceptions".

Beispiel) Klasse "EmployeeNotFoundException"

EmployeeNotFoundException.java


package my.example.jerseyspring.repository.exceptions;

public class EmployeeNotFoundException extends RuntimeException {
    public EmployeeNotFoundException() {
        super("Der Mitarbeiter mit dieser ID kann nicht gefunden werden.");
    }
}

Erstellen Sie andere Klassen auf die gleiche Weise.

Außerdem werde ich das "Transacation" -Paket mitbringen, das im Projekt Jersey + Spring Minimum Configuration verwendet wird. Das Paket "repository.transaction" ist in Ordnung.

(4) Junit-Testklasse

Erstellen Sie eine Testklasse für die Ausnahmeklasse.

Nehmen wir als Beispiel den Fall "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("Der Mitarbeiter mit dieser ID kann nicht gefunden werden.");
    }
}

Führen Sie in IntelliJ den Junit-Test aus und überprüfen Sie, ob er erfolgreich ist.

(5) Endgültige Konfiguration

Am Ende sollten Sie eine Ordnerstruktur wie diese haben:

$ 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. Serverapi-Moduleinstellungen

(1) Serviceklasse

Erstellen Sie eine "EmployeeService" -Klasse im Paket "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());
        //Wenn Sie ein neues erstellen, müssen Sie erstellt zurückgeben. In diesem Beispiel handelt es sich jedoch um einen Fehler, sodass immer OK zurückgegeben wird.
        return Response.ok().build();
    }

    @DELETE
    @Path("/{id}")
    @Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    public Response deleteEmployee(@PathParam("id") int id) {
        employeeRepository.delete(id);
        //Um den Status der Entität zurückzugeben, geben Sie ok zurück.
        //Wenn es akzeptiert wird, der Vorgang jedoch nicht abgeschlossen ist(Ich bin gerade auf das Stichwort gekommen usw.)Rückgabe akzeptiert
        //In diesem Beispiel ist nur das Löschen abgeschlossen und der entsprechende Inhalt ist erschöpft.
        return Response.noContent().build();
    }
}

Außerdem werde ich die im Projekt Jersey + Spring-Mindestkonfiguration verwendete Klasse "PaymentService" mitbringen.

(2) Ausnahmebehandlungsklasse

Erstellen Sie eine Klasse für die Ausnahmezuordnung im Paket "rest.handlers". Erforderlich für jede vom Repository-Modul erstellte Ausnahmeklasse. Klicken Sie hier, um eine Ausnahmezuordnung zu erhalten (https://qiita.com/kasa_le/items/3c7e7a426acf846ee64e#%E4%BE%8B%E5%A4%96%E3%83%8F%E3%83%B3%E3) Bitte beziehen Sie sich auf% 83% 89% E3% 83% AA% E3% 83% B3% E3% 82% B0).

Beispiel) Mapper der Klasse "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) Bean-Einstellungen

Erstellen Sie applicationContext.xml unter src / main / resources und machen Sie es wie folgt.

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) Einstellungen des Ordners "webapp"

Erstellen Sie einen Ordner "src / main / webapp".

--Erstelle web.xml unter webapp / WEB-INF und mache daraus den folgenden Inhalt

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>

--Erstelle index.jsp unter webapp / und mache es wie folgt

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) Junit-Testklasse

Erstellen Sie eine "EmployeeServiceTest" -Klasse und eine "PaymentServiceTest" -Klasse und schreiben Sie einen 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);
    }
}

In Beispiel mit mehreren Modulen ist der Teil, der das Sington-Objekt als "EmployeeRepository.getInstance" erworben hat, die von "@ Autowired" deklarierte Mitgliedsvariable. Zugriff und Änderung. Mit der Annotation "@ Autowired" wird Spring für Sie DI, aber tatsächlich scheinen verschiedene Instanzen für die Person, die tatsächlich mit dem Dienst arbeitet, und die Person, die testet, erstellt zu werden (es sollte ein Singleton sein, aber wahrscheinlich der Test). Ich habe einen Artikel gesehen, der besagt, dass es in Ordnung ist, "ResourceConfig # register (this)" auszuführen (da sich der Kontext vom Anwendungskontext unterscheidet). Ist es nicht ein Fehler, der damit funktioniert? Weil es eine Warnmeldung gibt? Es gibt auch eine Meinung, daher weiß ich nicht, ob es richtig ist. https://stackoverflow.com/questions/34453448/how-to-access-spring-bean-from-jerseytest-subclass

Dieses Mal ist die Repository-Klasse nicht das Hauptthema, sondern bietet nur temporären Datenzugriff, daher bin ich damit zufrieden. Beachten Sie jedoch, dass es möglicherweise nicht mehr funktioniert, wenn die Version von Spring oder Jersey steigt.

Die Klasse "PaymentServiceTest" wird auch aus dem Beispiel für die Mindestkonfiguration (https://qiita.com/kasa_le/items/59ebd6b5490945dd5580) übernommen. Bringen Sie außerdem die Klasse "TransactionBoMock" ein, die für den DI-Mock-Ersatztest erstellt wurde.

Fügen Sie dann eine applicationContext.xml in den resources-Ordner des test-Ordners ein, damit Sie die Bean ersetzen können, und gehen Sie wie folgt vor:

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>

Da EmployeeRepository keine Schnittstelle hat, werde ich sie dieses Mal nicht ersetzen, aber ursprünglich sollte ich die Mocked-Klasse zum Testen der ursprünglichen Implementierung verwenden, die Daten aus der Datenbank usw. abruft.

(6) Endgültige Konfiguration

Die Struktur des Ordners "serverapi" sollte folgendermaßen aussehen:

.
├── 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. Ausführen, testen

(1)JUnitTest Führen Sie JUnitTest aus, um festzustellen, ob alle Tests bestanden wurden.

$ mvn clean test

(2) Funktionsprüfung

Richten Sie Tomcat ein und stellen Sie es bereit.

Sie sollten eine Seite wie die folgende sehen, auf den Link klicken und den API-Rückgabewert sehen.

jersey-spring-restful.png

Wenn Sie die Ein- / Ausgabe des POST-Systems oder von Json / Xml überprüfen möchten, überprüfen Sie dies bitte mit Curl oder Postman.

Die bisherigen Projekte werden unten hochgeladen. https://github.com/le-kamba/spring-jersey-sample/tree/spring_jersey

Impressionen

Ich konnte es reibungslos machen, ohne süchtig zu werden, bis auf den Singleton-Teil.

Übrigens wurde die Ordnerstruktur gelöscht, indem der Befehl ** tree ** in Brew auf dem Mac eingefügt wurde. Ich musste es von der Installation von Homebrew an erster Stelle tun. (Hast du es nicht reingelegt?)

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

#Installation des Baumes
$ brew install tree

Referenz

Was ist der Unterschied zwischen Spring DI und New? https://qiita.com/uqichi/items/5f59817beb3dff9c0c1e

Recommended Posts

RESTful API-Beispiel für mehrere Module mit IntelliJ + Jersey + Spring Framework
Beispiel für eine minimale RESTful-API-Konfiguration mit Jersey + Spring Framework
Spring Boot: Restful API-Beispielprojekt
Spring Boot Access Authorization RESTful API
Implementieren Sie die REST-API mit Spring Boot
Starten Sie mit IntelliJ ein (altes) Spring Boot-Projekt
Erstellen Sie mit IntelliJ ein Java Spring Boot-Projekt
Wichtige Änderungen in der Kernfunktionalität von Spring Framework 5.0
Beispielcode zum Aufrufen der Yahoo! Shopping Product Search (v3) -API mit Spring RestTemplate
Ich habe eine Funktion zum Registrieren von Bildern bei der API in Spring Framework erstellt. Teil 1 (API Edition)