【问题标题】:Spring @ComponentScan not detecting REST controllerSpring @ComponentScan 未检测到 REST 控制器
【发布时间】:2019-11-11 04:59:36
【问题描述】:

在 StackOverflow 上搜索了各种类似的问题后,我确定大多数遭受此问题的人都没有正确扫描他们的控制器所在的模块。一些解决方案要求将要扫描的文件组合到与应用程序相同的模块中(有效),但我不想移动我的任何 .java 文件。相反,我想让@ComponentScan 工作。

我有以下项目结构

Project
      |
      settings.gradle
      build.gradle
      module1
            |
            src/main/java/com.test.application
            |                                |
            |                                Application.java
            |                                SwaggerConfig.java
            build.gradle
      module2
            |
            src/main/java/com.test.service
            |                            |
            |                            Service.java
            |                            Controller.java
            build.gradle

Application.java

@SpringBootApplication
@ComponentScan("com.test")
public class Application {

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }
}

请注意,我在组件扫描中扫描com.test。我在 @ComponentScan documentation(或 StackOverflow 上)中找不到任何表明我错误地使用了 @ComponentScan 的内容。

Controller.java

@RestController
@RequestMapping("/test")
public class GearParsingController {
  private SomeService someService;

  @Autowired
  public SomeController(SomeService someService) {
    this.someService = someService;
  }

  @GetMapping("/path")
  public ResponseEntity<String> getSomeService() {
    return new ResponseEntity<String>("Default message", HttpStatus.OK);
  }
}

项目 build.gradle

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:2.1.6.RELEASE")
    }
}

subprojects {
    repositories {
        mavenCentral()
    }

    apply plugin: 'java'
    apply plugin: 'idea'
    apply plugin: 'org.springframework.boot'
    apply plugin: 'io.spring.dependency-management'
}

module1 build.gradle

dependencies {
    implementation project(':module2')

    implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '2.1.6.RELEASE'
}

module2 build.gradle

dependencies {
    implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '2.1.6.RELEASE'

    testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.5.0-RC2'
}

当我尝试在localhost:8080/test/path 访问我的端点时,我遇到了:

{
    "timestamp": "2019-06-29T19:19:52.275+0000",
    "status": 404,
    "error": "Not Found",
    "message": "No message available",
    "path": "/test/path"
}

在调试模式下启动程序时,我得到以下输出:

