返回介绍

7.1.1 查看配置明细

发布于 2025-04-21 21:10:10 字数 10363 浏览 0 评论 0 收藏

关于 Spring 组件扫描和自动织入,最常遭人抱怨的问题之一就是很难看到应用程序中的组件是如何装配起来的。Spring Boot 自动配置让这个问题变得更严重,因为 Spring 的配置更少了。在有显式配置的情况下,你至少还能看到 XML 文件或者配置类,对 Spring 应用程序上下文里的 Bean 关系有个大概的了解。

我个人从不担心这个问题。也许是因为我意识到,在 Spring 出现之前,根本就没有应用程序组件的映射关系。

但是,如果你担心自动配置隐藏了 Spring 应用程序上下文中 Bean 的装配细节,那么我要告诉你一个好消息!Actuator 有一些端点不仅可以显示组件映射关系,还可以告诉你自动配置在配置 Spring 应用程序上下文时做了哪些决策。

1. 获得 Bean 装配报告

要了解应用程序中 Spring 上下文的情况,最重要的端点就是/beans。它会返回一个 JSON 文档,描述上下文里每个 Bean 的情况,包括其 Java 类型以及注入的其他 Bean。向/beans(在本地运行时是 http://localhost:8080/beans )发起 GET 请求后,你会看到与代码清单 7-1 示例类似的信息。

代码清单 7-1 /beans 端点提供的 Spring 应用程序上下文 Bean 信息

[
  {
    "beans": [
      {
        "bean": "application",      ←---Bean ID
        "dependencies": [],
        "resource": "null",
        "scope": "singleton",       ←---资源文件
        "type": "readinglist.Application$$EnhancerBySpringCGLIB$$f363c202"
      },
      {
        "bean": "amazonProperties",
        "dependencies": [],
        "resource": "URL [jar:file:/../readinglist-0.0.1-SNAPSHOT.jar!
                                      /readinglist/AmazonProperties.class]",  ←---依赖
        "scope": "singleton",
        "type": "readinglist.AmazonProperties"
      },
      {
        "bean": "readingListController",
        "dependencies": [         ←---Bean 作用域
          "readingListRepository",
          "amazonProperties"
        ],
        "resource": "URL [jar:file:/../readinglist-0.0.1-SNAPSHOT.jar!
                               /readinglist/ReadingListController.class]",
        "scope": "singleton",
        "type": "readinglist.ReadingListController"
      },
      {
        "bean": "readerRepository",
        "dependencies": [
          "(inner bean)#219df4f5",
          "(inner bean)#2c0e7419",
          "(inner bean)#7d86037b",
          "jpaMappingContext"
        ],
        "resource": "null",
        "scope": "singleton",
        "type": "readinglist.ReaderRepository"     ←---Java 类型
      },
      {
        "bean": "readingListRepository",
        "dependencies": [
          "(inner bean)#98ce66",
          "(inner bean)#1fd7add0",
          "(inner bean)#59faabb2",
          "jpaMappingContext"
        ],
        "resource": "null",
        "scope": "singleton",
        "type": "readinglist.ReadingListRepository"
      },
      ...
    ],
    "context": "application",
    "parent": null
  }
]

代码清单 7-1 是阅读列表应用程序 Bean 信息的一个片段。如你所见,所有的 Bean 条目都有五类信息。

  • bean :Spring 应用程序上下文中的 Bean 名称或 ID。

  • resource :.class 文件的物理位置,通常是一个 URL,指向构建出的 JAR 文件。这会随着应用程序的构建和运行方式发生变化。

  • dependencies :当前 Bean 注入的 Bean ID 列表。

  • scope :Bean 的作用域(通常是单例,这也是默认作用域)。

  • type :Bean 的 Java 类型。

虽然 Bean 报告不用具体绘图告诉你 Bean 是如何装配的(例如,通过属性或构造方法),但它帮你直观地了解了应用程序上下文中 Bean 的关系。实际上,写出一个工具,把 Bean 报告处理一下,用图形化的方式来展现 Bean 关系,这并不难。请注意,完整的 Bean 报告会包含很多 Bean,还有很多自动配置的 Bean,画出来的图会非常复杂。

2. 详解自动配置

/beans 端点产生的报告能告诉你 Spring 应用程序上下文里都有哪些 Bean。/autoconfig 端点能告诉你为什么会有这个 Bean,或者为什么没有这个 Bean。

正如第 2 章里说的,Spring Boot 自动配置构建于 Spring 的条件化配置之上。它提供了众多带有 @Conditional 注解的配置类,根据条件决定是否要自动配置这些 Bean。/autoconfig 端点提供了一个报告,列出了计算过的所有条件,根据条件是否通过进行分组。

