目录
@Import 注解的作用
1.简介
在平时看源码或者很多配置类上面都会出现 @Import 注解,功能就是和Spring XML 里面的 一样。@Import 注解是用来导入配置类或者一些需要前置加载的类。
2.源码解析
2.1 导入配置的三种类型
@Import 支持三种方式
- 带有 @Configuration 的配置类(4.2 版本之前只可以导入配置类,4.2 版本之后也可以导入
普通类) - ImportSelector 的实现
- ImportBeanDefinitionRegistrar 的实现
2.2 源码解释
/**
* Indicates one or more {@link Configuration @Configuration} classes to import.
*
*功能类似XML 里面的 <import/> ,可以导入 @Configuration配置类,ImportSelector、
* ImportBeanDefinitionRegistrar 的实现,4.2 版本之后可以导入普通类(类似AnnotationConfigApplicationContext#register
* )
* <p>Provides functionality equivalent to the {@code <import/>} element in Spring XML.
* Allows for importing {@code @Configuration} classes, {@link ImportSelector} and
* {@link ImportBeanDefinitionRegistrar} implementations, as well as regular component
* classes (as of 4.2; analogous to {@link AnnotationConfigApplicationContext#register}).
*
* 可以在类级别声明或作为元注释声明
* <p>May be declared at the class level or as a meta-annotation.
* 如需要引入XML或其他类型的文件,使用@ImportResource注解
* <p>If XML or other non-{@code @Configuration} bean definition resources need to be
* imported, use the {@link ImportResource @ImportResource} annotation instead.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
/**
* {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
* or regular component classes to import.
*/
Class<?>[] value();
}
3、测试例子
3.1 导入普通类
- 新建一个 TestA 类
public class TestA {
public void fun(String str) {
System.out.println(str);
}
public void printName() {
System.out.println("类名 :" + Thread.currentThread().getStackTrace()[1].getClassName());
}
}
- 新建一个 ImportConfig,在类上面加上 @Configuration,加上 @Configuration 是为了能让 Spring 扫描到这个类,并且直接通过 @Import 引入TestA 类
@Import({TestA.class})
@Configuration
public class ImportConfig {
}
- 测试结果
TestA 是一个普通的类,现在可以被 @Autowired 注释然后调用,就直接说明已经被Spring 注入并管理了,普通的类都是需要先实例化
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = ApplicationMain.class)
public class ImportAnnotionTest {
@Autowired
TestA testA;
@Test
public void TestA() {
testA.printName();
}
}
打印:
类名 :com.test.importdemo.TestA
3.2 导入带有 @Configuration 的配置类
- 新建 TestB 类
@Configuration
public class TestB {
public void fun(String str) {
System.out.println(str);
}
public void printName() {
System.out.println("类名 :" + Thread.currentThread().getStackTrace()[1].getClassName());
}
}
- 在 ImportConfig.class 里面直接引入 TestB 类
@Import({TestA.class, TestB.class})
@Configuration
public class ImportConfig {
}
- 测试结果
TestB.class 的类上面已经有了 @Configuration 注解,本身就会配 Spring 扫到并实例,@import 引入带有 @Configuration 的配置文件,是需要先实例这个配置文件再进行相关操作
@Autowired
TestB testB;
@Test
public void TestB(){
testB.printName();
}
打印:
ImportAnnotionTest in 8.149 seconds (JVM running for 10.104)
类名 :com.test.importdemo.TestB
3.3 通过 ImportSelector 方式导入的类
- 新建 TestC 类
public class TestC {
public void fun(String str) {
System.out.println(str);
}
public void printName() {
System.out.println("类名 :" + Thread.currentThread().getStackTrace()[1].getClassName());
}
}
- 新建 SelfImportSelector.class 实现ImportSelector 接口,注入 TestC 类
public class SelfImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.test.importdemo.TestC"};
}
}
- ImportConfig 上面引入 SelfImportSelector.class
@Import({TestA.class, TestB.class, SelfImportSelector.class})
@Configuration
public class ImportConfig {
}
- 测试结果
@Autowired
TestC testC;
@Test
public void TestC() {
testC.printName();
}
打印:
ImportAnnotionTest in 7.23 seconds (JVM running for 9.065)
类名 :com.test.importdemo.TestC
3.4 通过 ImportBeanDefinitionRegistrar 方式导入的类
- 新建 TestD 类
public class TestD {
public void fun(String str) {
System.out.println(str);
}
public void printName() {
System.out.println("类名 :" + Thread.currentThread().getStackTrace()[1].getClassName());
}
}
- 新建 SelfImportBeanDefinitionRegistrar.class,实现接口ImportBeanDefinitionRegistrar,注入 TestD 类
public class SelfImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
RootBeanDefinition root = new RootBeanDefinition(TestD.class);
registry.registerBeanDefinition("testD", root);
}
}
- ImportConfig 类上加上导入 SelfImportBeanDefinitionRegistrar.class
@Import({TestA.class, TestB.class, SelfImportSelector.class,
SelfImportBeanDefinitionRegistrar.class})
@Configuration
public class ImportConfig {
}
- 测试结果
@Autowired
TestD testD;
@Test
public void TestD() {
testD.printName();
}
打印:
ImportAnnotionTest in 7.817 seconds (JVM running for 9.874)
类名 :com.test.importdemo.TestD
通过以上几种方法都是能成功注入 Spring。
4. 详细过程解析
这里主要看 ConfigurationClassParser.java 里面的doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) 这个方法。具体定位到源码的302行代码
为啥不从头梳理,这里 Spring 启动过程比较复杂,要是从头梳理,涉及的东西比较多,好多人看了就会累了,放弃了,我们就单个点熟悉,最后再进行汇总

