乐趣区

关于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>

退出移动版