深入浅出org.springframework.context.annotation.Import、ImportSelector、ImportBeanDefinitionRegistrar

Import

https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans

源码注释

Indicates one or more component classes to import — typically @Configuration classes.

标记要导入的一个或者多个组件类,尤其是被 @Configuration 标记的类。


Provides functionality equivalent to the 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).

和 Spring XML中的 import 标签相同的功能。允许导入@Configuration 类,ImportSelector 和ImportBeanDefinitionRegistrar 的实现类,以及常规组件类()

AnnotationConfigApplicationContext.register源码

	@Override
	public void register(Class<?>... componentClasses) {
		Assert.notEmpty(componentClasses, "At least one component class must be specified");
		StartupStep registerComponentClass = this.getApplicationStartup().start("spring.context.component-classes.register")
				.tag("classes", () -> Arrays.toString(componentClasses));
		this.reader.register(componentClasses);
		registerComponentClass.end();
	}

If XML or other non-@Configuration bean definition resources need to be imported, use the @ImportResource annotation instead.

如果XML或者其他非@Configuration bean definition 资源需要导入,使用@ImportResource注解替代。

源码

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {	
	Class<?>[] value();
}

只有一个参数接收Class数组。

常见用法

DataSourceAutoConfiguration

	@Import({ DataSourcePoolMetadataProvidersConfiguration.class,
	 DataSourceInitializationConfiguration.class })

	@Import(EmbeddedDataSourceConfiguration.class)
	
	@Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
			DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.OracleUcp.class,
			DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class })

问题一:Import数组的顺序性

在DefaultListableBeanFactory类中preInstantiateSingletons方法中的beanNames列表中
在这里插入图片描述

for (String beanName : beanNames) {}

由于源码中使用for循环解析,所以import导入的类是按照顺序依次加载的。

问题二 Import一个普通类

在注释上基本没有提到普通类,下面我们来测试一下。

public class Car {

    String name = "123";

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

在这里插入图片描述
测试方法

    @Test
    void contextLoads() {
        System.out.println(car.getName());
    }

总结:Import可以导入一个普通类进入容器,并且可以使用Autowired注解来获取到实例。

ImportSelector

Import 引入、导入
Selector 选择器

源码

public interface ImportSelector {
	//选择导入
	String[] selectImports(AnnotationMetadata importingClassMetadata);
	//获取过滤器
	@Nullable
	default Predicate<String> getExclusionFilter() {
		return null;
	}
}

根据源码分析,ImportSelector 可以获取注解中的一些值从而选择导入的类。

实现类

在这里插入图片描述

实例

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(ImportSelectorImpl.class)
public @interface EnableImportSelector {

    int value();
}
public class ImportSelectorImpl implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        Map<String, Object> value = importingClassMetadata.getAnnotationAttributes(EnableImportSelector.class.getName());
        int type = (int) value.get("value");
        String[] strs = new String[1];
        if (type == 1) {
            System.out.println("type == 1");
            strs[0] = A.class.getName();
        } else {
            System.out.println("type == 其他");
            strs[0] = B.class.getName();
        }
        return strs;
    }
}
public class A {
    public A() {
        System.out.println("A is init ...");
    }
}
public class B {
    public B() {
        System.out.println("B is init ...");
    }
}
@EnableImportSelector(2)
type == 其他
B is init ...

将 @EnableImportSelector(2) 改为 1

type == 1
A is init ...

总结:通过ImportSelector中的selectImports方法来返回一个类名数组,然后容器会加载这些类。

ImportBeanDefinitionRegistrar

这是一个常见接口,经常出现在Enable注解中,通过这个接口实现注册BeanDefinitions。

实现类

可以看到在源码中有广泛应用,这里出现三份是因为我本地有三个不同版本。
在这里插入图片描述

源码

	default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
	 BeanDefinitionRegistry registry,BeanNameGenerator importBeanNameGenerator) {
		registerBeanDefinitions(importingClassMetadata, registry);
	}

	default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
	 BeanDefinitionRegistry registry) {	}

根据源码分析,ImportBeanDefinitionRegistrar 是根据注解信息来注册BeanDefinitions。我们需要继承接口然后重写第二个方法,第二个方法过滤掉了第三个参数BeanNameGenerator ,运行后它是这样一个类型。

springframework.context.annotation.FullyQualifiedAnnotationBeanNameGenerator@537b32ef

接口中有一个方法generateBeanName根据注释 Generate a bean name for the given bean definition.为给定的bean定义生成一个bean名称。

String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry);

我们进入FeignClientsRegistrar类找到registerBeanDefinitions发现并没有用到第三次参数,这个先忽略,后面用到再回来补充。

	@Override
	public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
		registerDefaultConfiguration(metadata, registry);
		registerFeignClients(metadata, registry);
	}

下面我们来扩展一下registerFeignClients

    public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        BeanDefinition b1 = new GenericBeanDefinition();
        registry.registerBeanDefinition("beanName", b1);
        BeanDefinition b2 = registry.getBeanDefinition("beanName");
        logger.info("registerFeignClients ... ");
    }

调用过程

	private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
		registrars.forEach((registrar, metadata) ->
				registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
	}

loadBeanDefinitionsForConfigurationClass

Read a particular ConfigurationClass, registering bean definitions for the class itself and all of its Bean methods.

loadBeanDefinitionsForConfigurationClass(从配置类加载BeanDefinitions):157行,

	//调用loadBeanDefinitionsFromRegistrars
	loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());

	public Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> getImportBeanDefinitionRegistrars() {
		return this.importBeanDefinitionRegistrars;
	}
	
	private final Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars = new LinkedHashMap<>();

loadBeanDefinitions

Read configurationModel, registering bean definitions with the registry based on its contents.

	public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
		TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
		//循环调用loadBeanDefinitionsForConfigurationClass
		for (ConfigurationClass configClass : configurationModel) {
			loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
		}
	}

参数是ConfigurationClass类型的set,

processConfigBeanDefinitions

Build and validate a configuration model based on the registry of Configuration classes.

	//loadBeanDefinitions
	this.reader.loadBeanDefinitions(configClasses);
	//configClasses
	Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());

BeanDefinition

深入浅出Spring bean的生命周期

案例

官方示例

https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-java-configuration-annotation

@Configuration
public class ConfigA {

    @Bean
    public A a() {
        return new A();
    }
}

@Configuration
@Import(ConfigA.class)
public class ConfigB {

    @Bean
    public B b() {
        return new B();
    }
}

只需要提供ConfigB,通过@Import(ConfigA.class)同样将A加入容器。

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);

    // now both beans A and B will be available...
    A a = ctx.getBean(A.class);
    B b = ctx.getBean(B.class);
}

As of Spring Framework 4.2, @Import also supports references to regular component classes, analogous to the AnnotationConfigApplicationContext.register method. This is particularly useful if you want to avoid component scanning, by using a few configuration classes as entry points to explicitly define all your components.

DeferredImportSelector

public interface DeferredImportSelector extends ImportSelector 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值