【问题标题】:Scan components of different maven modules/JARs in a Spring Boot application在 Spring Boot 应用程序中扫描不同 maven 模块/JAR 的组件
【发布时间】:2017-01-02 16:12:52
【问题描述】:

我有两个 Maven 模块。 第一个名为“application”,包含 spring boot Application 类,其中仅包含以下几行:

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 模块和包 org.example.application 中,我有一个 RestController,它使用一个 Component,它又使用下面描述的另一个 Maven 模块的组件。

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

所以,流程是这样的:

org.example 包中的 Maven 模块 application
SpringBootApplication -> RestController -> MyComponent

并且应该在MyComponent 中自动装配的组件是在包org.example.model 下的model Maven 模块中的组件。

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

***************************
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"}) 但这似乎没有帮助。

那我做错了什么?

【问题讨论】:

    标签: java spring spring-boot


    【解决方案1】:

    您应该想知道的第一件事是:为什么要声明 @ComponentScan@SpringBootApplication 的目标之一是(除其他外)启用组件扫描?
    来自Spring Boot documentation

    @SpringBootApplication 注解等价于使用 @Configuration@EnableAutoConfiguration@ComponentScan 与他们的 默认属性

    请注意,当您在 Spring Boot 应用程序的类上声明 @ComponentScan 以将值指定为 basePackages 时,它会覆盖 @SpringBootApplication 默认使用的 basePackages,即该类所在的当前包居住。因此,要将 Spring Boot Application 类的包和缺少的附加包都作为基础包,您必须显式设置它们。

    除了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 原型注解:@Component@Repositories@Repositories,... 位于 Spring Boot Application 类的同一包或子包中,仅声明 @SpringBootApplication 就是你所需要的。

    2) 不使用提供零配置的 Spring Boot 自动配置的包布局的情况(避免)。

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

    Here is the documentation关于这部分(重点是我的):

    80.3 使用 Spring 数据存储库

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

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

    Spring Boot 尝试猜测您的 @Repository 的位置 定义,基于它找到的@EnableAutoConfiguration。要得到 更多控制,使用 @EnableJpaRepositories 注释(来自 Spring 数据 JPA)。


    示例

    1) 使用包布局的情况(支持),该包布局提供零配置的 Spring Boot 自动配置。

    对于在org.example包中声明的Spring Boot应用程序,以及在同一包或org.example的子包中声明的所有bean类(包括Repositories),以下声明对于Spring Boot应用程序就足够了:

    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 自动配置的包布局的情况(避免)。

    对于在org.example.application 包中声明的 Spring Boot 应用程序,而不是在同一包或 org.example.application 的子包中声明的所有 bean 类(包括存储库),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);
        }
    }
    

    bean 类可能如下所示。

    可能来自外部 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 配置尽可能简单。

    【讨论】:

    • 我也试过了,但我得到了同样的错误。所以我然后假设组件扫描是在同一个包中进行的在同一个 JAR/Maven 模块中。我想让 SpringBootApplication 扫描同一包中的组件但另一个 JAR/maven模块。
    • 组件扫描在运行时执行。无论类驻留在哪里,如果它在类路径中,它应该可以工作。你可以发布项目的 pom.xml 吗?
    • 啊,我现在找到了解决方案。在我上面的帖子中,我稍微混淆了代码,SpringBootApplication 类实际上位于org.example.application 包中。如果我将SpringBootApplication 移动到包org.example 中,它就可以工作。但是我仍然不明白为什么当我显式进行组件扫描以扫描org.example 下的所有内容时它不能在org.example.application 下,例如@ComponentScan({"org.example.model", "org.example"})
    • 太棒了 :) 它与@SpringBootApplication 无关,因为您没有指定scanBasePackages 属性,因此导致默认扫描包到当前包org.example.application?您可以按照我的回答使用@SpringBootApplication(scanBasePackages={"org.example"}) 再试一次吗?并删除多余的@ComponentScan。它应该可以工作。
    • 如果添加@ComponentScan,那么这里指定的包将仅适用于Spring bean的组件扫描,而不适用于Spring Data存储库。如果您想为此指定包,则必须在包中添加适当的@Enable*Repositories,或者只需将 scanBasePackages 属性添加到 SpringBootApplication,它适用于组件扫描、Spring 数据存储库、实体扫描等。
    猜你喜欢
    • 2018-08-23
    • 2019-03-26
    • 2015-12-12
    • 2019-10-18
    • 2017-02-09
    • 2020-12-03
    • 2017-01-26
    • 2013-12-03
    相关资源
    最近更新 更多