程序员最近都爱上了这个网站  程序员们快来瞅瞅吧!  it98k网:it98k.com

本站消息

站长简介/公众号

  出租广告位,需要合作请联系站长


+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

2023-06(3)

重读Spring系列(2)-一些配置注解的使用与源码解析

发布于2021-06-12 14:08     阅读(214)     评论(0)     点赞(9)     收藏(2)


最开始的Spring系列我主要着重的是整个代码逻辑的实力,到现在有些都快一年了,或者也有几个月了,自己现在去看也有些看不下去了,毕竟代码逻辑有些复杂。并且自己以前也是刚开始了解Spring的源码,也是主要想了解Spring的代码到底是怎样的处理的。现在准备重新梳理下Spring我想了解的内容,这次我们不陷入代码的具体的实现,我们主要梳理Spring的一些结构、逻辑实现,同时也整理梳理过程中的一些Spring的组件用法。

​ 我们这次主要是梳理下Spring中的一些引入直接:@ComponentComponentScansComponentScan@Bean@PropertySources@PropertySource,以及与@Import以及配套的ImportSelectorImportBeanDefinitionRegistrar

一、使用&结构分析

1、@Component

1)、结构、子类关系

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

   String value() default "";

}

​ 这个接口是用来引入组件的(各种类型的组件),其的属性value是用来指定这个组件的名称的。这个注解的功能也是注入一个Bean,类似于@Bean的功能,只是这个直接有更多的用法,并且我们一般并不自动使用这个注解,而是使用引用它的组件注解:ServiceConfigurationRepositoryController这些。我们可以看下有引用它的注解:

在这里插入图片描述

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {

	@AliasFor(annotation = Component.class)
	String value() default "";

}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {

   @AliasFor(annotation = Component.class)
   String value() default "";

}

​ 而这些注解的用法我们都已经知道了,比较简单,我们就不再看其的用法了,所以我们使用的@Service@Controller这些注解,Spring在底层扫描都是看有没有@Component

2、@ComponentScan & @Import的使用

​ 这两个都是用来扫描组件的,其中@Component是用来扫描指定包下面的组件,@Import是用来加载指定的类资源。

1)、结构

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

   Class<?>[] value();

}

​ 这个是用来导入指定的类,可以为@ConfigurationImportSelector这样的配置加载组件,也可以为一般的组件例如注入简单的Bean对象。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {

	@AliasFor("basePackages")
	String[] value() default {};

​ 这个是用来注入扫描包的,但其实这个注解还可以有一些其他的参数,这个就先不研究了,一般是使用这个简单的用法。

然后@ComponentScans是用来指定多个@ComponentScan的我们就不再额外说明了:

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

   ComponentScan[] value();

}

2)、demo使用

​ 这里我们使用官方的简单demo测试用例。

@Test
public void componentScanOverlapsWithImport() {
   AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
   ctx.register(Config1.class);
   ctx.register(Config2.class);
   ctx.refresh(); // no conflicts found trying to register SimpleComponent
   ctx.getBean(SimpleComponent.class); // succeeds -> there is only one bean of type SimpleComponent
}
@Test
public void componentScanViaImport() {
   AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
   ctx.register(Config3.class);
   ctx.refresh();
   ctx.getBean(SimpleComponent.class);
}
@ComponentScan("org.springframework.context.annotation.componentscan.simple")
static final class Config1 {
}


@Import(org.springframework.context.annotation.componentscan.simple.SimpleComponent.class)
static final class Config2 {
}


@Import(ImportedConfig.class)
static final class Config3 {
}

@ComponentScan("org.springframework.context.annotation.componentscan.simple")
@ComponentScan("org.springframework.context.annotation.componentscan.importing")
public static final class ImportedConfig {
}
package org.springframework.context.annotation.componentscan.simple;

import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
public class SimpleComponent {

   @Bean
   public String exampleBean() {
      return "example";
   }

}
package org.springframework.context.annotation.componentscan.simple;

import org.springframework.stereotype.Component;

public class ClassWithNestedComponents {

   @Component
   public static class NestedComponent extends ClassWithNestedComponents {
   }

   @Component
   public static class OtherNestedComponent extends ClassWithNestedComponents {
   }

}

​ 这个就是官方的简单测试用例,其中Config1是使用的@ComponentScan来扫描包的,Config2是使用的@Import扫描具体的Bean资源,然后Config3是通过@Import再去引入其他配置组件,这里是@ComponentScan。这上面3个都能加载到SimpleComponent类。

