关于java:SpringBoot扫描不到组件给你提供几种方案

最近接手一套基于SpringBoot我的项目,对我的项目进行重构调整,将公共局部抽离成子项目。在实际的过程中,发现抽离之后的模板中组件并没有被初始化。于是将排查解决过程中收集到的计划及常识汇总分享给大家。

问题起因

问题的起因很简略,因多套零碎的package命名不统一。比方业务零碎的包命名为com.abc.xx,而公共(common)局部的包命名为com.efg.xx,引入公共jar包时默认是无奈初始化的。

对于SpringBoot我的项目,咱们晓得扫描的门路从启动类所在包开始,扫描以后包及其子级包下的所有文件。上图如果启动类在com.abc包下,必定是无奈扫描到com.def包内的组件的。

场景延长

SpringBoot的这个机制还延长出另外两个场景。

第一个场景是如果SpringBoot的启动类放的包门路靠下,那么在它下级目录中的组件是无奈被扫描并初始化的。老手往往会因放错地位导致启动时异样。

第二个场景是成心将一些不须要纳入SpringBoot容器的类放在其余包中,防止被SpringBoot容器加载。当然此时也能够应用ComponentScan来指定排除对应的包。

├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── secbro
│   │   │           ├── SpringBootMainApplication.java
│   │   │           ├── controller
│   │   │           │   └── DruidController.java
│   │   │           ├── model
│   │   │           │   └── Order.java
│   │   │           └── service
│   │   │               ├── OrderService.java
│   │   │               └── impl
│   │   │                   └── OrderServiceImpl.java

上述我的项目构造中,如果将类间接放在com目录或com目录的其余子目录下,默认是不会被初始化的。

通过@ComponentScan扫描

回到正题,遇到相似不被初始化的状况,咱们能够应用的最简略的计划就是手动指定扫描包门路。

在启动类上的@SpringBootApplication注解外部集成了@ComponentScan注解。此时咱们能够显示的指定扫描的包。

@SpringBootApplication
@ComponentScan({"com.abc.xx","com.def.xx"})
public class SpringBootMainApplication {

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

}

此种用法肯定要先蕴含本我的项目要扫描的门路“com.abc.xx”,而后再在前面增加上common我的项目要扫描的门路“com.def.xx”。

如果其余我的项目不须要初始化common中的内容,则可不进行指定。

自定义@Enable**注解

上述办法尽管可能解决问题,但如果间接写包名,不免没有个对立的标准。此时可思考应用@Enable类型的注解。

理解SpringBoot机制的敌人都晓得,最重要的一个注解便是@EnableAutoConfiguration。相似的,咱们定义一个能够通过注解之后便可应用的Enable注解。

定义配置类,在配置类中指定要扫描的包门路:

@Component
@ComponentScan("com.def.xx")
public class CommonConfig {
}

定义Enable注解类,并通过@Import导入配置类:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CommonConfig.class)
public @interface EnableCommon {
}

而后,在启动类中便可应用@EnableCommon此注解来指定实例化对应的package。

@EnableCommon
@SpringBootApplication
public class SpringBootMainApplication {
// ...
}

在此过程中须要留神的是CommonConfig是位于common我的项目当中的。如果CommonConfig间接可被SpringBoot扫描到,那也就不须要EnableCommon注解了。

自定义starter

咱们应用SpringBoot之所以不便,得益于它的个性之一便是能够应用曾经集成好的starter。同样,咱们也能够自定义一套starter来达到自动化配置的成果。

因为这种模式更实用于自动化集成某一个组件,并不太适宜这里说的common公共我的项目。因而就不再代码演示,只说一下大略的思路。具体实例可参考我的新书《SpringBoot技术底细:架构设计与实现原理》。

定义starter首先须要依赖主动配置的组件,也就是pom文件中增加如下配置:

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-autoconfigure</artifactId>
  </dependency>
</dependencies>

而后再定义具体的服务(或初始化)类,比方HelloWorldService以及该服务类初始化的参数类HelloWorldProperties。通过@ConfigurationProperties注解能够将Application中对应的属性初始化到类的属性中。

而后呢,再提供一个基于@ConditionalOnClass配置的HelloWorldAutoConfiguration类,指定当HelloWorldService存在于类门路时,便会进行初始化。

最初一步,在META-INF目录下创立spring.factories,启动增加相似如下配置:

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.secbro.HelloWorldAutoConfiguration

该类是为SpringBoot提供的扫描入口。

此时,当其余我的项目须要该starter时,间接引入便可注入应用HelloWorldService类了。

对于此处倡议大家专门看一篇相干的实战文章,能够更好的了解。这里只提供了一个大略的思路。

小结

对于SpringBoot的@ComponentScan基本上曾经能够满足需要了,第二种计划是基于@ComponentScan的改良计划。而第三种计划更多的是基于SpringBoot的外围原理来解决的。当然最好是防止同一个我的项目应用多个顶级package。

通过本篇文章的脉络,咱们能够看到一种学习的形式,通过一个知识点或一个实战中的问题,能够逐渐将常识从点裁减到面,这样不仅能加大学习的范畴,也能构建更牢固的常识图谱。

<center>程序新视界:精彩和成长都不容错过</center>

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理