web-dev-qa-db-fra.com

Analyser les composants de différents modules / JAR maven dans une application Spring Boot

J'ai deux modules Maven. La première, appelée "application", contient la classe d'application spring boot Qui ne contient que ces lignes:

package org.example.application;

@SpringBootApplication
@ComponentScan({"org.example.model", "org.example"})
public class Application {
    public static void main(String[] args) {
        ApplicationContext ctx = SpringApplication.run(Application.class, args);
    }
}

Dans le même module et package Maven, org.example.application, J'ai un RestController qui utilise un Component qui à son tour utilise les composants de l'autre module Maven décrit ci-dessous.

L'autre module Maven, appelé "modèle", contient les composants spring boot (Référentiels crud, entités, etc.). Toutes ces classes sont sous la même structure de package que le premier module Maven (org.example) Mais dans des sous-packages de celui-ci, comme org.example.model.entities, org.example.model.repositories Etc.

Ainsi, le flux est comme ceci:

Module Maven application dans le package org.example:
SpringBootApplication -> RestController -> MyComponent

Et les composants qui doivent être câblés automatiquement dans MyComponent sont ceux du module Maven model sous le package org.example.model.

Mais quand je démarre l'application, je reçois juste l'erreur:

***************************
APPLICATION FAILED TO START
***************************

Description:

Field myRepository in org.example.MyComponent required a bean of type 'org.example.model.repositories.MyRepository' that could not be found.

Action:

Consider defining a bean of type 'org.example.model.repositories.MyRepository' in your configuration.

org.example.model.repositories.MyRepository Existe dans le module Maven "modèle" mais ne peut pas être trouvé par la classe SpringBootApplication!

Comme vous pouvez le voir, j'ai essayé de définir explicitement les composants de numérisation pour: @ComponentScan({"org.example.model", "org.example"}) mais cela ne semble pas aider.

Alors qu'est-ce que j'ai fait de mal?

12
Rox

La première chose que vous devriez vous demander est: pourquoi déclarez-vous @ComponentScan Alors que l'un des objectifs de @SpringBootApplication Est (entre autres) de permettre l'analyse des composants?
De documentation Spring Boot :

L'annotation @SpringBootApplication Équivaut à utiliser @Configuration, @EnableAutoConfiguration Et @ComponentScan Avec leurs attributs par défaut

Notez que lorsque dans la classe de votre application Spring Boot, vous déclarez @ComponentScan Pour spécifier une valeur comme basePackages, il remplace le basePackages utilisé par défaut par @SpringBootApplication qui est le package actuel où réside la classe. Donc, pour avoir comme package de base le package de la classe Spring Boot Application et les packages supplémentaires qui manquaient, vous devez les définir explicitement.

De plus, basePackages est récursif. Donc, pour activer l'analyse à la fois pour les classes localisées dans les packages "org.example" Et "org.example.model", Spécifier "org.example" Suffit car "org.example.model" En est un sous-package.

Essayez ça:

@SpringBootApplication(scanBasePackages={"org.example"})

Ou bien :

@SpringBootApplication
@ComponentScan("org.example")

Quand spécifier @ EnableJpaRepositories/@ ComponentScan/scanBasePackages dans une application Spring Boot?

Lorsque vous concevez la disposition de votre application Spring Boot, vous avez deux cas:

1) cas (à privilégier) où vous utilisez une disposition de package qui fournit la configuration automatique de Spring Boot avec une configuration nulle.

Pour résumer: si vos classes annotées avec des stéréotypes Spring Bean: @Component, @Repositories, @Repositories, ... sont situées dans le même package ou un sous-package du Spring Boot Application class, déclarant seulement @SpringBootApplication Est tout ce dont vous avez besoin.

2) cas (à éviter) où vous n'utilisez pas une disposition de package qui fournit la configuration automatique de Spring Boot avec une configuration nulle.

