springboot框架四个基础核心二(自动装配)

176 阅读3分钟

1.引言

springboot框架同样有四个基础核心,分别是:

  • 依赖管理
  • 自动装配
  • 监控Actuator
  • CLI工具

2.自动装配

springboot应用,从启动类开始,通常启动类位于src/main/java目录下。像这样:

image.png

通常启动类的内容,看着非常简单:

/**
 * 启动类
 *
 * @author ThinkPad
 * @version 1.0
 */
@SpringBootApplication
public class EduCommonApplication {

	public static void main(String[] args) {
		SpringApplication.run(EduCommonApplication.class, args);
	}

}

一个注解,一个run方法,即完成了springboot应用的两个关键流程事项:

  • @SpringBootApplication:开启组件扫描,和自动装配
  • SpringApplication.run():引导启动应用程序

@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 {
..............省略属性定义..................
}

这里有关于java注解的基础知识,简单解释下:

  • @Target({ElementType.TYPE}):表示该注解,可以标注在哪些地方。ElementType.TYPE表示可以用在类,接口上。常见的取值:FIELD、METHOD、PARAMETER、CONSTRUCTOR、PACKAGE等,见文知意,很好理解
  • @Retention(RetentionPolicy.RUNTIME):表示该注解,在JVM处理类的过程(著名的家宴准备了西式菜)中的哪些地方会保留。RetentionPolicy.RUNTIME表示保留到运行时,通过反射机制可以拿到它。常见的取值还有:SOURCE、CLASS,见文知意,很好理解。

铺垫完java注解的基础知识,回到重点关注点,分别是:

  • @ComponentScan子注解:启用组件扫描,默认会从启动类所在的包开始扫描至所有子包,这也是为什么通常建议将启动类,放置在顶层包的原因
  • @SpringBootConfiguration子注解:它就是@Configuration,是spring框架提供的注解,表示这是一个javaconfig配置类,关于javaconfig的相关知识,你应该还没有忘记吧!
  • @EnableAutoConfiguration子注解:表示开启springboot应用的自动配置能力,它会依据应用的依赖、classpath有没有某个类,以及自定义的一系列bean。来决定应用最终的装配需求,并注册到IoC容器中。

下面我们就来详细分析注解:@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 {};
}

我们把关注点,放到这行代码上:

@Import({AutoConfigurationImportSelector.class})

@Import注解的作用是导入类,并将目标导入类的bean注入到IoC容器中。

这里是将AutoConfigurationImportSelector的bean注入到IoC容器,AutoConfigurationImportSelector中有一个方法,值得关注:

public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            // 前面的知识点:spring factories  即spi机制在spring中的引用
            // 它会加载META-INF/spring.factories文件,从中找到需要自动装配的需求
            AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }

getAutoConfigurationEntry方法:

protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            // 从META-INF/spring.factorie配置中,获取自动需求
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            configurations = this.removeDuplicates(configurations);
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.getConfigurationClassFilter().filter(configurations);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
        }
    }

getCandidateConfigurations方法:

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    // 终于看到我们熟悉的:SpringFactoriesLoader了
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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方法:

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        String factoryTypeName = factoryType.getName();
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    }

loadSpringFactories方法:

image.png

META-INF/spring.factories文件,内容太多,我就截图了,关注关键点即可:

image.png

从上面的截图,我们看到spring.factories文件中,有一个配置属性:org.springframework.boot.autoconfigure.EnableAutoConfiguration,指定了一系列自动装配的需求。挑几个我们熟悉的看看

image.png

以上就是springboot应用的自动装配。下一篇文章,我们再来看启动流程,即run方法内部的行为。