​ 同时在componentScanOverlapsWithImport()方法中是有将SimpleComponent加载两次的,同时并没有什么一次,这说明Spring内部是有重复判断的。这里问题1,到时候源码的时候我能看其是怎样处理的。

3、@ImportResource

1)、结构

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

   @AliasFor("locations")
   String[] value() default {};

   @AliasFor("value")
   String[] locations() default {};

   Class<? extends BeanDefinitionReader> reader() default BeanDefinitionReader.class;

}

​ 这个就是用来加载资源的,当我们有些不想用注解,或者注解不能满足,还是想用配置文件的时候我们就可以使用这个注解来加载资源。这里的reader()是用来指定读取解析器的,就是使用指定的BeanDefinitionReader来读取这个加载的资源。

2)、demo1使用

@Configuration
@ImportResource("classpath:org/springframework/context/annotation/configuration/ImportXmlConfig-context.xml")
static class ImportXmlAutowiredConfig {
   @Autowired TestBean xmlDeclaredBean;

   public @Bean String xmlBeanName() {
      return xmlDeclaredBean.getName();
   }
}
@Test
public void importXmlWithAutowiredConfig() {
   AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ImportXmlAutowiredConfig.class);
   String name = ctx.getBean("xmlBeanName", String.class);
   assertThat(name, equalTo("xml.declared"));
   ctx.close();
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">

   <bean id="xmlDeclaredBean" class="org.springframework.tests.sample.beans.TestBean">
      <constructor-arg value="xml.declared"/>
   </bean>

   <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
      <property name="properties">
         <map>
            <entry key="name" value="myName"/>
         </map>
      </property>
   </bean>

   <!-- should not cause infinite loop (SPR-11858) but rather simply be ignored -->
   <bean class="org.springframework.context.annotation.ConfigurationClassPostProcessor"/>

</beans>

​ 通过这个demo我们了解,当我们不想使用@Configuration注解去加载内容的,时候就可以使用这个@ImportResource注解去加载xml配置。同时我们可以看到在ImportXmlAutowiredConfig类上面是有一个@Configuration注解的,但其实在这里这个注解更多的是一个象征其是配置类的意义,可以不加这个注解,Spring也可以扫描到这个@ImportResource

3)、demo2使用

​ 我们上面是使用@ImportSource来加载xml,但其实我们还可以其来加载properties文件。

@Configuration
@ImportResource(locations = "classpath:org/springframework/context/annotation/configuration/ImportNonXmlResourceConfig-context.properties", reader = PropertiesBeanDefinitionReader.class)
static class ImportNonXmlResourceConfig {
}
propertiesDeclaredBean.(class)=org.springframework.tests.sample.beans.TestBean
@Test
public void importNonXmlResource() {
   AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ImportNonXmlResourceConfig.class);
   assertTrue(ctx.containsBean("propertiesDeclaredBean"));
   ctx.close();
}

​ 我们可以看这个demo其是用指定PropertiesBeanDefinitionReader来读取加载的资源。不过使用这个PropertiesBeanDefinitionReader我们可以看到其需要写一些特殊的标记,例如这里的.(class),我们再看这个类的内部定义


public static final String CLASS_KEY = "(class)";

public static final String PARENT_KEY = "(parent)";

public static final String SCOPE_KEY = "(scope)";

public static final String SINGLETON_KEY = "(singleton)";

public static final String ABSTRACT_KEY = "(abstract)";

public static final String LAZY_INIT_KEY = "(lazy-init)";

​ 还可以指定这些去配置含义。不过这种方式一般应该很少用。

4、@PropertySource

1)、结构

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(PropertySources.class)
public @interface PropertySource {

   String name() default "";

   String[] value();

​ 同样@PropertySources也是用来加载多个@PropertySource的,我们也不重复讲@PropertySources了。

2)、demo使用

@Configuration
@PropertySource("classpath:org/springframework/context/annotation/p1.properties")
static class ConfigWithImplicitName {

   @Inject Environment env;

   @Bean
   public TestBean testBean() {
      return new TestBean(env.getProperty("testbean.name"));
   }
}
testbean.name=p1TestBean
from.p1=p1Value
base.package=org/springframework/context/annotation
spring.profiles.active=test
@Test
public void withImplicitName() {
   AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
   ctx.register(ConfigWithImplicitName.class);
   ctx.refresh();
   assertTrue("property source p1 was not added",
         ctx.getEnvironment().getPropertySources().contains("class path resource [org/springframework/context/annotation/p1.properties]"));
   assertThat(ctx.getBean(TestBean.class).getName(), equalTo("p1TestBean"));
}
public TestBean(String name) {
   this.name = name;
}

