SpringBoot自动装配原理总结

1.相关注解

@SpringBootApplication:

image.png 此注解是SpringBoot程序主启动类上的注解,而这个注解是一个组合注解,里面主要包含@SpringBootConfiguration和@EnableAutoConfiguration两个注解

@SpringBootConfiguration:

image.png 此注解其实也是一个组合注解,里面是@Configuration,所以起到的也是@Configuration的作用,就是将当前类标识为配置类

@EnableAutoConfiguration:

image.png 这个注解就是SpringBoot自动装配的关键所在,有了这个注解才能实现自动装配,这个注解里面也是主要包含两个注解,@AutoConfigurationPackage和@Import({AutoConfigurationImportSelector.class}),而我们主要关注的是@Import注解,可以看到这个注解导入了AutoConfigurationImportSelector这个类

@Import:

此注解主要作用是将导入的类创建Bean对象,交给Spring容器管理,我们可以从Spring容器中拿到,作用和@Component等注解作用类似,但是@Component注解需要添加注解扫描@ComponentScan,我们要导入的类需要在扫描的路径下,否则就无法从容器中拿到Bean对象,而@Import注解不需要开启注解扫描,可以直接创建对应类的Bean对象。

2.原理

因为截图太大会导致字体太小,所以有些地方只截图了一部分,建议配合SpringBoot2.0源码观看 image.png 上面AutoConfigurationImportSelector类实现了ImportSelector接口,会实现selectImports方法,我们可以看到这个方法的返回值是一个String数组,而这个数组从哪里来的呢,我们这里可以往后看,

image.png image.png

我们可以看到这个数组来自一个自动配置Entry,方法返回的是这个Entry中的一个configurations属性,是一个List集合. 那么我们再来看一下Entry的来源 image.png 这里我们可以看到是这个get方法根据两个元数据来获取到entry,我们可以简单翻译为根据自动配置元数据和注解元数据获取配置数据,那么我们再进入此方法再看一下 image.png image.png 此方法中首先getAttributes方法获取到了我们当前注解的属性名,这里可以看到就是我们@SpringBootApplication注解中的两个属性,主要作用就是排除某些配置的自动装配,这个属性名称在后续会用到

然后getCandidateConfigurations方法根据注解元数据和属性名获取配置数据configurations,我们进到这个方法中看一下实现

image.png 先不看其他的就看上面这一行,这个文字太小了,我直接翻译一下,这里就是一个断言,如果上面获取到的configurations为空,就会有一条提示信息,提示信息的意思就是:在META-INF/spring.factories文件中没有自动配置类信息,如果你用了自定义的包,请确定文件是正确的。其实这个意思已经很明显了,我们的这个configurations配置信息就是来自于自动装配依赖下面的META-INF目录下的spring.factories文件,这里先放一下,待会看这个文件。

我们先看一下这个被断言的configurations具体是怎么来的 image.png 我们可以看到它其实是来源于上面的这个loadFactoryNames这个方法,那么我们就点进去看看此方法 image.png 可以看到这个方法其实是有两个参数的,第一个参数factoryClass其实就是我们EnableAutoConfiguration注解的类对象,第二个参数classLoader就是一个类加载器 image.png 具体的方法体我们可以看到这里是获取了EnableAutoConfiguration的全类名,然后调用了一个loadSpringFactories,我们再来看一下这个loadSpringFactories方法 image.png 首先是这个方法的第一部分,先去缓存中根据类加载器拿对应的数据,如果数据不为空,则返回 image.png 如果数据为空,则执行上面的代码首先就是根据FACTORIES_RESOURCE_LOCATION获取一个urls集合,我们看一下这个FACTORIES_RESOURCE_LOCATION到底是个啥,其实就是一个路径,指向META-INFG/spring.factories这个文件 image.png 再来往下看 image.png image.png 首先是遍历整个urls集合,我们可以看到这里的url其实就是spring.factories这个文件的绝对路径 根据这个路径加载对应的文件然后读取成一个Properties文件,我们来大概看一下spring.factories这个 文件,这个文件就在我们的自动装配依赖包下的META-INFO包中 image.png

image.png 可以看到这个文件里基本都是以注解的全类名作为键,这个注解相关的类的全类名作为值的形式存储的,我们具体来看一下EnableAutoConfiguration这里的,因为这是我们自动配置的关键,我们可以看到这里的值都是以Configuration结尾的配置类的全类名,其实到这我们大概也能猜到这些类就是我们自动装配默认的配置类 再来看下面的代码 image.png 其实下面的带代码基本上很清楚,就是将properties中的数据拿出来,将每一个value(配置类或者相关类的全类名)与key(注解的全类名)对应,然后存储到result中,这个result是一个LinkedMultiValueMap

