SpringBoot扫描不到组件?给你提供几种方案

前言最近接手一套基于SpringBoot项目 , 对项目进行重构调整 , 将公共部分抽离成子项目 。 在实践的过程中 , 发现抽离之后的模板中组件并没有被初始化 。 于是将排查解决过程中搜集到的方案及知识汇总分享给大家 。
问题原因问题的原因很简单 , 因多套系统的package命名不一致 。 比如业务系统的包命名为com.abc.xx , 而公共(common)部分的包命名为com.efg.xx , 引入公共jar包时默认是无法初始化的 。
SpringBoot扫描不到组件?给你提供几种方案文章插图
对于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【SpringBoot扫描不到组件?给你提供几种方案】上述项目结构中 , 如果将类直接放在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注解 。
SpringBoot扫描不到组件?给你提供几种方案文章插图
定义配置类 , 在配置类中指定要扫描的包路径:
@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@SpringBootApplicationpublic class SpringBootMainApplication {// ...}在此过程中需要注意的是CommonConfig是位于common项目当中的 。 如果CommonConfig直接可被SpringBoot扫描到 , 那也就不需要EnableCommon注解了 。
自定义starter我们使用SpringBoot之所以方便 , 得益于它的特性之一便是可以使用已经集成好的starter 。 同样 , 我们也可以自定义一套starter来达到自动化配置的效果 。
由于这种模式更适用于自动化集成某一个组件 , 并不太适合这里说的common公共项目 。 因此就不再代码演示 , 只说一下大概的思路 。 详细实例可参考我的新书《SpringBoot技术内幕:架构设计与实现原理》 。
定义starter首先需要依赖自动配置的组件 , 也就是pom文件中添加如下配置:
org.springframework.bootspring-boot-autoconfigure然后再定义具体的服务(或初始化)类 , 比如HelloWorldService以及该服务类初始化的参数类HelloWorldProperties 。 通过@ConfigurationProperties注解可以将Application中对应的属性初始化到类的属性中 。
然后呢 , 再提供一个基于@ConditionalOnClass配置的HelloWorldAutoConfiguration类 , 指定当HelloWorldService存在于类路径时 , 便会进行初始化 。