​ 这个demo我们看withImplicitName()方法。这个就是将p1.properties中的内容加载到Environment环境变量中,然后这里还注入了一个Bean对象即TestBean,然后其初始化的时候将p1.properties中设置的testbean.name对应的值设置为其的name

5、@Import以及配套的类

1)、结构

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

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

}

​ 通过这个注释我们了解到,其实用来犹如@Configuration,或配套的ImportSelectorImportBeanDefinitionRegistrar的,又或者一般的组件。

public interface ImportSelector {

   String[] selectImports(AnnotationMetadata importingClassMetadata);

}

​ 这个用来返回扫描筛选的类全路径名。

public interface ImportBeanDefinitionRegistrar {

   void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);

}

​ 这个入参有BeanDefinitionRegistry,就表示其可以注入BeanDefinition。也就是我们可以通过importingClassMetadata从这个入参获取到我们需要的信息,然后动态筛选注册到BeanDefinitionRegistry中。

2)、demo1使用ImportSelector

@Test
public void testWithImporter() {
   ApplicationContext context = new AnnotationConfigApplicationContext(Wrapper.class);
   assertEquals("foo", context.getBean("value"));
}
@Configuration
@Import(Selector.class)
protected static class Wrapper {
}
protected static class Selector implements ImportSelector {
   @Override
   public String[] selectImports(AnnotationMetadata importingClassMetadata) {
      return new String[] {Config.class.getName()};
   }
}

@Configuration
protected static class Config {
   @Bean
   public FooFactoryBean foo() {
      return new FooFactoryBean();
   }
   @Bean
   public String value() throws Exception {
      String name = foo().getObject().getName();
      Assert.state(name != null, "Name cannot be null");
      return name;
   }
   @Bean
   @Conditional(NoBarCondition.class)
   public String bar() throws Exception {
      return "bar";
   }
}

​ 这个demo 就是能通过扫描Config导入其中定义的Bean,然后借由Selector返回这个Config全路径,再由@Import导入到Spring容器中去解析。最后通过上面的方式我们就能获取到foo(FooFactoryBean foo())这个Bean实例了。

3)、demo2使用ImportBeanDefinitionRegistrar

@Test
public void importRegistrar() throws Exception {
   ImportedRegistrar.called = false;
   AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
   ctx.register(ImportingRegistrarConfig.class);
   ctx.refresh();
   assertNotNull(ctx.getBean("registrarImportedBean"));
   assertNotNull(ctx.getBean("otherImportedConfigBean"));
}
@Configuration
@EnableImportRegistrar
static class ImportingRegistrarConfig {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(ImportedRegistrar.class)
public @interface EnableImportRegistrar {
}


static class ImportedRegistrar implements ImportBeanDefinitionRegistrar {

   static boolean called;

   @Override
   public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
      GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
      beanDefinition.setBeanClassName(String.class.getName());
      registry.registerBeanDefinition("registrarImportedBean", beanDefinition);
      GenericBeanDefinition beanDefinition2 = new GenericBeanDefinition();
      beanDefinition2.setBeanClass(OtherImportedConfig.class);
      registry.registerBeanDefinition("registrarImportedConfig", beanDefinition2);
      Assert.state(!called, "ImportedRegistrar called twice");
      called = true;
   }
}

​ 这个demo就是通过ImportedRegistrar来注入一个GenericBeanDefinition,这个GenericBeanDefinition是直接new的当然我们一般可以从AnnotationMetadata获取我们信息来注入相应的内容。

6、@Bean的使用

@Configuration
   static class StandardConfig {

      @Bean
      public TestBean testBean1() {
         return new TestBean("interesting");
      }
   }
@Test
public void testStandard() {
   AnnotationConfigApplicationContext ctx =
         new AnnotationConfigApplicationContext(StandardConfig.class);
   assertFalse(ctx.getBeanFactory().containsSingleton("testBean1"));
}

​ 我们这样就能注入一个Bean了。

二、ConfigurationClassPostProcessor流程源码分析

​ 这里我们使用前面的democomponentScanOverlapsWithImport来debug。

@Test
public void componentScanOverlapsWithImport() {
   AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
   ctx.register(Config1.class);
   ctx.register(Config2.class);
   ctx.refresh(); // no conflicts found trying to register SimpleComponent
   ctx.getBean(SimpleComponent.class); // succeeds -> there is only one bean of type SimpleComponent
}

1、ConfigurationClassPostProcessor

Spring处理扫描上面的这些注解主要就是这个ConfigurationClassPostProcessor类。

