Springboot框架自动装配原理之@Import注解作用

12 篇文章 15 订阅

1. 简介

在平时看源码或者很多配置类上面都会出现@Import注解,功能就是和Spring XML 里面 的 一样. @Import注解是用来导入配置类或者一些需要前置加载的类.,springboot框架实现自动装配的原理之一正是利用这一特性解决了spring框架中重量级的xml配置

2. 源码解析

@Import注解源码以及源码描述的翻译

/**
 - Indicates one or more <em>component classes</em> to import &mdash; typically
 - {@link Configuration @Configuration} classes.
表示要导入的一个或多个组件类 通常@Configuration 类。
 -
 - <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}).
 提供与 Spring XML 中的 <import/> 元素等效的功能。允许导入 @Configuration类、
 ImportSelector和ImportBeanDefinitionRegistrar 实现,
 以及常规组件类(从 4.2 开始;类似于AnnotationConfigApplicationContext#register) 
 - 
 - <p>{@code @Bean} definitions declared in imported {@code @Configuration} classes should be
 - accessed by using {@link org.springframework.beans.factory.annotation.Autowired @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 {@code @Configuration} class methods.
在导入的 @Configuration 类中声明的 @Bean 定义应该是
通过使用 @Autowired访问注射。 bean 本身可以自动装配,或者配置类实例
声明 bean 可以自动装配。 后一种方法允许显式的、IDE 友好的
@Configuration 类方法之间的导航。
 
 - <p>May be declared at the class level or as a meta-annotation.
 -  16. <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 @Configuration}, {@link ImportSelector},
	 * {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
	 */
	Class<?>[] value();

通过源码的描述翻译可以总结出@Import可以通过以下四种方式导入一个bean(其实只有三种):

  • 导入普通类(4.2 版本之前只可以导入配置类,4.2版本之后 也可以导入 普通类)
  • 导入@Configuration配置类(@Configuration配置类已经是被注册为bean了,因此再用Import导入没什么意义
  • 导入ImportSelector实现类
  • 导入ImportBeanDefinitionRegistrar实现类

3. 测试例子

3.1 导入普通类测试

配置类ImportConfig

package com.eureka.config;

import com.eureka.register.SelfImportBeanDefinitionRegistrar;
import com.eureka.selector.SelfImportSelector;
import com.eureka.service.TestA;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@Import({TestA.class})
public class ImportConfig {
}

普通类TestA

public class TestA {

    public void printName() {
        System.out.println("我是一个注入的测试类A, " + TestA.class.getName());
    }
}

测试:

@SpringBootTest
public class ImprotTest {
    @Autowired
    private TestA testA;
    
    @Test
    public void test() {
        testA.printName();
    }
}

测试结果:
在这里插入图片描述

3.2 导入ImportSelector实现类测试

ImportSelector实现类如何导入bean,我们来分析下ImportSelector的源码

public interface ImportSelector {

	/**
	 * Select and return the names of which class(es) should be imported based on
	 * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
	 * @return the class names, or an empty array if none
	 * 查询基于import导入Configuration 类的 AnnotationMetadata中需要导入的所有类的类全名
	 * 并返回这些类。
	 */
	String[] selectImports(AnnotationMetadata importingClassMetadata);

	/**
	 * Return a predicate for excluding classes from the import candidates, to be
	 * transitively applied to all classes found through this selector's imports.
	 * <p>If this predicate returns {@code true} for a given fully-qualified
	 * class name, said class will not be considered as an imported configuration
	 * class, bypassing class file loading as well as metadata introspection.
	 * @return the filter predicate for fully-qualified candidate class names
	 * of transitively imported configuration classes, or {@code null} if none
	 * @since 5.2.4
	 * 返回用于从导入候选中排除类的谓词,为传递地应用于通过此选择器的导入找到的所有类。
	 * 如果此谓词为给定的完全限定条件返回true,表示类不会被视为导入的配置类,绕过类文件
	 * 加载以及元数据内省。
	 */
	@Nullable
	default Predicate<String> getExclusionFilter() {
		return null;
	}

}

通过ImportSelector 源码的描述翻译可以得知ImportSelector 类的通过 selectImports方法返回的一个类全名的数组,spring容器根据这些类全名查找这些类并注册为bean;如果需要还可以通过getExclusionFilter方法过滤 selectImports返回的不需要注册为bean的类全名


下面分别创建TestB和TestC以及ImportSelector实现类:

/**
 * 测试类B
 */
public class TestB {

    public void printInfo() {
        System.out.println("我是一个注入的测试类B, " + TestA.class.getName());
    }
}
/**
 * 测试类C
 */
public class TestC {

    public void printInfo() {
        System.out.println("我是一个注入的测试类C, " + TestC.class.getName());
    }
}
/**
 * ImportSelector实现类
 */
public class SelfImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{"com.eureka.service.TestC","com.eureka.service.TestB"};
    }
}
/**
 * ImportConfig 配置类
 */
@Configuration
@Import({SelfImportSelector.class})
public class ImportConfig {
}

测试:

@SpringBootTest
public class ImprotTest {
    @Autowired
    private TestB testB;
    @Autowired
    private TestC testC;
   
    @Test
    public void test1() {
        testB.printInfo();
        testC.printInfo();
    }
}

测试结果:
在这里插入图片描述

3.3 导入ImportBeanDefinitionRegistrar实现类

