6. spring-容器: 注解:@Import

spring容器注册Bean的方式

通常有如下几种方式:

  • ComponentScan + 注解(@Component, @Service, @Controller): 通常是自己写的类。
  • @Bean注解:通常用于导入第三方包中的组件。
  • @Import注解:快速向Spring容器中导入组件
@Import源码
package org.springframework.context.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Indicates one or more <em>component classes</em> to import &mdash; typically
 * {@link Configuration @Configuration} classes.
 *
 * <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>{@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.
 *
 * <p>May be declared at the class level or as a meta-annotation.
 *
 * <p>If XML or other non-{@code @Configuration} bean definition resources need to be
 * imported, use the {@link ImportResource @ImportResource} annotation instead.
 *
 * @author Chris Beams
 * @author Juergen Hoeller
 * @since 3.0
 * @see Configuration
 * @see ImportSelector
 * @see ImportBeanDefinitionRegistrar
 * @see ImportResource
 */
@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();

}

从注释中可得到以下信息:

  • 可导入四种Class: Configuration, ImportSelector, ImportBeanDefinitionRegistrar以及普通的JavaBean
  • 在spring4.2后类似于AnnotationConfigApplicationContext#register功能
  • 该注解可以被定义在类级别或作为一个元注解
  • 如果是XML格式或非@Configuration格式的bean Definition,需要使用@ImportResource注解替代
直接注入Java Bean

此时的Javabean是一个普通类,不需要在@ComponentScan扫描路径,也不需要被加注了@Service, @Component等注解。见以下示例:

新建一个POJO

package win.elegentjs.spring.ioc.imports.javabeans;

import lombok.Data;

@Data
public class Org {

    private String name;

    private String desc;
}

一个非常普通的JavaBean, 使用@Import导入

package win.elegentjs.spring.ioc.imports.javabeans;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@Import(Org.class)
public class OrgConfig {
}

写测试类进行测试,看Org是否注册到spring容器中

package win.elegentjs.spring.ioc.imports.javabeans;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import win.elegentjs.spring.util.ArraysUtil;

@Slf4j
public class OrgImportSample {

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(OrgConfig.class);
        String[] beanDefinitions = context.getBeanDefinitionNames();

        log.info(ArraysUtil.toString(beanDefinitions));
    }
}

// result
2021-05-24 19:40:07.124 [main] INFO  w.e.spring.ioc.imports.javabeans.OrgImportSample-
[org.springframework.context.annotation.internalConfigurationAnnotationProcessor, 
org.springframework.context.annotation.internalAutowiredAnnotationProcessor, 
org.springframework.context.annotation.internalCommonAnnotationProcessor, 
org.springframework.context.event.internalEventListenerProcessor, 
org.springframework.context.event.internalEventListenerFactory, 
orgConfig, 
win.elegentjs.spring.ioc.imports.javabeans.Org, 
]

可以看出Org类被注册到了spring容器中,但有一点不同,通常采用@Configuration, @Component等注册的bean,最后的bean name默认是类名首字母小写的形式。而采用@Import注册的普通bean,bean name是类的完全限定名。

注入ImportSelector

注意:ImportSelector接口是spring中导入外部配置的核心。在SpringBoot的自动化配置和@EnableXXX(功能性注解)都有它的存在。

ImportSelector接口源码
package org.springframework.context.annotation;

import org.springframework.core.type.AnnotationMetadata;

