简体   繁体   English

Spring Boot @ComponentScan 无法通过多模块项目中的自定义注释工作

[英]Spring Boot @ComponentScan not working via custom annotation in multi module project

I have a multi module project (gradle) with following structure:我有一个具有以下结构的多模块项目(gradle):

Root project 'mp-search'
+--- Project ':analyzer'
+--- Project ':common'
|    \--- Project ':common:es-model'
...

Description:描述:

  • :analyzer : contains a spring boot application :analyzer : 包含一个 Spring Boot 应用程序
    • Contains @SpringBootApplication and all other needed dependencies (Web, Feign, etc.)包含 @SpringBootApplication 和所有其他需要的依赖项(Web、Feign 等)
  • :common:es-model : contains models + repositories for Spring Data Elasticsearch :common:es-model : 包含 Spring Data Elasticsearch 的模型 + 存储库
    • Contains only the "spring-boot-starter-data-elasticsearch" dependency (no @SpringBootApplication)仅包含“spring-boot-starter-data-elasticsearch”依赖项(无 @SpringBootApplication)

Say I have the following classes in :common:es-model with package com.example.esmodel.document.model :假设我在:common:es-model中有以下类,包com.example.esmodel.document.model

package com.example.esmodel.document.model;

//imports

@org.springframework.data.elasticsearch.annotations.Document(indexName = "document")
public class Document {
    @Id
    private String documentId;

    @Field
    private String content;
// Getter + Setter + Constructor
}
package com.example.esmodel.document.repository;

// imports

@Repository
public interface DocumentRepository extends ElasticsearchRepository<Document, String> {
}

Furthermore I created a configuration class with @ComponentScan to find them此外,我使用@ComponentScan创建了一个配置类来找到它们

package com.example.esmodel.document.configuration;

// Imports

@Configuration
@ComponentScan(basePackages = "com.example.esmodel.document")
public class DocumentConfiguration {
}

And an custom annotation for simple inclusion in the application which imports the configuration:还有一个自定义注释,用于简单地包含在导入配置的应用程序中:

package com.example.esmodel.document;

//Imports

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(DocumentConfiguration.class)
public @interface EnableDocumentModel {
}

I want to use the DocumentRepository in a controller in my application project ( :analyzer ).我想在我的应用程序项目( :analyzer )的控制器中使用DocumentRepository So I want to include it via the EnableDocumentModel annotation like this:所以我想通过这样的EnableDocumentModel注释包含它:

package com.example.analyzer;

import com.example.esmodel.document.EnableDocumentModel;
// Other imports

@SpringBootApplication
@EnableFeignClients
@EnableDocumentModel // [1]
//@Import(DocumentConfiguration.class) // [2]
//@ComponentScan(basePackages = "com.example.esmodel.document") // [3]
public class AnalyzerApplication {
    public static void main(String[] args) {
        SpringApplication.run(AnalyzerApplication.class, args);
    }
}

Of these three tests [1] and [2] are not working.这三个测试 [1] 和 [2] 都不起作用。 The classes ( EnableDocumentModel , DocumentConfiguration ) are found and application tries to start, but fails with a UnsatisfiedDependencyException :找到类( EnableDocumentModelDocumentConfiguration )并且应用程序尝试启动,但失败并出现UnsatisfiedDependencyException

***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of constructor in com.example.analyzer.controller.DocumentController required a bean of type 'com.example.esmodel.document.repository.DocumentRepository' that could not be found.

But test [3], using the same @ComponentScan(basePackages = "com.example.esmodel.document") from DocumentConfiguration in the AnalyzerApplication , works fine.但是测试 [3],使用来自AnalyzerApplicationDocumentConfiguration的相同@ComponentScan(basePackages = "com.example.esmodel.document")工作正常。 But this is not what I desire.但这不是我想要的。

Do I miss something?我错过了什么吗? Any ideas?有任何想法吗?

Thanks in advance!提前致谢!

Edit :编辑

Just to make sure the DocumentConfiguration is considered by spring at all, I added @Import(DocumentRepository.class) in DocumentConfiguration , but it throws an BeanInstantiationException because it's an interface (which is of course reasonable).只是为了确保 Spring 完全考虑DocumentConfiguration ,我在DocumentConfiguration中添加了@Import(DocumentRepository.class) ,但它抛出了BeanInstantiationException因为它是一个接口(这当然是合理的)。

Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.example.esmodel.document.repository.DocumentRepository]: Specified class is an interface
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:70) ~[spring-beans-5.3.19.jar:5.3.19]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1326) ~[spring-beans-5.3.19.jar:5.3.19]
    ... 31 common frames omitted

