Spring中@Import 用法
@Import 的作用: @Import用来导入@Configuration注解的配置类、注入普通类、导入ImportSelector的实现类或导入ImportBeanDefinitionRegistrar的实现类。源码如下:
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 — 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(); }
由Feign的开启注解@EnableFeignClients引出的Import的使用方法。
开启Feign的注解如下:
package org.springframework.cloud.openfeign; 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; import org.springframework.context.annotation.Import; @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Documented @Import({FeignClientsRegistrar.class}) public @interface EnableFeignClients { String[] value() default {}; String[] basePackages() default {}; Class<?>[] basePackageClasses() default {}; Class<?>[] defaultConfiguration() default {}; Class<?>[] clients() default {}; }
核心是引入了一个FeignClientsRegistrar 类。也就是这个类做了手脚。
1. Import 实践
1. Springboot主启动类: 所在的包是cn.qz 所以默认只能扫描本包及子包
package cn.qz; import cn.qz.user.UserDao; import cn.zz.BeanConfiguration; import cn.zz.DeptDao; import cn.zz.DocumentImportBeanDefinitionRegistrar; import cn.zz.MsgBeanImportSelector; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Import; @ComponentScan @Import({DeptDao.class, BeanConfiguration.class, MsgBeanImportSelector.class, DocumentImportBeanDefinitionRegistrar.class}) //@EnableAspectJAutoProxy public class App { public static void main(String[] args) { //在指定目录下生成动态代理类,我们可以反编译看一下里面到底是一些什么东西 // System.setProperty("cglib.debugLocation", "F:/proxy"); AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(App.class); UserDao bean = applicationContext.getBean(UserDao.class); bean.test1(); } }
2. BeanConfiguration 配置类
package cn.zz; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class BeanConfiguration { @Bean public GroupDao groupDao() { return new GroupDao(); } }
GroupDao
package cn.zz; public class GroupDao { public GroupDao() { System.out.println("GroupDao"); } }
3. DeptDao
package cn.zz; import org.springframework.stereotype.Component; @Component public class DeptDao { public DeptDao() { System.out.println("DeptDao*****"); } public void test1() { System.out.println("UserDao#test1"); } }
4. MsgBeanImportSelector
package cn.zz; import org.springframework.context.annotation.ImportSelector; import org.springframework.core.type.AnnotationMetadata; public class MsgBeanImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{Msg.class.getName()}; } }
Msg
package cn.zz; public class Msg { public Msg() { System.out.println("msg"); } }
5. DocumentImportBeanDefinitionRegistrar
package cn.zz; 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; public class DocumentImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { registerSyntheticBeanIfMissing(registry, "document", Document.class); } // 检查并注册Bean private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name, Class<?> beanClass) { // 检查指定类型的Bean name数组是否存在,如果不存在则创建Bean并注入到容器中 RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass); beanDefinition.setSynthetic(true); // 注册Bean registry.registerBeanDefinition(name, beanDefinition); } }
Document
package cn.zz; public class Document { public Document() { System.out.println("Document"); } }
启动App主类查看控制台:
DeptDao*****
GroupDao
msg
Document
UserDao#test1
2. 机制理解
在之前IoC学习中,了解到配置方式注册BeanDefinition的入口是:org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { List<BeanDefinitionHolder> configCandidates = new ArrayList(); String[] candidateNames = registry.getBeanDefinitionNames(); String[] var4 = candidateNames; int var5 = candidateNames.length; for(int var6 = 0; var6 < var5; ++var6) { String beanName = var4[var6]; BeanDefinition beanDef = registry.getBeanDefinition(beanName); if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) { if (this.logger.isDebugEnabled()) { this.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)); } } if (!configCandidates.isEmpty()) { configCandidates.sort((bd1, bd2) -> { int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); return Integer.compare(i1, i2); }); SingletonBeanRegistry sbr = null; if (registry instanceof SingletonBeanRegistry) { sbr = (SingletonBeanRegistry)registry; if (!this.localBeanNameGeneratorSet) { BeanNameGenerator generator = (BeanNameGenerator)sbr.getSingleton("org.springframework.context.annotation.internalConfigurationBeanNameGenerator"); if (generator != null) { this.componentScanBeanNameGenerator = generator; this.importBeanNameGenerator = generator; } } } if (this.environment == null) { this.environment = new StandardEnvironment(); } ConfigurationClassParser parser = new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); Set<BeanDefinitionHolder> candidates = new LinkedHashSet(configCandidates); HashSet alreadyParsed = new HashSet(configCandidates.size()); do { parser.parse(candidates); parser.validate(); Set<ConfigurationClass> configClasses = new LinkedHashSet(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(Arrays.asList(candidateNames)); Set<String> alreadyParsedClasses = new HashSet(); Iterator var12 = alreadyParsed.iterator(); while(var12.hasNext()) { ConfigurationClass configurationClass = (ConfigurationClass)var12.next(); alreadyParsedClasses.add(configurationClass.getMetadata().getClassName()); } String[] var23 = newCandidateNames; int var24 = newCandidateNames.length; for(int var14 = 0; var14 < var24; ++var14) { String candidateName = var23[var14]; if (!oldCandidateNames.contains(candidateName)) { BeanDefinition bd = registry.getBeanDefinition(candidateName); if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && !alreadyParsedClasses.contains(bd.getBeanClassName())) { candidates.add(new BeanDefinitionHolder(bd, candidateName)); } } } candidateNames = newCandidateNames; } } while(!candidates.isEmpty()); if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) { sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry()); } if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) { ((CachingMetadataReaderFactory)this.metadataReaderFactory).clearCache(); } } }
1. 创建parser以及解析
ConfigurationClassParser parser = new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); Set<BeanDefinitionHolder> candidates = new LinkedHashSet(configCandidates); HashSet alreadyParsed = new HashSet(configCandidates.size()); do { parser.parse(candidates); parser.validate(); ...
org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>) 解析如下:
public void parse(Set<BeanDefinitionHolder> configCandidates) { Iterator var2 = configCandidates.iterator(); while(var2.hasNext()) { BeanDefinitionHolder holder = (BeanDefinitionHolder)var2.next(); BeanDefinition bd = holder.getBeanDefinition(); try { if (bd instanceof AnnotatedBeanDefinition) { this.parse(((AnnotatedBeanDefinition)bd).getMetadata(), holder.getBeanName()); } else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition)bd).hasBeanClass()) { this.parse(((AbstractBeanDefinition)bd).getBeanClass(), holder.getBeanName()); } else { this.parse(bd.getBeanClassName(), holder.getBeanName()); } } catch (BeanDefinitionStoreException var6) { throw var6; } catch (Throwable var7) { throw new BeanDefinitionStoreException("Failed to parse configuration class [" + bd.getBeanClassName() + "]", var7); } } this.deferredImportSelectorHandler.process(); }
接着调用:
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException { this.processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER); } protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException { if (!this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { ConfigurationClass existingClass = (ConfigurationClass)this.configurationClasses.get(configClass); if (existingClass != null) { if (configClass.isImported()) { if (existingClass.isImported()) { existingClass.mergeImportedBy(configClass); } return; } this.configurationClasses.remove(configClass); this.knownSuperclasses.values().removeIf(configClass::equals); } ConfigurationClassParser.SourceClass sourceClass = this.asSourceClass(configClass, filter); do { sourceClass = this.doProcessConfigurationClass(configClass, sourceClass, filter); } while(sourceClass != null); this.configurationClasses.put(configClass, configClass); }
接下来就是到org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass 处理配置信息,Import类也是在这里处理,如下:
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { if (configClass.getMetadata().isAnnotated(Component.class.getName())) { // Recursively process any member (nested) classes first processMemberClasses(configClass, sourceClass); } // Process any @PropertySource annotations for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) { if (this.environment instanceof ConfigurableEnvironment) { processPropertySource(propertySource); } else { logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment"); } } // Process any @ComponentScan annotations Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { for (AnnotationAttributes componentScan : componentScans) { // The config class is annotated with @ComponentScan -> perform the scan immediately Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // Check the set of scanned definitions for any further config classes and parse recursively if needed for (BeanDefinitionHolder holder : scannedBeanDefinitions) { BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); if (bdCand == null) { bdCand = holder.getBeanDefinition(); } if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } } // Process any @Import annotations processImports(configClass, sourceClass, getImports(sourceClass), true); // Process any @ImportResource annotations AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); if (importResource != null) { String[] resources = importResource.getStringArray("locations"); Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader"); for (String resource : resources) { String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); configClass.addImportedResource(resolvedResource, readerClass); } } // Process individual @Bean methods Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass); for (MethodMetadata methodMetadata : beanMethods) { configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } // Process default methods on interfaces processInterfaces(configClass, sourceClass); // Process superclass, if any if (sourceClass.getMetadata().hasSuperClass()) { String superclass = sourceClass.getMetadata().getSuperClassName(); if (superclass != null && !superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) { this.knownSuperclasses.put(superclass, configClass); // Superclass found, return its annotation metadata and recurse return sourceClass.getSuperClass(); } } // No superclass -> processing is complete return null; }
接下来从processImports(configClass, sourceClass, getImports(sourceClass), true); 分析
1. processImports(configClass, sourceClass, getImports(sourceClass), true); 开始处理Import 信息
(1) 首先是getImports 获取所有的配置类:
org.springframework.context.annotation.ConfigurationClassParser#getImports
private Set<ConfigurationClassParser.SourceClass> getImports(ConfigurationClassParser.SourceClass sourceClass) throws IOException { Set<ConfigurationClassParser.SourceClass> imports = new LinkedHashSet(); Set<ConfigurationClassParser.SourceClass> visited = new LinkedHashSet(); this.collectImports(sourceClass, imports, visited); return imports; } private void collectImports(ConfigurationClassParser.SourceClass sourceClass, Set<ConfigurationClassParser.SourceClass> imports, Set<ConfigurationClassParser.SourceClass> visited) throws IOException { if (visited.add(sourceClass)) { Iterator var4 = sourceClass.getAnnotations().iterator(); while(var4.hasNext()) { ConfigurationClassParser.SourceClass annotation = (ConfigurationClassParser.SourceClass)var4.next(); String annName = annotation.getMetadata().getClassName(); if (!annName.equals(Import.class.getName())) { this.collectImports(annotation, imports, visited); } } imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value")); } }
可以看到是递归获取类上注解信息,如果是Import 则获取其value属性,然后返回去。
(2) 经过这一步获取到的Set<ConfigurationClassParser.SourceClass> 信息如下:
2. org.springframework.context.annotation.ConfigurationClassParser#processImports 方法开始处理@Import 上面引入的类,如下:
private void processImports(ConfigurationClass configClass, ConfigurationClassParser.SourceClass currentSourceClass, Collection<ConfigurationClassParser.SourceClass> importCandidates, Predicate<String> exclusionFilter, boolean checkForCircularImports) { if (!importCandidates.isEmpty()) { if (checkForCircularImports && this.isChainedImportOnStack(configClass)) { this.problemReporter.error(new ConfigurationClassParser.CircularImportProblem(configClass, this.importStack)); } else { this.importStack.push(configClass); try { Iterator var6 = importCandidates.iterator(); while(var6.hasNext()) { ConfigurationClassParser.SourceClass candidate = (ConfigurationClassParser.SourceClass)var6.next(); Class candidateClass; if (candidate.isAssignable(ImportSelector.class)) { candidateClass = candidate.loadClass(); ImportSelector selector = (ImportSelector)ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class, this.environment, this.resourceLoader, this.registry); Predicate<String> selectorFilter = selector.getExclusionFilter(); if (selectorFilter != null) { exclusionFilter = exclusionFilter.or(selectorFilter); } if (selector instanceof DeferredImportSelector) { this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector)selector); } else { String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); Collection<ConfigurationClassParser.SourceClass> importSourceClasses = this.asSourceClasses(importClassNames, exclusionFilter); this.processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false); } } else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) { candidateClass = candidate.loadClass(); ImportBeanDefinitionRegistrar registrar = (ImportBeanDefinitionRegistrar)ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class, this.environment, this.resourceLoader, this.registry); configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata()); } else { this.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); this.processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter); } } } catch (BeanDefinitionStoreException var17) { throw var17; } catch (Throwable var18) { throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" + configClass.getMetadata().getClassName() + "]", var18); } finally { this.importStack.pop(); } } } }
可以看到:
如果不是ImportSelector, 也不是ImportBeanDefinitionRegistrar, 会默认作为@Configuration 来处理。
如果是ImportSelector: 先反射创建对象,然后调用其Aware 接口。 然后调用selectImports 返回的类作为配置类再次递归收集配置类
如果是ImportBeanDefinitionRegistrar 类, 先反射创建对象,然后反射Aware 接口,然后把返回的对象缓存起来。
org.springframework.context.annotation.ParserStrategyUtils#invokeAwareMethods 调用的相关接口如下:
public static void invokeAwareMethods(Object parserStrategyBean, Environment environment, ResourceLoader resourceLoader, BeanDefinitionRegistry registry) { if (parserStrategyBean instanceof Aware) { if (parserStrategyBean instanceof BeanClassLoaderAware) { ClassLoader classLoader = (registry instanceof ConfigurableBeanFactory ? ((ConfigurableBeanFactory) registry).getBeanClassLoader() : resourceLoader.getClassLoader()); if (classLoader != null) { ((BeanClassLoaderAware) parserStrategyBean).setBeanClassLoader(classLoader); } } if (parserStrategyBean instanceof BeanFactoryAware && registry instanceof BeanFactory) { ((BeanFactoryAware) parserStrategyBean).setBeanFactory((BeanFactory) registry); } if (parserStrategyBean instanceof EnvironmentAware) { ((EnvironmentAware) parserStrategyBean).setEnvironment(environment); } if (parserStrategyBean instanceof ResourceLoaderAware) { ((ResourceLoaderAware) parserStrategyBean).setResourceLoader(resourceLoader); } } }
@Import({DeptDao.class, BeanConfiguration.class, MsgBeanImportSelector.class, DocumentImportBeanDefinitionRegistrar.class}) 注解如上。
1》先处理 DeptDao.class,走的方法是下面代码:(也就是最后的else里面的代码)
this.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); this.processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException { if (!this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { ConfigurationClass existingClass = (ConfigurationClass)this.configurationClasses.get(configClass); if (existingClass != null) { if (configClass.isImported()) { if (existingClass.isImported()) { existingClass.mergeImportedBy(configClass); } return; } this.configurationClasses.remove(configClass); this.knownSuperclasses.values().removeIf(configClass::equals); } ConfigurationClassParser.SourceClass sourceClass = this.asSourceClass(configClass, filter); do { sourceClass = this.doProcessConfigurationClass(configClass, sourceClass, filter); } while(sourceClass != null); this.configurationClasses.put(configClass, configClass); } }
(1)do 方法块来调用org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass
@Nullable protected final ConfigurationClassParser.SourceClass doProcessConfigurationClass(ConfigurationClass configClass, ConfigurationClassParser.SourceClass sourceClass, Predicate<String> filter) throws IOException { if (configClass.getMetadata().isAnnotated(Component.class.getName())) { this.processMemberClasses(configClass, sourceClass, filter); } Iterator var4 = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class, PropertySource.class).iterator(); AnnotationAttributes importResource; while(var4.hasNext()) { importResource = (AnnotationAttributes)var4.next(); if (this.environment instanceof ConfigurableEnvironment) { this.processPropertySource(importResource); } else { this.logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment"); } } Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { Iterator var14 = componentScans.iterator(); while(var14.hasNext()) { AnnotationAttributes componentScan = (AnnotationAttributes)var14.next(); Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); Iterator var8 = scannedBeanDefinitions.iterator(); while(var8.hasNext()) { BeanDefinitionHolder holder = (BeanDefinitionHolder)var8.next(); BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); if (bdCand == null) { bdCand = holder.getBeanDefinition(); } if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { this.parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } } this.processImports(configClass, sourceClass, this.getImports(sourceClass), filter, true); importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); if (importResource != null) { String[] resources = importResource.getStringArray("locations"); Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader"); String[] var20 = resources; int var22 = resources.length; for(int var23 = 0; var23 < var22; ++var23) { String resource = var20[var23]; String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); configClass.addImportedResource(resolvedResource, readerClass); } } Set<MethodMetadata> beanMethods = this.retrieveBeanMethodMetadata(sourceClass); Iterator var18 = beanMethods.iterator(); while(var18.hasNext()) { MethodMetadata methodMetadata = (MethodMetadata)var18.next(); configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } this.processInterfaces(configClass, sourceClass); if (sourceClass.getMetadata().hasSuperClass()) { String superclass = sourceClass.getMetadata().getSuperClassName(); if (superclass != null && !superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) { this.knownSuperclasses.put(superclass, configClass); return sourceClass.getSuperClass(); } } return null; }
(2) 然后this.configurationClasses.put(configClass, configClass); 添加到配置类中
2》 接下来处理BeanConfiguration,这个也是走最后的else代码块,和上面不同的是调用org.springframework.context.annotation.ConfigurationClassParser#retrieveBeanMethodMetadata 会获取到@Bean 的方法,然后调用下面代码加到缓存中:
while(var18.hasNext()) { MethodMetadata methodMetadata = (MethodMetadata)var18.next(); configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); }
最后调用下面方法将自己加到缓存中:
this.configurationClasses.put(configClass, configClass);
3》接下来处理MsgBeanImportSelector , 走的candidate.isAssignable(ImportSelector.class) 的代码块
然后走下面代码进行处理,也是加到缓存中
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); Collection<ConfigurationClassParser.SourceClass> importSourceClasses = this.asSourceClasses(importClassNames, exclusionFilter); this.processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
4》接下来处理DocumentImportBeanDefinitionRegistrar:
(1)走candidate.isAssignable(ImportBeanDefinitionRegistrar.class) 代码块:
(2) org.springframework.context.annotation.ConfigurationClass#addImportBeanDefinitionRegistrar 添加到缓存中
public void addImportBeanDefinitionRegistrar(ImportBeanDefinitionRegistrar registrar, AnnotationMetadata importingClassMetadata) { this.importBeanDefinitionRegistrars.put(registrar, importingClassMetadata); } private final Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars = new LinkedHashMap();
可以看到上面有个递归调用的过程,如果是普通类和Configuration类会作为配置类,递归做处理,这个稍后验证。
5》 上述流程走完之后加的缓存信息如下:
org.springframework.context.annotation.ConfigurationClassParser#configurationClasses
2. 利用上面的缓存开始生成注册BeanDefinition
Set<ConfigurationClass> configClasses = new LinkedHashSet(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);
org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitions如下:
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) { ConfigurationClassBeanDefinitionReader.TrackedConditionEvaluator trackedConditionEvaluator = new ConfigurationClassBeanDefinitionReader.TrackedConditionEvaluator(); Iterator var3 = configurationModel.iterator(); while(var3.hasNext()) { ConfigurationClass configClass = (ConfigurationClass)var3.next(); this.loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator); } }
也就是上面的五个:
接下来就是生成BeanDefinition信息,org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass:
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass, ConfigurationClassBeanDefinitionReader.TrackedConditionEvaluator trackedConditionEvaluator) { if (trackedConditionEvaluator.shouldSkip(configClass)) { String beanName = configClass.getBeanName(); if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) { this.registry.removeBeanDefinition(beanName); } this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName()); } else { if (configClass.isImported()) { this.registerBeanDefinitionForImportedConfigurationClass(configClass); } Iterator var3 = configClass.getBeanMethods().iterator(); while(var3.hasNext()) { BeanMethod beanMethod = (BeanMethod)var3.next(); this.loadBeanDefinitionsForBeanMethod(beanMethod); } this.loadBeanDefinitionsFromImportedResources(configClass.getImportedResources()); this.loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()); } }
分析org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForBeanMethod 用@Bean 注册的Bean信息
private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) { ConfigurationClass configClass = beanMethod.getConfigurationClass(); MethodMetadata metadata = beanMethod.getMetadata(); String methodName = metadata.getMethodName(); if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) { configClass.skippedBeanMethods.add(methodName); } else if (!configClass.skippedBeanMethods.contains(methodName)) { AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class); Assert.state(bean != null, "No @Bean annotation attributes"); List<String> names = new ArrayList(Arrays.asList(bean.getStringArray("name"))); String beanName = !names.isEmpty() ? (String)names.remove(0) : methodName; Iterator var8 = names.iterator(); while(var8.hasNext()) { String alias = (String)var8.next(); this.registry.registerAlias(beanName, alias); } if (this.isOverriddenByExistingDefinition(beanMethod, beanName)) { if (beanName.equals(beanMethod.getConfigurationClass().getBeanName())) { throw new BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(), beanName, "Bean name derived from @Bean method '" + beanMethod.getMetadata().getMethodName() + "' clashes with bean name for containing configuration class; please make those names unique!"); } } else { ConfigurationClassBeanDefinitionReader.ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinitionReader.ConfigurationClassBeanDefinition(configClass, metadata); beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource())); if (metadata.isStatic()) { if (configClass.getMetadata() instanceof StandardAnnotationMetadata) { beanDef.setBeanClass(((StandardAnnotationMetadata)configClass.getMetadata()).getIntrospectedClass()); } else { beanDef.setBeanClassName(configClass.getMetadata().getClassName()); } beanDef.setUniqueFactoryMethodName(methodName); } else { beanDef.setFactoryBeanName(configClass.getBeanName()); beanDef.setUniqueFactoryMethodName(methodName); } if (metadata instanceof StandardMethodMetadata) { beanDef.setResolvedFactoryMethod(((StandardMethodMetadata)metadata).getIntrospectedMethod()); } beanDef.setAutowireMode(3); beanDef.setAttribute(RequiredAnnotationBeanPostProcessor.SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE); AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata); Autowire autowire = (Autowire)bean.getEnum("autowire"); if (autowire.isAutowire()) { beanDef.setAutowireMode(autowire.value()); } boolean autowireCandidate = bean.getBoolean("autowireCandidate"); if (!autowireCandidate) { beanDef.setAutowireCandidate(false); } String initMethodName = bean.getString("initMethod"); if (StringUtils.hasText(initMethodName)) { beanDef.setInitMethodName(initMethodName); } String destroyMethodName = bean.getString("destroyMethod"); beanDef.setDestroyMethodName(destroyMethodName); ScopedProxyMode proxyMode = ScopedProxyMode.NO; AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(metadata, Scope.class); if (attributes != null) { beanDef.setScope(attributes.getString("value")); proxyMode = (ScopedProxyMode)attributes.getEnum("proxyMode"); if (proxyMode == ScopedProxyMode.DEFAULT) { proxyMode = ScopedProxyMode.NO; } } BeanDefinition beanDefToRegister = beanDef; if (proxyMode != ScopedProxyMode.NO) { BeanDefinitionHolder proxyDef = ScopedProxyCreator.createScopedProxy(new BeanDefinitionHolder(beanDef, beanName), this.registry, proxyMode == ScopedProxyMode.TARGET_CLASS); beanDefToRegister = new ConfigurationClassBeanDefinitionReader.ConfigurationClassBeanDefinition((RootBeanDefinition)proxyDef.getBeanDefinition(), configClass, metadata); } if (logger.isTraceEnabled()) { logger.trace(String.format("Registering bean definition for @Bean method %s.%s()", configClass.getMetadata().getClassName(), beanName)); } this.registry.registerBeanDefinition(beanName, beanDefToRegister); } } }
信息如下:
这里看到了用方法名称作为BeanNAME,然后注入到IoC容器。
最后两行代码如下:
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
第一行是导入@ImportResource 引入的资源文件,这个注解是为了导入 xml 配置的bean, 也就是兼容原来的xml 配置的方式。
第二行代码就是递归遍历ImportBeanDefinitionRegistrar, 调用其registerBeanDefinitions 方法:org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsFromRegistrars 手动进行注入
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) { registrars.forEach((registrar, metadata) -> registrar.registerBeanDefinitions(metadata, this.registry)); }
补充:验证@Import 的普通类不用加@Component 注解,以及@Import 引入的普通类是作为配置类被递归处理的,可以注册Bean
App:
package cn.qz; import cn.qz.user.UserDao; import cn.zz.BeanConfiguration; import cn.zz.DeptDao; import cn.zz.DocumentImportBeanDefinitionRegistrar; import cn.zz.MsgBeanImportSelector; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Import; @ComponentScan @Import({DeptDao.class, BeanConfiguration.class, MsgBeanImportSelector.class, DocumentImportBeanDefinitionRegistrar.class}) //@EnableAspectJAutoProxy public class App { public static void main(String[] args) { //在指定目录下生成动态代理类,我们可以反编译看一下里面到底是一些什么东西 // System.setProperty("cglib.debugLocation", "F:/proxy"); AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(App.class); UserDao bean = applicationContext.getBean(UserDao.class); bean.test1(); } }
Dept:
package cn.zz; import org.springframework.context.annotation.Bean; public class DeptDao { public DeptDao() { System.out.println("DeptDao*****"); } public void test1() { System.out.println("UserDao#test1"); } @Bean public PackageDao packageDao() { return new PackageDao(); } }
Package:
package cn.zz; public class PackageDao { public PackageDao() { System.out.println("PackageDao"); } }
结果:
DeptDao*****
PackageDao
GroupDao
msg
Document
UserDao#test1
补充: 普通类作为配置类使用是因为递归调用了调用org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass 方法来处理,这个里面也处理了@Import,因此@Import 也可以被传递调用
Dept:
package cn.zz; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; @Import(Product.class) public class DeptDao { public DeptDao() { System.out.println("DeptDao*****"); } public void test1() { System.out.println("UserDao#test1"); } @Bean public PackageDao packageDao() { return new PackageDao(); } }
Product:
package cn.zz; public class Product { public Product() { System.out.println("product+++"); } }
结果:
product+++ DeptDao***** PackageDao GroupDao msg Document UserDao#test1
总结:@Import用来导入@Configuration注解的配置类、注入普通类、导入ImportSelector的实现类或导入ImportBeanDefinitionRegistrar的实现类。同时由于处理的时候是递归处理,因此在@Import 引入的类又可以@Import 其他类。@Import 导入的类可以不写@Component注解或者其他注册注解,默认会作为配置类(Configuration)处理,这样在不写Configuration的前提下可以用@Bean 注入Bean信息。