4.1 getImports 方法
在分析这个方法之前,我们先看一下 getImports 方法,这个方法就是获取所有的 @import 里面的类
这里是获取 @import 里面的类,大致流程如下:
- 定义一个 visited 的集合,用作 是否已经 判断过的标志
- 这里就是获取 sourceClass 上面的 所有的 annotation,并挨个判断, 如果不是 @import,那就 进一步
递归 调用对应的 annotation,直到全部结束 - 加载 sourceClass 里面 的 @Import annotation 里面对应的类名,最后返回
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
Set<SourceClass> imports = new LinkedHashSet<>();
Set<SourceClass> visited = new LinkedHashSet<>();
collectImports(sourceClass, imports, visited);
return imports;
}
// 这里就是获取sourceClass上面的所有的annotation,如果不是@import,那就进一步递归调用对应的annotation,直到全部结束
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
throws IOException {
if (visited.add(sourceClass)) {
for (SourceClass annotation : sourceClass.getAnnotations()) {
String annName = annotation.getMetadata().getClassName();
if (!annName.equals(Import.class.getName())) {
collectImports(annotation, imports, visited);
}
}
imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
}
}
4.2 processImports 方法
processImports 这个方法 的代码逻辑也很清晰,流程图如下:

大致的流程如下:
- 判断 importCandidates 是否为空,为空 退出
- 判断 isChainedImportOnStack,如果为 true,加入 problemReporter 里面的 error,并退出
- 把当前的 configClass 加入到 ImportStack 里面,ImportStack 是继承了 ArrayDeque 和实现了 ImportRegistry
- 对 getImports 里面获取到的需要 import 的类进行遍历处理
4.1 如果是 ImportSelector 类型,首先实例一个 ImportSelector 对象,然后 对其进行 Aware 扩展(如果实现了 Aware 接口)
4.1.1 进一步判断是否是 DeferredImportSelector 类型,如果是,加入到deferredImportSelectors 里面,最后处理,这里可以看一下方法parse(Set configCandidates),里面最后一行才调用,这也就是有的时候,如果想最后注入,就可以定义为 deferredImportSelectors 类型
4.1.2 如果不是 DeferredImportSelector 类型,那就调用 selectImports 方法,获取到所有的需要 注入的类,这时再次调用 processImports 方法,这里调用 processImports 方法,其实是把这些需要注入的类当成普通的 @Configuration 处理 - 如果是 ImportBeanDefinitionRegistrar 类型,这里也是先实例一个对象,然后加入到 importBeanDefinitionRegistrars 里面,后续会在 ConfigurationClassBeanDefinitionReader 这个类里面的 loadBeanDefinitionsFromRegistrars 方法处理的
- 如果上面两种类型都不是,那就是当初普通的带有 @Configuration 的类进行处理了

@ImportResource 注解的作用
1.简介
@ImportResource 注解用于导入Spring 的配置文件。
2.作用解析
以前写的 springmvc.xml、applicationContext.xml 在 SpringBoot 里面并没有,我们自己编写的配置文件也不能自动识别,这里就可以使用 @ImportResource 标注在一个配置类上让 Spring 的配置文件生效。
注意!不使用 @ImportResource 注解,程序根本不能对我们 Spring 的配置文件进行加载
3.测试例子


@ImportResource(locations = "classpath:applicationContext.xml")
@SpringBootApplication
@RestController
public class FirstSpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(FirstSpringbootApplication.class, args);
}
}
@SpringBootTest
class FirstSpringbootApplicationTests {
@Autowired
ApplicationContext applicationContext;
@Test
void testapplication() {
Object a = applicationContext.getBean("dog1");
System.out.println(a);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dog1" class="com.yangzhenxu.firstspringboot.bean.Dog">
<property name="name" value="zhangxue"/>
<property name="age" value="27"/>
</bean>
</beans>
本文详细介绍了Spring框架中的@Import和@ImportResource注解。@Import主要用于导入配置类或组件,支持@Configuration、ImportSelector和ImportBeanDefinitionRegistrar三种方式。@ImportResource则用于导入XML配置文件,确保Spring能够识别和加载自定义的配置。文中通过实例展示了四种导入方式,并解析了注解的源码流程。
2391

被折叠的 条评论
为什么被折叠?



