2021春招面试,程序员必备技能之SpringBoot的自动装配原理




  ConfigurableApplicationContext这个对象其实是 ApplicationContext接口的一个子接口



![请添加图片描述](https://img-blog.csdnimg.cn/img_convert/e166c148a6de9d4423ea0c410793a559.png)



  那么上面的代码可以调整为



@SpringBootApplication

@MapperScan(“com.bobo.mapper”)

public class SpringBootVipDemoApplication {

public static void main(String[] args) {

    // 基于配置文件的方式

    ApplicationContext ac1 = new ClassPathXmlApplicationContext("");

    // 基于Java配置类的方式

    ApplicationContext ac2 = new AnnotationConfigApplicationContext(SpringBootVipDemoApplication.class);

    // run 方法执行完成后返回的是一个 ApplicationContext 对象

    // 到这儿我们是不是可以猜测 run 方法的执行 其实就是Spring的初始化操作[IoC]

    ApplicationContext ac3 = SpringApplication.run(SpringBootVipDemoApplication.class, args);

}

}




  根据返回结果,我们猜测SpringBoot项目的启动其实就是Spring的初始化操作【IoC】。



![请添加图片描述](https://img-blog.csdnimg.cn/img_convert/7c201621ee2e73c0df8829d6f654cd92.png)



下一步:



![请添加图片描述](https://img-blog.csdnimg.cn/img_convert/238e74e9c49446972bfb5696b3c5dfa7.png)



下一步:



![请添加图片描述](https://img-blog.csdnimg.cn/img_convert/596c69b66c5223364a7ec342d8750990.png)



直接调用:



![请添加图片描述](https://img-blog.csdnimg.cn/img_convert/8d2e0d7ad076086a74eff9b31a45304b.png)



![请添加图片描述](https://img-blog.csdnimg.cn/img_convert/06b30842d20d942d7bb121b70c5ecb28.png)



  到这儿,其实我们就可以发现SpringBoot项目的启动,本质上就是Spring的初始化操作。但是并没有涉及到SpringBoot的核心装配。



### [](https://gitee.com/vip204888/java-p7)2.3 @SpringBootApplication



  @SpringBootApplication点开后我们能够发现@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}

)}

)




  我们发现@SpringBootApplication注解的前面四个注解是JDK中自动的`元注解` (用来修饰注解的注解)



@Target({ElementType.TYPE}) // 表明 修饰的注解的位置 TYPE 表示只能修饰类

@Retention(RetentionPolicy.RUNTIME) // 表明注解的作用域

@Documented // API 文档抽取的时候会将该注解 抽取到API文档中

@Inherited // 表示注解的继承




  还有就是@ComponentScan注解,该注解的作用是用来指定扫描路径的,如果不指定特定的扫描路径的话,扫描的路径是当前修饰的类所在的包及其子包。



  @SpringBootConfiguration这个注解的本质其实是@Configuration注解。



@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented

// 上面是3个元注解

// @Configuration 注解修饰的Java类是一个配置类

@Configuration

// @Indexed

@Indexed

public @interface SpringBootConfiguration {

@AliasFor(

    annotation = Configuration.class

)

boolean proxyBeanMethods() default true;

}




  这样一来7个注解,咱们清楚了其中的6个注解的作用,而且这6个注解都和SpringBoot的自动装配是没有关系的。



![请添加图片描述](https://img-blog.csdnimg.cn/img_convert/e2ee6eb37bc93ceaa9c68180cd5feaa3.png)



### [](https://gitee.com/vip204888/java-p7)2.4 @EnableAutoConfiguration



  @EnableAutoConfiguration这个注解就是SpringBoot自动装配的关键。



@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 {};

}




  我们发现要搞清楚EnableAutoConfiguration注解的关键是要弄清楚@Import注解。这个内容我们前面在注解编程发展中有详细的介绍。AutoConfigurationImportSelector实现了ImportSelector接口,那么我们清楚只需要关注selectImports方法的返回结果即可



public String[] selectImports(AnnotationMetadata annotationMetadata) {

    if (!this.isEnabled(annotationMetadata)) {

        return NO_IMPORTS;

    } else {

        AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);

        // 返回的就是需要注册到IoC容器中的对象对应的类型的全类路径名称的字符串数组

        // ["com.bobo.pojo.User","com.bobo.pojo.Person", ....]

        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());

    }

} 



  我们清楚了该方法的作用就是要返回需要注册到IoC容器中的对象对应的类型的全类路径名称的字符串数组。那么我接下来分析的关键是返回的数据是哪来的?所以呢进入getAutoConfigurationEntry方法中。



protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {

    if (!this.isEnabled(annotationMetadata)) {

        return EMPTY_ENTRY;

    } else {

        // 获取注解的属性信息

        AnnotationAttributes attributes = this.getAttributes(annotationMetadata);

        // 获取候选配置信息 加载的是 当前项目的classpath目录下的 所有的 spring.factories 文件中的 key 为

        // org.springframework.boot.autoconfigure.EnableAutoConfiguration 的信息

        List configurations = this.getCandidateConfigurations(annotationMetadata, attributes);

        configurations = this.removeDuplicates(configurations);

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

    }

} 



  先不进入代码,直接DEBUG调试到 候选配置信息这步。我们发现里面有很多个Java类。



![请添加图片描述](https://img-blog.csdnimg.cn/img_convert/55df5294446984dde90cb2d6fb1bbd3a.png)



  然后进入getCandidateConfiguration方法中,我们可以发现加载的是 META-INF/spring.factories 文件中的配置信息



![请添加图片描述](https://img-blog.csdnimg.cn/img_convert/3d9e680e2e45f03be69b24cdff225929.png)



  然后我们可以验证,进入到具体的META-INF目录下查看文件。  

![请添加图片描述](https://img-blog.csdnimg.cn/img_convert/18182393f6b18330dfd9cea4b665447d.png)



  最后几个



![请添加图片描述](https://img-blog.csdnimg.cn/img_convert/75dec32d3a1c5d86a6a3f27e6969a0dd.png)



  在我们的Debug中还有一个配置文件(MyBatisAutoConfiguration)是哪来的呢?



![请添加图片描述](https://img-blog.csdnimg.cn/img_convert/607d7ce6f2503f37c09f21000c6cdfd3.png)



  深入源码也可以看到真正加载的文件



![请添加图片描述](https://img-blog.csdnimg.cn/img_convert/9bc78e3086b94ed207119b24b4c117db.png)



  然后我们继续往下看源码



protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {

    if (!this.isEnabled(annotationMetadata)) {

        return EMPTY_ENTRY;

    } else {

        AnnotationAttributes attributes = this.getAttributes(annotationMetadata);

        List configurations = this.getCandidateConfigurations(annotationMetadata, attributes);

        // 因为会加载多个 spring.factories 文件,那么就有可能存在同名的,

        // removeDuplicates方法的作用是 移除同名的

        configurations = this.removeDuplicates(configurations);

        // 获取我们配置的 exclude 信息

        // @SpringBootApplication(exclude = {RabbitAutoConfiguration.class})

        // 显示的指定不要加载那个配置类

总结

本文从基础到高级再到实战,由浅入深,把MySQL讲的清清楚楚,明明白白,这应该是我目前为止看到过最好的有关MySQL的学习笔记了,我相信如果你把这份笔记认真看完后,无论是工作中碰到的问题还是被面试官问到的问题都能迎刃而解!

重要的事:需要领取完整版的MySQL学习笔记的话,请转发+关注后点这里免费获取到免费的下载方式!

MySQL50道高频面试题整理:

ons);

        // 获取我们配置的 exclude 信息

        // @SpringBootApplication(exclude = {RabbitAutoConfiguration.class})

        // 显示的指定不要加载那个配置类

总结

本文从基础到高级再到实战,由浅入深,把MySQL讲的清清楚楚,明明白白,这应该是我目前为止看到过最好的有关MySQL的学习笔记了,我相信如果你把这份笔记认真看完后,无论是工作中碰到的问题还是被面试官问到的问题都能迎刃而解!

重要的事:需要领取完整版的MySQL学习笔记的话,请转发+关注后点这里免费获取到免费的下载方式!

MySQL50道高频面试题整理:

你可能感兴趣的:(程序员,后端,java,面试)