/**
 * Interface to be implemented by types that determine which @{@link Configuration}
 * class(es) should be imported based on a given selection criteria, usually one or
 * more annotation attributes.
 *
 * <p>An {@link ImportSelector} may implement any of the following
 * {@link org.springframework.beans.factory.Aware Aware} interfaces,
 * and their respective methods will be called prior to {@link #selectImports}:
 * <ul>
 * <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li>
 * <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}</li>
 * <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}</li>
 * <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}</li>
 * </ul>
 *
 * <p>Alternatively, the class may provide a single constructor with one or more of
 * the following supported parameter types:
 * <ul>
 * <li>{@link org.springframework.core.env.Environment Environment}</li>
 * <li>{@link org.springframework.beans.factory.BeanFactory BeanFactory}</li>
 * <li>{@link java.lang.ClassLoader ClassLoader}</li>
 * <li>{@link org.springframework.core.io.ResourceLoader ResourceLoader}</li>
 * </ul>
 *
 * <p>{@code ImportSelector} implementations are usually processed in the same way
 * as regular {@code @Import} annotations, however, it is also possible to defer
 * selection of imports until all {@code @Configuration} classes have been processed
 * (see {@link DeferredImportSelector} for details).
 *
 * @author Chris Beams
 * @since 3.1
 * @see DeferredImportSelector
 * @see Import
 * @see ImportBeanDefinitionRegistrar
 * @see Configuration
 */
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);

}

从文档的注释中可得到以下信息:

  • 它的主要作用是收集需要导入的配置类,selectImports方法的返回值就是我们向Spring容器中导入的类的全类名。

  • 如果ImportSelector接口的实现类同时实现了各种Aware接口,如:EnvironmentAware, BeanFactoryAware, BeanClassLoaderAware, ResourceLoaderAware。那么在调用selectImports之前上面接口的对应方法会先得到执行。这句话理解起来其实不难:上面各种XXXAware是spring容器的实例化机制,容器检测到当前类实现了各种XXXAware会优先调用接口实现方法注入对应对象。

  • 如果有需要控制加载时机,即需要在所有的@Configuration处理完成后再处理@Import导入,可实现DeferredImportSelector接口

分析AnnotationMetadata
/**
	 * 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);

AnnotationMetadata是selectImports方法的参数,该参数能获取到当前标注@Import注解的类的所有注解信息。

ImportSelector接口实例

自定义ImportSelector

package win.elegentjs.spring.ioc.imports.selector;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import win.elegentjs.spring.ioc.imports.javabeans.Org;

/**
 * 自定义的ImportSelector,实现导入Org Bean,
 * bean name: win.elegentjs.spring.ioc.imports.javabeans.Org
 */
public class MyImportSelector implements ImportSelector, BeanFactoryAware {
    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[] {
                Org.class.getName()
        };
    }
}

再做Import导入

package win.elegentjs.spring.ioc.imports.selector;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@Import(MyImportSelector.class)
public class MyImportSelectorConfig {}

最后写测试类测试

package win.elegentjs.spring.ioc.imports.selector;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import win.elegentjs.spring.ioc.imports.javabeans.OrgConfig;
import win.elegentjs.spring.util.ArraysUtil;

@Slf4j
public class MyImportSelectorSample {

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(MyImportSelectorConfig.class);
        String[] beanDefinitions = context.getBeanDefinitionNames();

        log.info(ArraysUtil.toString(beanDefinitions));
    }
}

// result

2021-05-24 20:24:16.255 [main] INFO  w.e.s.ioc.imports.selector.MyImportSelectorSample-
[org.springframework.context.annotation.internalConfigurationAnnotationProcessor, 
org.springframework.context.annotation.internalAutowiredAnnotationProcessor, 
org.springframework.context.annotation.internalCommonAnnotationProcessor, 
org.springframework.context.event.internalEventListenerProcessor, 
org.springframework.context.event.internalEventListenerFactory, 
myImportSelectorConfig, 
win.elegentjs.spring.ioc.imports.javabeans.Org, 
]