public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
      PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {

​ 这里我们主要关注其是实现了BeanDefinitionRegistryPostProcessor接口。

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {

   void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

}

​ 然后主要是这个postProcessBeanDefinitionRegistry方法来触发扫描加载的过程。

2、postProcessBeanDefinitionRegistry方法调用过程

@Override
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);
}

​ 这个方法主要就是从例如@Configuration这样的配置中跟进一步的加载Bean,也就是通过上面的@Component@Import这些加载Bean

​ 这里其主要逻辑处理是processConfigBeanDefinitions方法,而入参BeanDefinitionRegistry就是我们以前就梳理过的加载、保存、创建所有BeanDefinition以及Bean对象的类,一般实现是DefaultListableBeanFactory

3、processConfigBeanDefinitions(BeanDefinitionRegistry registry)

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
   List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
   String[] candidateNames = registry.getBeanDefinitionNames();

   for (String beanName : candidateNames) {
      BeanDefinition beanDef = registry.getBeanDefinition(beanName);
      ............
   }

   // Return immediately if no @Configuration classes were found
   if (configCandidates.isEmpty()) {
      return;
   }

   // 这里会进行排序
   configCandidates.sort((bd1, bd2) -> {
      int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
      int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
      return Integer.compare(i1, i2);
   });
		............

   // Parse each @Configuration class
   ConfigurationClassParser parser = new ConfigurationClassParser(
         this.metadataReaderFactory, this.problemReporter, this.environment,
         this.resourceLoader, this.componentScanBeanNameGenerator, registry);

   Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
   Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
   do {
      parser.parse(candidates);
      ........
   }
   while (!candidates.isEmpty());
		............
   }
}

​ 上面我们简化了一些内容,下面我们就来分布解析

1)、configCandidates添加

List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();

for (String beanName : candidateNames) {
   BeanDefinition beanDef = registry.getBeanDefinition(beanName);
   if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
      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));
   }
}
// Return immediately if no @Configuration classes were found
if (configCandidates.isEmpty()) {
	return;
}
public static boolean isFullConfigurationClass(BeanDefinition beanDef) {
   return CONFIGURATION_CLASS_FULL.equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE));
}

public static boolean isLiteConfigurationClass(BeanDefinition beanDef) {
   return CONFIGURATION_CLASS_LITE.equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE));
}

​ 这里就是从所有加载的BeanDefinition中,筛选是配置类(@Configuration)的BeanDefinition。主要是使用ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory),然后beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null是用来判断这个BeanDefinition是否已经解析为配置类了,如果是就不需要再次解析了,也就是会直接return;了。如果还没有解析,就通过方法判断是不是配置类。

2)、ConfigurationClassUtils.checkConfigurationClassCandidate方法

public static boolean checkConfigurationClassCandidate(
      BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {

   String className = beanDef.getBeanClassName();
   if (className == null || beanDef.getFactoryMethodName() != null) {
      return false;
   }

   AnnotationMetadata metadata;
   if (beanDef instanceof AnnotatedBeanDefinition &&
         className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
      // Can reuse the pre-parsed metadata from the given BeanDefinition...
      metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
   }
   else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass())    {		
       ...............   (不同情况对metadata的赋值),从BeanDefinition中获取metadata
   }

   if (isFullConfigurationCandidate(metadata)) {
		beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
	}
	else if (isLiteConfigurationCandidate(metadata)) {
		beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
	}
	else {
		return false;
	}
		...........
   return true;
}

​ 这个方法主要就是检查BeanDefinition是不是配置类。

​ 通过metadata.getAnnotationAttributes(Configuration.class.getName())看其是有没有@Configuration注解,如果有,这里就加一个标记CONFIGURATION_CLASS_ATTRIBUTE,表明其是一个Configuration配置类,这里就与前面的判断关联上了。

​ 上面isFullConfigurationCandidate是对@Configuration注解的判断,下面的isLiteConfigurationCandidate又是另一个配置类型的判断了。

3)、isFullConfigurationCandidate(AnnotationMetadata metadata)

public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
   return metadata.isAnnotated(Configuration.class.getName());
}

4)、isLiteConfigurationCandidate(AnnotationMetadata metadata)

public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {
   // Do not consider an interface or an annotation...
   if (metadata.isInterface()) {
      return false;
   }

   // Any of the typical annotations found?
   for (String indicator : candidateIndicators) {
      if (metadata.isAnnotated(indicator)) {
         return true;
      }
   }

   // Finally, let's look for @Bean methods...
   try {
      return metadata.hasAnnotatedMethods(Bean.class.getName());
   }
   catch (Throwable ex) {
      if (logger.isDebugEnabled()) {
         logger.debug("Failed to introspect @Bean methods on class [" + metadata.getClassName() + "]: " + ex);
      }
      return false;
   }
}
private static final Set<String> candidateIndicators = new HashSet<>(8);

