【问题标题】:@SpringBootApplication and @ComponentScan not working together (bean configuration)@SpringBootApplication 和 @ComponentScan 不能一起工作(bean 配置)
【发布时间】:2018-12-26 21:30:50
【问题描述】:

我有一个多模块项目,但我的配置有问题。 我在包 nl.example.hots.boot 中有一个我的主要方法

@SpringBootApplication
@EnableAutoConfiguration
@ComponentScan(basePackages = {"nl.*"})
@EntityScan("nl.*")
public class HotsApplication {

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

在 nl.example.hots.core.* 包中我有这个类:

@Service
@AllArgsConstructor
@Transactional(propagation = Propagation.REQUIRED)
public class MapImportService {

    private MapInputModelMapper mapInputModelMapper;
    private MapEntityRepository mapEntityRepository;

    public void add(final MapInputModel mapInputModel) {
        System.out.println(mapInputModel.getName());
        mapEntityRepository.save(mapInputModelMapper.mapToEntiy(mapInputModel));
    }

和:

@Component
@Mapper
public interface MapInputModelMapper {

    MapInputModel mapToInputModel(final MapEntity n);

    MapEntity mapToEntiy(final MapInputModel n);

}

存储库位于 nl.example.hots.persistence.* 包中

运行应用程序时出现以下错误:

Description:

Parameter 0 of constructor in nl.example.hots.core.dataimport.MapImportService.MapImportService required a bean of type 'nl.timonschultz.hots.core.map.mapper.MapInputModelMapper' that could not be found.


Action:

Consider defining a bean of type 'nl.example.hots.core.map.mapper.MapInputModelMapper' in your configuration.

当我删除 @EnableAutoConfiguration 和 @ComponentScan 注释时,它可以工作。应用程序启动时没有 bean 错误。

在这种情况下,但是我的 restcontroller 不再工作了:

{
    "timestamp": "2018-07-18T20:48:39.414+0000",
    "status": 404,
    "error": "Not Found",
    "message": "No message available",
    "path": "/maps"
}

当我删除 MapImportService 类(并且没有弹出 bean 错误)时,它在同一个 url 上工作。

package nl.example.hots.api.data_import;

@RestController
@AllArgsConstructor
public class ImportController {

    private static final String URL = // a url
    private Reader reader;

    @RequestMapping("/maps")
    public String abilityStreamImport() {
        reader.readStream(URL); // calls a class in nl.example.hots.core.*
        return "Greetings from APP!";
    }
}

我尝试了几种不同的组合,并且我有一个不同的项目作为示例,其中注释一起使用并且可以正常工作。有人可以解释为什么注释一起使用时会产生 bean 错误吗?以及为什么控制器只使用@SpringBootApplication 时会报错?

在我的 Pom.XML 中,引导模块依赖于 API 层,该 API 层依赖于核心层,而核心层又依赖于持久层。 我在项目中使用了 mapstruct 和 Lombok。

--- 编辑:存储库---

@Entity(name = "MAPS")
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Getter
public class MapEntity extends HasId<Long> {

    private String name;

    @ElementCollection
    private List<String> translations;

}

@Repository
public interface MapEntityRepository extends JpaRepository<MapEntity, Long> {
}

@MappedSuperclass
public abstract class HasId<T> {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Setter
    @Getter
    private T id;
}

项目结构:

hots-application;
- hots-api
    - pom.xml
    - nl.example.hots.api.dataimport.ImportController;
- hots-boot
    - pom.xml
    - nl.example.hots.boot.HotsApplication;
- hots-core
    - pom.xml
    - nl.example.hots.core.dataimport.mapImportService.MapImportService;
    - nl.example.hots.core.map.mapper.MapInputModelMapper
- hots-persistence
    - pom.xml
    - nl.example.hots.persistence.common.HasId;
    - nl.example.hots.persistence.map.MapEntity;
    - nl.example.hots.persistence.map.MapEntityRepository;
pom.xml

【问题讨论】:

