在 Spring Boot 应用程序中扫描不同 maven 模块/JAR 的组件

2022-09-01 15:47:43

我有两个 Maven 模块。第一个称为“application”,它包含仅包含以下行的 Application 类:spring boot

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);
    }
}

在同一个 Maven 模块和包中,我有一个使用 a,而后者又使用下面描述的其他 Maven 模块的组件。org.example.applicationRestControllerComponent

另一个 Maven 模块称为“模型”,包含组件(crud 存储库、实体等)。所有这些类都与第一个 Maven 模块 () 处于相同的包结构下,但在子包中,如 等。spring bootorg.exampleorg.example.model.entitiesorg.example.model.repositories

所以,流程是这样的:

Maven module in package org.exampleapplication
SpringBootApplication -> RestController -> MyComponent

应该自动连接的组件是包下的Maven模块中的组件 。MyComponentmodelorg.example.model

但是当我启动应用程序时,我只得到错误:

***************************
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确实存在于 Maven 模块“模型”中,但无法通过 SpringBootApplication 类找到!

如您所见,我已尝试将扫描组件显式定义为:但这似乎无济于事。@ComponentScan({"org.example.model", "org.example"})

那么我做错了什么呢?


答案 1

您应该想知道的第一件事是:为什么声明而目标之一是(除其他外)启用组件扫描?
弹簧启动文档@ComponentScan@SpringBootApplication

该批注等效于使用 ,并具有 其默认属性@SpringBootApplication@Configuration@EnableAutoConfiguration@ComponentScan

请注意,在 Spring Boot 应用程序的类上,您声明将值指定为 ,它将覆盖默认情况下使用的该类所在的当前包。因此,要将 Spring Boot Application 类的包和缺少的其他包作为基础包,必须显式设置它们。@ComponentScanbasePackagesbasePackages@SpringBootApplication

此外是递归的。因此,要对位于 和 包中的类启用扫描,指定和它的子包一样就足够了。basePackages"org.example""org.example.model""org.example""org.example.model"

试试看:

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

或者:

@SpringBootApplication
@ComponentScan("org.example")

当在Spring Boot应用程序中指定@EnableJpaRepositories/ @ComponentScan / scanBasePackages时?

在设计 Spring Boot 应用程序布局时,您有两种情况:

1)情况(偏爱)您使用提供零配置的Spring Boot自动配置的软件包布局。

总结一下:如果你的类用Spring Bean刻板印象注释:,,,...位于 Spring Boot Application 类的同一包或子包中,声明仅是您所需要的全部。@Component@Repositories@Repositories@SpringBootApplication

2)情况(要避免)您没有使用提供Spring Boot自动配置的软件包布局,而零配置。

这通常意味着您要扫描的候选类不在用 注释的类的包(或子包)中。
在这种情况下,您可以添加属性或 add 来指定要扫描的包。
但是,此外,如果您的存储库不位于带有 注释的类的包或子包中,则必须声明其他内容,例如:@SpringBootApplicationscanBasePackages@ComponentScan@SpringBootApplication@EnableJpaRepositories(="packageWhereMyRepoAreLocated")

以下是关于这部分的文档(重点是我的):

80.3 使用 Spring 数据存储库

Spring Data可以创建各种风格的@Repository接口的实现。Spring Boot会为您处理所有这些问题,只要这些@Repositories包含在@EnableAutoConfiguration类的同一包(或子包)中即可。

对于许多应用程序,您只需要将正确的Spring Data依赖项放在类路径上(JPA有一个spring-boot-starter-data-jpa和一个用于Mongodb的spring-boot-starter-data-mongodb),并创建一些存储库接口来处理@Entity对象。示例在 JPA 示例和 Mongodb 示例中。

Spring Boot 会尝试根据找到的@EnableAutoConfiguration来猜测@Repository定义的位置。要获得更多控制,请使用@EnableJpaRepositories注释(来自Spring Data JPA)。


例子

1)情况(偏爱)您使用提供零配置的Spring Boot自动配置的软件包布局。

在软件包中声明一个 Spring Boot 应用程序,并且在同一个软件包或 子软件包中声明所有 Bean 类(包括存储库),以下声明对于 Spring Boot 应用程序来说就足够了:org.exampleorg.example

package org.example;

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

这些存储库可以位于包中,例如:org.example.repository

package org.example.repository;

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

package org.example.repository;

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

控制器可以位于包装中:org.example.controller

package org.example.controller;

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

所以...

2)情况(要避免)您没有使用提供Spring Boot自动配置的软件包布局,而零配置。

在软件包中声明了 Spring Boot 应用程序,并且并非所有 Bean 类(包括存储库)都声明在同一软件包或 子软件包中,则 Spring Boot 应用程序将需要以下声明:org.example.applicationorg.example.application

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);
    }
}

豆类可以如下所示。

可能来自外部 JAR 的存储库可以位于包中,例如:org.thirdparty.repository

package org.thirdparty.repository;

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

package org.thirdparty.repository;

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

控制器可以位于包装中:org.example.controller

package org.example.controller

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

所以...

结论:在命名空间的基本包中定义Spring Boot应用程序确实被鼓励使Spring Boot配置尽可能简单。


答案 2

推荐