[JAVA] Micro service facile avec Spark Framework!

1. Qu'est-ce que Spark Framework?

C'est un framework d'application web très simple, décrit sur le site officiel (http://sparkjava.com/) comme suit:

Spark - A micro framework for creating web applications in Kotlin and Java 8 with minimal effort

Il a une note de: star: 7199 sur github (au 3/4/2018), il semble donc être un framework assez utilisé.

En tant que fonctionnalité, il est très facile d'implémenter une application Web à l'aide d'expressions lambda et de méthodes statiques. Vous trouverez ci-dessous un échantillon de la documentation officielle.

HelloWorld.java


import static spark.Spark.*;

public class HelloWorld {
    public static void main(String[] args) {
        get("/hello", (req, res) -> "Hello World");
    }
}

Exécutez comme une application Java normale avec une méthode principale. Une fois l'application lancée, accédez à http: // localhost: 4567 / hello avec un navigateur Web et vous verrez Hello World. C'est très simple! Cette fois, je voudrais créer une application Todo de service REST à l'aide de ce Spark Framework.

2. Spécifications de l'application Todo

Todo.java


package com.example.spark.demo;

import java.io.Serializable;
import java.util.Date;

public class Todo implements Serializable {

    private static final long serialVersionUID = 1L;
    
    private String todoId;
    private String todoTitle;
    private Date createdAt;
    private boolean finished;
    
    // constructor, setter, getter omitted
}
Numéro d'article chemin Méthode HTTP La description
1 /api/todo POST Créer un TODO avec les données transmises
2 /api/todo/:todoId GET Obtient le TODO spécifié par todoId
3 /api/todo/:todoId PUT Mettre à jour le TODO spécifié par todoId
4 /api/todo/:todoId DELETE Supprimer le TODO spécifié par todoId

2. Créez un projet

Tout d'abord, créez un projet vierge avec mvn.

Exemple de commande dans Windows


mvn archetype:generate ^
-DinteractiveMode=false ^
-DarchetypeArtifactId=maven-archetype-quickstart ^
-DgroupId=com.example.spark.demo ^
-DartifactId=spark-demo

Après avoir créé un projet vierge, ajoutez la bibliothèque à utiliser cette fois dans pom.xml. Cette fois, nous utiliserons GSON pour convertir le format JSON. Les autres bibliothèques sont bien. Puisque le document officiel de Spark Framework utilisait GSON, j'ai décidé d'utiliser GSON cette fois.

pom.xml


    <!-- add spark framework -->
    <dependency>
      <groupId>com.sparkjava</groupId>
      <artifactId>spark-core</artifactId>
      <version>2.7.1</version>
    </dependency>
    <!-- add gson -->
    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>2.8.2</version>
    </dependency>

Lorsque l'ajout de la bibliothèque à pom.xml est terminé, la bibliothèque sera acquise, alors essayons de construire avec la commande suivante. C'est OK si «BUILD SUCCESS» s'affiche.

Commande de construction d'essai


mvn package -Dmaven.test.skip=true

3. Code source

3.1. Classe d'application

App.java


package com.example.spark.demo;

import static spark.Spark.*;

/**
 *★ Point 1
 * spark demo app
 */
public class App {
    //★ Point 1
    public static void main(String[] args) {
        //★ Point 2
        // initialize
        initialize();
        //★ Point 3
        // define api
        TodoApi.api();
        // omitted
    }

    //★ Point 2
    private static void initialize() {
        // server port
        port(8090);
        // static files
        staticFiles.location("/public");
        // connection pool
        // maxThreads, minThreads, timeOutMillis
        threadPool(8, 2, 30000);
    }
}

** ★ Point 1 ** L'application Spark Framework est implémentée comme une application Java normale exécutée par la méthode Main.

** ★ Point 2 ** Le processus de paramétrage initial (configuration) de l'application a été découpé à la méthode ʻinitialize`. Cette fois, j'ai effectué les trois réglages initiaux suivants qui sont susceptibles d'être utilisés fréquemment.

Modification du port du serveur de la valeur par défaut «4567» à «8090» avec la méthode «port».

Ce n'est pas nécessaire pour un service REST normal, mais il peut être utilisé comme un simple serveur Web, je vais donc vous expliquer comment publier des fichiers statiques sous le chemin de classe vers le Web. Spécifiez le répertoire à publier par la méthode staticFiles.location.

Dans les exemples de paramètres, vous pouvez obtenir le fichier / spark-demo / src / main / resources / public / css / styles.css en accédant à http: // localhost: 8090 / css / styles.css avec un navigateur Web. ..

Définissez le pool de connexions avec la méthode threadPool. Les arguments sont le nombre maximum de threads, le nombre minimum de threads et le délai d'expiration (en millisecondes) dans l'ordre.

** ★ Point 3 ** Nous avons décidé de définir l'API Web comme une classe distincte en tenant compte de la maintenabilité lors d'ajouts ou de modifications.

3.2. Classes d'API Web

TodoApi.java


package com.example.spark.demo;

import static spark.Spark.*;

import java.util.HashMap;
import java.util.Map;

/**
 *★ Point 4
 * Web API for TODO
 */
public class TodoApi {
    //★ Point 4
    public static void api() {
        //★ Point 5
        TodoService todoService = new TodoService();
        JsonTransformer jsonTransformer = new JsonTransformer();

        //★ Point 6
        path("/api", () -> {
            path("/todo", () -> {
                post("", (request, response) -> {
                    String json = request.body();
                    Todo todo = jsonTransformer.fromJson(json, Todo.class);
                    return todoService.create(todo);
                }, jsonTransformer);
                get("/:todoId", (request, response) -> {
                    return todoService.find(request.params(":todoId"));
                }, jsonTransformer);
                put("/:todoId", (request, response) -> {
                    String json = request.body();
                    Todo todo = jsonTransformer.fromJson(json, Todo.class);
                    todo.setTodoId(request.params(":todoId"));
                    return todoService.update(todo);
                }, jsonTransformer);
                delete("/:todoId", (request, response) -> {
                    todoService.delete(request.params(":todoId"));
                    return success();
                }, jsonTransformer);
            });
            //★ Point 7
            // set response-type to all request of under '/api'
            after("/*", (request, response) -> {
                response.type("application/json;charset=UTF-8");
            });
        });
    }

    private static Map<String, String> success() {
        Map<String, String> map = new HashMap<String, String>();
        map.put("result", "success!");
        return map;
    }
}

** ★ Point 4 ** Définissez le traitement de l'API Web comme une classe régulière. Comme il existe de nombreuses méthodes statiques dans l'API Spark Framework, j'ai décidé de la définir comme une méthode statique ʻapi` cette fois.

** ★ Point 5 ** Créez une instance des classes de logique métier (TodoService) et de transformation de format (JsonTransformer). Nous parlerons des deux classes plus tard.

** ★ Point 6 ** Tel est le but de cet article.

Voir Official Documentation Routes et Request pour plus d'informations.

** ★ Point 7 ** Vous pouvez ajouter un traitement avant et après l'API Web avec la fonction appelée filtre de Spark Framework. Dans l'exemple, nous avons défini un filtre «après» qui définit «application / json; charset = UTF-8» sur «content-type» dans l'en-tête de réponse pour toutes les requêtes dont le chemin est sous «/ api».

En plus du filtre «après», il existe également des filtres «avant» et «après». Pour plus d'informations sur les filtres, consultez la documentation officielle (http://sparkjava.com/documentation#filters).

3.3. Classe de conversion de format

JsonTransformer.java


package com.example.spark.demo;

import com.google.gson.Gson;
import spark.ResponseTransformer;

//★ Point 8
public class JsonTransformer implements ResponseTransformer {

    private Gson gson = new Gson();

    //★ Point 8
    @Override
    public String render(Object model) throws Exception {
        return gson.toJson(model);
    }

    //★ Point 9
    public <T> T fromJson(String json, Class<T> classOfT) {
        return gson.fromJson(json, classOfT);
    }
}

** ★ Point 8 ** Définit une classe qui implémente l'interface spark.ResponseTransformer. Le but de cette interface est de convertir le résultat du traitement de la méthode API (le résultat de l'expression lambda au point 6) en une chaîne à écrire dans la réponse HTTP. Implémentez ce processus dans render car @ Override est donné. Cette fois, le processus décrit dans le document officiel est utilisé tel quel, et il est converti au format JSON à l'aide de GSON.