static {
   candidateIndicators.add(Component.class.getName());
   candidateIndicators.add(ComponentScan.class.getName());
   candidateIndicators.add(Import.class.getName());
   candidateIndicators.add(ImportResource.class.getName());
}

​ 可以看到这里是另外注解的判断,如果是@Component@Import@ImportResource@Bean这些的话,其也是会识别为配置类,返回true,然后将其添加到configCandidates中。

5)、重要分割线说明

上面这些我们主要是从BeanDefinition*中筛选出配置类,下面我们就要来具体分析加载这些配置类要加载的Bean定义了

在这里插入图片描述

​ 这里我们看到,我们自己定义的两个Config1Config2都是被加载到BeanDefinition定义中。

在这里插入图片描述

​ 然后由于这两个都是配置类,所以我们可以看到其都被添加到了configCandidates中了。

6)、parser.parse(candidates)解析阶段

// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
      this.metadataReaderFactory, this.problemReporter, this.environment,
      this.resourceLoader, this.componentScanBeanNameGenerator, registry);

Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
   parser.parse(candidates);
   parser.validate();

​ 下面重要是使用ConfigurationClassParser来解析,通过parser.parse(candidates),然后会将解析出来的具体Bean形成ConfigurationClass,添加到其的configurationClasses属性中。这个parser.parse方法里面会有些喜欢解析加载,例如你可以通过@ComponentScan去加载@Import,再在@Import里面嵌套@ComponentScan这些。

private final Map<ConfigurationClass, ConfigurationClass> configurationClasses = new LinkedHashMap<>();

在这里插入图片描述

​ 我们可以看到现在configurationClasses里面还没有内容。

在这里插入图片描述

​ 最终解析后,其就有了所有的加载资源。

7)、this.reader.loadBeanDefinitions(configClasses)

Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);

// Read the model and create bean definitions based on its content
if (this.reader == null) {
   this.reader = new ConfigurationClassBeanDefinitionReader(
         registry, this.sourceExtractor, this.resourceLoader, this.environment,
         this.importBeanNameGenerator, parser.getImportRegistry());
}
this.reader.loadBeanDefinitions(configClasses);

​ 之后通过this.reader.loadBeanDefinitions(configClasses)方法,再将这些ConfigurationClass都加载转换为BeanDefinition并加载到BeanDefinitionRegistry(DefaultListableBeanFactory),再借由BeanDefinition去创建对应的Bean实例。

4、阶段分割线

​ 上面我们梳理了通过配置类将一些要加载的Bean转换为基础的ConfigurationClass,然后再由ConfigurationClass转换为BeanDefinition。现在我们就来具体分析下parser.parse(candidates)(ConfigurationClassParser)方法。

5、parser.parse(candidates)

public void parse(Set<BeanDefinitionHolder> configCandidates) {
   this.deferredImportSelectors = new LinkedList<>();

   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);
      }
   }

   processDeferredImportSelectors();
}

​ 这里主要是判断其是哪种类型的BeanDefinition,再分别调用不同的parse方法:

protected final void parse(@Nullable String className, String beanName) throws IOException {
   Assert.notNull(className, "No bean class name for configuration class bean definition");
   MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
   processConfigurationClass(new ConfigurationClass(reader, beanName));
}

protected final void parse(Class<?> clazz, String beanName) throws IOException {
   processConfigurationClass(new ConfigurationClass(clazz, beanName));
}

protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
   processConfigurationClass(new ConfigurationClass(metadata, beanName));
}

​ 但我们可以看到其最终都是调用到processConfigurationClass方法,同时会通过入参构建为ConfigurationClass

6、processConfigurationClass

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);
}

​ 这里也是先通过this.configurationClasses.get(configClass)判断这个ConfigurationClass是不是已经加载了,毕竟多个注解可能会重复扫描,这里也就解答了我们最前面的问题1了。这里最前面的this.conditionEvaluator.shouldSkip也是一个重要的内容,我们先跳过,之后文章我们来通过实例demo来说明。记住这个processConfigurationClass方法,之后会循环,再次调用这个方法来循环解析。

​ 然后再通过doProcessConfigurationClass方法来具体解析(会将configClass先转换为SourceClass)。

在这里插入图片描述

在这里插入图片描述

7、doProcessConfigurationClass(核心)

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
      throws IOException {

   // 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)) {
      ...........
   }

   // 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)) {
     	..............
      }
   }

   // 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()) {
    ...........
   }
   // No superclass -> processing is complete
   return null;
}