2019-06-29 13:58:58.345  INFO 8272 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2019-06-29 13:58:58.387  INFO 8272 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2019-06-29 13:58:58.387  INFO 8272 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.21]
2019-06-29 13:58:58.522  INFO 8272 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2019-06-29 13:58:58.522 DEBUG 8272 --- [           main] o.s.web.context.ContextLoader            : Published root WebApplicationContext as ServletContext attribute with name [org.springframework.web.context.WebApplicationContext.ROOT]
2019-06-29 13:58:58.522  INFO 8272 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1786 ms
2019-06-29 13:58:58.544 DEBUG 8272 --- [           main] o.s.b.w.s.ServletContextInitializerBeans : Mapping filters: characterEncodingFilter urls=[/*], hiddenHttpMethodFilter urls=[/*], formContentFilter urls=[/*], requestContextFilter urls=[/*]
2019-06-29 13:58:58.545 DEBUG 8272 --- [           main] o.s.b.w.s.ServletContextInitializerBeans : Mapping servlets: dispatcherServlet urls=[/]
2019-06-29 13:58:58.583 DEBUG 8272 --- [           main] o.s.b.w.s.f.OrderedRequestContextFilter  : Filter 'requestContextFilter' configured for use
2019-06-29 13:58:58.583 DEBUG 8272 --- [           main] .s.b.w.s.f.OrderedHiddenHttpMethodFilter : Filter 'hiddenHttpMethodFilter' configured for use
2019-06-29 13:58:58.583 DEBUG 8272 --- [           main] s.b.w.s.f.OrderedCharacterEncodingFilter : Filter 'characterEncodingFilter' configured for use
2019-06-29 13:58:58.584 DEBUG 8272 --- [           main] o.s.b.w.s.f.OrderedFormContentFilter     : Filter 'formContentFilter' configured for use
2019-06-29 13:58:58.896 DEBUG 8272 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : 5 mappings in 'requestMappingHandlerMapping'
2019-06-29 13:58:58.990  INFO 8272 --- [           main] pertySourcedRequestMappingHandlerMapping : Mapped URL path [/v2/api-docs] onto method [public org.springframework.http.ResponseEntity<springfox.documentation.spring.web.json.Json> springfox.documentation.swagger2.web.Swagger2Controller.getDocumentation(java.lang.String,javax.servlet.http.HttpServletRequest)]
2019-06-29 13:58:59.045 DEBUG 8272 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Patterns [/**/favicon.ico] in 'faviconHandlerMapping'
2019-06-29 13:58:59.128  INFO 8272 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2019-06-29 13:58:59.146 DEBUG 8272 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : ControllerAdvice beans: 0 @ModelAttribute, 0 @InitBinder, 1 RequestBodyAdvice, 1 ResponseBodyAdvice
2019-06-29 13:58:59.257 DEBUG 8272 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Patterns [/webjars/**, /**] in 'resourceHandlerMapping'
2019-06-29 13:58:59.263 DEBUG 8272 --- [           main] .m.m.a.ExceptionHandlerExceptionResolver : ControllerAdvice beans: 0 @ExceptionHandler, 1 ResponseBodyAdvice
2019-06-29 13:58:59.442  INFO 8272 --- [           main] d.s.w.p.DocumentationPluginsBootstrapper : Context refreshed
2019-06-29 13:58:59.481  INFO 8272 --- [           main] d.s.w.p.DocumentationPluginsBootstrapper : Found 1 custom documentation plugin(s)
2019-06-29 13:58:59.519  INFO 8272 --- [           main] s.d.s.w.s.ApiListingReferenceScanner     : Scanning for api listing references

所以,我的问题是:为什么 ComponentScan 没有检测到我的 REST 控制器?

【问题讨论】:

  • 这些单独的“文件夹”看起来像单独的模块/jar,并且您不会显示从启动器到实现的依赖关系。 (请注意,将org.springframework.web 上的日志记录设置为调试,您将在启动时获得每个 Web 映射的列表。)
  • @chrylis folder1 的 build.gradle 依赖于 folder2 模块。我已经更新了我的问题,以使这一点更清楚。如果这不是你的意思,请告诉我。
  • 是的,这就是我的意思。打开您的调试日志(您可以通过在调试模式下启动应用程序来获得,如--debug 或您的 IDE 设置)以查看有关 MVC 注册的信息。
  • @buratino 只是为了确保检查 com/test/application 被声明为包而不是名称为“com.test.application”的单个包
  • @chrylis 我已将输出添加到我的帖子中。

标签: java spring spring-boot gradle


【解决方案1】:

我认为如果您想从“com.test”包中获取控制器,请使用 @ComponentScan 的 basePackages 选项:

@ComponentScan(basePackages = "com.test")

【讨论】:

  • 这相当于我使用的语法,根据 ComponentScan 的 documentation如果不需要其他属性,则允许更简洁的注释声明 - 例如,@ComponentScan("org .my.pkg") 而不是 @ComponentScan(basePackages = "org.my.pkg")。
  • 我注意到的另一件事是,您的主 Application.java 类与所有其他组件位于类似的包层次结构中 - src/main/java/com.test.application & src/main/java /com.test.service。如果您将 Application.java 移动到上面的一个包,则更有可能正确扫描:例如,将您的 Application.java 放入包中:src/main/java/com/test
  • 我希望保持包层次结构不变。我会在我的问题中更新它。
【解决方案2】:

您的主应用程序Application.java 位于com.test.application 目录下,这意味着当springboot 应用程序默认启动时,@SpringBootApplication 注释将在packgae com.test.application 下查找bean 和配置。在springbootdocumentation中指定我们应该将主应用程序放在根包下,以便spring扫描它下面的所有内容。

@SpringBootApplication 是一个方便的注解,它添加了所有 以下:

  • @Configuration 将类标记为应用程序上下文的 bean 定义源。
  • @EnableAutoConfiguration 告诉 Spring Boot 根据类路径设置、其他 bean 和各种属性开始添加 bean
    设置。
  • 通常您会为 Spring MVC 应用程序添加 @EnableWebMvc,但 Spring Boot 在看到 spring-webmvc 时会自动添加它 类路径。这会将应用程序标记为 Web 应用程序,并且
    激活关键行为,例如设置 DispatcherServlet。
  • @ComponentScan 告诉 Spring 在 hello 包中查找其他组件、配置和服务,使其能够
    找到控制器。

因此,要解决您的问题,请尝试将您的主应用程序移至包的根目录 com.test 或者您可以使用注释 @ComponentScan 指定需要扫描的所有包,例如:

@ComponentScan(basePackages = "com.test")

【讨论】:

  • @buratino 您可以尝试将您的主应用程序移动到您的 packgae 的根目录吗?还要验证你的 build.gradle 配置是否正确,以便模块 2 也被编译。
  • 将主应用程序移动到模块 2 工作得很好。但是,我想将主应用程序保留在 module1 中并改用组件扫描。
  • 尝试将 SpringBootApplication 注解替换为 EnableAutoConfiguration 和 Configuration。也许来自 SpringBootApplication 的 ComponentScan 覆盖了您编写的 ComponentScan。
  • @buratino 将主应用程序移至模块 2 中的 com.test 尝试将其移至模块 1 中的 com.test 本身应该可以工作,但这并不意味着您的应用程序结构会发生巨大变化.
【解决方案3】:

以这种方式制作文件:

Project build.gradle(将 spring 插件应用程序移除到所有模块项目)

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:2.1.6.RELEASE")
    }
}

subprojects {
    repositories {
        mavenCentral()
    }

    apply plugin: 'java'
    apply plugin: 'idea'
}

module1 build.gradle(应用 spring 插件和 bootJar 目标)

dependencies {
    implementation project(':module2')

    implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '2.1.6.RELEASE'
}

apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

bootJar {
    baseName = 'module1'
    version = '0.0.1-SNAPSHOT'
}

module2 build.gradle(添加 jar 目标)

dependencies {
    implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '2.1.6.RELEASE'
}

jar {
    baseName = 'module2'
    version = '0.0.1-SNAPSHOT'
}

settings.gradle

include 'module1'
include 'module2'

重点是我们希望将第二个模块构建为普通 jar,并将主模块构建为 spring boot runnable。使用该设置只需 gradle build,然后是主项目文件夹中的 gradle bootRun

【讨论】:

  • 在项目中添加其他不同控制器的模块时,如何扩展此方案?
  • module2一样添加就行了,有问题吗?
  • 类似问题,但使用 maven,没有 gradle。您是说我们不能将 springboot 用作非应用程序模块的依赖项?需要底层的 spring-web/data/core 依赖吗?是否没有注释告诉spring检查哪些包来检查RestControllers?
  • 不,在其他模块中使用这些依赖项应该没有问题,我到处都这样做。然后,您可以使用 ComponentScan、Import 或其他可能的选项 - 关于它的好文章 -> reflectoring.io/spring-boot-modules
【解决方案4】:

根据 Spring Boot 约定,控制器必须在 com.test.controller 包中。

【讨论】:

  • 你能提供你的来源吗?
  • 你的意思是我所说的事实的例子还是来源?如果是第二个,那只是我的经验。我也有几次同样的问题。
猜你喜欢
  • 1970-01-01
  • 2019-01-25
  • 1970-01-01
  • 1970-01-01
  • 2020-03-22
  • 1970-01-01
  • 1970-01-01
  • 2016-02-08
  • 1970-01-01
相关资源
最近更新 更多