我们在使用Spring注入Bean对象时,会使用不同注解,比如@Component @Service @Controller @Import @Bean等。由于@Service @Controller 等都可以归为@Component,那么@Component 和@Import 、@Bean是何时被加载的,以及他们之间的顺序呢,下面就来分析一下。
首先Spring的启动肯定是由AbstractApplicationContext.refresh()方法开始的。之后直接进入invokeBeanFactoryPostProcessors(beanFactory)方法,该方法用来处理要载入的Bean对象。会进入PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors())方法。
之后会进入ConfigurationClassPostProcessor类的postProcessBeanDefinitionRegistry方法。
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {int registryId = System.identityHashCode(registry);if (this.registriesPostProcessed.contains(registryId)) {throw new IllegalStateException("postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);}if (this.factoriesPostProcessed.contains(registryId)) {throw new IllegalStateException("postProcessBeanFactory already called on this post-processor against " + registry);}this.registriesPostProcessed.add(registryId);processConfigBeanDefinitions(registry);}
进入processConfigBeanDefinitions(registry);该方法主要加载就是如下这段
........................
ConfigurationClassParser parser = new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter, this.environment,this.resourceLoader, this.componentScanBeanNameGenerator, registry);Set candidates = new LinkedHashSet<>(configCandidates);Set alreadyParsed = new HashSet<>(configCandidates.size());do {parser.parse(candidates);parser.validate();Set configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());configClasses.removeAll(alreadyParsed);// Read the model and create bean definitions based on its contentif (this.reader == null) {this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor, this.resourceLoader, this.environment,this.importBeanNameGenerator, parser.getImportRegistry());}this.reader.loadBeanDefinitions(configClasses);}
........................
这段parser.parse(candidates);中candidates就是最初启动的bean对象名字,比如springboot应用中的主类带@Springboot标识的,如下
@SpringBootApplication
public class DemoApplication {public static void main(String[] args) {new SpringApplication(DemoApplication.class).run(args);}
}
那么candidates 就会包含这个DemoApplication 。进入parser.parse(candidates)方法
public void parse(Set configCandidates) {for (BeanDefinitionHolder holder : configCandidates) {BeanDefinition bd = holder.getBeanDefinition();try {if (bd instanceof AnnotatedBeanDefinition) {parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());}else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());}else {parse(bd.getBeanClassName(), holder.getBeanName());}}catch (BeanDefinitionStoreException ex) {throw ex;}catch (Throwable ex) {throw new BeanDefinitionStoreException("Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);}}this.deferredImportSelectorHandler.process();}
会继续进行解析,根据不同类型,但一般都是AnnotatedBeanDefinition。之后继续进入
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {processConfigurationClass(new ConfigurationClass(metadata, beanName));}protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {return;}ConfigurationClass existingClass = this.configurationClasses.get(configClass);if (existingClass != null) {if (configClass.isImported()) {if (existingClass.isImported()) {existingClass.mergeImportedBy(configClass);}// Otherwise ignore new imported config class; existing non-imported class overrides it.return;}else {// Explicit bean definition found, probably replacing an import.// Let's remove the old one and go with the new one.this.configurationClasses.remove(configClass);this.knownSuperclasses.values().removeIf(configClass::equals);}}// Recursively process the configuration class and its superclass hierarchy.SourceClass sourceClass = asSourceClass(configClass);do {sourceClass = doProcessConfigurationClass(configClass, sourceClass);}while (sourceClass != null);this.configurationClasses.put(configClass, configClass);}
主要就是将 DemoApplication 封装成ConfigurationClass类。然后通过asSourceClass(configClass)解析出原始类DemoApplication.class。之后调用doProcessConfigurationClass方法
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)throws IOException {// **1**if (configClass.getMetadata().isAnnotated(Component.class.getName())) {// Recursively process any member (nested) classes firstprocessMemberClasses(configClass, sourceClass);}// Process any @PropertySource annotationsfor (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");}}// **2**Set 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// ** 2.1 **Set scannedBeanDefinitions =this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());// Check the set of scanned definitions for any further config classes and parse recursively if neededfor (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// **3**processImports(configClass, sourceClass, getImports(sourceClass), true);// Process any @ImportResource annotationsAnnotationAttributes 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// **4**Set beanMethods = retrieveBeanMethodMetadata(sourceClass);for (MethodMetadata methodMetadata : beanMethods) {configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));}// Process default methods on interfacesprocessInterfaces(configClass, sourceClass);// Process superclass, if anyif (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 recursereturn sourceClass.getSuperClass();}}// No superclass -> processing is completereturn null;}
// **1** :处理内部类。比如我有一个@Component的类,里面有内部类A,且内部类中也有包含@Bean、@Component、@Import的注解,那么会继续处理内部类A。
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {Collection memberClasses = sourceClass.getMemberClasses();if (!memberClasses.isEmpty()) {List candidates = new ArrayList<>(memberClasses.size());for (SourceClass memberClass : memberClasses) {if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&!memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {candidates.add(memberClass);}}OrderComparator.sort(candidates);for (SourceClass candidate : candidates) {if (this.importStack.contains(configClass)) {this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));}else {this.importStack.push(configClass);try {processConfigurationClass(candidate.asConfigClass(configClass));}finally {this.importStack.pop();}}}}}
在处理内部类中,会继续调用processConfigurationClass方法。此时的入参会将内部类A封装成ConfigurationClass类。
// **2** :处理@Component中包含@ComponentScans注解的,去扫描其他Component对象,并直接注册成Bean。在 // ** 2.1 ** 方法中实现。
// **3** :处理@Import注解。该方法会将@Import分为三类。一类是ImportSelector,例如EnableAutoConfiguration中的AutoConfigurationImportSelector。第二类是ImportBeanDefinitionRegistrar。第三类就当作@Component继续处理。代码如下:
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,Collection 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) {if (candidate.isAssignable(ImportSelector.class)) {// Candidate class is an ImportSelector -> delegate to it to determine importsClass> candidateClass = candidate.loadClass();ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);ParserStrategyUtils.invokeAwareMethods(selector, this.environment, this.resourceLoader, this.registry);if (selector instanceof DeferredImportSelector) {this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);}else {String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());Collection 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 definitionsClass> 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 {// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->// process it as an @Configuration classthis.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();}}}
// **4** :处理类中带@Bean的方法。Set
private Set retrieveBeanMethodMetadata(SourceClass sourceClass) {AnnotationMetadata original = sourceClass.getMetadata();Set beanMethods = original.getAnnotatedMethods(Bean.class.getName());..............}
并且加入到之前封装的configClass的beanMethod方法中
Set beanMethods = retrieveBeanMethodMetadata(sourceClass);for (MethodMetadata methodMetadata : beanMethods) {configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));}
至此,刚才的processConfigBeanDefinitions(registry);方法的parse方法就完事了。把上面的代码站下来。
Set candidates = new LinkedHashSet<>(configCandidates);Set alreadyParsed = new HashSet<>(configCandidates.size());do {parser.parse(candidates);parser.validate();Set configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());configClasses.removeAll(alreadyParsed);// Read the model and create bean definitions based on its contentif (this.reader == null) {this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor, this.resourceLoader, this.environment,this.importBeanNameGenerator, parser.getImportRegistry());}this.reader.loadBeanDefinitions(configClasses);
执行完 parser.parse(candidates) ,此时Spring容器中装载的Bean有 DemoApplication 扫描下的所有@Component注解的类,以及该类中包含@ComponentScan路径下带有@Component注解的类。至于内部类和Import中的第二类ImportBeanDefinitionRegistrar以及BeanMethod还没有装载。
继续执行this.reader.loadBeanDefinitions(configClasses);
public void loadBeanDefinitions(Set configurationModel) {TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();for (ConfigurationClass configClass : configurationModel) {loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);}}private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {// 1if (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());return;}// 2if (configClass.isImported()) {registerBeanDefinitionForImportedConfigurationClass(configClass);}// 3for (BeanMethod beanMethod : configClass.getBeanMethods()) {loadBeanDefinitionsForBeanMethod(beanMethod);}loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());// 4loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());}
//1:判断之前生成的configclass 是否符合注入条件,就是判断包含的@Conditional注解。如果不符合直接return
//2:判断是否为内部类,如果是且带@Component就注入。比如 A 类中有内部类B,且类B也有@Component注解,那么此时Spring容器才会将B注入。
//3:将之前类中包含@Bean的method方法,注入Spring容器中,当作Bean对象。
//4:注入实现ImportBeanDefinitionRegistrar接口的类,比如aop的AspectJAutoProxyRegistrar。
1、执行完 parser.parse(candidates) ,此时Spring容器中装载的Bean有 DemoApplication 扫描下的所有@Component注解的类,以及该类中包含@ComponentScan路径下带有@Component注解的类。
2、注入内部类
3、注入Bean方法
4、注入实现ImportBeanDefinitionRegistrar接口的类
上一篇:吃透8图1模板,人人可以做架构
下一篇:趣味三角——第12章——tanx