经过Spring Boot中的手动Bean定义提升启动性能

原文:https://blog.csdn.net/qq_4288...react

使用Spring Boot时你不想使用@EnableAutoConfiguration。你应该怎么作?Spring本质上是快速且轻量级的,可是如何让Spring更快?其中一条建议是能够改善启动时间,那就是考虑手动导入Spring Boot配置,而不是自动所有配置。web

对全部应用程序来讲,它不是正确的作法,但它可能会有所帮助,理解选项是什么确定不会有害。在本文中,咱们将探讨各类手动配置方法并评估其影响。spring

彻底自动配置:Hello World WebFlux

做为基准,让咱们看一下具备单个HTTP端点的Spring Boot应用程序:编程

@SpringBootApplication
@RestController
public class DemoApplication {

  @GetMapping("/")
  public Mono<String> home() {
    return Mono.just("Hello World");
  }

  public void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
  }

}

这个应用启动大约一秒钟,或者更长一些,具体取决于您的硬件。它在这段时间内作了不少工做 - 设置日志系统,读取和绑定配置文件,启动Netty并侦听端口8080,提供到@GetMapping应用程序的路由,还提供默认的错误处理。若是Spring Boot Actuator在类路径上,你还会获得一个/ health和/ info端点(因为这个缘由,启动它须要更长的时间)。app

@SpringBootApplication注释实际包含@EnableAutoConfiguration功能,可以自动提供全部有用的功能。这就是Spring Boot流行的缘由,因此咱们不想丢弃这个功能,但咱们能够仔细看看实际发生的事情,也许能够手动完成一些,看看咱们是否学到了什么。函数

注意:若是你想尝试这个代码,很容易从Spring Initializr得到一个空的WebFlux应用程序。只需选中“Reactive Web”复选框并下载项目便可。spring-boot

手动导入自动配置

虽然@EnableAutoConfiguration能够轻松地为应用程序添加功能,但它也能够控制启用哪些功能。大多数人都乐意作出妥协 ,可是过分追求易用性也会失控,可能存在性能损失 , 应用程序可能会启动时慢一点,由于Spring Boot必须作一些工做才能找到全部这些功能并安装它们。事实上,找到正确的功能并无太大的努力:首先类路径扫描,通过仔细优化后,实现有条件的评估很是快。其中应用程序的批量启动时间(80%左右)实际上是由JVM加载类时间,所以实际上使其启动更快的惟一方法是经过安装更少的功能来让JVM加载更少。工具

咱们能够在注释@EnableAutoConfiguration中使用exclude属性禁用自动配置。一些单独的自动配置也有本身的布尔配置标志,能够在外部设置,例如咱们可使用的JMX spring.jmx.enabled=false(例如, 做为系统属性或在属性文件中)。咱们能够走这条路并手动关闭咱们不想使用的全部东西,可是这有点笨拙,而且若是类路径改变也不会阻碍其余组件功能被发现。性能

如今咱们只是使用咱们想要使用的那些功能,咱们能够将其称为“点菜”方式,而不是“彻底自动配置autoconfiguration”中的“全部的你必须吃进去”。自动配置也其实自动寻找@Configuration标注的类,咱们可使用@Import替代@EnableAutoConfiguration,例如,如下是上面的应用程序,具备咱们想要的全部功能(不包括执行器):测试

@SpringBootConfiguration
@Import({
    WebFluxAutoConfiguration.class,
    ReactiveWebServerFactoryAutoConfiguration.class,
    ErrorWebFluxAutoConfiguration.class,
    HttpHandlerAutoConfiguration.class,
    ConfigurationPropertiesAutoConfiguration.class,
    PropertyPlaceholderAutoConfiguration.class
})
@RestController
public class DemoApplication {

  @GetMapping("/")
  public Mono<String> home() {
    return Mono.just("Hello World");
  }

  public void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
  }

}