​ 这个方法就是解析的核心类了,以这个类为核心,来具体扫描这些配置类需要加载的Bean

1)、processMemberClasses

// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass);

​ 这个方法主要就是来加载这个类的子类的,因为其子类可能也嵌套有这些注解,触发再次的循环解析。

private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
   Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
   if (!memberClasses.isEmpty()) {
      List<SourceClass> 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();
            }
         }
      }
   }
}
public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
   return (isFullConfigurationCandidate(metadata) || isLiteConfigurationCandidate(metadata));
}
//前面的两个判断是否是配置类的方法。

​ 这个是通过sourceClass.getMemberClasses()来获取其子类资源。然后循环判断,再通过ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata())方法来判断其子类中是否有这些配置类,如果有就添加到candidates。之后再是processConfigurationClass(candidate.asConfigClass(configClass)),再次循环遍历,这个processConfigurationClass方法我们前面提过,用来再次调用循环解析。

2)、@PropertySource的解析

// 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.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
            "]. Reason: Environment must implement ConfigurableEnvironment");
   }
}

​ 这个方法就是将@PropertySource中的内容加载到environment中。

private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
   String name = propertySource.getString("name");
   if (!StringUtils.hasLength(name)) {
      name = null;
   }
    ...................
   String[] locations = propertySource.getStringArray("value");
   Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
   boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");

   Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");
   PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
         DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));

   for (String location : locations) {
      try {
         String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
         Resource resource = this.resourceLoader.getResource(resolvedLocation);
         addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
      }
      catch (IllegalArgumentException | FileNotFoundException | UnknownHostException | SocketException ex) {
        ..................
      }
   }
}
private void addPropertySource(PropertySource<?> propertySource) {
   String name = propertySource.getName();
   MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources();
	............
   if (this.propertySourceNames.isEmpty()) {
      propertySources.addLast(propertySource);
   }
   else {
      String firstProcessed = this.propertySourceNames.get(this.propertySourceNames.size() - 1);
      propertySources.addBefore(firstProcessed, propertySource);
   }
   this.propertySourceNames.add(name);
}

​ 这个就是通过地址获取资源Resource resource = this.resourceLoader.getResource(resolvedLocation),再解析为propertySource,然后将其添加到this.environment

MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources();

ropertySources.addBefore(firstProcessed, propertySource);

​ 我们以上面的加载@PropertySource注解的demo为例,debug到这里:

在这里插入图片描述

3)、@ComponentScan解析

// 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());
         }
      }
   }
}

​ 这部分就是用来解析@ComponentScan的,这里是通过this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName())扫描出这个包下面的所有BeanDefinition

public class ClassWithNestedComponents {

   @Component
   public static class NestedComponent extends ClassWithNestedComponents {
   }

   @Component
   public static class OtherNestedComponent extends ClassWithNestedComponents {
   }

}

在这里插入图片描述

​ 然后又会再次通过ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)方法来看这些BeanDefinitionHolder中又有没有配置类,如果有,就又会通过parse(bdCand.getBeanClassName(), holder.getBeanName())方法来循环调用扫描批处理。

4)、@Import的解析及其附属类的解析

// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), true);
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
   Set<SourceClass> imports = new LinkedHashSet<>();
   Set<SourceClass> visited = new LinkedHashSet<>();
   collectImports(sourceClass, imports, visited);
   return imports;
}
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
			throws IOException {

if (visited.add(sourceClass)) {
	for (SourceClass annotation : sourceClass.getAnnotations()) {
		String annName = annotation.getMetadata().getClassName();
		if (!annName.startsWith("java") && !annName.equals(Import.class.getName())) {
			collectImports(annotation, imports, visited);
		}
	 }
	imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
	}
}

​ 这里实现是获取如果有@Improt使用的注解,然后获取其value将其添加到imports返回。就是下面的importCandidates方法。注意这个processImports方法名,后面也会进行自我调用。

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
      Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
	..........
   else {
      this.importStack.push(configClass);
      try {
         for (SourceClass candidate : importCandidates) {
            if (candidate.isAssignable(ImportSelector.class)) {
               ............
            }
            else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
              ............
            }
            else {
               // 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 (Throwable ex) {
         throw new BeanDefinitionStoreException(
               "Failed to process import candidates for configuration class [" +
               configClass.getMetadata().getClassName() + "]", ex);
      }
      finally {
         this.importStack.pop();
      }
   }
}

​ 这个是解析@Import相关内容的。我们看起步骤,实现是添加到importStack栈中,再最后会出栈this.importStack.pop(),然后就是这个@Improt的三种情况了。