代码清单 7-2 是阅读列表应用程序自动配置报告里的一个片段,里面有一个通过的条件,还有一个没通过的条件。

代码清单 7-2 阅读列表应用程序的自动配置报告

{
  "positiveMatches": {      ←---成功条件
  ...
  "DataSourceAutoConfiguration.JdbcTemplateConfiguration
                                                  #jdbcTemplate": [
    {
      "condition": "OnBeanCondition",
      "message": "@ConditionalOnMissingBean (types:
          org.springframework.jdbc.core.JdbcOperations;
          SearchStrategy: all) found no beans"
    }
  ],
  ...
  },
  "negativeMatches": {      ←---失败条件
  "ActiveMQAutoConfiguration": [
    {
      "condition": "OnClassCondition",
      "message": "required @ConditionalOnClass classes not found:
         javax.jms.ConnectionFactory,org.apache.activemq
         .ActiveMQConnectionFactory"
    }
  ],
  ...
  }
}

positiveMatches 里,你会看到一个条件,决定 Spring Boot 是否自动配置 JdbcTemplate Bean。匹配到的名字是 DataSourceAutoConfiguration.JdbcTemplateConfiguration#jdbcTemplate ,这是运用了条件的具体配置类。条件类型是 OnBeanCondition ,意味着条件的输出是由某个 Bean 的存在与否来决定的。在本例中, message 属性已经清晰地表明了该条件是检查是否有 JdbcOperations 类型( JbdcTemplate 实现了该接口)的 Bean 存在。如果没有配置这种 Bean,则条件成立,创建一个 JdbcTemplate Bean。

与之类似,在 negativeMatches 里,有一个条件决定了是否要配置 ActiveMQ。这是一个 OnClassCondition ,会检查 Classpath 里是否存在 ActiveMQConnectionFactory 。因为 Classpath 里没有这个类,条件不成立,所以不会自动配置 ActiveMQ。

3. 查看配置属性

除了要知道应用程序的 Bean 是如何装配的,你可能还对能获取哪些环境属性,哪些配置属性注入了 Bean 里感兴趣。

/env 端点会生成应用程序可用的所有环境属性的列表,无论这些属性是否用到。这其中包括环境变量、JVM 属性、命令行参数,以及 applicaition.properties 或 application.yml 文件提供的属性。

代码清单 7-3 的示例代码是/env 端点获取信息的一个片段。

代码清单 7-3 /env 端点会报告所有可用的属性

{
  "applicationConfig: [classpath:/application.yml]": {    ←---应用属性
    "amazon.associate_id": "habuma-20",
    "error.whitelabel.enabled": false,
    "logging.level.root": "INFO"
  },
  "profiles": [],
  "servletContextInitParams": {},
  "systemEnvironment": {          ←---环境变量
    "BOOK_HOME": "/Users/habuma/Projects/BookProjects/walls6",
    "GRADLE_HOME": "/Users/habuma/.sdkman/gradle/current",
    "GRAILS_HOME": "/Users/habuma/.sdkman/grails/current",
    "GROOVY_HOME": "/Users/habuma/.sdkman/groovy/current",
    ...
  },
  "systemProperties": {      ←---JVM 系统属性
    "PID": "682",
    "file.encoding": "UTF-8",
    "file.encoding.pkg": "sun.io",
    "file.separator": "/",
    ...
  }
}

基本上,任何能给 Spring Boot 应用程序提供属性的属性源都会列在/env 的结果里,同时会显示具体的属性。

代码清单 7-3 中的属性来源有很多,包括应用程序配置(application.yml)、Spring Profile、Servlet 上下文初始化参数、系统环境变量和 JVM 系统属性。(本例中没有 Profile 和 Servlet 上下文初始化参数。)

属性常用来提供诸如数据库或 API 密码之类的敏感信息。为了避免此类信息暴露到/env 里,所有名为 password、secret、key(或者名字中最后一段是这些)的属性在/env 里都会加上“ * ”。举个例子,如果有一个属性名字是 database.password,那么它在/env 中的显示效果是这样的:

"database.password":"******"

/env 端点还能用来获取单个属性的值,只需要在请求时在/env 后加上属性名即可。举例来说,对阅读列表应用程序发起 /env/amazon.associate_id 请求,获得的结果是 habuma-20(纯文本形式)。

回想第 3 章,这些环境属性可以通过 @ConfigurationProperties 注解很方便地使用。这些环境属性会注入带有 @ConfigurationProperties 注解的 Bean 的实例属性。/configprops 端点会生成一个报告,说明如何进行设置(注入或其他方式)。代码清单 7-4 是阅读列表应用程序的配置属性报告片段。