此版本的应用程序仍将具备咱们上面描述的全部功能,但启动速度更快(可能大约30%左右)。那么咱们为了更快的启动而放弃了什么呢?这是一个快速的概述:

  • Spring Boot自动配置的完整功能包括实际应用程序中可能实际须要的其余内容,而不是特定的小样本。换句话说,30%的加速并不适用于全部应用程序,您的状况可能会有所不一样。
  • 手动配置很脆弱,很难猜到。若是您编写了另外一个执行稍微不一样的应用程序,则须要进行不一样的配置导入。您能够经过将其提取到便利类或注释中并从新使用它来缓解此问题。
  • @Import行为方式与 @EnableAutoConfiguration配置类的排序方式不一样。@Import在某些类具备依赖于早期类的条件行为的状况下,顺序很重要,若是你的配置类有先后依赖顺序关系,你必需要当心。
  • 在典型的实际应用中存在另外一个排序问题。要模仿@EnableAutoConfiguration,首先须要处理用户配置,以便它们能够覆盖Spring Boot中的条件配置。若是使用@ComponentScan而不是@Imports,则没法控制扫描的顺序,或者处理这些类的处理顺序,您可使用不一样的注释来缓解这种状况(参见下文)。
  • Spring Boot自动配置实际上从未被设计为以这种方式使用,使用这种方式可能会在您的应用程序中引入细微的错误。对此的惟一缓解方式就是是详尽的测试,让它以您指望的方式工做,而且对升级持谨慎态度。

增长Actuators

若是咱们也能够在类路径上添加Actuator:

@SpringBootConfiguration
@Import({
    WebFluxAutoConfiguration.class,
    ReactiveWebServerFactoryAutoConfiguration.class,
    ErrorWebFluxAutoConfiguration.class,
    HttpHandlerAutoConfiguration.class,
    EndpointAutoConfiguration.class,
    HealthIndicatorAutoConfiguration.class, HealthEndpointAutoConfiguration.class,
    InfoEndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class,
    ReactiveManagementContextAutoConfiguration.class,
    ManagementContextAutoConfiguration.class,
    ConfigurationPropertiesAutoConfiguration.class,
    PropertyPlaceholderAutoConfiguration.class
})
@RestController
public class DemoApplication {

  @GetMapping("/")
  public Mono<String> home() {
    return Mono.just("Hello World");
  }

  public void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
  }

}

与完整@EndpointAutoConfiguration应用程序相比,此应用程序启动速度更快 (甚至可能快50%),由于咱们只包含与两个默认端点相关的配置。Spring Boot使用自动配置则默认地会激活全部端点,但不会将它们暴露给HTTP,若是咱们只关心/ health和/ info,使用自动配置可能有些浪费,但自动配置也会在表中留下许多很是有用的功能。

Spring Boot可能会在未来作更多工做,以禁用未曝光或未使用过的执行器。

有什么不一样?

手动配置的应用程序有51个bean,而彻底引导的自动配置应用程序有107个bean(不计算执行器)。因此它启动起来可能并不使人意外。在咱们采用不一样的方式实现示例应用程序以前,让咱们先看看咱们遗漏了什么,这样才能更快地启动它。若是在两个应用程序中都列出bean定义,您将看到全部差别来自咱们遗漏的自动配置,以及Spring Boot不会有条件地排除这些自动配置。这是列表(假设您使用spring-boot-start-webflux时没有手动排除):

AutoConfigurationPackages
CodecsAutoConfiguration
JacksonAutoConfiguration
JmxAutoConfiguration
ProjectInfoAutoConfiguration
ReactorCoreAutoConfiguration
TaskExecutionAutoConfiguration
TaskSchedulingAutoConfiguration
ValidationAutoConfiguration
HttpMessageConvertersAutoConfiguration
RestTemplateAutoConfiguration
WebClientAutoConfiguration

这就是咱们不须要的12个自动配置(不管如何),而且在自动配置的应用程序中致使了56个额外的bean。它们都提供了有用的功能,因此咱们可能但愿有一天再次将它们包括在内,可是如今让咱们假设咱们更愿意在没有他们的状况下使用。

spring-boot-autoconfigure有122个自动配置(还有更多spring-boot-actuator-autoconfigure)和彻底引导的自动配置的示例应用程序上面只使用了其中的18个。计算使用哪些是很是早的,而且在任何类甚至加载以前,大多数都被Spring Boot丢弃,这种计算很是快(几毫秒)

