1.前言
SpringBoot是当前开发中用到最多的框架,它就像一个脚手架,我们需要集成其他框架的话使用@Enable***注解引入即可。SpringBoot引入第三方框架,将第三方框架中的类注册成BeanDefinition,交给SpirngIOC去管理。
那么咱们思考一个问题: SpringBoot怎样将第三方框架中的类注册成BeanDedinition?
2.@Import()注解
在学习Spring框架的时候,能看到很多地方使用@Import()注解,那么这个注解的作用是什么呢?
作用
@Import()注解就是将引入的类注册成BeanDefinition,交给SpringIOC容器管理。
使用方式
共有3种常见的使用方式:
① 引入一个普通类,将这个类注册成BeanDefinition;
② 引入一个ImportSelectors实现类,批量注册BeanDefinition;
③ 引入一个ImportBeanDefinitionRegister类,获得一个BeanDefinition读取器。
SpringBoot就是使用的第二种-ImportSelectors,将第三方框架种的Class注册成BeanDefinition。
ImportSelector
ImportSelector是一个接口,通过这个接口的selectImports()方法返回一个数组,数组内存放的是一个个的类的全限定名,根据这些类名,去注册BeanDefinition。
DeferredImportSelector
DeferredImportSelector接口继承于ImportSelector接口,DeferredImportSelector有两个特点:延迟解析、分组排序;
- 延迟解析
就是说Spring在解析@Import注解时,如果引入的是DeferredImportSelector的实现类,Spring会在最后去解析它。- 分组排序
将@Import()注解引入的类进行分组排序,并不会影响到咱们自己注册的Bean的顺序。
3.源码分析
@SpringBootApplication注解
@SpringBootApplication注解是SpringBoot的主配置类,SpringBoot通过这个配置类的main()方法去启动。下面我们研究一下这个注解。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM,classes = {TypeExcludeFilter.class}),
@Filter(type = FilterType.CUSTOM,classes = {AutoConfigurationExcludeFilter.class}
)
)
public @interface SpringBootApplication {...省略代码...}
@Target()
设置当前注解可以收用的位置。
@Retention()
当注解标注的类编译以什么方式保留
@Documented
java doc会生成注解信息
@Inherited
是否会被继承
@SpringBootConfiguration
表示是一个配置类,这个注解上标注了@Configuration起作用
@EnableAutoConfiguration
这个注解是SpringBoot的关键,通过这个注解去开启自动配置,将加载自动配置类。
@ComponentScan()
扫描包路径
@EnableAutoConfiguration
通过这个注解的名称,大概知道通过这个注解去开启配置。我们研究一下这个注解。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
@AutoConfigurationPackage
添加该注解的类所在的package 作为 自动配置package 进行管理。也就是说SpringBoot在启动时会将启动类所在的package作为自动配置的package。
@Import({AutoConfigurationImportSelector.class})
通过@Import()注解引入AutoConfigurationImportSelector类,这个类实现了向DeferredImportSelector接口,从而拥有了延迟解析、分组排序的功能。
疑问:为什么SpringBoot中要使用DeferredImportSelector而不使用ImportSelector?
举例:如果通过ImportSelector引入一批类进行注册,其中包含一个SqlSessionFactory;我们自己也在自定义的配置类中通过@Bean注册一个SqlSessionFactory...因为存放BeanDefinition的Map的key为beanName,value为BeanDefinition,所以同一个类只会存在一个BeanDefinition,哪么到底哪个生效呢?
当然我们想用自己注册的,因为有可能我们定制化了一些功能,如果最后解析引入的这批类就能解决这个问题,所以使用DeferredImportSelector。
AutoConfigurationImportSelector
解析AutoConfigurationImportSelector的逻辑:
- 根据getImportGroup()的返回结果判断接下来的执行逻辑。如果返回null,调用selectImports()方法返回一个数组。否则返回一个自定义的Group-AutoConfigurationGroup。
- 在AutoConfigurationGroup类的process()方法内去调用getAutoConfigurationEntry()方法。
- getAutoConfigurationEntry()方法的内部逻辑:
- 调用getCandidateConfigurations(),去所有jar包和类路径下读取META/spring.factories文件,获取到全部自动配置类。
- 去重
- 排除。根据@EnableAutoConfiguration注解内设置的exclude()和excludeName()排除不需要的自动配置类。
- 过滤。获取3个Filter,根据pom文件中设置srarter过滤出来需要的自动配置类。
- 最终拿到我们所需要的自动配置类,注册成为BeanDefinition。