@Import注解的作用是导入其他的配置类或者组件,等同于在applicationContext.xml文件中添加如下配置
<import/>
在通过@Bean注解导入组件比较繁琐时,可以考虑通过@Import导入。
@Import支持三种方式的导入:
下面分别介绍这几种方式。
使用@Import注解可以直接向容器中注入一个普通的类:
/**
* @Auther: ZhangShenao
* @Date: 2018/9/21 10:15
* @Description:Spring配置类
*/
@Configuration
@ComponentScan
@Import(Red.class) //注入一个普通的Red类实例
public class MainConfig {
}
获取容器中的Red实例:
/**
* @Auther: ZhangShenao
* @Date: 2018/9/21 10:17
* @Description:
*/
public class AnnotationMain {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
Map<String, Red> redBeans = applicationContext.getBeansOfType(Red.class);
if (!CollectionUtils.isEmpty(redBeans)){
redBeans.forEach((name, redBean) -> System.err.println("name: " + name + ",bean: " + redBean));
}
}
}
结果如下:
name: william.annotation.entity.Red,bean: william.annotation.entity.Red@431cd9b2
可以看到,通过@Import注解向容器中导入了一个Red实例,该Bean的name是类的全限定类名。
ImportSelector是Spring提供的一个接口,通过实现该接口,可以指定哪些类需要被导入到IoC容器中。
下面自定义ImportSelector,导入指定类的实例:
/**
* @Auther: ZhangShenao
* @Date: 2018/9/28 11:40
* @Description:自定义ImportSelector
*/
public class ColorImportSelector implements ImportSelector{
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//导入指定类的实例
return new String[] {
"william.annotation.entity.Blue",
"william.annotation.entity.Red"
};
}
}
使用@Import导入自定义ImportSelector
@Configuration
@ComponentScan
@Import(ColorImportSelector.class)
public class MainConfig {
}
打印容器中所有Bean的name:
/**
* @Auther: ZhangShenao
* @Date: 2018/9/21 10:17
* @Description:
*/
public class AnnotationMain {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
Arrays.stream(beanDefinitionNames).forEach(System.err::println);
}
}
可看到控制台输出:
william.annotation.entity.Blue
william.annotation.entity.Red
通过@Import导入ImportSelector,可实现自定义Bean的注入,Bean的name同样为类的全限定类名。
ImportBeanDefinitionRegistrar接口的作用同ImportSelector类似,可以在BeanDefinition注册中心中注册额外的BeanDefinition。
首先自定义ImportBeanDefinitionRegistrar实现类,向注册中心中注册BeanDefinition对象
/**
* @Auther: ZhangShenao
* @Date: 2018/9/28 12:50
* @Description:自定义ImportBeanDefinitionRegistrar,向IoC容器中注册额外的BeanDefinition
*/
public class RainbowImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar{
/**
* 向BeanDefinitionRegistry中注册自定义的BeanDefinition
* @param importingClassMetadata @Import注解所在类的注解元信息
* @param registry BeanDefinition注册中心,管理目前容器中所有的BeanDefinition
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
if (registry.containsBeanDefinition("william.annotation.entity.Blue") &&
registry.containsBeanDefinition("william.annotation.entity.Red")){
registry.registerBeanDefinition("rainbow",new RootBeanDefinition(Rainbow.class));
}
}
}
使用@Import注解导入ImportBeanDefinitionRegistrar
@Configuration
@ComponentScan
@Import({ColorImportSelector.class, RainbowImportBeanDefinitionRegistrar.class})
public class MainConfig {
}
打印容器中的Bean实例,可以看到Rainbow实例被注入到了容器中,BeanName是自定义的
william.annotation.entity.Blue
william.annotation.entity.Red
rainbow
SpringBoot中大量使用了@Import注解导入组件,下面以SpringBoot的自动配置原理为例,介绍它的使用。
使用过SpringBoot进行开发的朋友们都很熟悉,通过@SpringBootApplication注解就可以将一个入口类标记为SpringBoot的启动类,然后就可以直接启动一个SpringBoot应用,SpringBoot会将项目所需的各种组件一次性自动注入到IoC容器中。这些自动配置功能是如何实现的呢?
打开@SpringBootApplication源码,可以看到:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication
可以看到一个注解:@EnableAutoConfiguration,SpringBoot正是通过这个注解开启了自动配置功能。查看@EnableAutoConfiguration的源码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
可以看到,@EnableAutoConfiguration使用了@Import注解,导入了AutoConfigurationImportSelector类,正是上面说的ImportSelector的实现类,导入了自动配置相关的组件。查看AutoConfigurationImportSelector的实现:
public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
BeanFactoryAware, EnvironmentAware, Ordered {
private static final String[] NO_IMPORTS = {};
private static final Log logger = LogFactory
.getLog(AutoConfigurationImportSelector.class);
private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";
private ConfigurableListableBeanFactory beanFactory;
private Environment environment;
private ClassLoader beanClassLoader;
private ResourceLoader resourceLoader;
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
try {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
configurations = sort(configurations, autoConfigurationMetadata);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return StringUtils.toStringArray(configurations);
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
}
selectImports()方法的实现,就是拿到META-INF/spring.factories文件中所有的xxxAutoConfiguration类的全限定类名,将他们注入到IoC容器中。
下面以@Import使用ImportSelector为例,分析导入过程的源码。
IoC容器启动时,调用AbstractApplicationContext的refresh()方法。该方法是Spring整个容器十分重要的一个方法,前面章节中对其进行过详细介绍,这里不再赘述。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex);
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
refresh()的处理中有一步是调用所有的BeanFactoryPostProcessor进行后处理,即invokeBeanFactoryPostProcessors()方法。BeanFactoryPostProcessor作用同BeanPostProcessor类似,区别在于BeanFactoryPostProcessor的处理是容器级别的,针对于BeanDefinition进行处理。查看invokeBeanFactoryPostProcessors()方法会调用PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()代理,该方法会扫描容器内部分所有BeanDefinitionRegistryPostProcessor,调用其postProcessBeanDefinitionRegistry()方法,进行BeanDefinition注册的后处理。
BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的子接口,主要功能是在注册BeanDefinition后进行一些后置处理,它的一个重要的实现类是ConfigurationClassPostProcessor,主要处理基于@Configuration配置的组件。ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry()方法会调用processConfigBeanDefinitions()方法,处理基于配置的BeanDefinition,源码如下:
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
//获取所有标记了@Configuration注解的类,封装成BeanDefinitionHolder集合
List<BeanDefinitionHolder> configCandidates = new ArrayList<BeanDefinitionHolder>();
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
//如果没有@Configuraion,理解返回
if (configCandidates.isEmpty()) {
return;
}
//如果@Configuraion配置了@Order,则对其进行排序
Collections.sort(configCandidates, new Comparator<BeanDefinitionHolder>() {
@Override
public int compare(BeanDefinitionHolder bd1, BeanDefinitionHolder bd2) {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;
}
});
SingletonBeanRegistry singletonRegistry = null;
if (registry instanceof SingletonBeanRegistry) {
singletonRegistry = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet && singletonRegistry.containsSingleton(CONFIGURATION_BEAN_NAME_GENERATOR)) {
BeanNameGenerator generator = (BeanNameGenerator) singletonRegistry.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
//解析@Configuration类
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());
do {
parser.parse(candidates);
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
candidates.clear();
if (registry.getBeanDefinitionCount() > candidateNames.length) {
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<String>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<String>();
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
for (String candidateName : newCandidateNames) {
if (!oldCandidateNames.contains(candidateName)) {
BeanDefinition beanDef = registry.getBeanDefinition(candidateName);
if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(beanDef.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(beanDef, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
}
while (!candidates.isEmpty());
if (singletonRegistry != null) {
if (!singletonRegistry.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
singletonRegistry.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
}
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
}
}
该方法的主要功能就是扫描所有标记了@Configuration的类,将其封装成BeanDefinitionHolder集合,并对配置了@Order的元素进行排序。排序后,调用ConfigurationClassParser对象的parse()方法解析@Configuration类。
ConfigurationClassParser类的作用就是对@Configuration类进行解析,核心处理在doProcessConfigurationClass()方法中:
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
processMemberClasses(configClass, sourceClass);
// 解析@PropertySource注解
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
//解析@ComponentScan注解
AnnotationAttributes componentScan = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ComponentScan.class);
if (componentScan != null && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
if (ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(), this.metadataReaderFactory)) {
parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
}
}
}
//解析@Import注解
processImports(configClass, sourceClass, getImports(sourceClass), true);
//解析@ImportResource注解
if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
String[] resources = importResource.getAliasedStringArray("locations", ImportResource.class, sourceClass);
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
...
}
解析@Import的注解在processImports()方法中:
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) throws IOException {
if (importCandidates.isEmpty()) {
return;
}
if (checkForCircularImports && this.importStack.contains(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
//处理基于ImportSelector方式的导入,调用selectImports()方法获取所有需要导入的类的全限定类名
if (candidate.isAssignable(ImportSelector.class)) {
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
invokeAwareMethods(selector);
if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
this.deferredImportSelectors.add(
new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
}
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
//处理基于ImportBeanDefinitionRegistrar方式的导入,调用registerBeanDefinitions()方法注册额外的BeanDefinition
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
invokeAwareMethods(registrar);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
//处理普通Configuration或者Bean的导入
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Exception ex) {
throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
finally {
this.importStack.pop();
}
}
}