情况一

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 {
      String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
      Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
      processImports(configClass, currentSourceClass, importSourceClasses, false);
   }
}

​ 这个就是我们前面demo1中使用的ImportSelector。现在我们用前面的demo1来看下这个类的处理过程。

在这里插入图片描述

​ 现在已经将这个注入的Selector.class实例化为对象了。同时我们还可以看到这个实例化出来的对象还可以实现Aware这些接口,并且还会对其进行赋值。

ParserStrategyUtils.invokeAwareMethods(
      selector, this.environment, this.resourceLoader, this.registry);
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);
      }
   }

​ 在上面后,就会进入else,因为我们现在是ImportSelector。而DeferredImportSelector这个我们以后再看。

else {
   String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
   Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
   processImports(configClass, currentSourceClass, importSourceClasses, false);
}

​ 然后是通过其的selectImports方法返回importClassNames,即类的全路径

在这里插入图片描述

在这里插入图片描述

​ 然后再通过类的全路径名转换为SourceClass。之后再调用processImports方法进行处理。然后其实这个processImports方法是自我调用,所以最后还是会到一般的组件交给最后的else分支。

情况二

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());
}

​ 这部分是用来处理ImportBeanDefinitionRegistrar的可以看到这里同样是先通过Class<?> candidateClass去创建实例对象BeanUtils.instantiateClass。然后再调用其configClass.addImportBeanDefinitionRegistrar来完成注册BeanDefinition情况三

else {
   // 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));
}

​ 这个就是当做为一般的配置类,然后又再循环调用了我们前面的开始方法processConfigurationClass,发起新一轮的解析。

5)、@ImportResource解析

// 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);
   }
}

​ 处理了上面的@Import后,现在是处理ImportResource了,我们这个这个注解一般是用来加载xmlproperties这些文件的。

​ 这里是先通过sourceClass.getMetadata(),获取当前SourceClass的所有@ImportSource的,然后通过locations获取其的文件地址,再获取对应的reader,用于之后的解析读取,当然这个也可以自定义。然后再将其添加达到待解析的资源中configClass.addImportedResource(resolvedResource, readerClass)

public void addImportedResource(String importedResource, Class<? extends BeanDefinitionReader> readerClass) {
   this.importedResources.put(importedResource, readerClass);
}

​ 我们先记下这个addImportedResource方法,之后我们会调用getImportedResources来获取这里添加的资源地址。

public Map<String, Class<? extends BeanDefinitionReader>> getImportedResources() {
   return this.importedResources;
}

​ 然后会在parser.parse(candidates)方法后去解析。我个我们后面再说。

6)、@Bean的解析

​ 现在我们就会来获取被@Bean注解的方法了。我们也以前面使用的@Bean demo来说明

// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
   configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
image-20210607225157884

​ 我们可以看到,这里就获取解析到了被@Bean注解的方法testBean1。这里解析出来后就会将这个MethodMetadata添加到configClass中。

public void addBeanMethod(BeanMethod method) {
   this.beanMethods.add(method);
}

​ 这里我们同样注意之后的getBeanMethods()方法的获取调用。

public Set<BeanMethod> getBeanMethods() {
   return this.beanMethods;
}

7)、再然后又是超类的循环。

// 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;

​ 这里再配合入口方法。

protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
 		..........
   // 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);
}

​ 所以这里有很多的循环调用。

​ 自此我们关于这些注解的解析&加载工作已经完成了,所以这里的整个过程都是将这些注解的相关内容解析加载到其对应的ConfigurationClass

protected final void parse(Class<?> clazz, String beanName) throws IOException {
   processConfigurationClass(new ConfigurationClass(clazz, beanName));
}

​ 例如以前面的parse方法为起点的转换为ConfigurationClass

8、再回到processConfigBeanDefinitions方法

​ 通过前面这个深入的过程,我们已经了解了其对这些注解的加载解析过程,现在我们可以来了解是怎样通过ConfigurationClass来形成对应的BeanDefinition的。

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
   List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
   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));
      }
   }

   // Return immediately if no @Configuration classes were found
   if (configCandidates.isEmpty()) {
      return;
   }

   // Sort by previously determined @Order value, if applicable
   configCandidates.sort((bd1, bd2) -> {
      int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
      int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
      return Integer.compare(i1, i2);
   });

   // Detect any custom bean name generation strategy supplied through the enclosing application context
   SingletonBeanRegistry sbr = null;
   if (registry instanceof SingletonBeanRegistry) {
      sbr = (SingletonBeanRegistry) registry;
      if (!this.localBeanNameGeneratorSet) {
         BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
         if (generator != null) {
            this.componentScanBeanNameGenerator = generator;
            this.importBeanNameGenerator = generator;
         }
      }
   }

   if (this.environment == null) {
      this.environment = new StandardEnvironment();
   }

   // Parse each @Configuration class
   ConfigurationClassParser parser = new ConfigurationClassParser(
         this.metadataReaderFactory, this.problemReporter, this.environment,
         this.resourceLoader, this.componentScanBeanNameGenerator, registry);

   Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
   Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
   do {
      parser.parse(candidates);
      parser.validate();

      Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
      configClasses.removeAll(alreadyParsed);

      // Read the model and create bean definitions based on its content
      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);
		............
   }
   while (!candidates.isEmpty());

}

