Spring 源码解读(四):@SpringBootApplication使用及原理详解

发布时间:2024年01月23日

1、概述

之前我们已经对@ComponentScan@Import分别进行了详细的介绍和总结

那么接下来,我们来看看Springboot 最核心的注解:@SpringBootApplication, 这也是SpringBoot自动装配相关所需依赖和配置的依仗。

2、@SpringBootApplication

我们先看看他的定义

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

  // 排除掉自动配置的class
  @AliasFor(annotation = EnableAutoConfiguration.class)
  Class<?>[] exclude() default {};

  // 排除掉自动配置的全路径类名
  @AliasFor(annotation = EnableAutoConfiguration.class)
  String[] excludeName() default {};

  // 配置扫描的包路径
  @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
  String[] scanBasePackages() default {};

  // 配置扫描的类
  @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
  Class<?>[] scanBasePackageClasses() default {};

  // beanName生成器
  @AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")
  Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

  // 配置类代理模式:proxyBeanMethods:代理bean的方法
  //     Full(proxyBeanMethods = true)、【保证每个@Bean方法被调用多少次返回的组件都是单实例的】
  //     Lite(proxyBeanMethods = false)【每个@Bean方法被调用多少次返回的组件都是新创建的】
  @AliasFor(annotation = Configuration.class)
  boolean proxyBeanMethods() default true;

}

可以看到,这是一个复合注解,所以接下来我们一个个看

2.1、@SpringBootConfiguration

@SpringBootConfiguration的定义如下:

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

	@AliasFor(annotation = Configuration.class)
	boolean proxyBeanMethods() default true;

}

我们看到了我们熟悉的@Configuration,也就是说,这个东西本质上就是一个配置类

2.2、@EnableAutoConfiguration

从名字上看,@EnableAutoConfiguration是自动配置核心所在,先看看其定义:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

	Class<?>[] exclude() default {};

	String[] excludeName() default {};

}

这又是一个复合注解,我们继续一个个看