** ★ Point 9 ** Comme le nom de ResponseTransformer l'indique, il s'agit à l'origine d'une classe de conversion de données de réponse, mais j'ai décidé de convertir la conversion d'objet de JSON au moment de la demande ici. (Je voulais juste réutiliser les objets GSON)

À propos, il n'existe pas de RequestTransformer qui transforme les données de demande dans Spark Framework.

3.4. Classe de logique métier

TodoService.java


package com.example.spark.demo;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

//★ Point 10
public class TodoService {

    private Map<String, Todo> store = new HashMap<String, Todo>();

    public Todo find(String todoId) {
        return store.get(todoId);
    }

    public void delete(String todoId) {
        store.remove(todoId);
        System.out.println("delete todoId : " + todoId);
    }
    
    public Todo update(Todo todo) {
        Todo updatedTodo = store.get(todo.getTodoId());
        if (updatedTodo != null) {
            updatedTodo.setTodoTitle(todo.getTodoTitle());
            updatedTodo.setFinished(todo.isFinished());
        }
        return updatedTodo;
    }

    public Todo create(Todo todo) {
        String todoId = UUID.randomUUID().toString();
        Todo registeredTodo = new Todo(todoId, todo.getTodoTitle(), new Date(),
                false);
        store.put(todoId, registeredTodo);
        System.out.println("registeredTodo : " + registeredTodo);
        return registeredTodo;
    }
}