Spring Boot自动配置导入

能够经过使用不一样的注释来部分地解决与用户配置(必须最后应用)和自动配置之间的差别相关联的排序问题。Spring Boot为此提供了一个注释:@ImportAutoConfiguration来自Spring Boot Test附带spring-boot-autoconfigure的 测试切片功能。所以,您能够替换上面示例中的注释@Import, @ImportAutoConfiguration效果是推迟自动配置的处理,直到全部用户配置加载(例如,经过@ComponentScan或接收@Import)。

若是咱们准备将自动配置列表整理成自定义注释,咱们甚至能够更进一步。不只仅是直接使用@ImportAutoConfiguration,咱们能够写一个自定义注释:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ImportAutoConfiguration
public @interface EnableWebFluxAutoConfiguration {
}

此注释的主要特征是它带有元注释 @ImportAutoConfiguration。有了这个,咱们能够在咱们的应用程序中添加新的注释:

@SpringBootConfiguration
@EnableWebFluxAutoConfiguration
@RestController
public class DemoApplication {

  @GetMapping("/")
  public Mono<String> home() {
    return Mono.just("Hello World");
  }

  public void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
  }

}

并列出实际的配置类/META-INF/spring.factories:

com.example.config.EnableWebFluxAutoConfiguration=\
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration

这样作的好处是应用程序代码再也不须要手动枚举配置,并且如今由Spring Boot处理排序(属性文件中的条目在使用以前会进行排序)。缺点是它仅对须要精确这些特征的应用程序有用,而且须要在想要作一些不一样的事情的任何应用程序中进行替换或扩充,固然它仍然会很快 - Spring Boot为排序作了一些额外的工做,但实际上并非不少。在合适的硬件上,它可能仍然会在不到700毫秒的时间内启动,并带有正确的JVM标志。

函数Bean定义

在前面的文章中,我提到函数bean定义将是使用Spring启动应用程序的最有效方法。咱们能够经过从新编写全部Spring Boot自动配置为ApplicationContextInitializers来将这个应用程序额外挤出10%左右。

您能够手动执行此操做,或者您可使用已为您准备的一些初始化程序,只要您不介意尝试某些实验性功能便可。目前有2个项目正在积极探索基于函数bean定义的新工具和新编程模型的概念:Spring Fu和 Spring Init。二者都提供至少一组函数bean定义来替换或包装Spring Boot自动配置。

Spring Fu是基于API(DSL)的,不使用反射或注释;Spring Init具备函数bean定义,而且还具备用于“单点”配置的基于注释的编程模型的原型。其余地方都有更详细的介绍。

这里要注意的要点是函数bean定义更快,但若是这是你主要考虑的问题,请记住它只有10%的效果。只要将全部功能放回到咱们上面剥离的应用程序中,您就能够从新加载全部必需的类,并从新回到大体相同的启动时间。换句话说,@Configuration自己运行时处理的成本 并非彻底能够忽略不计,但它也不是很高(在这些小应用程序中可能只有10%左右,或者多是100毫秒)。

总结

Spring Boot自动配置很是方便,但能够称之为“吃进全部你能够吃的东西”。目前(从2.1.x开始)它可能提供比某些应用程序使用或要求更多的功能。在“菜单单点”方法中,您可使用Spring Boot做为准备和预测试配置的便捷集合,并选择您使用的部件。若是你这样作,那么@ImportAutoConfiguration是工具包的一个重要部分,可是当咱们进一步研究这个主题时,你应该如何最好地使用它。

Spring Boot的将来版本以及可能的其余新项目(如Spring Fu或Spring Init)将使得在运行时使用的配置选择变得更加容易,不管是自动仍是经过显式选择。注意,@Configuration自己在运行时处理并非免费的,但它也不是特别昂贵(特别是使用Spring Boot 2.1.x)。您使用的功能数量越少,加载的类越少,启动速度越快。最后,咱们不但愿 @EnableAutoConfiguration失去其价值或受欢迎程度。

写在最后:

既然看到这里了,以为笔者写的还不错的就点个赞,加个关注呗!点关注,不迷路,持续更新!!!

相关文章
相关标签/搜索