2.2.1、@AutoConfigurationPackage
/**
 * 用AutoConfigurationPackages注册包。如果没有指定基包或基包类,则注册带注释的类的包。
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

	String[] basePackages() default {};

	Class<?>[] basePackageClasses() default {};

}

看一下注释,大概意思是把包下的路径注册到spring容器中,如果没有基包(一般就是我们的启动类所在的包路径),则注册带这个注解的包路径。继续往下看

AutoConfigurationPackages.Registrar.class这个类我们可以看看

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
        
	@Override
	public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
			register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
	}
        
    //.....

}

我们可以看得到,这是通过@Import注入对象的其中一种方式实现类的注入

registerBeanDefinitions这里的metadata相当于我们注解的源信息,一般就是我们的启动类

在这里插入图片描述

接下来我们来看这个register()方法

在这里插入图片描述

通过打断点,我们可以看到,传的packageNames传的就是启动类所在的包名

registry.containsBeanDefinition(BEAN) 点进去,我们可以看到,并没有匹配的上的,也就是说,启动的时候,容器总并没有包含这个AutoConfigurationPackages的定义

在这里插入图片描述

所以这个时候就会直接创建一个BasePackagesBeanDefinition对象,然后将包名赋值给他的basePackages属性

static final class BasePackagesBeanDefinition extends GenericBeanDefinition {

    private final Set<String> basePackages = new LinkedHashSet<>();

    BasePackagesBeanDefinition(String... basePackages) {
        setBeanClass(BasePackages.class);
        setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        addBasePackages(basePackages);
    }

    @Override
    public Supplier<?> getInstanceSupplier() {
        return () -> new BasePackages(StringUtils.toStringArray(this.basePackages));
    }

    private void addBasePackages(String[] additionalBasePackages) {
        this.basePackages.addAll(Arrays.asList(additionalBasePackages));
    }

}

从这段代码中,我们可以看到,basePackages是个set集合,如果容器当中始终只有一个BasePackagesBeanDefinition对象,只要代码当中添加@AutoConfigurationPackage注解,就会将注解所在的包名添加到basePackages集合当中。

这之后,再把AutoConfigurationPackages加到beanDefinitionMap中,value就是刚刚创建的BasePackagesBeanDefinition

在这里插入图片描述

总结一下:其实相比于@import一个一个导,使用@AutoConfigurationPackage相当于一次性把我们主程序路径下的程序都加载到容器中去,这就是这个类的主要作用。

2.2.2、@Import(AutoConfigurationImportSelector.class)

这个注解配置是Spring Boot的自动装配核心所在

我们先看看AutoConfigurationImportSelector的定义

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
            
            //.....
 
        }

可以看到AutoConfigurationImportSelector除了实现一系列的aware接口获取相关信息之外,就是实现了DeferredImportSelector接口,DeferredImportSelectorImportSelector的子接口,Deferred延迟的意思。

根据之前我们总结的 @Import使用及原理详解 可知,对@Import的解析会来到ConfigurationClassParser#processImports(),方法代码片段如下:

在这里插入图片描述

这里会判断当前selectorDeferredImportSelector还是ImportSelector

如果是DeferredImportSelector,会进入执行this.deferredImportSelectorHandler.handle()

该方法会把DeferredImportSelector封装成DeferredImportSelectorHolder放入到this.deferredImportSelectors集合中。根据DeferredImportSelector意思来看,就是延迟注入的意思,所以他会等Spring对配置类相关其他注解进行解析完之后,才执行这里的注入逻辑,这可以从DeferredImportSelector的注释中看得到

在这里插入图片描述

也可从ConfigurationClassParser#parse()方法得到验证:

 public void parse(Set<BeanDefinitionHolder> 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();
  }

来到DeferredImportSelectorHolder#process()方法:

class ConfigurationClassParser {

    //....

    public void process() {
        List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
        this.deferredImportSelectors = null;
        try {
            if (deferredImports != null) {
                DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
                deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
                // 遍历调用handler的register()
                deferredImports.forEach(handler::register);
                // 遍历完之后执行processGroupImports()
                handler.processGroupImports();
            }
        } finally {
            this.deferredImportSelectors = new ArrayList<>();
        }
    }

    public void register(DeferredImportSelectorHolder deferredImport) {
        Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
        DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
                (group != null ? group : deferredImport),
                key -> new DeferredImportSelectorGrouping(createGroup(group)));
        grouping.add(deferredImport);
        this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
                deferredImport.getConfigurationClass());
    }
}

这段代码我们可以很清晰的看到,遍历deferredImportSelectors集合,每个都会调用handler的#register()方法

在这里插入图片描述

这个集合,我们通过debug,其实也可以看得到,只有这一个。

	@Override
	public Class<? extends Group> getImportGroup() {
		return AutoConfigurationGroup.class;
	}

这个方法将AutoConfigurationImportSelector的内部类AutoConfigurationGroup添加到groupings集合当中,并将对应的配置类添加到configurationClasses当中。

    public void add(DeferredImportSelectorHolder deferredImport) {
		this.deferredImports.add(deferredImport);
	}

继续深入,我们会发现,其实就是添加到deferredImports这个List里面。

遍历完deferredImportSelectors之后,调用handler.processGroupImports()

    public void processGroupImports() {
        for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
            Predicate<String> exclusionFilter = grouping.getCandidateFilter();
            grouping.getImports().forEach(entry -> {
                ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
                try {
                    processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
                            Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
                            exclusionFilter, false);
                } catch (BeanDefinitionStoreException ex) {
                    throw ex;
                } catch (Throwable ex) {
                    throw new BeanDefinitionStoreException(
                            "Failed to process import candidates for configuration class [" +
                                    configurationClass.getMetadata().getClassName() + "]", ex);
                }
            });
        }
    }

遍历之前放在groupings中的DeferredImportSelectorGrouping对象,调用#getImports()方法,该方法返回的是延迟注入的类名封装成的Entry结点的迭代器对象。

	public Iterable<Group.Entry> getImports() {
		for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
			this.group.process(deferredImport.getConfigurationClass().getMetadata(),
					deferredImport.getImportSelector());
		}
		return this.group.selectImports();
	}

这里我们可以看到,其实封装的就是之前我们register的那些类。

this.group点进去可以看到,这个groupAutoConfigurationImportSelector的内部类AutoConfigurationGroup

	@Override
	public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
		Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
				() -> String.format("Only %s implementations are supported, got %s",
						AutoConfigurationImportSelector.class.getSimpleName(),
						deferredImportSelector.getClass().getName()));
          // 获取自动配置类entry
		AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
				.getAutoConfigurationEntry(annotationMetadata);
         // 放入到autoConfigurationEntries集合中
		this.autoConfigurationEntries.add(autoConfigurationEntry);
		for (String importClassName : autoConfigurationEntry.getConfigurations()) {
			this.entries.putIfAbsent(importClassName, annotationMetadata);
		}
	}

可以看到,该方法得到自动配置结点,将其添加到autoConfigurationEntries集合当中。再遍历自动配置结点的所有配置类的类名,添加到entries集合当中。

	protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
         // 获取到所有自动配置类的全限定类名
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
         // 根据相关设置就行排除
		configurations = removeDuplicates(configurations);
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);
		configurations = getConfigurationClassFilter().filter(configurations);
		fireAutoConfigurationImportEvents(configurations, exclusions);
        // 封装成AutoConfigurationEntry返回
		return new AutoConfigurationEntry(configurations, exclusions);
	}

再深入的看看这个getCandidateConfigurations(annotationMetadata, attributes)

  protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
        getBeanClassLoader());
    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
        + "are using a custom packaging, make sure that file is correct.");
    return configurations;
  }

我们看到了老熟人SpringFactoriesLoader.loadFactoryNames(),最终在这里加载了META-INF/spring.factories目录下的自动配置类

拿到所有自动配置类之后回到上面的#processGroupImports()grouping.getImports()获取到所有需要自动装配的类封装对象,接下来会进行一一遍历,调用#processImports()进行注入,至此Spring Boot就完成了自动装配。

文章来源:https://blog.csdn.net/qq_43602877/article/details/135700676
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。