当前位置: 首页 > 科技观察

大牛深度解析SpringBoot核心运行原理及源码

时间:2023-03-15 14:06:54 科技观察

SpringBoot核心运行原理SpringBoot的核心功能是自动配置。正如我们在第1章中提到的,功能的实??现是基于“约定优于配置”的规则。那么SpringBoot是如何约定的,又是如何实现自动配置功能的呢?本章将带领大家通过源码了解SpringBoot的核心运行原理。内容涉及自动配置的运行原理、核心功能模块、核心注解以及核心使用的源码分析。核心运行原理在使用SpringBoot时,我们只需要导入相应的Starters,SpringBoot在启动时会自动加载相关依赖,配置相应的初始化参数,以最快最简单的方式集成第三方软件。是SpringBoot的自动配置功能。下面我们来整体了解一下SpringBoot在运行机制实现中涉及到的核心部分,如图2-1所示。图2-1描述了SpringBoot自动配置功能运行中涉及的几个核心函数及其相互关系,包括@EnableAutoConfiguration、spring.factories、各组件对应的AutoConfiguration类、@Conditional注解、各种Starters。整个过程可以用一句话来描述:SpringBoot通过@EnableAutoConfiguration注解开启自动配置,加载在spring.factories中注册的各种AutoConfiguration类。、配置或Spring容器中是否有Bean等),实例化AutoConfiguration类中定义的Bean(组件等)注入到Spring容器中,完成依赖框架的自动配置。我们先从概念和作用来理解图2-1所示各部分的作用和相互关系。在接下来的章节中,我们将从源码层面对各个功能和组件进行讲解。·@EnableAutoConfiguration:该注解由组合注解@SpringBootApplication引入,完成自动配置,扫描各个jar包下的spring.factories文件,加载文件中注册的AutoConfiguration类等。·spring.factories:配置文件,位于jar包的META-INF目录下,注册了AutoConfiguration类,用于按照指定的格式进行自动配置。spring.factories也可以包含其他类型的要注册的类。这个配置文件不仅存在于SpringBoot项目中,也存在于自定义自动配置(或Starter)项目中。AutoConfiguration类:自动配置类,代表SpringBoot中一类名为XXAutoConfiguration的自动配置类。定义了三方组件集成Spring需要初始化的bean和条件。·@Conditional:在AutoConfiguration类上使用条件注解及其派生注解,当条件注解满足时,将实例化AutoConfiguration类。·Starters:三方组件的依赖和配置,SpringBoot已经预置的组件。SpringBoot默认的Starters项目往往只包含一个pom依赖项目。如果是自定义的starter,项目还需要包含spring.factories文件,AutoConfiguration类等配置类。以上从概念层面介绍了SpringBoot自动配置的整体流程和基本运行原理。下面将详细介绍这些核心部分的组成结构和源代码。工作原理源码分析Z@EnableAutoConfiguration@EnableAutoConfiguration是开启自动配置的注解。在创建的SpringBoot工程中是不能直接看到这个注解的。它由组合注解@SpringBootApplication引入。我们先了解一下入口类和@SpringBootApplication注解的作用,再详细了解@EnableAutoConfiguration注解的组成和作用。入口类和@SpringBootApplication注解SpringBoot项目创建后,默认会生成一个*Application入口类。默认情况下,无论是通过IDEA还是通过官方创建基于Maven的SpringBoo项目,入口类的命名规则都是artifactld+Application。通过该类的main方法可以启动SpringBoot项目,代码如下。@SpringBootApplicationpublicclassSpringLearnApplication{publicstaticvoidmain(String[]args){SpringApplication.run(DemoApplication.class,args);}}这里的main方法没什么特别的,是一个标准Java应用的main方法,用来启动SpringBoot入口到项目。默认情况下,根据上述规则命名并包含main方法的类称为入口类。在SpringBoot入口类中(单元测试除外),唯一的注解是@SpringBootApp-lication。它是SpringBoot项目的核心注解,用于开启自动配置。准确的说,是通过在这个注解中结合@EnableAutoConfiguration开启自动配置。@SpringBootApplication的部分源码如下。@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan(excludeFilters={@Filter(type=FilterType.CUSTOM,classes=TypeExcludeFilter.class),@Filter(type=FilterType.CUSTOM,classes=AutoConfigurationExcludeFilter.class)})public@interfaceSpringBootApplication{//排除指定的自动配置类@AliasFor(annotation=EnableAutoConfiguration.class)Class[]exclude()default{};//排除指定的自动配置自动配置类名@AliasFor(annotation=EnableAutoConfiguration.class)String[]excludeName()default{//指定扫描的基础包,激活解决方案组件的初始化@AliasFor(annotation=ComponentScan.class,attribute="basePackages")String[]scanBasePackages()default{};//指定扫描类进行初始化@AliasFor(annotation=ComponentScan.class,attribute="basePackageClassClass[]scanBasePackageClasses()default{};//指定是否代理@Bean方法强制执行bean的生命周期行为@AliasFor(annotation=Configuration.class)booleanproxyBeanMethods()defaulttrue;}从源码可以看出,该注解提供了如下成员属性(注解中的成员变量体现在以方法的形式)。exclude:根据类(Class)排除指定的自动配置。该成员属性覆盖在@SpringBoot-Application中组合的@EnableAutoConfiguration中定义的排除成员属性。excludeName:根据类名排除指定的自动配置,覆盖@EnableAutoConfiguration中excludeName的成员属性。:scanBasePackages:指定扫描的基础包,用于激活@Component等注解类的初始化。scanBasePackageClasses:扫描指定类进行组件初始化。:proxyBeanMethods:指定是否代理@Bean方法来强制执行bean的生命周期行为。此功能需要通过CGLIB子类的运行时生成进行方法拦截。这个子类有一定的限制,比如配置类及其方法不能声明为final等调用配置的@Bean方法。如果@Bean方法是自包含的,只提供容器常用工程方法的功能,可以设置为false,避免处理CGLIB子类。SpringBoot2.2版本上线后,增加了该成员属性。后面章节涉及到的自动配置类基本都会用到proxyBeanMethods,一般配置为false。通过上面的源码,我们会发现@AliasFor注解在SpringBoot中被广泛使用,用于桥接其他注解,在注解的属性中指定要桥接的注解类。如果点进去查看,会发现@SpringBootApplication定义的属性已经在其他注解中定义过了。之所以在@SpringBootApplication中使用@AliasFor注解并重新定义它,更多的是为了减少用户使用多个注解带来的麻烦。@SpringBootApplication注解结合了@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan。所以在实际中也可以用这三个注解代替@SpringBootApplication。在早期版本的SpringBoot中,并没有@SpringBootConfiguration注解。版本升级后,增加了@SpringBootConfiguration,并在其中合并了@Configuration。@EnableAutoConfiguration注解结合了@AutoConfigurationPackage。我们忽略了一些基本的注释和元注释。@SpringBootApplication注解的组合结构可以参考图2-2。在图2-2中,除了结合元注解,@SpringBootApplication的核心功能包括:激活@EnableAutoConfiguration用于SpringBoot自动配置,@ComponentScan用于激活@Component扫描,@Configuration用于激活配置类。其中@ComponentScan注解和@Configuration注解是Spring日常使用中经常用到的,也是非常基础的。大家应该有所了解,这里不再赘述。下面详细介绍@EnableAuto-Configuration的作用。注解@EnableAutoConfiguration作用分析在不使用SpringBoot的情况下,Bean的生命周期由Spring管理,但是Spring无法自动配置注解@Configuration的类。而SpringBoot的核心功能之一就是按照约定自动管理注解类。用于实现此功能的组件之一是@EnableAutoConfiguration注释。@EnableAutoConfiguration位于spring-boot自动配置包中。使用@SpringBootApplication注解时,@EnableAutoConfiguration注解会自动生效。@EnableAutoConfiguration的主要作用是在启动Spring应用上下文时自动配置,它会尝试猜测并配置项目可能需要的bean。自动配置通常基于项目类路径中引入的类和定义的bean。在这个过程中,自动配置的组件来自于项目本身和项目所依赖的jar包。例如:如果你把tomcat-embedded.jar加入classpath,那么@EnableAutoConfiguration就会认为你要使用TomcatServletWebServerFactory类,帮你初始化相关配置。同时如果自定义一个基于ServletWebServerFactory的Bean,@EnableAutoConfiguration不会初始化TomcatServletWebServerFactory类。这一系列的操作判断都是由SpringBoot来完成的。我们来看一下@EnableAutoConfiguration注解的源码。@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@AutoConfigurationPackage@Import(AutoConfigurationImportSelector.class)public@interfaceEnableAutoConfiguration{//lid配置开启/关闭自动配置功能StringENABLED.OVERRIDE_PROPERTY="spring.boot.enableautoconfiguration";//根据类(Class)排除指定的自动配置Class[]exclude()default{};//根据类名排除指定的自动配置String[]excludeName()default{};}@EnableAutoConfiguration注解提供了一个常量和两个成员参数的定义。ENABLEDOVERRIDEPROPERTY:用于覆盖开启/关闭自动配置的功能。-exclude:根据类(Class)排除指定的自动配置。excludeName:根据类名排除指定的自动配置。上面提到@EnableAutoConfiguration会猜测你需要使用的Bean,但是如果你在实战中不需要它来预置和初始化Bean,你可以使用注解的exclude或者excludeName参数来进行针对性的排除。例如,当不需要数据库的自动配置时,可以通过以下两种方式使自动配置失效。//排除DataSourceAutoConfigurationvia@SpringBootApplication@SpringBootApplication(exclude=DataSourceAutoConfiguration.class)publicclassSpringLearnApplication{}或者//排除DataSourceAutoConfiguration@Configuration@EnableAutoConfiguration(exclude=DataSourceAutoConfiguration.class)@EnableAutoConfiguration@EnableAutoConfiguration注解类的封装也有特定含义,通常作为扫描注解@Entity的根路径。这也是为什么在使用@SpringBootApplication注解时需要将注解类放在顶层包下的原因。如果放在下层,则无法扫描到其所在包的同层或上层类。对于入口类及其main方法,它不依赖于@SpringBootApplication注解和@EnableAuto-Configuration注解,也就是说该注解可以用在其他类上而不是入口类上。