ImportBeanDefinitionRegistrar实现类如何导入bean,我们来分析ImportBeanDefinitionRegistrar的源码

public interface ImportBeanDefinitionRegistrar {

	/**
	 * Register bean definitions as necessary based on the given annotation metadata of
	 * the importing {@code @Configuration} class.
	 * <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
	 * registered here, due to lifecycle constraints related to {@code @Configuration}
	 * class processing.
	 * <p>The default implementation delegates to
	 * {@link #registerBeanDefinitions(AnnotationMetadata, BeanDefinitionRegistry)}.
	 * @param importingClassMetadata annotation metadata of the importing class
	 * @param registry current bean definition registry
	 * @param importBeanNameGenerator the bean name generator strategy for imported beans:
	 * {@link ConfigurationClassPostProcessor#IMPORT_BEAN_NAME_GENERATOR} by default, or a
	 * user-provided one if {@link ConfigurationClassPostProcessor#setBeanNameGenerator}
	 * has been set. In the latter case, the passed-in strategy will be the same used for
	 * component scanning in the containing application context (otherwise, the default
	 * component-scan naming strategy is {@link AnnotationBeanNameGenerator#INSTANCE}).
	 * @since 5.2
	 * @see ConfigurationClassPostProcessor#IMPORT_BEAN_NAME_GENERATOR
	 * @see ConfigurationClassPostProcessor#setBeanNameGenerator
	 */
	default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
			BeanNameGenerator importBeanNameGenerator) {

		registerBeanDefinitions(importingClassMetadata, registry);
	}

	/**
	 * Register bean definitions as necessary based on the given annotation metadata of
	 * the importing {@code @Configuration} class.
	 * <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
	 * registered here, due to lifecycle constraints related to {@code @Configuration}
	 * class processing.
	 * <p>The default implementation is empty.
	 * @param importingClassMetadata annotation metadata of the importing class
	 * @param registry current bean definition registry
	 */
	default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
	}

}

根据源码可知 registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) 方法的参数BeanDefinitionRegistry 来注册一个bean对象,我们可以通过将需要注册为bean的类封装成一个BeanDefinition对象交由BeanDefinitionRegistry对象来处理,同时还可以为该类配置相应的属性,此操作相当于spring xml中的bean标签内的所有配置


下面分别创建 DefaultTestService类 和 ImportTestService类 以及 ImportBeanDefinitionRegistrar 实现类 SelfImportBeanDefinitionRegistrar

/**
 * TestService接口类
 */
public interface TestService {
    void printInfo();
}
/**
 * TestService实现类DefaultTestService 
 */
public class DefaultTestService implements TestService {

    private String beanName;
    private String beanType;
    private Integer beanTime;

    @Override
    public void printInfo() {
        System.out.println("我是一个注入的default测试类, " + DefaultTestService.class.getName());
    }

	...getter setter略...
    
    @Override
    public String toString() {
        return "DefaultTestService{" +
                "beanName='" + beanName + '\'' +
                ", beanType='" + beanType + '\'' +
                ", beanTime=" + beanTime +
                '}';
    }
}
/**
 * TestService实现类ImportTestService 
 */
public class ImportTestService implements TestService {

    private String importName;
    private String importType;
    private String importTime;

    @Override
    public void printInfo() {
        System.out.println("我是一个注入的import测试类, " + ImportTestService.class.getName());
    }

	...getter setter略...
    
   	@Override
    public String toString() {
        return "ImportTestService{" +
                "importName='" + importName + '\'' +
                ", importType='" + importType + '\'' +
                ", importTime='" + importTime + '\'' +
                '}';
    }
}
public class SelfImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    	// 创建DefaultTestService类的BeanDefinition对象
        RootBeanDefinition root = new RootBeanDefinition(DefaultTestService.class);
        MutablePropertyValues mpv = root.getPropertyValues();
        // 给DefaultTestService类设置属性
        mpv.addPropertyValue("beanName", "defaultTestService");
        mpv.addPropertyValue("beanType", DefaultTestService.class.getTypeName());
        mpv.addPropertyValue("beanTime", (int)(Math.random()*1000 + 200));
        // 创建ImportTestService类的BeanDefinition对象
        RootBeanDefinition root1 = new RootBeanDefinition(ImportTestService.class);
        MutablePropertyValues mpv1 = root1.getPropertyValues();
        // 给ImportTestService类设置属性
        mpv1.addPropertyValue("importName", "importTestService");
        mpv1.addPropertyValue("importType", ImportTestService.class.getTypeName());
        mpv1.addPropertyValue("importTime", (int)(Math.random()*1000 + 200));
        registry.registerBeanDefinition("defaultTestService", root);
        registry.registerBeanDefinition("importTestService", root1);
    }
}
/**
 * ImportConfig 配置类
 */
@Configuration
@Import({SelfImportBeanDefinitionRegistrar.class})
public class ImportConfig {
}

测试类:

@SpringBootTest
public class ImprotTest {
    @Resource(name = "defaultTestService")
    private TestService defaultTestService;
    @Resource(name = "importTestService")
    private TestService importTestService;

    @Test
    public void test3() {
        defaultTestService.printInfo();
        System.out.println(defaultTestService.toString());
        importTestService.printInfo();
        System.out.println(importTestService.toString());
    }
}

测试结果:
在这里插入图片描述

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值