面试真是越来越卷了,近又有小伙伴在微信上问到松哥这样一个面试题,想到这两个的区别其实还有点意思,因为整一篇文章和小伙伴们捋一捋。
首先,从名字上看,这两个注解意义特别接近,@AutoConfigurationPackage
就是自动配置包,自动配置包的目的是能让系统扫描到包内的 Bean;@ComponentScan
则是组件扫描,这个松哥在之前的教程中也多次提到过了,就不再赘述了,所以这里就有一个问题,这两个注解有啥区别?
首先大家思考这样一个问题:
现在大多数项目可能都是用的 MyBatis 或者 MyBatis-Plus 这样的数据持久化框架,当我们在 Spring Boot 中使用 MyBatis 的时候,我们一般需要在 Mapper 接口上添加一个 @Mapper 注解,类似下面这样:
@Mapper
public interface UserMapper {
}
或者在启动类上加 Mapper 扫描注解去统一扫描所有的 Mapper 接口,类似下面这样:
@SpringBootApplication
@MapperScan(basePackages = "org.javaboy.auto_package")
public class AutoPackageApplication {
public static void main(String[] args) {
SpringApplication.run(AutoPackageApplication.class, args);
}
}
平时我们都这样写,没有任何问题,现在假设我们换一个写法,假如我的类结构如下:
├── main
│ ├── java
│ │ └── org
│ │ └── javaboy
│ │ └── auto_package
│ │ ├── config
│ │ │ ├── AutoPackageApplication.java
│ │ │ └── UserController.java
│ │ ├── mapper
│ │ │ └── UserMapper.java
│ │ └── service
│ │ └── UserService.java
小伙伴们看到,我把启动类和 UserController 放在一个单独的包中,UserMapper 和 UserService 也分别位于不同的包中,其中在 UserController 中注入了 UserService,在 UserService 中则注入了 UserMapper,大致上就这么一个关系。
按照我们之前对 Spring Boot 的理解,这个项目启动肯定会报错,因为默认情况下,系统扫描的 Bean 是启动类所在的包以及子包下的所有 Bean(因为 @SpringBootApplication
注解在启动类上),所以上面这个项目启动的时候,能扫描到 UserController,但是扫描不到 UserService,所以启动的时候会报错,如下:
大家看下,这意思很明确,UserService 找不到,所以启动失败。
解决这个问题的办法很简单,要么将启动类放到根包下面,这样所有的 Bean 默认就都能扫描到了,要么我们重新配置包扫描,这里我采用第二种方案,我们在启动类上加 @ComponentScan
注解,重新指定扫描的包,如下:
@SpringBootApplication
@ComponentScan(basePackages = "org.javaboy.auto_package")
public class AutoPackageApplication {
public static void main(String[] args) {
SpringApplication.run(AutoPackageApplication.class, args);
}
}
加上之后,我们再次启动,发现又报错了,如下:
虽然再次出错,但是跟之前的错误并不一样,这次是没找到 UserMapper 这个 Bean,说明 UserService 是找到了!
从这里我们就可以看出来,@ComponentScan
注解扫描组件是不会扫描到 @Mapper 注解的!
事实上,@ComponentScan
注解主要是扫描 Spring 家族的各种 Bean,如 @Controller、@Service、@Component、@Repository 以及由此衍生出来的一些其他的 Bean,对于 Spring 家族之外的 Bean,如 MyBatis 的 @Mapper、@MapperScan,JPA 的 @Entity 等,@ComponentScan
都扫不到!
谁能扫到呢?那就是我们今天的另外一个主角 @AutoConfigurationPackage
,这个注解其实就是专门用来扫这些第三方的各种 Bean 的。
现在,我们在项目启动上加上 @AutoConfigurationPackage
注解,并设置需要扫描的位置,如下:
@SpringBootApplication
@AutoConfigurationPackage(basePackages = "org.javaboy.auto_package")
@ComponentScan(basePackages = "org.javaboy.auto_package")
public class AutoPackageApplication {
public static void main(String[] args) {
SpringApplication.run(AutoPackageApplication.class, args);
}
}
此时,项目就可以成功启动了,因为 @AutoConfigurationPackage(basePackages = "org.javaboy.auto_package")
注解可以扫描到 @Mapper 注解。
当然,这里只是为了给大家演示问题,实际场景下直接在启动类上加
@MapperScan(basePackages = "org.javaboy.auto_package")
注解就可以了。因为 @MapperScan 注解是 mybatis-spring 提供的,而 @Mapper 是 MyBatis 自己提供的,两个注解的出处本身就不同。
默认情况下,Spring Boot 项目的启动注解中,实际上已经包含了 @AutoConfigurationPackage
注解,具体位置在 @SpringBootApplication
->@EnableAutoConfiguration
->@AutoConfigurationPackage
,默认该注解没有指定 basePackages 属性,表示使用启动类所在的包作为根包,扫描该包下的所有第三方 Bean,所以我们平时在 Spring Boot 中使用 MyBatis 的时候,是不需要额外加 @AutoConfigurationPackage
注解的。
经过上面问题的演示,相信小伙伴们已经搞明白了 @AutoConfigurationPackage
和 @ComponentScan
的区别了吧?
小结
总结一下:
两者都是用来扫描 Bean 的。 @ComponentScan
主要用来扫描和 Spring 容器相关的 Bean。@AutoConfigurationPackage
主要用来扫描第三方的 Bean。
仅此而已。