在使用Spring Boot开发后端应用程序时,很多时候我们使用四层架构来完成对单体应用程序的开发。虽然四层架构在SSM单体应用程序中能够很清晰明了地划分每一层,从数据到功能,最后到API,但是随着我们单体项目功能的增加,项目仍然会变得更加臃肿,比如说当我们再打开很久之前的项目,或者接手其它项目时,看到侧边栏一长条的xxxDAO
或者xxxService
时,很多时候一时半会也是缓不过神的:
当然,这仅仅是一个还未开发完成的小型单体项目,如果功能再复杂一点,那么其臃肿程度我们将无法想象,也使得我们继续开发、维护变得困难。
这时我们可以对项目分模块,即按照功能拆分为多个Maven模块,然后可以通过依赖或者配置的方式将多个功能集成在主要模块上,甚至还可以控制是否启用某个功能。
需要注意的是,这里的应用拆分并不是把应用拆分成Spring Cloud分布式微服务多模块,而是仅对一个单体项目而言,它仍然是单体项目,但是每一个功能放在每个模块中,而不再是所有功能放在一个Spring Boot工程中。
要想实现Spring Boot模块化开发,我们可以借助@Import
注解,实现在一个模块中,导入另一个模块中的类并将其也初始化为Bean注册到IoC容器。
下面,我们就通过一个简单的例子来学习一下。
1,再看@ComponentScan
在学习今天的内容之前,我们可以先回顾一下关于IoC容器扫描组件的基本知识。
1) IoC容器的扫描起点
相信大家对@SpringBootApplication
这个注解并不陌生,我们创建的每个Spring Boot工程主类都长这样差不多:
package com.gitee.swsk33.mainmodule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MainModuleApplication {
public static void main(String[] args) {
SpringApplication.run(MainModuleApplication.class, args);
}
}
从初学Spring Boot开始,我们就知道要想让一个类被扫描并实例化成Bean交给IoC容器托管,除了给那些类标注相关的注解(比如@Component
)之外,还需要将其放在主类(也就是标注了@SpringBootApplication
的类)所在的软件包或者其子包层级下,这样在IoC容器初始化时,我们的类才会被扫描到。
可见@SpringBootApplication
事实上标注了IoC容器创建Bean时扫描的起点,不过@SpringBootApplication
是一个复杂的复合注解,它是下列注解的组合:
而事实上,真正起到标注扫描起点作用的注解是@ComponentScan
,当该注解标注在一个类上时,这个类就会被标记为IoC容器的扫描起点,相信大家初学Spring时都写过这样类似的入门示例:
package com.gitee.swsk33.springdemo;
import com.gitee.swsk33.springdemo.service.MessageService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.ApplicationContext;
@ComponentScan
public class Main {
public static void main(String[] args) {
// 创建基于注解的上下文容器实例,并传入配置类Main以实例化其它标注了Bean注解的类
ApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
// 利用Spring框架,取出Bean:MessageService对象
MessageService messageService = context.getBean(MessageService.class);
// 这时,就可以使用了!
messageService.printMessage();
}
}
可见上述是我的主类,其标注了@ComponentScan
注解,主类位于软件包com.gitee.swsk33.springdemo
,那么IoC容器初始化时,就会递归扫描位于软件包com.gitee.swsk33.springdemo
中及其下所有子包中标注了相关注解(例如@Component
、@Service
)的类,并将它们实例化为Bean放入IoC容器托管,上述代码中,MessageService
位于com.gitee.swsk33.springdemo
中的子包service下且标注了相关注解,因此能够被实例化为Bean并放入IoC容器,后续我们可以取出。
事实上,无论是@ComponentScan
还是@SpringBootApplication
注解,都是可以指定扫描位置的,比如说:
@SpringBootApplication(scanBasePackages = "com.gitee.swsk33.mainmodule")
public class MainModuleApplication {
// ...
}
这表示启动程序时指定扫描软件包com.gitee.swsk33.mainmodule
中及其所有子包下对应的类,只不过平时大多数时候我们都缺省这个参数,这样默认情况下,@ComponentScan