我有两个Maven模块。第一个称为“应用程序”,包含spring boot仅包含以下行的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模块和程序包中org.example.application,我有一个RestController使用Component,又使用了下面描述的其他Maven模块的组件。
org.example.application
RestController
Component
其他的Maven模块称为“模型”,其中包含spring boot组件(原始存储库,实体等)。所有这些类都与第一个Maven模块(org.example)具有相同的包结构,但位于其子包中,例如org.example.model.entities,org.example.model.repositories等等。
org.example
org.example.model.entities
org.example.model.repositories
因此,流程如下所示:
application包 org.example中的 Maven模块: SpringBootApplication -> RestController -> MyComponent
application
SpringBootApplication -> RestController -> MyComponent
并且应该自动接线MyComponent的model组件是包装下Maven模块中的组件org.example.model。
MyComponent
model
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类找不到!
org.example.model.repositories.MyRepository
如您所见,我尝试将扫描组件明确定义为: @ComponentScan({"org.example.model", "org.example"})但这似乎无济于事。
@ComponentScan({"org.example.model", "org.example"})
那我做错了什么?
您应该想知道的第一件事是:为什么要声明(其中@ComponentScan之一)的目的之一@SpringBootApplication是启用组件扫描? 从Spring Boot文档中:
@ComponentScan
@SpringBootApplication
该@SpringBootApplication注解相当于使用 @Configuration,@EnableAutoConfiguration并@ComponentScan与他们的默认属性
@Configuration
@EnableAutoConfiguration
请注意,在Spring Boot应用程序的类上时,您声明@ComponentScan将值指定为basePackages,它将覆盖basePackages默认使用的值,@SpringBootApplication因为默认值是该类所在的当前包。因此,要同时具有Spring Boot Application类的软件包和缺少的其他软件包作为基本软件包,必须显式设置它们。
basePackages
除了basePackages是递归的。因此,要对位于"org.example"和"org.example.model"包中的类都启用扫描,只需指定它的子包"org.example"就足够"org.example.model"了。
"org.example"
"org.example.model"
尝试:
@SpringBootApplication(scanBasePackages={"org.example"})
或者:
@SpringBootApplication @ComponentScan("org.example")
scanBasePackages时?
在设计Spring Boot应用程序布局时,有两种情况:
1)(首选)使用软件包布局的情况,该布局为Spring Boot的自动配置提供零配置。
总结:如果你用的Spring bean注解的类定型:@Component,@Repositories,@Repositories,…都位于同一个包或者Spring引导应用程序类的子包,仅声明 @SpringBootApplication是你所需要的。
@Component
@Repositories
2)在这种情况下(为避免),您不使用将Spring Boot的自动配置提供零配置的程序包布局。
通常,这意味着您要扫描的候选类不在用注释的类的包(或子包)中@SpringBootApplication。 在这种情况下,您可以添加scanBasePackages属性或添加@ComponentScan以指定要扫描的软件包。 但是,此外,如果您的存储库不在您的类的包或子包中,并带有注释@SpringBootApplication,则必须声明其他内容,例如:@EnableJpaRepositories(="packageWhereMyRepoAreLocated")
scanBasePackages
@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)。
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)。
在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软件包中,例如:
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包装中:
org.example.controller
package org.example.controller; @RestController @RequestMapping("/api/foos") public class FooController {...}
所以…
在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包中,例如:
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>, { }
package org.example.controller @RestController @RequestMapping("/api/foos") public class FooController {...}
结论 :强烈建议在名称空间的基本包中定义Spring Boot应用程序,以使Spring Boot配置尽可能简单。