代码清单 7-4 配置属性报告

{
  "amazonProperties": {      ←---Amazon 配置
    "prefix": "amazon",
    "properties": {
      "associateId": "habuma-20"
    }
  },
  ...
  "serverProperties": {      ←---服务器配置
    "prefix": "server",
    "properties": {
      "address": null,
      "contextPath": null,
      "port": null,
      "servletPath": "/",
      "sessionTimeout": null,
      "ssl": null,
      "tomcat": {
        "accessLogEnabled": false,
        "accessLogPattern": null,
        "backgroundProcessorDelay": 30,
        "basedir": null,
        "compressableMimeTypes": "text/html,text/xml,text/plain",
        "compression": "off",
        "maxHttpHeaderSize": 0,
        "maxThreads": 0,
        "portHeader": null,
        "protocolHeader": null,
        "remoteIpHeader": null,
        "uriEncoding": null
      },
      ...
    }
  },
  ...
}

片段中的第一个内容是我们在第 3 章里创建的 amazonProperties Bean。报告显示它添加了 @ConfigurationProperties 注解,前缀为 amazonassociateId 属性设置为 habuma-20。这是因为在 application.yml 里,我们把 amazon.associateId 属性设置成了 habuma-20。

你还会看到一个 serverProperties 条目(前缀是 server ),还有一些属性。它们都有默认值,你也可以通过设置 server 前缀的属性来改变这些值。举例来说,你可以通过设置 server.port 属性来修改服务器监听的端口。

除了展现运行中应用程序的配置属性如何设置,这个报告也能作为一个快速参考指南,告诉你有哪些属性可以设置。例如,如果你不清楚怎么设置嵌入式 Tomcat 服务器的最大线程数,可以看一下配置属性报告,里面会有一条 server.tomcat.maxThreads ,这就是你要找的属性。

4. 生成端点到控制器的映射

在应用程序相对较小的时候,很容易搞清楚控制器都映射到了哪些端点上。如果 Web 界面的控制器和请求处理方法数量多,那最好能有一个列表,罗列出应用程序发布的全部端点。

/mappings 端点就提供了这么一个列表。代码清单 7-5 是阅读列表应用程序的映射报告片段。

代码清单 7-5 阅读列表应用程序的控制器/端点映射

{
  ...

  "{[/],methods=[GET],params=[],headers=[],consumes=[],produces=[],    ←---ReadingListController 映射
                                                       custom=[]}": {
    "bean": "requestMappingHandlerMapping",
    "method": "public java.lang.String readinglist.ReadingListController.
              readersBooks(readinglist.Reader,org.springframework.ui.Model)"
  },
  "{[/],methods=[POST],params=[],headers=[],consumes=[],produces=[],
                                                       custom=[]}": {
    "bean": "requestMappingHandlerMapping",
    "method": "public java.lang.String readinglist.ReadingListController
                            .addToReadingList(readinglist.Reader,readinglist.
     Book)"
  },
  "{[/autoconfig],methods=[GET],params=[],headers=[],consumes=[]   ←---自动配置报告的映射
                                          ,produces=[],custom=[]}": {
    "bean": "endpointHandlerMapping",
    "method": "public java.lang.Object org.springframework.boot
                   .actuate.endpoint.mvc.EndpointMvcAdapter.invoke()"
  },
  ...
}

这里我们可以看到不少端点的映射。每个映射的键都是一个字符串,其内容就是 Spring MVC 的 @RequestMapping 注解上设置的属性。实际上,这个字符串能让你清晰地了解控制器是如何映射的,哪怕不看源代码。每个映射的值都有两个属性: beanmethodbean 属性标识了 Spring Bean 的名字,映射源自这个 Bean。 method 属性是映射对应方法的全限定方法签名。

头两个映射关乎应用程序中 ReadingListController 的请求如何处理。第一个表明 readersBooks() 方法处理根路径(/)的 HTTP GET 请求。第二个表明 POST 请求映射到 addToReadingList() 方法上。

接下来的映射是 Actuator 提供的端点。/autoconfig 端点的 HTTP GET 请求由 Spring Boot 的 EndpointMvcAdapter 类的 invoke() 方法来处理。当然,还有很多其他 Actuator 的端点没有列在代码清单 7-5 里,这种省略完全是为了简化代码示例。

Actuator 的配置端点能很方便地让你了解应用程序是如何配置的。能看到应用程序在运行时究竟发生了什么,这很有趣、很实用。度量端点能展示应用程序运行时内部状况的快照。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。