一尘不染

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

spring-boot

我有两个Maven模块。第一个称为“应用程序”,包含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组件(原始存储库,实体等)。所有这些类都与第一个Maven模块(org.example)具有相同的包结构,但位于其子包中,例如org.example.model.entitiesorg.example.model.repositories等等。

因此,流程如下所示:

applicationorg.example中的 Maven模块:
SpringBootApplication -> RestController -> MyComponent

并且应该自动接线MyComponentmodel组件是包装下Maven模块中的组件org.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"})但这似乎无济于事。

那我做错了什么?


阅读 271

收藏
2020-05-30

共1个答案

一尘不染

您应该想知道的第一件事是:为什么要声明(其中@ComponentScan之一)的目的之一@SpringBootApplication是启用组件扫描?
Spring Boot文档中

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

请注意,在Spring
Boot应用程序的类上时,您声明@ComponentScan将值指定为basePackages,它将覆盖basePackages默认使用的值,@SpringBootApplication因为默认值是该类所在的当前包。因此,要同时具有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引导应用程序类的子包,仅声明
@SpringBootApplication是你所需要的。

2)在这种情况下(为避免),您不使用将Spring Boot的自动配置提供零配置的程序包布局。

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

这是有关此部分的文档(重点是我的):

80.3使用Spring数据仓库

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

对于许多应用程序,您需要做的就是在类路径上放置正确的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的自动配置提供零配置。

org.example包中声明了Spring
Boot应用程序,并且在Bean的同一包或子包中声明了所有bean类(包括存储库)org.example之后,对于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配置尽可能简单。

2020-05-30