Spring探索01 – @Import注解

Overview

Spring中 @Import注解最初主要是在配置类中使用,目的是引入其他的配置类( @Configuration)并实现自动注入。

目前 Import并不只是支持引入 @Configuration注解的类,也支持引入 ImportSelectorImportBeanDefinitionRegistrar接口的实现类,甚至可以引入普通的Java Bean并完成注入。

写了一个简单的应用来进行测试:spring-boot-import

做些说明。在应用中定义了一个 Worker类,应用做的事情就是结合 @Import注解用不同的方式注入 Worker类的多个Bean实例。 每个 Worker Bean的实例通过name进行区分。

代码的一个核心是 MyConfig类,代码如下:

这个类中包含 @Configuration注解,说明是一个配置类,Spring会自动注入这个类的实例。此外这个类还通过 @Bean注解注入了一个Worker Bean实例“tom”,又通过 @Import接口引用三个其他类,目的是尝试注入其他的Worker Bean实例。最后在 WorkerService中尝试获取并逐行打印注入的Worker实例:

接下来详细介绍下这个过程中是如何使用 @Import接口的。

引入普通的Java Bean

MyConfig类中使用 @Import注解注入的 MyAnotherConfig类没有继承任何超类或实现任何接口:

可以看到,如果不是内部的一个方法使用了 @Bean注解,它就是一个普通的Java Bean了。也是通过这个 @Bean注解,实现了另一个Worker Bean的注入。

引入ImportSelector实现类

根据Spring的文档, ImportSelector的作用是根据一些注解的属性来决定使用哪些 @Configuration类。也就是配置类的选择器。通常在spring的引用包中会看到 ImportSelector的实现。

因此这里定义了另一个配置类 MySelectConfig,不过为了避免当前应用下Spring的自动注入,没有在这个类中添加 @Configuration注解。

看起来和前面的 MyAnotherConfig是一样的。不过和前例不一样的是: MySelectConfig类的注入是通过 MyImportSelector来实现的。

MyImportSelector的实现如下:

这里没有基于AnnotationMetadata进行判定就直接返回了配置类的名称,在实际工作中不是一个好的实践。不过我们这里只是做一个演示,不需纠结太多。

引入ImportBeanDefinitionRegistrar实现类

ImportBeanDefinitionRegistrarImportSelector的作用是有着根本上的不同的: ImportSelector的作用是提供配置类;而 ImportBeanDefinitionRegistrar的作用则是根据类定义完成相应Bean实例的创建。

通常 ImportBeanDefinitionRegistrar多与 ClassPathMapperScanner配合使用。 ClassPathMapperScanner可以用来扫描指定的package,获取目标类并完成相应实例的创建。具体应用如MyBatis的 @Mapper注解的解释。

看下在我们示例中的使用:

在这里通过 Worker类的定义创建了一个名为“jerry”的实例。需要注意:这里虽然完成了 Worker实例的创建,但是并没有配置任何属性。等在输出注入的Worker Bean的时候我们会看到这个实例的属性都是默认值。

引入Spring Component

使用 @Import注解不仅可以引入普通的Java Bean,也可以引入Spring组件类,即需要使用 @Component或者 @Service等注解标记的类。组建类中通过 @Autowired注解引用的其他组件也会被递归引用并注入。

示例应用中的 WorkerService类并没有使用任何注解标记,而是在使用的时候通过 @Import注解进行的引入。

这样虽然也可以使用,但并不建议这么做。

引入@Configuration注解的类

这个留到最后是因为一开始比较困惑:既然已经有 @Configuration注解了,Spring就一定会自动引入这个类的,应该就没必要再使用 @Import注解进行引用并注入了。

后来意识到我的想法是有漏洞的:比如一些第三方spring组件包中的配置类,既没有配置packageScan,也没有配置starter,直接使用肯定是不行的。此时使用 @Import注解来导入相关的配置类及组件是一个很好地解决方案。

测试

执行 WorkerControllerTest类的测试方法 all(),观察测试结果,期间会输出我们创建的几个Worker Bean实例:

可以看到一个Worker实例的属性都是默认值,这个实例即是通过 ImportBeanDefinitionRegistrar创建的Worker Bean “jerry”。

其他

关于 @Import注解的实现原理可以参考 AbstractApplicationContext.refresh -> BeanFactoryPostProcessor -> ConfigurationClassPostProcessor -> ConfigurationClassParser.processImports()。具体就不展开了。

此外,还有另外一个注解 @ImportResource主要用来引入xml或groovy配置文件。


发表评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据