Spring 开发系列-springboot自动配置

SpringBoot能简化spring应用开发,其中最核心的就是自动配置和起步依赖,起步依赖主要是由Maven插件实现,减少了繁杂的jar包管理;而自动配置针对很多Spring应用程序常见的功能,能自动的配置相关配置,同时允许开发人员修改配置。我们只需要简单引入starter,就能很快搭建出一个应用构架,极大的提升了开发效率。下面讲一下自动配置的实现原理。

 

1spring扩展机制:springfactories

spring-core包里定义了SpringFactoriesLoader类,这个类实现了检索META-INF/spring.factories文件,并获取指定接口的配置的功能。在这个类中定义了两个对外的方法:

loadFactories:根据接口类获取其实现类的实例,这个方法返回的是对象列表。

loadFactoryNames:根据接口获取其接口类的名称,这个方法返回的是类名的列表。

上面的两个方法的关键都是从指定的ClassLoader中获取spring.factories文件,并解析得到类名的列表。

很多包中都能够找到spring.factories文件,我们也可以在自己的jar中配置spring.factories文件,不会影响到其它地方的配置,也不会被别人的配置覆盖,按照properties的格式编写来实现相应的实现类注入Spirng容器中。在springboot中使用factories机制可以让starter的使用只需要很少或者不需要进行配置,只需要在服务中引入我们的jar包即可。

 

接下来我们看看spring-boot和spring-boot-autoconfigure包中的spring.factories文件.

spring-boot

 

这个spring.factories文件主要配置的是spring本身启动需要的处理类,里面包含了许多类型的接口实现类,在spring启动的时候会读取里面的配置用作启动初始化。如PropertySourceLoader处理properties、yml配置文件的、ApplicationListener用于spring容器启动到某个阶段时触发执行的事件、ApplicationContextInitializer初始化spring容器。

spring-boot-autoconfigure

Spring 开发系列-springboot自动配置

这spring.factories文件和上面的文件包含了相同的键,它们会被spring合并,形成多个实现类,它们都是spring启动时默认加载的配置信息。这里涉及到spring是何时加载、实例话这些配置类的对象,以及它们的执行逻辑、实现的具体功能。本次主要讲解springboot自动配置相关的实现,其他的大家可以自行查看SpringApplication类的run方法是如何运用这些参数。

 

我们在应用中引用一些starter,再在spring.properties中简单配置几个参数就能使用对应的第三方插件了,是因为每个starter都包含一个factories文件并且配置了自动配置的实现类,即org.springframework.boot.autoconfigure.EnableAutoConfiguration键值对。在spring-boot-autoconfigure包中的键值包含很多实现类(上面的配置信息我删除了一些),比如常用的数据源、事务管理、springmvc、aop、内嵌web容器等。由于我们在应用主类上添加了@SpringBootApplication注解。@SpringBootApplication注解又基于spring的@Import注解功能,在@Import上配置了EnableAutoConfigurationImportSelector.class,这个类会使用SpringFactoriesLoader.loadFactoryNames()方法将自动配置的实现类全部导入,导入过程在EnableAutoConfigurationImportSelector类的selectImports()方法中。

 

2Import注解

@Import是spring中常见的注解,可以用来动态创建bean,很多地方都使用到了,因此值得我们学习,在开发中也能用类似方式来对bean实例做控制;

在@Import注解的参数中可以传入类名,根据类Test的不同类型,spring容器有以下四种处理方式:

1. 如果Test类实现了ImportSelector接口,spring容器就会实例化Test类,并且调用其selectImports方法;

2. DeferredImportSelector是ImportSelector的子类,如果Test类实现了DeferredImportSelector接口,spring容器就会实例化Test类,并且调用其selectImports方法,和ImportSelector的实例不同的是,DeferredImportSelector的实例的selectImports方法调用时机晚于ImportSelector的实例,要等到@Configuration注解中相关的业务全部都处理完了才会调用;

3. 如果Test类实现了ImportBeanDefinitionRegistrar接口,spring容器就会实例化Test类,并且调用其registerBeanDefinitions方法;

4. 如果Test是普通类,即POJO,spring容器就会实例化Test类;

 

通过查看源代码可以发现@Import注解的实现主要在ConfigurationClassPostProcessor、ConfigurationClassParser 、ConfigurationClassBeanDefinitionReader这三个类上。下图是它们的调用关系:

Spring 开发系列-springboot自动配置

ConfigurationClassPostProcessor类的processConfigBeanDefinitions方法:

Spring 开发系列-springboot自动配置 

ConfigurationClassParser类的doProcessConfigurationClass方法:

 

ConfigurationClassParser类的processImports方法:

 

由于ConfigurationClassPostProcessor类实现了BeanDefinitionRegistryPostProcessor接口,spring在启动时会调用postProcessBeanDefinitionRegistry()方法,这个方法负责处理@Configuration注解相关的逻辑;往下看这个方法里又调用了ConfigurationClassParser类的parse()方法,顺着这个方法一直下去,直到doProcessConfigurationClass()方法,这个方法里面处理了@PropertySource、@ComponentScan、@Import、@ImportResource注解,@PropertySource注解将properties配置文件中的值存储到Spring的 Environment中;@ComponentScan 注解扫描对应类包下更多的配置类,并根据需要递归;@ImportResource注解主要导入xml方式配置的文件;@Import注解实现逻辑对应了processImports()方法,processImports()方法中包含了对ImportSelector实现类、ImportBeanDefinitionRegistrar实现类、普通类和DeferredImportSelector实现类的处理。processImports方法中对ImportSelector实现类的处理,涉及到对processImports方法的递归调用。DeferredImportSelector实现类处理时机比起ImportSelector实现类晚,是在处理完所有@Configuration注解类后。

最后多种方式导入的bean都被保存在ConfigurationClassParser实例中,再回到ConfigurationClassPostProcessor类的processConfigBeanDefinitions方法,ConfigurationClassBeanDefinitionReader类的loadBeanDefinitions()方法处理了保存再ConfigurationClassParser实例中的bean定义并注册到spring容器。

 

到此springboot自动配置的主要原理就讲的差不多了。

 

另外,我们举个例子来分析一下内嵌的tomcat中间件是如何自动配置的。

首先,大家都非常熟悉java程序启动入口是从main方法开始运行,而web应用的程序运行在web容器中,我们写的servlet类或相关业务处理类,能够运行是因为我们把类放入到了web容器中,其实web容器同样是从main方法开始运行,只是这个main方法主要是启动容器并加载容器自身的配置等信息,等容器启动好了才通过java类加载机制将我们的程序代码加载到JVM中再运执行。

 

在springboot应用中默认引入的是tomcat嵌入式web容器,应用的启动方式也是从main方法开始的,这和上面说的观点是一致的。这种方式的实现是由EmbeddedServletContainerAutoConfiguration和ServerPropertiesAutoConfiguration自动配置类实现。

在EmbeddedServletContainerAutoConfiguration中配置了多种嵌入式容器,包括tomcat和jetty,又根据上面讲的Import原理可以看到源码中,最后在容器中添加了一个spring的后置处理器EmbeddedServletContainerCustomizerBeanPostProcessor,也就是说再获取内嵌容器Bean时,会根据spring.properties中的参数设置容器的参数值,如IP,端口,线程池大小等。web容器初始化的地方在AbstractApplicationContext的onRefresh方法中。

原创文章,作者:EBCloud,如若转载,请注明出处:https://www.sudun.com/ask/33192.html

(0)
EBCloud's avatarEBCloud
上一篇 2024年4月2日 下午3:28
下一篇 2024年4月2日 下午3:28

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注