​ 前面是parser.parse(candidates);方法的深入,现在我们可以通过this.reader.loadBeanDefinitions(configClasses)来看是怎样形成解析为BeanDefiniton的,注意这里Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()),已经将前面的所有ConfigurationClassparse中提取到了Set<ConfigurationClass> configClasses中。

9、ConfigurationClassBeanDefinitionReader对ConfigurationClass的解析

public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
   TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
   for (ConfigurationClass configClass : configurationModel) {
      loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
   }
}

​ 这个就是解析configurationModel的内容,将其加载注册为BeanDefinition

private void loadBeanDefinitionsForConfigurationClass(
      ConfigurationClass configClass, 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());
      return;
   }

   if (configClass.isImported()) {
      registerBeanDefinitionForImportedConfigurationClass(configClass);
   }
   for (BeanMethod beanMethod : configClass.getBeanMethods()) {
      loadBeanDefinitionsForBeanMethod(beanMethod);
   }

   loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
   loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

​ 我们可以看到这里就是对不同注解的解析内容解析处理。我们这里简单的configClass.isImported()这个来说明下,这个其实就是如果本身是是一个import类型的注解,就注册其本身为一个BeanDefintion

private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) {
   AnnotationMetadata metadata = configClass.getMetadata();
   AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata);

   ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef);
   configBeanDef.setScope(scopeMetadata.getScopeName());
   String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);
   AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata);

   BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName);
   definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
   this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
   configClass.setBeanName(configBeanName);

   if (logger.isDebugEnabled()) {
      logger.debug("Registered bean definition for imported class '" + configBeanName + "'");
   }
}

​ 这里的注册逻辑就是this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());

​ 然后我们再简单看下,是怎样处理@Bean()的:

private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
   ConfigurationClass configClass = beanMethod.getConfigurationClass();
   MethodMetadata metadata = beanMethod.getMetadata();
   String methodName = metadata.getMethodName();
		..........
   // Consider name and any aliases
   List<String> names = new ArrayList<>(Arrays.asList(bean.getStringArray("name")));
   String beanName = (!names.isEmpty() ? names.remove(0) : methodName);

   // Register aliases even when overridden
   for (String alias : names) {
      this.registry.registerAlias(beanName, alias);
   }
	.............
   ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata);
   beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));
		...........
   beanDef.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
   beanDef.setAttribute(RequiredAnnotationBeanPostProcessor.SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE);

   AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata);

   Autowire autowire = bean.getEnum("autowire");
   if (autowire.isAutowire()) {
      beanDef.setAutowireMode(autowire.value());
   }
   String initMethodName = bean.getString("initMethod");
   if (StringUtils.hasText(initMethodName)) {
      beanDef.setInitMethodName(initMethodName);
   }

   String destroyMethodName = bean.getString("destroyMethod");
   beanDef.setDestroyMethodName(destroyMethodName);
	...........
   // Replace the original bean definition with the target one, if necessary
   BeanDefinition beanDefToRegister = beanDef;
		...........
   this.registry.registerBeanDefinition(beanName, beanDefToRegister);
}

​ 我们可以看到是先注册了别名this.registry.registerAlias(beanName, alias);,再是构建ConfigurationClassBeanDefinition(configClass, metadata)。之后再说设置autowireinitMethod这些,最后就是调用this.registry.registerBeanDefinition(beanName, beanDefToRegister)来注册。

​ 自此我们就再次梳理了@Import@ImportSource@ComponentScan等这些注解的使用以及简单的源码解析流程。

原文链接:https://blog.csdn.net/qq_25179481/article/details/117718432



所属网站分类: 技术文章 > 博客

作者:长这么胖

链接:http://www.javaheidong.com/blog/article/222346/1c70cf4963c3a62c0875/

来源:java黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

9 0
收藏该文
已收藏

评论内容:(最多支持255个字符)