  • 不确定是否可以在basePackages 中使用“*”。据我记得,您需要提供多个基本包,例如basePackages={"a.b.c", "d.e.f"}
  • 或者在这种情况下,只需使用"nl"
  • 试过了:@ComponentScan(basePackages = {"nl.example.hots.api", "nl.example.hots.core", "nl.example.hots.boot", "nl .example.hots.persistence"}) 以及“nl”但结果相同。 @ComponentScan(basePackages = {"nl.*"}) 在我使用的另一个示例项目中工作正常。
  • 仅使用 @SpringBootApplication 时会出现错误,因为默认情况下它会扫描带注释的类的包(在您的情况下为 HotsApplication)。我通常将我的主类放在包层次结构的根部,否则你需要指定 ComponentScan
  • 将您的 HotsApplication 移动到 nl.example.hots 并仅保留 @SpringBootApplication 注释。这样扫描时会自动覆盖所有子包。 Spring Boot 将扫描 @SpringBootApplication 注释类所在的包和子包。您在 boot 子包中,因此它不会扫描任何内容,这意味着您基本上必须手动配置所有内容,而不是能够使用Spring Boot 自动配置特性。

标签: java spring-boot


【解决方案1】:

Spring Boot 将扫描所有以 @SpringBootApplication 注释类所在的包开头的包和子包。您的类在 nl.example.hots.boot 中仅扫描该包。其他类位于不同的、未扫描的包中。

由于这种包结构并且不遵循best practices,您基本上失去了很多自动配置功能(Spring Data JPA、ReST 等),您必须求助于手动启用/配置它。部分通过添加额外的@ComponentScan 注释,对于JPA 的@EntityScan 注释。但是您还需要添加所有 @EnableJpaRepository 等注释,因为不再添加这些注释(至少没有使用正确的包)。

修复相当简单。将您的 @SpringBootApplication 注释类移动到 nl.example.hots 包(如 best practices 中所述)。删除除@SpringBootApplication 之外的注释,然后简单地启动您的应用程序。

【讨论】:

  • 这看起来很有希望。我现在正在工作,要真正测试它是否有效有点困难,但我会在今天晚些时候回家时检查。但是,我很好奇为什么注释在我的项目中似乎不能一起工作,而在我使用的示例项目中它们似乎确实(一起)工作。
  • 它们确实协同工作,但问题是您基本上缺少一些额外的注释。您还应该需要带有基本包的@EnableJpaRepositories,并且可能还需要@EnableRestRepositories(或任何注释)。这完全是因为这些通常使用基本包配置 Spring Boot,但这里的基本包是(对于所有nl.example.hots.boot 导致无法检测到您的 jpa 存储库等)。
  • 好的,谢谢!今天晚些时候我会再试一次,我会在这里返回我的发现。
【解决方案2】:

您的@AllArgsConstructor 上缺少@Autowired 注释,请尝试:

@AllArgsConstructor(onConstructor = @__(@Autowired))

【讨论】:

  • 谢谢,我试过了,还删除了 AllArgsConstructor 并使用 Autowired 注释 MapInputModelMapper 和 MapEntityRepository。结果相同。
【解决方案3】:

经过反复试验,我设法让它大部分工作。 我将 scanBasePackages = "nl" 添加到 SpringBootApplication 注释中。

Mapstruct 确实给我带来了一些麻烦,但是当我删除它并自己制作一个快速映射器时,它一切正常。 感谢所有帮助和建议以及阅读链接!对于我的下一个项目,我会牢记(最佳实践)建议。 下面是我现在的主要课程

package nl.example.hots.boot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@SpringBootApplication(scanBasePackages = "nl")
@EntityScan("nl.*")
@EnableJpaRepositories("nl.*")
public class HotsApplication {

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

【讨论】:

    【解决方案4】:

    Spring 没有创建 MapInputModelMapperBean。我从未使用过 MapStruct,但它让我想起了 mybatis。你用@Mapper 和@Component 注释MapInputModelMapperBean 对我来说看起来很奇怪。 spring 应该如何创建 bean?您是否正在使用某种执行此魔法的 spring-boot-mapstruct 包?

    我快速搜索并找到了这个https://www.credera.com/blog/technology-solutions/mapping-domain-data-transfer-objects-in-spring-boot-with-mapstruct/,您似乎需要这样的东西:

    @Mapper(componentModel = "spring")
    public interface MapInputModelMapper
    

    我猜这会生成带有 @Component 或类似的映射器的实现。

    【讨论】:

    • 我认为问题不在于 Mapstruct,而在于组件扫描。我尝试了你的建议,但我得到了同样的错误。当我从 MapImportService 类中删除 MapInputModelMapper 并只保留存储库(不使用 mapstruct)时,我得到了同样的错误(但随后它抱怨 MapEntityRepository)。
    • 你能把仓库的代码贴出来吗?也许是项目的包层次结构。
    • 你有一个@EnableJpaRepositories 覆盖适当的包吗?
    • 我以前做过,但我在试错过程中删除了它,所以我不能确定它在我之前尝试过的时候还在那里。今天晚些时候我会再次检查它(现在正在工作)。谢谢!
    最近更新 更多