分析@EnableTransactionManagement注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {

上面为@EnableTransactionManagement的源码,只保留了注解的定义部分。其他细节等后面讲到事务时再详细讲解。

从上面源码可以看出这是一个复合注解,可以加载到类上,该注解上标注了@Import注解,当@EnableTransactionManagement加注到类上时,间接的@Import也会被注解。

下面分析下TransactionManagementConfigurationSelector类,源码如下:

package org.springframework.transaction.annotation;

import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.AdviceModeImportSelector;
import org.springframework.context.annotation.AutoProxyRegistrar;
import org.springframework.transaction.config.TransactionManagementConfigUtils;
import org.springframework.util.ClassUtils;

/**
 * Selects which implementation of {@link AbstractTransactionManagementConfiguration}
 * should be used based on the value of {@link EnableTransactionManagement#mode} on the
 * importing {@code @Configuration} class.
 *
 * @author Chris Beams
 * @author Juergen Hoeller
 * @since 3.1
 * @see EnableTransactionManagement
 * @see ProxyTransactionManagementConfiguration
 * @see TransactionManagementConfigUtils#TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME
 * @see TransactionManagementConfigUtils#JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME
 */
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {

	/**
	 * Returns {@link ProxyTransactionManagementConfiguration} or
	 * {@code AspectJ(Jta)TransactionManagementConfiguration} for {@code PROXY}
	 * and {@code ASPECTJ} values of {@link EnableTransactionManagement#mode()},
	 * respectively.
	 */
	@Override
	protected String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return new String[] {AutoProxyRegistrar.class.getName(),
						ProxyTransactionManagementConfiguration.class.getName()};
			case ASPECTJ:
				return new String[] {determineTransactionAspectClass()};
			default:
				return null;
		}
	}

	private String determineTransactionAspectClass() {
		return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ?
				TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME :
				TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME);
	}

}

该类的继承体系如下:

在这里插入图片描述
在父类AdviceModeImportSelector中实现了ImportSelector接口,我们看下它的实现:

/**
	 * This implementation resolves the type of annotation from generic metadata and
	 * validates that (a) the annotation is in fact present on the importing
	 * {@code @Configuration} class and (b) that the given annotation has an
	 * {@linkplain #getAdviceModeAttributeName() advice mode attribute} of type
	 * {@link AdviceMode}.
	 * <p>The {@link #selectImports(AdviceMode)} method is then invoked, allowing the
	 * concrete implementation to choose imports in a safe and convenient fashion.
	 * @throws IllegalArgumentException if expected annotation {@code A} is not present
	 * on the importing {@code @Configuration} class or if {@link #selectImports(AdviceMode)}
	 * returns {@code null}
	 */
	@Override
	public final String[] selectImports(AnnotationMetadata importingClassMetadata) {
		Class<?> annType = GenericTypeResolver.resolveTypeArgument(getClass(), AdviceModeImportSelector.class);
		Assert.state(annType != null, "Unresolvable type argument for AdviceModeImportSelector");

		AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
		if (attributes == null) {
			throw new IllegalArgumentException(String.format(
					"@%s is not present on importing class '%s' as expected",
					annType.getSimpleName(), importingClassMetadata.getClassName()));
		}

		AdviceMode adviceMode = attributes.getEnum(getAdviceModeAttributeName());
		String[] imports = selectImports(adviceMode);
		if (imports == null) {
			throw new IllegalArgumentException("Unknown AdviceMode: " + adviceMode);
		}
		return imports;
	}

它实现的核心是找出配置的AdviceMode,然后交由子类的selectImports方法实现,返回的Class类数组就是要加载的Bean。

以下是TransactionManagementConfigurationSelector部分源码:

/**
	 * Returns {@link ProxyTransactionManagementConfiguration} or
	 * {@code AspectJ(Jta)TransactionManagementConfiguration} for {@code PROXY}
	 * and {@code ASPECTJ} values of {@link EnableTransactionManagement#mode()},
	 * respectively.
	 */
	@Override
	protected String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return new String[] {AutoProxyRegistrar.class.getName(),
						ProxyTransactionManagementConfiguration.class.getName()};
			case ASPECTJ:
				return new String[] {determineTransactionAspectClass()};
			default:
				return null;
		}
	}
自己实现一个@EnableXX注解

定义一个Enable注解

package win.elegentjs.spring.ioc.imports.enables;

import org.springframework.context.annotation.Import;
import win.elegentjs.spring.ioc.imports.selector.MyImportSelector;

import java.lang.annotation.*;

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

}

定义一个Config引入该注解

