一、前世今生
在弄清楚为什么有自动装配这个概念前,不妨让我们回顾下,作为JAVA后端程序员,我们历史上是怎么做的。
- spring 配置大量的xml,形如如下的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<bean id="moocAppctx" class="imooc_spring.test.aware.MoocApplicationContext"
init-method="hhhh">
</bean>
</beans>
- spring boot时代,你会惊奇的发现,这些配置文件都不存在了。但来的是你加了些基础依赖后,直接启动部分调用如下代码后,整个spring就起飞了。
@SpringBootApplication(scanBasePackages = {"com.bianque.dqc"})
@MapperScan(basePackages = "com.test.mybatis.mapper", annotationClass = Mapper.class)
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(DqcApplication.class, args);
}
}
作为对springboot不熟悉的你,会比较疑惑,为什么会有这种情况出现,是不是
- spring boot重复造了套轮子,没有在使用这套xml技术了。
- spring boot使用了牛逼的技术,跳出三界之外了。
带着这样的疑惑,我们发现spring boot的开发与以往的spring开发相比,优势就是只需要很少的代码,就可以实现基础的功能,也就是大家常说的开箱即用。哪么为什么能这么简单呢。
答案就是今天的主角:<b>自动装配</b>。
总结一句话:自动装配技术,使得我们在使用springboot框架时,通过以往积累的经验,针对大部分的配置采用默认化,从大量重复的劳动中解放出来,专注业务本身。
这里可能有同学会问,没有SpringBoot的前提下,我们可以完成Spring程序开发嘛,答案是一定可以,只是技术的进步是解放生产力,由于SpringBoot。解放了大量的配置劳动。此时生产力自然提升。
二、抽丝剥茧
在理解了为什么SpringBoot出现的背景后,接下来,我们要核心的讲解下,他是怎么实现的自动装配。学习理解框架,最直接的版本,就是读源码。所以接下来,我们会以一个实际的测试工程的源码开始,来一层层抽丝剥茧的系统讲解,自动装配的实现逻辑。
1. @SpringBootApplication
在很多源码剖析的书中或者文章中,都开篇来将这个注解。我们也不能免俗,大家通过idea新建一工程后,首先看到的代码大概如下所示:
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
点击到SpringBootApplication内部,我们发现最核心的就
- @SpringBootConfiguration
- @EnableAutoConfiguration
- @ComponentScan
核心就看EnableAutoConfiguration
2. @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 {};
}
三、返璞归真
SpringBoot所有自动配置类都是在启动的时候进行扫描并加载,通过spring.factories可以找到自动配置类的路径,但是不是所有存在于spring,factories中的配置都进行加载,而是通过@ConditionalOnClass注解进行判断条件是否成立(只要导入相应的stater,条件就能成立),如果条件成立则加载配置类,否则不加载该配置类。
总结如下
1.通过各种注解实现了类与类之间的依赖关系,容器在启动的时候Application.run,会调用EnableAutoConfigurationImportSelector.class的selectImports方法(其实是其父类的方法)--这里需要注意,调用这个方法之前发生了什么和是在哪里调用这个方法需要进一步的探讨
2.selectImports方法最终会调用SpringFactoriesLoader.loadFactoryNames方法来获取一个全面的常用BeanConfiguration列表
3.loadFactoryNames方法会读取FACTORIES_RESOURCE_LOCATION(也就是spring-boot-autoconfigure.jar 下面的spring.factories),获取到所有的Spring相关的Bean的全限定名ClassName,大概120多个
4.selectImports方法继续调用filter(configurations, autoConfigurationMetadata);这个时候会根据这些BeanConfiguration里面的条件,来一一筛选,最关键的是
@ConditionalOnClass,这个条件注解会去classpath下查找,jar包里面是否有这个条件依赖类,所以必须有了相应的jar包,才有这些依赖类,才会生成IOC环境需要的一些默认配置Bean
5.最后把符合条件的BeanConfiguration注入默认的EnableConfigurationPropertie类里面的属性值,并且注入到IOC环境当中