Spring提供了几种方式来注册Bean,日常开发中使用最多的是ComponentScan。得益于ComponentScan,注册bean非常的简单,只需要在被注册的类上声明@Component或者@Service等注解即可。
除了ComponentScan,Spring还支持使用Configuration注解来注册Bean。在大型的项目中,模块化开发能极大地降低系统的复杂性,这时需要每个模块来定义本模块Bean注册情况,Configuration发挥着巨大的作用。
1 | java复制代码@Configuration |
每个模块定义了Configuration之后,需要将多个模块的Configuration组合。Spring提供了Import注解来实现多个Configuration组合。
1 | java复制代码@Import(WxLoginConfiguration.class) |
Spring官方文档中关于Import的描述如下:
Provides functionality equivalent to the <import/> element in Spring XML. Allows for importing @Configuration classes, ImportSelector and ImportBeanDefinitionRegistrar implementations, as well as regular component classes (as of 4.2; analogous to AnnotationConfigApplicationContext.register(java.lang.Class<?>...)). @Bean definitions declared in imported @Configuration classes should be accessed by using @Autowired injection. Either the bean itself can be autowired, or the configuration class instance declaring the bean can be autowired. The latter approach allows for explicit, IDE-friendly navigation between @Configuration class methods.
除了Configuration,Import还支持引入ImportSelector和ImportBeanDefinitionRegistrar。既然要全面了解Import机制,那另外两个也要一探究竟。
ImportSelector
Spring官方文档中,对ImportSelector的描述如下:Interface to be implemented by types that determine which @Configuration class(es) should be imported based on a given selection criteria, usually one or more annotation attributes.
从字面上理解,ImportSelector可以根据注解里面的一个或多个属性来决定引入哪些Configuration。举个例子:
小伙伴都用过Transactional注解,Transactional注解生效的前提是EnableTransactionManagement生效。看过EnableTransactionManagement源代码的小伙伴应该都知道,它通过Import引入了一个ImportSelector。
1 | java复制代码@Target(ElementType.TYPE) |
而TransactionManagementConfigurationSelector会根据注解里面的AdviceMode不同,来确定引入不同的Configuration。
1 | java复制代码protected String[] selectImports(AdviceMode adviceMode) { |
ImportBeanDefinitionRegistrar
Spring官方文档中,对ImportBeanDefinitionRegistrar的描述如下:Interface to be implemented by types that register additional bean definitions when processing @Configuration classes. Useful when operating at the bean definition level (as opposed to @Bean method/instance level) is desired or necessary.
字面意思是,通过继承这个接口可以额外定义Bean。举个例子:
在使用Mybatis的时候,会使用到MapperScan这个注解,这个注解通过Import引入了ImportBeanDefinitionRegistrar,这也解释了为什么我们只在Interface上申明了一个Mapper,mybatis就帮我们生成好了Bean。
1 | java复制代码@Retention(RetentionPolicy.RUNTIME) |
有小伙伴在编码过程中,并没有使用MapperScan,为什么也能正常使用呢?其实是Mybatis starter的功劳。在MybatisAutoConfiguration里面定义了ImportBeanDefinitionRegistrar,当MapperScan没有激活时,它就会生效。
1 | java复制代码@org.springframework.context.annotation.Configuration |
Import执行流程
了解了Import支持的三种不同类型的资源之后,接下来debug看一下import的执行过程。通过设置断点,发现在ConfigurationClassParser类中,通过深度遍历来处理Import。
1 | java复制代码private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited) |
而上面介绍的ImportSelector,需要调用selectImports方法进行解析。
1 | java复制代码private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, |
1 | 复制代码这样递归调用,就实现了资源的加载。 |
1 | 复制代码 |
本文转载自: 掘金