在使用springboot进行开发的时候,只需要在application.properties中进行参数配置,不配置也会有一个默认值,tomcat,映射关系都不用配置了,那么spring是如何实现自动配置的呢?

springboot如何实现自动装配

自动配置的。这都要归功于springboot强大的注解功能,也是springboot的一大特点

举例:在springboot主运行程序中,下面是常见的springboot启动类

1
2
3
4
5
6
@SpringBootApplication
public class SpringbootSrcApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootSrcApplication.class, args);
}
}

看起来平平无奇,但为什么一运行main方法就会启动容器?

spring会在启动的时候new自己—>new SpringApplication(primarySources).run(args)这时候就会初始化,并加载扫描到所有的组件进入缓存

1
2
3
4
5
6
7
8
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> result = cache.get(classLoader);
//后面这个类会经常用到,所以spring做了缓存,如果有直接返回
if (result != null) {
return result;
}
//没有会扫描META-INF/spring.factories的所有组件
}

第一次扫描结果如下,返回18个结果,其中就有后面需要的自动配置包,如红框所示

image-20210812175255284

这么多组件,哪个才会真正的被容器所实例化?

其实真正发挥作用的是@Import(),它导什么,容器实例化什么

先来从@SpringBootApplication这个注解说起

image-20210810114455286

主要的注解为@SpringBootConfiguration+@ComponentScan+@EnableAutoConfiguration

前两个没什么好说的,@SpringBootConfiguration就是封装了@Configuration注解,表示他是一个配置类

image-20210810114947459

@ComponentScan:扫描注解,表示哪些会被spring扫描到

那么重要的就是@EnableAutoConfiguration,见名知意拥有自动配置的能力,点进去看下

image-20210812181648668

主要注解为@AutoConfigurationPackage+@Import,而@AutoConfigurationPackage其中也是@Import,所以起到主要作用的就是@Import这个注解了

@Import:给容器中导入一个组件,前提是必须作用于spring的组件下,可以自动调用组件的无参构造器创建对象

1
2
3
4
5
6
7
8
9
10
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
/**
* {@link Configuration @Configuration}, {@link ImportSelector},
* {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
*/
Class<?>[] value();
}

由上图可知import导入了AutoConfigurationImportSelector.class这个组件,那么程序中是什么时候导入的?

spring容器启动会刷新IOC容器(refreshContext(context))执行invokeBeanFactoryPostProcessors方法,最终调用到getImports()这个方法,这个方法会将标有@Import的组件进行实例化

1
2
3
4
5
6
7
8
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
//核心process
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
return this.group.selectImports();
}

上述方法会执行AutoConfigurationImportSelector类的process方法

调用核心方法SpringFactoriesLoader.loadFactoryNames导入一系列组件

image-20210810150102886

拓展部分,怎么导入的?

接上段程序,调用关键语句

1
2
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());

其中getSpringFactoriesLoaderFactoryClass()返回的正是@EnableAutoConfiguration这个注解

1
2
3
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}

点进去loadFactoryNames

1
2
3
4
5
6
7
8
9
10
11
12
13
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
//根据上面代码获得EnableAutoConfiguration的完整类名(全类名,包括包名)
//org.springframework.boot.autoconfigure.EnableAutoConfiguration
String factoryTypeName = factoryType.getName();
//loadSpringFactories(classLoaderToUse)就是刚开始初始化的18个结果集
//后面的get方法就是Map的方法,根据这个全类名可以获取到自动配置包下的所有组件(类)
//也就是上面截图中的131个组件
return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}

怎么实例化的

spring在刷新IOC的最后一步会调用finishBeanFactoryInitialization(beanFactory)方法,来实例化剩余的非lazy的单例,在调用过程中beanFactory.preInstantiateSingletons(),该方法会遍历所有的bean

image-20210812184747238

然后调用工厂的getBean方法进行实例化

image-20210812185015122

但并不是这133个都会被实例化,还得看条件注解,实现了按需配置

注解有如下几项:

@ConditionalOnBean:当容器里有指定的bean的条件下

@ConditionalOnMissingBean:当容器里不存在指定bean的条件下

@ConditionalOnClass:当类路径下有指定类的条件下

@ConditionalOnMissingClass:当类路径下不存在指定类的条件下

@ConditionalOnProperty:指定的属性是否有指定的值,比如@ConditionalOnProperties(prefix=”xxx.xxx”, value=”enable”, matchIfMissing=true),代表当xxx.xxx为enable时条件的布尔值为true,如果没有设置的情况下也为true

比如DispatcherServletAutoConfiguration自动配置类中满足DispatcherServlet类存在,并且当前环境是SERVLET的时候才会进行实例化

image-20210812185340454

反观KafkaAutoConfiguration,由于没有导入对应的包KafkaTemplate类也就不存在了,自然这个自动配置类也不会进行实例化

image-20210812190428211

总结

springboot在启动的时候会实例化自动配置包下的组件和我们自定义的组件

image-20210812185624610

我们可以自定义组件也可以按需加载组件,还可以重写spring的组件,因为会优先取用户的配置,没有才会给你默认装配,以上就是关于springboot自动配置的一些原理