package win.elegentjs.spring.ioc.imports.enables;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import win.elegentjs.spring.ioc.imports.javabeans.Org;

@Configuration
@EnableOrg
public class EnableOrgConfig {

}

测试,看执行效果

package win.elegentjs.spring.ioc.imports.enables;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import win.elegentjs.spring.ioc.imports.selector.MyImportSelectorConfig;
import win.elegentjs.spring.util.ArraysUtil;

@Slf4j
public class MyEnableOrgSample {

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(EnableOrgConfig.class);
        String[] beanDefinitions = context.getBeanDefinitionNames();

        log.info(ArraysUtil.toString(beanDefinitions));
    }
}

// result:
2021-05-25 11:08:14.649 [main] INFO  w.e.spring.ioc.imports.enables.MyEnableOrgSample-
[org.springframework.context.annotation.internalConfigurationAnnotationProcessor, 
org.springframework.context.annotation.internalAutowiredAnnotationProcessor, 
org.springframework.context.annotation.internalCommonAnnotationProcessor, 
org.springframework.context.event.internalEventListenerProcessor, 
org.springframework.context.event.internalEventListenerFactory, 
enableOrgConfig, 
win.elegentjs.spring.ioc.imports.javabeans.Org, 
]

可以看出同样可以加载自定义的Bean。

ImportSelector在哪里被Spring容器解析

上面演示了自定义的ImportSelector可以被容器识别,那容器如何解析并加载Bean的,这里给个大概的实现,具体的过程后面学习spring容器加载原理时会详细介绍。

ConfigurationClassParser的processImports方法

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
                            Collection<SourceClass> importCandidates, boolean checkForCircularImports) {

    if (importCandidates.isEmpty()) {
        return;
    }

    if (checkForCircularImports && isChainedImportOnStack(configClass)) {
        this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
    }
    else {
        this.importStack.push(configClass);
        try {
            for (SourceClass candidate : importCandidates) {            //对ImportSelector的处理
                if (candidate.isAssignable(ImportSelector.class)) {
                    // Candidate class is an ImportSelector -> delegate to it to determine imports
                    Class<?> candidateClass = candidate.loadClass();
                    ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
                    ParserStrategyUtils.invokeAwareMethods(
                        selector, this.environment, this.resourceLoader, this.registry);
                    if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {                //如果为延迟导入处理则加入集合当中
                        this.deferredImportSelectors.add(
                            new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
                    }
                    else {                //根据ImportSelector方法的返回值来进行递归操作
                        String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                        Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
                        processImports(configClass, currentSourceClass, importSourceClasses, false);
                    }
                }
                else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                    // Candidate class is an ImportBeanDefinitionRegistrar ->
                    // delegate to it to register additional bean definitions
                    Class<?> candidateClass = candidate.loadClass();
                    ImportBeanDefinitionRegistrar registrar =
                        BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
                    ParserStrategyUtils.invokeAwareMethods(
                        registrar, this.environment, this.resourceLoader, this.registry);
                    configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
                }
                else {              // 如果当前的类既不是ImportSelector也不是ImportBeanDefinitionRegistar就进行@Configuration的解析处理
                    // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
                    // process it as an @Configuration class
                    this.importStack.registerImport(
                        currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
                    processConfigurationClass(candidate.asConfigClass(configClass));
                }
            }
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(
                "Failed to process import candidates for configuration class [" +
                configClass.getMetadata().getClassName() + "]", ex);
        }
        finally {
            this.importStack.pop();
        }
    }
}

使用ImportBeanDefinitionRegistrar注册Bean

这是使用@Import注入的第三种方式,先看源码:

package org.springframework.context.annotation;

import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.core.type.AnnotationMetadata;

/**
 * Interface to be implemented by types that register additional bean definitions when
 * processing @{@link Configuration} classes. Useful when operating at the bean definition
 * level (as opposed to {@code @Bean} method/instance level) is desired or necessary.
 *
 * <p>Along with {@code @Configuration} and {@link ImportSelector}, classes of this type
 * may be provided to the @{@link Import} annotation (or may also be returned from an
 * {@code ImportSelector}).
 *
 * <p>An {@link ImportBeanDefinitionRegistrar} may implement any of the following
 * {@link org.springframework.beans.factory.Aware Aware} interfaces, and their respective
 * methods will be called prior to {@link #registerBeanDefinitions}:
 * <ul>
 * <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li>
 * <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}
 * <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}
 * <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}
 * </ul>
 *
 * <p>Alternatively, the class may provide a single constructor with one or more of
 * the following supported parameter types:
 * <ul>
 * <li>{@link org.springframework.core.env.Environment Environment}</li>
 * <li>{@link org.springframework.beans.factory.BeanFactory BeanFactory}</li>
 * <li>{@link java.lang.ClassLoader ClassLoader}</li>
 * <li>{@link org.springframework.core.io.ResourceLoader ResourceLoader}</li>
 * </ul>
 *
 * <p>See implementations and associated unit tests for usage examples.
 *
 * @author Chris Beams
 * @author Juergen Hoeller
 * @since 3.1
 * @see Import
 * @see ImportSelector
 * @see Configuration
 */
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) {
	}

}

