文档章节

SpringBoot系列文章简介

SEOwhywhy
 SEOwhywhy
发布于 06/25 09:14
字数 1581
阅读 73
收藏 0

  SpringBoot系列文章简介
  
  SpringBoot源码阅读辅助篇:
  
  Spring IoC容器与应用上下文的设计与实现
  
  SpringBoot启动流程源码分析:
  
  SpringBoot启动流程分析(一):SpringApplication类初始化过程
  
  SpringBoot启动流程分析(二):SpringApplication的run方法
  
  SpringBoot启动流程分析(三):SpringApplication的run方法之prepareContext()方法
  
  SpringBoot启动流程分析(四):IoC容器的初始化过程
  
  SpringBoot启动流程分析(五):SpringBoot自动装配原理实现
  
  SpringBoot启动流程分析(六):IoC容器依赖注入
  
  笔者注释版Spring Framework与SpringBoot源码git传送门:请不要吝啬小星星
  
  spring-framework-5.0.8.RELEASE
  
  SpringBoot-2.0.4.RELEASE
  
  自定义Starter:
  
  SpringBoot应用篇(一):自定义starter
  
  一、前言
  
  上一篇文章,通过分析refresh()方法中的invokeBeanFactoryPostProcessors()方法,分析了IoC容器的初始化过程,这一节从代码上如下所示,接上一节ConfigurationClassParser类中的parse()方法,接着分析SpringBoot的自动装配原理。
  
  复制代码
  
  1 // ConfigurationClassParser类
  
  2 public void parse(Set<BeanDefinitionHolder> configCandidates) {
  
  3     this.deferredImportSelectors = new LinkedList<>();
  
  4     for (BeanDefinitionHolder holder : configCandidates) {
  
  5         BeanDefinition bd = holder.getBeanDefinition();
  
  6         try {
  
  7             // 如果是SpringBoot项目进来的,bd其实就是前面主类封装成的 AnnotatedGenericBeanDefinition(AnnotatedBeanDefinition接口的实现类)
  
  8             if (bd instanceof AnnotatedBeanDefinition) {
  
  9                 parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
  
  10             } else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
  
  11                 parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
  
  12             } else {
  
  13                 parse(bd.getBeanClassName(), holder.getBeanName());
  
  14             }
  
  15         } catch (BeanDefinitionStoreException ex) {
  
  16             throw ex;
  
  17         } catch (Throwable ex) {
  
  18             throw new BeanDefinitionStoreException(
  
  19                     "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
  
  20         }
  
  21     }
  
  22     // 加载默认的配置---》(对springboot项目来说这里就是自动装配的入口了)
  
  23     processDeferredImportSelectors();
  
  24 }
  
  复制代码
  
  二、SpringBoot自动装配原理。
  
  2.1、@SpringBootApplication注解
  
  对这个注解详细大家一定非常熟悉了。再来好好看看这个注解。
  
  复制代码
  
  1 @Target(ElementType.TYPE)
  
  2 @Retention(RetentionPolicy.RUNTIME)
  
  3 @Documented
  
  4 @Inherited
  
  5 @SpringBootConfiguration
  
  6 @EnableAutoConfiguration
  
  7 @ComponentScan(excludeFilters = {
  
  8         @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
  
  9         @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
  
  10 public @interface SpringBootApplication {
  
  11     ...
  
  12 }
  
  复制代码
  
  接着看@EnableAutoConfiguration
  
  复制代码
  
  1 @Target(ElementType.TYPE)
  
  2 @Retention(RetentionPolicy.RUNTIME)
  
  3 @Documented
  
  4 @Inherited
  
  5 @AutoConfigurationPackage
  
  6 @Import(AutoConfigurationImportSelector.class)
  
  7 public @interface EnableAutoConfiguration {
  
  8     ...
  
  9 }
  
  复制代码
  
  OK,看到@Import(AutoConfigurationImportSelector.class)导入了一个重要的类AutoConfigurationImportSelector。
  
  2.2、AutoConfigurationImportSelector
  
  复制代码
  
  1 // AutoConfigurationImportSelector类
  
  2 //自动装配
  
  3 @Override
  
  4 public String[www.rmutk.net] selectImports(AnnotationMetadata annotationMetadata) {
  
  5     if (!isEnabled(annotationMetadata)) {
  
  6         return NO_IMPORTS;
  
  7     }
  
  8     AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
  
  9             .loadMetadata(this.beanClassLoader);
  
  10     AnnotationAttributes attributes = getAttributes(annotationMetadata);
  
  11     //获取所有的自动配置类(META-INF/spring.factories中配置的key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的类)
  
  12     List<String> configurations = getCandidateConfigurations(annotationMetadata,
  
  13             attributes);
  
  14     configurations = removeDuplicates(configurations);
  
  15     //需要排除的自动装配类(springboot的主类上 @SpringBootApplication(exclude = {com.demo.starter.config.DemoConfig.class})指定的排除的自动装配类)
  
  16     Set<String> exclusions = getExclusions(annotationMetadata, attributes);
  
  17     checkExcludedClasses(configurations,www.chaoyuL.com  exclusions);
  
  18     //将需要排除的类从 configurations remove掉
  
  19     configurations.removeAll(exclusions);
  
  20     configurations = filter(configurations, autoConfigurationMetadata);
  
  21     fireAutoConfigurationImportEvents(configurations, exclusions);
  
  22     return StringUtils.toStringArray(configurations);
  
  23 }
  
  复制代码
  
  至于怎么从章节一中提到的ConfigurationClassParser类中的parse()===>processDeferredImportSelectors()==>AutoConfigurationImportSelector#selectImports(),篇幅有限不做过多介绍。
  
  List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
  
  我们来看一下getCandidateConfigurations(www.oushengyul.com)方法是怎么拿到这些自动配置类的。
  
  复制代码
  
  // AutoConfigurationImportSelector类
  
  1 protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
  
  2                                                   AnnotationAttributes attributes) {
  
  3     List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
  
  4             getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
  
  5     Assert.notEmpty(configurations,
  
  6             "No auto configuration classes found in META-INF/spring.factories. If you "
  
  7                     + "are using a custom packaging, make sure that file is correct.");
  
  8     return configurations;
  
  9 }
  
  复制代码
  
  是不是又看到一个十分熟悉的方法loadFactoryNames(),没错,其实我们在分析SpringBoot启动流程的第一篇文章的时候,就已经分析了,SpringBoot是如何从META-INF/spring.factories中加载指定key的value的。ok,我们在这里再次回顾一遍。
  
  看看loadFactoryNames()方法
  
  // SpringFactoriesLoader类
  
  1 public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
  
  2     String factoryClassName = factoryClass.getName();
  
  3     return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList(www.chaoyuepint.com));
  
  4 }
  
  debug,看看要从META-INF/spring.factories中加载的类的key,如下图所示:org.springframework.boot.autoconfigure.EnableAutoConfiguration
  
  回到selectImports()方法,debug,跳过List<www.oushenggw.com String> configurations = getCandidateConfigurations(annotationMetadata,attributes);看一下configurations
  
  竟然有110个,那这些类都在哪里呢?看spring-boot-autoconfigure(当然在SpringBoot的工程中,也不止这一个依赖包中存在该配置文件)工程下的META-INF/spring.factories,我们能看到org.springframework.boot.autoconfigure.EnableAutoConfiguration定义了一大堆。
  
  其中还有一个com.demo.starter.config.DemoConfig是我自定义的starter。如下所示,我在测试工程中添加了自定义starter的依赖,所以SpringBoot就能扫描到。
  
  1 <dependency>
  
  2     <groupId>com.demo</groupId>
  
  3     <artifactId>demo-spring-boot-starter<www.muyuyulept.com /artifactId>
  
  4     <version>0.0.1-RELEASE</version>
  
  5 </dependency>
  
    继续看Set<String> exclusions = getExclusions(annotationMetadata, attributes);方法,该方法是排除主类上@SpringBootApplication注解上排除的自动装配的类。比如我们在该注解上排除我们自定义starter的自动装配的类,@SpringBootApplication(exclude = {com.demo.starter.config.DemoConfig.class})(当然也可以用excludeName进行排除),那么在后面的configurations.removeAll(exclusions);方法中将会删除我们的com.demo.starter.config.DemoConfig.class。
  
  configurations = filter(configurations, autoConfigurationMetadata);该行代码将会过滤掉不需要装配的类。过滤的逻辑有很多,比如我们常用的@ConditionXXX注解。如下所示:
  
  复制代码
  
  1 @ConditionalOnBean:容器中有指定的Bean
  
  2 @ConditionalOnClass:当类路径下有指定的类
  
  3 @ConditionalOnExpression:基于SpEL表达式作为判断条件
  
  4 @ConditionalOnJava:基于JVM版本作为判断条件
  
  5 @ConditionalOnJndi:在JNDI存在的条件下查找指定的位置
  
  6 @ConditionalOnMissingBean:当容器中没有指定Bean的情况下
  
  7 @ConditionalOnMissingClass:当类路径下没有指定的类
  
  8 @ConditionalOnNotWebApplication:当前项目不是Web项目
  
  9 @ConditionalOnProperty:配置文件中指定的属性是否有指定的值
  
  10 @ConditionalOnResource:类路径下是否有指定的资源
  
  11 @ConditionalOnSingleCandidate:当指定Bean在容器中只有一个,或者虽然有多个但是指定首选Bean
  
  12 @ConditionalOnWebApplication:当前项目是Web项目的条件下
  
  复制代码
  
    至于如何将这些类解析成BeanDefinition并注册进beanDefinition中的,和上一节讲的过程是一样的,不再赘述了。
  
  debug,跳过refresh()方法中的invokeBeanFactoryPostProcessors(beanFactory);方法。如下图所示,最终在beanFactory的BeanDefinitionMap中找到了自定义starter中的自动装配的类。
  
  综合本文和上一篇博文我们详细的梳理了IoC容器的初始化过程,到此IoC容器的初始化过程就结束了。
  
  原创不易,转载请注明出处。
  
  如有错误的地方还请留言指正。
  
  小小的码农,大大的梦想

© 著作权归作者所有

SEOwhywhy
粉丝 8
博文 152
码字总数 335019
作品 0
私信 提问
SpringBootBucket 2.0.4 发布,代号“傲娇的小二晶”

SpringBootBucket 自从1.0.0版本发布后就有好多人喜欢,目前码云上面star数量接近1.2k。上个月还收到了红薯签名的1000 star奖杯,这个我自己也觉得很惊讶。 由于SpringBoot 1.x官方将终止维护...

一刀
2018/09/16
2.7K
4
SpringBootBucket 1.0.0 发布,SprintBoot 全家桶

Spring Boot 现在已经成为Java 开发领域的一颗璀璨明珠,它本身是包容万象的,可以跟各种技术集成。 本项目对目前Web开发中常用的各个技术,通过和SpringBoot的集成,并且对各种技术通过“一...

一刀
2018/03/05
8.7K
17
学习 Spring Boot 知识看这一篇就够了

从2016年因为工作原因开始研究 Spring Boot ,先后写了很多关于 Spring Boot 的文章,发表在技术社区、我的博客和我的公号内。粗略的统计了一下总共的文章加起来大概有六十多篇了,其中一部分...

ityouknow
2018/05/28
0
0
第一章:Maven环境下如何配置QueryDSL环境

QueryDSL是一个通用的查询框架,框架的核心原则是创建安全类型的查询,开始QueryDSL仅支持Hibernate(HQL),在不断开源人士加入QueryDSL团队后,陆续发布了针对JPA,JDO,JDBC,Lucene,Hib...