So Spring definitely considers DocumentConfiguration otherwise the exception wouldn't be thrown.所以 Spring 肯定会考虑DocumentConfiguration否则不会抛出异常。 So somehow the configuration is considered, but @ComponentScan(basePackages = "de.sva.medpower.esmodel.document") doesn't do its job...所以以某种方式考虑了配置,但@ComponentScan(basePackages = "de.sva.medpower.esmodel.document")没有完成它的工作......

I also found a tutorial on medium ( https://medium.com/trendyol-tech/how-to-write-a-spring-boot-library-project-7064e831b63b ) doing it that way, but somehow it doesn't work for me..我还找到了一个关于媒体的教程( https://medium.com/trendyol-tech/how-to-write-a-spring-boot-library-project-7064e831b63b )这样做,但不知何故它不起作用为了我..

@ComponentScan does not trigger Spring Data repository initialization. @ComponentScan不会触发 Spring Data 存储库初始化。 You'd need an @EnableElasticsearchRepositories somewhere in your configuration (ideally on a class in the package that you'd want to scan for ES repositories).您需要在配置中的某处有一个@EnableElasticsearchRepositories (理想情况下,在您想要扫描 ES 存储库的包中的一个类上)。

This is even activated automatically if Boot finds Spring Data Elasticsearch on the execution classpath, but in your case doesn't show any effect as the application's root package is com.example.analyzer (the package that your @SprignBootApplication class resides in).如果 Boot 在执行类路径上找到 Spring Data Elasticsearch,这甚至会自动激活,但在您的情况下,它不会显示任何效果,因为应用程序的根包是com.example.analyzer (您的@SprignBootApplication类所在的包)。

Annotating DocumentConfiguration with @EnableElasticsearchRepositories and point that to the packages that the ES repositories reside in should fix the issue.使用@EnableElasticsearchRepositories注释DocumentConfiguration并将其指向 ES 存储库所在的包应该可以解决问题。

Spring is just a framework, meaning it has some rules based on which it will just parse the code you have written, and convert it into some other code to have more functionalities avoiding boileplate code. Spring 只是一个框架,这意味着它有一些规则,它会根据这些规则解析您编写的代码,并将其转换为其他代码以具有更多功能,从而避免样板代码。

The point however is that spring has some rules based on which it reads your code and makes the conversion.然而,重点是 spring 有一些规则,它会根据这些规则读取您的代码并进行转换。

One of those rules for Spring Boot is that it begins it's search from the package that has the class with the annotation @SpringBootApplication and then automatically scans all other nested packages under the package where this class has been found. Spring Boot的其中一个规则是,它从具有注释@SpringBootApplication的类的包开始搜索,然后自动扫描找到该类的包下的所有其他嵌套包。

So by moving the @ComponentScan outside this default path that the framework will start searching for usuful metadata, you are actually hiding this meta information from spring.因此,通过将@ComponentScan移到框架将开始搜索常用元数据的默认路径之外,您实际上是在向 spring 隐藏此元信息。

Also by creating your own annotation @EnableDocumentModel , you are not changing how spring is parsing the code of your project.此外,通过创建自己的注释@EnableDocumentModel ,您不会改变 spring 解析项目代码的方式。 So even if with your custom annotation you point to some other class, java annotations will be considered by java compiler and not by spring framework when doing the parsing.因此,即使使用自定义注释指向其他类,java 编译器也会在解析时考虑 java 注释,而不是 Spring 框架。

You have to place @ComponentScan somewhere, where by default the framework will initially search and when it catches this metadata, it will be able to continue the search in some other packages as well.您必须将@ComponentScan放置在某个地方,默认情况下框架将在该位置进行初始搜索,当它捕获此元数据时,它也能够继续在其他一些包中进行搜索。 But this information contained in @ComponentScan must be in the default path that spring framework searches, meaning the package where the @SpringBootApplication is to be found or some other nested to this package.但是@ComponentScan中包含的这些信息必须在 spring 框架搜索的默认路径中,这意味着要找到@SpringBootApplication的包或其他嵌套到该包的包。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM