最近接手一套基于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>
发表回复