恒宇少年
2017/07/01
0
0
【SpringBoot2.0系列08】SpringBoot之redis数据缓存管理

【SpringBoot2.0系列01】初识SpringBoot 【SpringBoot2.0系列02】SpringBoot之使用Thymeleaf视图模板 【SpringBoot2.0系列03】SpringBoot之使用freemark视图模板 【SpringBoot2.0系列04】Spr...

余空啊
2018/08/20
0
0

没有更多内容

加载失败,请刷新页面

加载更多

SpringBoot中 集成 redisTemplate 对 Redis 的操作(二)

SpringBoot中 集成 redisTemplate 对 Redis 的操作(二) List 类型的操作 1、 向列表左侧添加数据 Long leftPush = redisTemplate.opsForList().leftPush("name", name); 2、 向列表右......

TcWong
今天
5
0
排序––快速排序(二)

根据排序––快速排序(一)的描述,现准备写一个快速排序的主体框架: 1、首先需要设置一个枢轴元素即setPivot(int i); 2、然后需要与枢轴元素进行比较即int comparePivot(int j); 3、最后...

FAT_mt
昨天
4
0
mysql概览

学习知识,首先要有一个总体的认识。以下为mysql概览 1-架构图 2-Detail csdn |简书 | 头条 | SegmentFault 思否 | 掘金 | 开源中国 |

程序员深夜写bug
昨天
10
0
golang微服务框架go-micro 入门笔记2.2 micro工具之微应用利器micro web

micro web micro 功能非常强大,本文将详细阐述micro web 命令行的功能 阅读本文前你可能需要进行如下知识储备 golang分布式微服务框架go-micro 入门笔记1:搭建go-micro环境, golang微服务框架...

非正式解决方案
昨天
8
0
前端——使用base64编码在页面嵌入图片

因为页面中插入一个图片都要写明图片的路径——相对路径或者绝对路径。而除了具体的网站图片的图片地址,如果是在自己电脑文件夹里的图片,当我们的HTML文件在别人电脑上打开的时候图片则由于...

被毒打的程序猿
昨天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部