Création d'un Starter Personnalisé avec Spring Boot

Conception de votre propre Starter

Un starter complet pour Spring Boot peut inclure les éléments suivants :

  • Module d'autoconfiguration : contient le code nécessaire à la configuration automatique.
  • Module starter : fournit les dépandances nécessaires, y compris celles du module d'autoconfiguration.

Si ces deux concepts ne sont pas distincts dans votre projet, ils peuvent être combinés en un seul module.

En résumé, un starter doit offrir tout ce qui est requis pour utiliser une bibliothèque spécifique.

1.1. Nommage

  • Le nom du module ne doit pas commencer par spring-boot.
  • Si des clés de configuration sont fournies, elles doivent avoir un espace de noms unique et ne pas entrer en conflit avec ceux utilisés par Spring Boot (par exemple, server, management, etc.).

Par exemple, si vous créez un starter pour "acme", l'autoconfigure pourrait être nommé acme-spring-boot-autoconfig, et le starter acme-spring-boot-starter.

1.2. Module d'autoconfiguration

Il est recommandé d'inclure cette dépendance dans le module d'autoconfiguration :

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-autoconfig-proc</artifactId>
    <optional>true</optional>
</dependency>

1.3. Module starter

Le starter est essentiellement un fichier jar vide dont le but est de gérer les dépendances requises.

Votre starter doit référencer directement ou indirectement le starter central de Spring Boot (spring-boot-starter).

2. Exemple Hello Starter

Voici comment créer un exemple de "Hello World" avec Spring Boot.

2.1. hello-spring-boot-autoconfig

Créons un projet Maven appelé hello-spring-boot-autoconfig.

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">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.5.RELEASE</version>
    </parent>
    <groupId>com.exmpl</groupId>
    <artifactId>hello-spring-boot-autoconfig</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfig</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-config-processor</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>
</project>

GreetProps.java

package com.exmpl.greet;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("my.greet")
public class GreetProps {
    private String name;
    private Integer age;
    private String hometown;

    // Getters and Setters
}

GreetService.java

package com.exmpl.greet;

public class GreetService {
    private String name;
    private Integer age;
    private String hometown;

    public GreetService(String name, Integer age, String hometown) {
        this.name = name;
        this.age = age;
        this.hometown = hometown;
    }

    public String sayHello(String name) {
        return "Bonjour, " + name;
    }

    public String greetWorld() {
        return String.format("[name=%s, age=%d, hometown=%s]", this.name, this.age, this.hometown);
    }
}

GreetServiceAutoConfig.java

package com.exmpl.greet;

import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableConfigurationProperties(GreetProps.class)
public class GreetServiceAutoConfig {
    private final GreetProps greetProps;

    public GreetServiceAutoConfig(GreetProps greetProps) {
        this.greetProps = greetProps;
    }

    @Bean
    @ConditionalOnMissingBean
    public GreetService greetService() {
        return new GreetService(this.greetProps.getName(), this.greetProps.getAge(), this.greetProps.getHometown());
    }
}

META-INF/spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.exmpl.greet.GreetServiceAutoConfig

mvn clean install

2.2. hello-spring-boot-starter

Dans hello-spring-boot-starter, référez-vous au module autoconfig.

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">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.exmpl</groupId>
    <artifactId>hello-spring-boot-starter</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>com.exmpl</groupId>
            <artifactId>hello-spring-boot-autoconfig</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>

Utilisation du starter dans un projet demo.

2.3. demo

application.properties

my.greet.name=EtudiantCheng
my.greet.age=28
my.greet.hometown=Suizhou, Hubei

DemoCtrl.java

package com.exmpl.demo.ctrl;

import com.exmpl.greet.GreetService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/demo")
public class DemoCtrl {

    private final GreetService greetService;

    public DemoCtrl(GreetService greetService) {
        this.greetService = greetService;
    }

    @GetMapping("/hello/{name}")
    public String hello(@PathVariable("name") String name) {
        return greetService.sayHello(name);
    }

    @GetMapping("/info")
    public String info() {
        return greetService.greetWorld();
    }
}

3. Amélioration du HelloWorld

Ajoutons un service de journalisation.

3.1. hello-spring-boot-autoconfig

MyLogAnnot.java

package com.exmpl.log;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLogAnnot {
    String desc() default "";
}

MyLogInterceptor.java

package com.exmpl.log;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Slf4j
public class MyLogInterceptor extends HandlerInterceptorAdapter {
    private static final ThreadLocal<Long> startTimeThreadLocal = new ThreadLocal<>();

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        MyLogAnnot myLogAnnot = method.getAnnotation(MyLogAnnot.class);
        if (null != myLogAnnot) {
            long startTime = System.currentTimeMillis();
            startTimeThreadLocal.set(startTime);
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        MyLogAnnot myLogAnnot = method.getAnnotation(MyLogAnnot.class);
        if (null != myLogAnnot) {
            long startTime = startTimeThreadLocal.get();
            long endTime = System.currentTimeMillis();
            long expendTime = endTime - startTime;

            String requestUri = request.getRequestURI();
            String methodName = method.getDeclaringClass().getName() + "#" + method.getName();
            String methodDesc = myLogAnnot.desc();
            String parameters = JSON.toJSONString(request.getParameterMap());

            log.info("\nDescription: {}\nPath: {}\nMethod: {}\nParameters: {}\nDuration: {}", methodDesc, requestUri, methodName, parameters, expendTime);
        }
    }
}

MyLogAutoConfig.java

package com.exmpl.log;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class MyLogAutoConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyLogInterceptor());
    }
}

META-INF/spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.exmpl.greet.GreetServiceAutoConfig,\
  com.exmpl.log.MyLogAutoConfig

3.2. demo

ProdCtrl.java

package com.exmpl.demo.ctrl;

import com.exmpl.log.MyLogAnnot;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/prod")
public class ProdCtrl {

    @MyLogAnnot(desc = "Rechercher Produits")
    @GetMapping("/list")
    public String list() {
        System.out.println("Rechercher Produits");
        return "ok";
    }

    @MyLogAnnot(desc = "Sauvegarder Produit")
    @PostMapping("/save")
    public String save(@RequestBody String productVO) {
        System.out.println("Sauvegarder Produit");
        return "ok";
    }

    @MyLogAnnot(desc = "Supprimer Produit")
    @GetMapping("/delete")
    public String delete(@RequestParam("productId") Long productId) {
        System.out.println("Supprimer Produit");
        return "ok";
    }

    @MyLogAnnot(desc = "Détails Produit")
    @GetMapping("/detail/{productId}")
    public String detail(@PathVariable("productId") Long productId) {
        System.out.println("Détails Produit");
        return "ok";
    }
}

Étiquettes: SpringBoot Java Maven Lombok FastJSON

Publié le 10 août à 08h32