image.png 这个LinkedMultiValueMap这里简单说一下,如果存储多个key相同的键值对的话,相同的key对应的不同的value会放在同一个LinkedList中存储,也就是底层其实存储的value是LinkedList.

image.png

最后将这个result存入到缓存中,返回。 拿到这个返回值后,我们再看后面的代码

image.png 这里可以看到返的Map调用了getOrDefault方法根据factoryClassName作为key,拿到对应的value,还记得之之前的factoryClassName的值吗,没错其实就是EnableAutoConfiguration的全类名,所以这里拿到的value其实就是我们自动装配的默认配置类全类名 image.png 可以看到这里的返回值果然是我们上面说的LinkedMultiValueMap存储的LinkedList,总共有215个类,看来自动装配的默认配置类还不少啊

image.png loadFactoryNames返回之后,再经过我们之前说的断言,整个getCandidateConfiguration方法终于走完了,那在看看外面的代码

image.png 整个方法刚走完第二行,不过没关系,后面的相较于前面的应该会简单很多 第三行是调用removeDuplicates方法,这个方法主要作用就是去重,其实里面的实现也很简单,就是将我们的configurations作为参数创建一个LinkedHashSet,要知道我们的LinkedHashSet是不能存储重复的值的,所以借助这个机制就可以完成去重。 第四行则是调用一个getExclusions方法,这个方法名翻译过来就是获取排除,等一下,我们要排除啥呢?我们不是在说SpringBoot的自动装配吗,其实这里就是排除某些自动装配,在哪里可以排除制定的自动装配呢,其实上面最开始的第一张图就有排除自动装配的配置

image.png 看到这个exclude属性配置没有其实这个就是排除自动装配的配置,这里我们排除了数据源的自动装配,当然了你也可以用excludeName属性根据名称来配置,或者在配置文件里使用spring.autoconfigure.exclude来配置,或者自定义AutoConfigurationImportFilter过滤器来配置 我们来大概看一下getExclusions方法

image.png

image.png 其实这个方法就是去检查我上面说的那前三种配置,看用户有没有进行配置,由于我这是配置了排除数据源的自动装配,所以方法执行结束时excluded中就是上图所示。 然后就是执行checkExcludedClasses方法,这个方法就不具体看了,主要就是检查排查的这些配置类存不存在,有没有被定义或者是否能被自动装配,如果不行就会抛异常。然后下面的removeAll方法也很简单了,就是将要排除自动装配的类去掉。 然后调用filter方法,这里调用filter方法其实就是排除掉我上面说的最后一种方式,就是使用过滤器来排除自动配置。这个方法也不具体看了,简单来说就是通过执行遍历自动装配过滤器,根据每个过滤器的match方法返回的值判断要排除自动装配的类然后,排除掉后然后将剩下的新建一个ArrayList方在里面返回给我们。

image.png 最后的fireAutoConfigurationImportEvents触发自动装配监听器的意思,里面的逻辑也很简单就是迭代监听器然后调用onAutoConfigurationImportEvent方法,当然在此之前包括上面的执行过滤其中的match方法都会调用invokeAwareMethod方法,主要是考虑到可能会实现Aware相关的接口。

image.png 上面这些方法执行完成之后,最后就是调用AutoConfigurationEntry的构造方法,返回Entry。终于把这个方法走完了。

然后再看selectImports方法 image.png 其实就是将就是将ArrayList类型的配置信息转成一个String数组,这个数组中就是我们要自动装配的默认配置类,返回这个数组后,SpringBoot会自动加载这些类,完成自动装配,至此自动装配的流程就结束了。

3.总结

SpringBoot自动装配的原理就是,EnableAutoConfigurations注解中导入了SelectImports类,这个类中的selectImports方法会读取spring.factories文件,然后获取到EnableAutoConfiguration对应的值,就是我们自动装配的默认配置类的全类名,拿到这个集合后经过验证,去重,排除自动装配以及执行监听器等操作最终返回String数组,此数组就是我们最终要自动装配的配置类集合,SpringBoot再根据这些数据进行配置累的加载,完成整个自动装配。

本文参考:https://dzzhyk.blog.csdn.net/article/details/106005176

猜你喜欢

转载自juejin.im/post/7106050775201087495