Cela signifie généralement que vous avez des classes candidates à analyser qui ne sont pas dans le package (ou sous-package) de votre classe annoté avec @SpringBootApplication.
Dans ce cas, vous ajoutez l'attribut scanBasePackages ou ajoutez @ComponentScan Pour spécifier les packages à analyser.
Mais en plus, si vos référentiels ne se trouvent pas dans un package ou sous-package de votre classe annoté avec @SpringBootApplication, Quelque chose d'autre doit être déclaré tel que: @EnableJpaRepositories(="packageWhereMyRepoAreLocated")

Voici la documentation sur cette partie (c'est moi qui souligne):

80.3 Utiliser les référentiels de données Spring

Spring Data peut créer des implémentations d'interfaces @Repository de différentes saveurs. Spring Boot gère tout cela pour vous, tant que ces @Repositories sont inclus dans le même package (ou un sous-package) de votre classe @EnableAutoConfiguration.

Pour de nombreuses applications, tout ce dont vous avez besoin est de placer les bonnes dépendances Spring Data sur votre chemin de classe (il existe un spring-boot-starter-data-jpa pour JPA et un spring-boot-starter-data-mongodb pour Mongodb) et en créer des interfaces de référentiel pour gérer vos objets @Entity. Des exemples sont dans l'exemple JPA et l'exemple Mongodb.

Spring Boot essaie de deviner l'emplacement de vos définitions @Repository, en fonction de la @EnableAutoConfiguration qu'il trouve. Pour obtenir plus de contrôle, utilisez l'annotation @EnableJpaRepositories (de Spring Data JPA).


Exemples

1) cas (à privilégier) où vous utilisez une disposition de package qui fournit la configuration automatique de Spring Boot avec une configuration nulle.

Avec une application Spring Boot déclarée dans le package org.example, Et toutes les classes de bean (référentiels inclus) déclarées dans le même package ou un sous-package de org.example, La déclaration suivante suffit pour le Spring Application de démarrage:

package org.example;

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        ApplicationContext ctx = SpringApplication.run(Application.class, args);
    }
}

Les référentiels peuvent être situés dans le package org.example.repository Tels que:

package org.example.repository;

@Repository
public interface FooRepository extends  JpaRepository<Foo, Long>,  { }

et

package org.example.repository;

@Repository
public interface BarRepository extends  JpaRepository<Bar, Long>,  { }

Les contrôleurs peuvent être situés dans le package org.example.controller:

package org.example.controller;

@RestController
@RequestMapping("/api/foos")
public class FooController  {...}

et donc pour ...

2) cas (à éviter) où vous n'utilisez pas une disposition de package qui fournit la configuration automatique de Spring Boot avec une configuration nulle.

Avec une application Spring Boot déclarée dans le package org.example.application Et pas toutes les classes de bean (référentiels inclus) déclarées dans le même package ou un sous-package de org.example.application, La déclaration suivante sera requise pour l'application Spring Boot:

package org.example.application;

@SpringBootApplication(scanBasePackages= {
                      "org.example", 
                      "org.thirdparty.repository"})
@EnableJpaRepositories("org.thirdparty.repository")
public class Application {
    public static void main(String[] args) {
        ApplicationContext ctx = SpringApplication.run(Application.class, args);
    }
}

Et les classes de haricots pourraient être comme ci-dessous.

Les référentiels qui peuvent provenir d'un JAR externe peuvent être situés dans le package org.thirdparty.repository Tels que:

package org.thirdparty.repository;

@Repository
public interface FooRepository extends  JpaRepository<Foo, Long>,  { }

et

package org.thirdparty.repository;

@Repository
public interface BarRepository extends  JpaRepository<Bar, Long>,  { }

Les contrôleurs peuvent être situés dans le package org.example.controller:

package org.example.controller

@RestController
@RequestMapping("/api/foos")
public class FooController  {...}

et donc pour ...

Conclusion : la définition de l'application Spring Boot dans le package de base de votre espace de noms est vraiment encouragée pour rendre la configuration Spring Boot aussi simple que possible.

19
davidxxx