1.引入案例

1.1 新建一个Springboot项目

新建时,勾选了spring-web组件依赖,创建了一个TestController,配置文件完全是空的,直接跑起来:

ubuntu springboot 服务 springboot autoconfigure_spring


浏览器访问http://localhost:8080/test 看看结果

ubuntu springboot 服务 springboot autoconfigure_bc_02


看到这里,对springboot不熟悉的小伙伴就会有疑问了(大神请忽略),springboot启动的时候,到底帮我们加载了一些什么配置,别忘了,我们在创建项目的时候在pom文件添加了spring-boot-starter-web组件,点击组件进去可以看到具体的依赖项(太多了,就不贴出来了,有tomcat、webMvc的等等),

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

那么Springboot到底是如何加载我们添加的组件依赖默认组件场景的,这个需要回看我们的主启动类头上有个注解:@SpringBootApplication,点开注解进去看,发现它是个组合注解:

@Target(ElementType.TYPE) //注解的作用域
@Retention(RetentionPolicy.RUNTIME) //注解的生存周期
@Documented //可以被文档化
@Inherited //注解用于标注一个父类的注解是否可以被子类继承
@SpringBootConfiguration //声明为一个配置类 proxyBeanMethods是否开启bean代理,默认是true,从IOC容器中取;如果是false则每次获取都是一个新的实例
@EnableAutoConfiguration //开启自动配置---这个将是我们研究的重点
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) //包扫描的规则

经过分析,开启自动配置的注解是 @EnableAutoConfiguration,点开注解进去看到又是一个组合注解,我们开始分析@Target,@Retention,@Documented,@Inherited这几个上面分析了,不是重点我们忽略掉,关注点放在:@AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class);

/** @author Phillip Webb
 * @author Stephane Nicoll
 * @since 1.0.0
 * @see ConditionalOnBean
 * @see ConditionalOnMissingBean
 * @see ConditionalOnClass
 * @see AutoConfigureAfter
 * @see SpringBootApplication
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
1.2 分析 @AutoConfigurationPackage,

这个类的注释上面写着是“注册packages”,我们看到

/**
 * Registers packages with {@link AutoConfigurationPackages}. When no {@link #basePackages
 * base packages} or {@link #basePackageClasses base package classes} are specified, the
 * package of the annotated class is registered.
 *
 * @author Phillip Webb
 * @since 1.3.0
 * @see AutoConfigurationPackages
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

@Import(AutoConfigurationPackages.Registrar.class),点进去,断点打上,看它做了什么,

ubuntu springboot 服务 springboot autoconfigure_spring_03


step into 到方法PackageImports上,拿到了启动类BootTestApplication所在的包路径,然后注册到容器中

正是因为有这个注解,我们上面的TestController是在这个com.wxq.csdn包路径下的,默认就能识别到,那其它路径能不能识别呢?

ubuntu springboot 服务 springboot autoconfigure_加载_04

ubuntu springboot 服务 springboot autoconfigure_spring_05


针对上面的疑问,我们来做一个测试,在主启动类外边再新建一个包,然后写一个TestController01,看看能不能访问到,这里给出结论是访问不到(如下面的两张图),因为Springboot自动配置包路径的时候默认是从启动类所在的包找的,其它路径它是不认识的;那有没有办法让springboot认识这个包路径,办法是有的,不然读源码干嘛,请继续往下看

ubuntu springboot 服务 springboot autoconfigure_bc_06


ubuntu springboot 服务 springboot autoconfigure_spring boot_07


针对上面问题,解决com.wxq.01这个包不识别的问题,

在启动类似@ComponentScan(value = “com.wxq.01”),告诉springboot启动的时候来扫描一下这个包下的类,注册和加载进容器,重启项目,访问刚刚404的地址,结果如下:

ubuntu springboot 服务 springboot autoconfigure_spring_08

ubuntu springboot 服务 springboot autoconfigure_bc_09

1.3 分析@Import(AutoConfigurationImportSelector.class)

1.1 先抛出结论:读取所有META-INF/spring.factories配置文件下写死的路径的类加载到容器中,主要是这个spring-boot-autoconfigure包下的spring.factories配置的类;

ubuntu springboot 服务 springboot autoconfigure_加载_10


1.2 看看springboot是怎么找到spring.factories文件的

通过断点调试发现,主启动类的run方法实现里面调用了refreshContext(context);,这个会调用到AutoConfigurationImportSelector里面的 process方法,下面我们一步一步断点进去看,

先进来主启动类的run方法

ubuntu springboot 服务 springboot autoconfigure_spring boot_11


F9继续走下去(因为我提前打好断点了,第一次调试的朋友请用F7 一步一步调进去),方法执行到上面说的process里面,注意一个getAutoConfigurationEntry,

ubuntu springboot 服务 springboot autoconfigure_bc_12


继续走进去看到这句:List configurations = getCandidateConfigurations(annotationMetadata, attributes); 继续往下走看到这个方法,解析出来的configurations正好是我们上面说的spring.factories配置的Auto Configure

ubuntu springboot 服务 springboot autoconfigure_加载_13


那么到这里,我们已经知道了springboot如何自动加载配置类,但是加载了不一定就生效的,我们拿下面的例子说明

打开spring-boot-autoconfigure包下的源码,看到的都是springboot默认帮我们准备好的配置场景(图没截取到全部)

ubuntu springboot 服务 springboot autoconfigure_加载_14


我们拿jdbc这个举例,因为这个大家应该很熟悉,如图,打开DataSourceAutoConfiguration发现有报错,因为我们根本没有引入Jdbc相关的依赖,所以这个配置类是不生效的,@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class }) 这个条件不满足

ubuntu springboot 服务 springboot autoconfigure_spring boot_15


我们拿一个生效的配置类举例,因为我们引入了spring-boot-starter-web组件,那么web场景的配置是生效的,我们依旧是看源码,web目录下的配置类,

ubuntu springboot 服务 springboot autoconfigure_加载_16


点开 DispatcherServletAutoConfiguration这个自动配置类,把DispatcherServlet注册到容器中,这个是springMvc的核心,

ubuntu springboot 服务 springboot autoconfigure_spring_17

总结

1.自动配置原理,关键在于2个注解**@AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class)**,上面写的看起来可能不太好懂,但是动手跟着上面的步骤去调试,相信应该能看懂。
2.自动配置类是否生效,需要看我们pom文件里面有没有使用到具体的业务场景,比如上面的案例,我们没有使用到jdbc相关的场景,它的配置类是不生效的。