@AutoConfigurationPackage 和 @ComponentScan 有何区别?

面试真是越来越卷了,近又有小伙伴在微信上问到松哥这样一个面试题,想到这两个的区别其实还有点意思,因为整一篇文章和小伙伴们捋一捋。

首先,从名字上看,这两个注解意义特别接近,@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.classargs);
    }

}

平时我们都这样写,没有任何问题,现在假设我们换一个写法,假如我的类结构如下:

├── 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.classargs);
    }

}

加上之后,我们再次启动,发现又报错了,如下:

虽然再次出错,但是跟之前的错误并不一样,这次是没找到 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.classargs);
    }

}

此时,项目就可以成功启动了,因为 @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 的区别了吧?

小结

总结一下:

  1. 两者都是用来扫描 Bean 的。
  2. @ComponentScan 主要用来扫描和 Spring 容器相关的 Bean。
  3. @AutoConfigurationPackage 主要用来扫描第三方的 Bean。

仅此而已。

相关文章