从源码看出ImportBeanDefinitionRegistrar是一个接口,通过registerBeanDefinitions()方法,我们可以向Spring容器注册Bean实例。Spring官方在注册Bean时,大部分是使用registerBeanDefinitions接口。

所有实现了该接口的类会被ConfigurationClassPostProcessor处理,该类实现了BeanFactoryPostProcessor接口,所以ImportBeanDefinitionRegistrar中注册的Bean是优先于依赖其的bean初始化的,也能被aop, validator等机制处理。

使用方式

需要搭配@Configuration, @Import使用。 以下为使用示例:

自定义ImportBeanDefinitionRegistrar,注册beanDefinition, bean name是org。

package win.elegentjs.spring.ioc.imports.registrar;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import win.elegentjs.spring.ioc.imports.javabeans.Org;

/**
 * 自定义的ImportBeanDefinitionRegistrar
 */
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    /**
     * AnnotationMetadata: 当前类的注解信息
     * BeanDefinitionRegistry:BeanDefinition注册类
     * 通过调用BeanDefinitionRegistry接口的registerBeanDefinition()方法,可以将所有需要添加到容器中的bean注入到容器中。
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry){
        BeanDefinition beanDefinition = new RootBeanDefinition(Org.class);
        registry.registerBeanDefinition("org", beanDefinition);
    }
}

使用@Import导入

package win.elegentjs.spring.ioc.imports.registrar;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@Import(MyImportBeanDefinitionRegistrar.class)
public class RegistrarOrgConfig {

}

写测试类

package win.elegentjs.spring.ioc.imports.registrar;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import win.elegentjs.spring.util.ArraysUtil;

@Slf4j
public class RegistrarOrgSample {

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(RegistrarOrgConfig.class);
        String[] beanDefinitions = context.getBeanDefinitionNames();

        log.info(ArraysUtil.toString(beanDefinitions));
    }
}

//result:
2021-05-25 11:39:21.584 [main] INFO  w.e.s.ioc.imports.registrar.RegistrarOrgSample-
[org.springframework.context.annotation.internalConfigurationAnnotationProcessor, 
org.springframework.context.annotation.internalAutowiredAnnotationProcessor, 
org.springframework.context.annotation.internalCommonAnnotationProcessor, 
org.springframework.context.event.internalEventListenerProcessor, 
org.springframework.context.event.internalEventListenerFactory, 
registrarOrgConfig, 
org, 
]

可以看出自定义的org bean已被注册。

小结

本章讲解了使用@Import注册bean的三种方式:

  • 普通java bean
  • ImportSelector: 自定义的@EnableXXX广泛使用的方式
  • ImportBeanDefinitionRegistrar:很多spring 自带的bean采用的此种方式

Spring容器如何加载各种类型的bean后面会重点介绍,敬请关注。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值