** ★ Point 10 ** Implémentez la logique métier de l'application TODO. Cependant, il s'agit d'un processus factice approprié car il n'utilise pas les fonctions de Spark Framework. Cette fois, nous n'avons pas accédé à la base de données et avons utilisé Map pour le traitement CRUD en mémoire.

4. Création d'un fichier jar exécutable

Puisqu'il s'agit d'un micro service, j'aimerais simplifier l'exécution. Je veux le construire comme un fichier jar exécutable comme ʻuber.jar`.

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>com.example.spark.demo</groupId>
  <artifactId>spark-demo</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>spark-demo</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <!-- add spark framework -->
    <dependency>
      <groupId>com.sparkjava</groupId>
      <artifactId>spark-core</artifactId>
      <version>2.7.1</version>
    </dependency>
    <!-- add gson -->
    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>2.8.2</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <!-- java8 -->
  <properties>
    <java.version>1.8</java.version>
    <maven.compiler.target>${java.version}</maven.compiler.target>
    <maven.compiler.source>${java.version}</maven.compiler.source>
  </properties>
  <!-- add for executable jar -->
  <build>
    <plugins>
      <plugin>
          <artifactId>maven-assembly-plugin</artifactId>
          <executions>
            <execution>
              <phase>package</phase>
              <goals>
                <goal>single</goal>
              </goals>
            </execution>
          </executions>
          <configuration>
            <archive>
              <manifest>
                <addClasspath>true</addClasspath>
                <mainClass>com.example.spark.demo.App</mainClass>
              </manifest>
            </archive>
            <descriptorRefs>
              <descriptorRef>jar-with-dependencies</descriptorRef>
            </descriptorRefs>
          </configuration>
      </plugin>
    </plugins>
  </build>
</project>

Une fois que vous avez un fichier jar exécutable, exécutez-le avec java -jar.

C:\tmp\spark\spark-demo>java -jar target/spark-demo-1.0-SNAPSHOT-jar-with-dependencies.jar
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

5. Enfin

Cette fois, j'ai expliqué la mise en œuvre de micro services à l'aide de Spark Framework. C'est très simple et facile à mettre en œuvre, et il peut être construit comme un fichier jar exécutable unique, donc je pense qu'il est facile à publier. Dans l'exemple, la vérification d'entrée est omise, mais il s'agit en fait d'un processus nécessaire. Java-json-tools / json-schema-validator lors de la vérification de l'entrée comme JSON, lors de la vérification de l'entrée comme Java Bean Validation est recommandé.

Recommended Posts

Micro service facile avec Spark Framework!
SaveAsBinaryFile avec Spark (partie 2)
(Java) BDD facile avec Spectrum?
Les microservices avec DevOps apportent des modifications
Scraping Web facile avec Jsoup
Introduction facile à la bibliothèque avec Maven!
Créer un micro service avec Spring Boot
Bonjour tout le monde! Avec Asakusa Framework!
Passez facilement des appels JDBC avec Commons DbUtils
Double soumission de mesures avec Play Framework
Contrôle d'entrée facile avec Bean Validation!
Liaison de données avec Spark et Cassandra
Accès facile à la base de données avec Java Sql2o
Contrôleur de cadre de test Spring avec Junit
Microservices avec Docker et Cloud Performance