Annotations——@Import注解

8 篇文章 1 订阅

@Import注解的作用

Spring 3.0之前,我们的Bean可以通过xml配置文件与扫描特定包下面的类来将类注入到Spring IOC容器内。Spring 3.0之后提供了JavaConfig的方式,也就是将IOC容器里Bean的元信息以java代码的方式进行描述。我们可以通过@Configuration与@Bean这两个注解配合使用来将原来配置在xml文件里的bean通过java代码的方式进行描述。@Import注解提供了@Bean注解的功能,同时还有xml配置文件里标签组织多个分散的xml文件的功能,当然在这里是组织多个分散的@Configuration。
其源码:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {

	/**
	 * {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
	 * or regular component classes to import.
	 */
	Class<?>[] value();
}

通过分析注释我们可以看出,@Import注解有四个作用:
分析类注释得出结论:

  • 声明一个bean
  • 导入@Configuration注解的配置类
  • 导入ImportSelector的实现类
  • 导入ImportBeanDefinitionRegistrar的实现类

声明Bean和导入@Configuration

导入普通的Java类并将其声明为一个Bean注入到Spring容器中的功能在Sprin4.2之后才可以使用,在 spring 4.2 以前,该注解,只能导入配置类,其功能与 标签类似。

@Configuration
@Import(value = FirstBean.class)
public class MyConfig {
    
}
class FirstBean {

}
public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
        String[] beanNames = context.getBeanDefinitionNames();
        for(int i=0;i<beanNames.length;i++){
            System.out.println("bean名称为:"+beanNames[i]);
        }
    }
class FirstBean {}

我们分别尝试用@Import和不用@Import引入FirstBean,控制台得到输出:
bean名称为:org.springframework.context.annotation.internalConfigurationAnnotationProcessor
bean名称为:org.springframework.context.annotation.internalAutowiredAnnotationProcessor
bean名称为:org.springframework.context.annotation.internalCommonAnnotationProcessor
bean名称为:org.springframework.context.event.internalEventListenerProcessor
bean名称为:org.springframework.context.event.internalEventListenerFactory
bean名称为:myConfig
bean名称为:com.example.demo.importTest.FirstBean
就是多出了FirstBean这个Bean,也证明了@Import这个注解确实可以注入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.
	 */
	String[] selectImports(AnnotationMetadata importingClassMetadata);
}

ImportSelector接口注释说了几点内容:

  1. 这个接口是用来搜集需要导入配置类的
  2. 如果该接口的实现类同时实现EnvironmentAware, BeanFactoryAware ,BeanClassLoaderAware或者ResourceLoaderAware,那么在调用其selectImports方法之前先调用上述接口中对应的方法
  3. 如果需要在所有的@Configuration处理完在导入时可以实现DeferredImportSelector接口

为了验证我们上面分析的,我们看一下SpringBoot中对@Import的使用:@EnableAutoConfiguration这个注解是开启自动配置的注解,该注解上使用了@Import(AutoConfigurationImportSelector.class),我们简单看一下AutoConfigurationImportSelector这个类:

public class AutoConfigurationImportSelector
		implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
		BeanFactoryAware, EnvironmentAware, Ordered {
		}

AutoConfigurationImportSelector正是实现了上述的几个接口。

导入ImportBeanDefinitionRegistrar的实现类

ImportBeanDefinitionRegistrar是spring对外提供动态注册beanDefinition的接口,spring内部大部分套路也是用该接口进行动态注册beanDefinition的。关于其的用法,SpringBoot的@AutoConfigurationPackage注解就是一个典型例子:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

}
/ **
* 我们可以看到Registrar正是实现了ImportBeanDefinitionRegistrar接口
*/
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

   	@Override
   	public void registerBeanDefinitions(AnnotationMetadata metadata,
   			BeanDefinitionRegistry registry) {
   		register(registry, new PackageImport(metadata).getPackageName());
   	}

   	@Override
   	public Set<Object> determineImports(AnnotationMetadata metadata) {
   		return Collections.singleton(new PackageImport(metadata));
   	}
   }

我们再看register方法的实现:

// 我在这个方法内打了断点,发现packageNames正是被@SpringBootApplication注解修饰的启动类所在的包
// registry是Spring的Bean管理器
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
		// 判断是否有org.springframework.boot.autoconfigure.AutoConfigurationPackages这个Bean来决定是新增一个bean还是对现有bean进行更新
		if (registry.containsBeanDefinition(BEAN)) {
			BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
			ConstructorArgumentValues constructorArguments = beanDefinition
					.getConstructorArgumentValues();
			constructorArguments.addIndexedArgumentValue(0,
					addBasePackages(constructorArguments, packageNames));
		}
		else {
			GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
			beanDefinition.setBeanClass(BasePackages.class);
			beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0,
					packageNames);
			beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
			// 注册一个新的bean
			registry.registerBeanDefinition(BEAN, beanDefinition);
		}
	}

通过上面的例子,给我们提供了一个向Spring容器中注入Bean的思路。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值