聊聊如何玩转spring-boot-admin

前言

1、何为spring-boot-admin?

Spring Boot Admin 是一个监控工具,旨在以良好且易于访问的方式可视化 Spring Boot Actuators 提供的信息

快速开始

如何搭建spring-boot-admin-server

1、在服务端项目的POM引入相应的GAV

  <dependency><groupId>de.codecentric</groupId><artifactId>spring-boot-admin-starter-server</artifactId><version>${spring-boot-admin.version}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>

2、新建springboot启动类并加上@EnableAdminServer

@SpringBootApplication
@EnableAdminServer
public class MonitorApplication {public static void main(String[] args) {SpringApplication.run(MonitorApplication.class);}
}

配置完,访问一下页面

虽然可以访问,但是这样不安全,接下来我们和spring security做个整合

3、整合spring security

a、 在服务端项目的pom引入security GAV

 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency>

b、 在服务端项目的application.yml配置相关用户名和密码

spring:security:user:name: ${MONITOR_USER:admin}password: ${MONITOR_PWD:admin}

c、 定制security config

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityMonitorConfig extends WebSecurityConfigurerAdapter {private final AdminServerProperties adminServer;private final WebEndpointProperties webEndpointProperties;@Overrideprotected void configure(HttpSecurity http) throws Exception {SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();successHandler.setTargetUrlParameter("redirectTo");successHandler.setDefaultTargetUrl(this.adminServer.path("/"));http.authorizeRequests().requestMatchers(new AntPathRequestMatcher(this.adminServer.path("/assets/**"))).permitAll().requestMatchers(new AntPathRequestMatcher(this.adminServer.path(webEndpointProperties.getBasePath() + "/info"))).permitAll().requestMatchers(new AntPathRequestMatcher(adminServer.path(webEndpointProperties.getBasePath() + "/health"))).permitAll().requestMatchers(new AntPathRequestMatcher(this.adminServer.path("/login"))).permitAll().anyRequest().authenticated().and().formLogin().loginPage(this.adminServer.path("/login")).successHandler(successHandler).and().logout().logoutUrl(this.adminServer.path("/logout")).and().httpBasic().and().csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()).ignoringRequestMatchers(new AntPathRequestMatcher(this.adminServer.path("/instances"), POST.toString()),new AntPathRequestMatcher(this.adminServer.path("/instances/*"), DELETE.toString()),new AntPathRequestMatcher(this.adminServer.path(webEndpointProperties.getBasePath() + "/**")));http.rememberMe((rememberMe) -> rememberMe.key(UUID.randomUUID().toString()).tokenValiditySeconds(1209600));}}

配置完访问一下页面


输入用户名和密码 admin/admin


如果对整合安全认证还有疑问,可以直接参考官网
https://docs.spring-boot-admin.com/current/security.html

4、页面定制

如果我们觉得登录的springboot admin logo个性化不强,我们可以简单定制一下

在application.yml做如下配置

spring:boot:admin:ui:title: ${UI_TITLE:LYB-GEEK Monitor}brand: <img src="assets/img/icon-spring-boot-admin.svg"><span>${spring.boot.admin.ui.title}</span>

配置好访问一下


如果有些导航栏,我们觉得不需要,比如去掉关于我们

spring:boot:admin:ui:view-settings:- name: "about"enabled: false

注: view-settings这个配置需要是2.3.1以上版本才有的属性

配置好访问一下


发现关于我们已经去掉了,以上只是简单定制,更多定制可以参考如下链接
https://docs.spring-boot-admin.com/current/customize_ui.html

5、与注册中心集成

a、 在服务端项目中pom引入eureka-client GAV

  <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency>

b、 在application.yml文件引入eureka 客户端相关配置

eureka:instance:instance-id: ${spring.application.name}:${spring.cloud.client.ip-address}:${spring.application.instance_id:${server.port}}prefer-ip-address: ${PREFER_IP:true}  #是否选择IP注册#   ip-address: ${IP_ADDRESS:localhost}   #指定IP地址注册lease-renewal-interval-in-seconds: 5  #续约更新时间间隔(默认30秒),使得eureka及时剔除无效服务lease-expiration-duration-in-seconds: 10 #续约到期时间(默认90秒)hostname: ${HOSTNAME:${spring.application.name}}client:service-url:defaultZone: ${EUREKA_CLIENT_SERVICEURL_DEFAULTZONE:http://localhost:8761/eureka/}#缩短延迟向服务端注册的时间、默认40sinitial-instance-info-replication-interval-seconds: 10#提高Eureka-Client端拉取Server注册信息的频率,默认30sregistry-fetch-interval-seconds: 5

访问eureka控制面板


服务端的配置暂且说到这边,接下来我们说下客户端集成

如何搭建spring-boot-admin-client

1、在客户端项目的POM配置相关GAV

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>de.codecentric</groupId><artifactId>spring-boot-admin-starter-client</artifactId><version>${spring-boot-admin-client.version}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency>

2、客户端暴露actuator相关端点

management:endpoints:web:exposure:include: "*" endpoint:health:show-details: ALWAYS

3、配置spring-boot-admin服务端地址

spring:boot:admin:client:url: http://localhost:8080

启动观察控制台,会发现有如下信息

原因是因为我们服务端配置了鉴权,因此我们客户端还需做如下配置

spring:boot:admin:client:url: http://localhost:8080username: adminpassword: admin

配置好,观察控制台,发现没异常信息,此时我们访问服务端监控面板


如图说明客户端搭建成功

4、配置应用信息

默认我们查看服务端监控面板–应用列表详情,会发现


这个信息是空的,我们可以在yml配置形如下内容

info:groupId: @project.groupId@artifactId: @project.artifactId@version: @project.version@describe: 这是一个微服务应用

再次访问服务端监控面板

其实这个采的就是actuator/info端点。当然可以像官方介绍的示例,在项目的POM引入springboot插件,并指定goal为build-info

<build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><executions><execution><goals><goal>build-info</goal></goals></execution></executions></plugin></plugins>
</build>

5、在服务端监控面板集成客户端日志

默认是没集成客户端日志,如图


通过官网
在这里插入图片描述
我们知道要配置logging.file.path或者logging.file.name

示例配置

logging:file:path: ${LOG_FILE_PATH:/data/logs/cloud-mini-comsumer}

logback-spring相关配置如下

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false"><include resource="org/springframework/boot/logging/logback/defaults.xml"/><property name="serviceName" value="cloud-mini-comsumer"/><property name="logHome" value="/data/logs/${serviceName}"/><contextName>${serviceName}</contextName><!--输出到控制台--><appender name="console" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>${CONSOLE_LOG_PATTERN}</pattern></encoder></appender><!--按天生成日志--><appender name="logFile" class="ch.qos.logback.core.rolling.RollingFileAppender"><Prudent>true</Prudent><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><FileNamePattern>${logHome}/%d{yyyy-MM-dd}/%d{yyyy-MM-dd}.log</FileNamePattern><maxHistory>30</maxHistory></rollingPolicy><layout class="ch.qos.logback.classic.PatternLayout"><Pattern>%d{yyyy-MM-dd HH:mm:ss} -%msg%n</Pattern></layout></appender><root level="info"><appender-ref ref="console"/><appender-ref ref="logFile"/></root></configuration>

我们配置后,出现日志文件按钮,点击后出现

那就很诡异,明明按官网配置了,后面排查发现,其他服务可以出现日志,他们配置日志目录底下,都会生成一个spring.log日志,那意味着只要能生成spring.log即可。于是我们调整一下logback-spring,将

 <include resource="org/springframework/boot/logging/logback/defaults.xml"/>

调整为

 <include resource="org/springframework/boot/logging/logback/base.xml" />

然后重新访问服务端监控面板


发现有日志出来了。那为毛加了这个base.xml就有用,那是因为这个日志采集的端点是actuator/logfile。因为本文不是讲解源码,我就把相关核心源码,贴在下面,感兴趣的朋友可以根据下面提供的源码,进行debug调试

核心源码

@WebEndpoint(id = "logfile")
public class LogFileWebEndpoint {private static final Log logger = LogFactory.getLog(LogFileWebEndpoint.class);private File externalFile;private final LogFile logFile;public LogFileWebEndpoint(LogFile logFile, File externalFile) {this.externalFile = externalFile;this.logFile = logFile;}@ReadOperation(produces = "text/plain; charset=UTF-8")public Resource logFile() {Resource logFileResource = getLogFileResource();if (logFileResource == null || !logFileResource.isReadable()) {return null;}return logFileResource;}private Resource getLogFileResource() {if (this.externalFile != null) {return new FileSystemResource(this.externalFile);}if (this.logFile == null) {logger.debug("Missing 'logging.file.name' or 'logging.file.path' properties");return null;}return new FileSystemResource(this.logFile.toString());}}
public class LogFile {/*** The name of the Spring property that contains the name of the log file. Names can* be an exact location or relative to the current directory.* @deprecated since 2.2.0 in favor of {@link #FILE_NAME_PROPERTY}*/@Deprecatedpublic static final String FILE_PROPERTY = "logging.file";/*** The name of the Spring property that contains the directory where log files are* written.* @deprecated since 2.2.0 in favor of {@link #FILE_PATH_PROPERTY}*/@Deprecatedpublic static final String PATH_PROPERTY = "logging.path";/*** The name of the Spring property that contains the name of the log file. Names can* be an exact location or relative to the current directory.* @since 2.2.0*/public static final String FILE_NAME_PROPERTY = "logging.file.name";/*** The name of the Spring property that contains the directory where log files are* written.* @since 2.2.0*/public static final String FILE_PATH_PROPERTY = "logging.file.path";private final String file;private final String path;/*** Create a new {@link LogFile} instance.* @param file a reference to the file to write*/LogFile(String file) {this(file, null);}/*** Create a new {@link LogFile} instance.* @param file a reference to the file to write* @param path a reference to the logging path to use if {@code file} is not specified*/LogFile(String file, String path) {Assert.isTrue(StringUtils.hasLength(file) || StringUtils.hasLength(path), "File or Path must not be empty");this.file = file;this.path = path;}/*** Apply log file details to {@code LOG_PATH} and {@code LOG_FILE} system properties.*/public void applyToSystemProperties() {applyTo(System.getProperties());}/*** Apply log file details to {@code LOG_PATH} and {@code LOG_FILE} map entries.* @param properties the properties to apply to*/public void applyTo(Properties properties) {put(properties, LoggingSystemProperties.LOG_PATH, this.path);put(properties, LoggingSystemProperties.LOG_FILE, toString());}private void put(Properties properties, String key, String value) {if (StringUtils.hasLength(value)) {properties.put(key, value);}}@Overridepublic String toString() {if (StringUtils.hasLength(this.file)) {return this.file;}return new File(this.path, "spring.log").getPath();}

加了那个logback-base可以的原因是,点开base.xml

6、客户端与注册中心集成

说实话spring-boot-admin我看过的,基本上都是用在微服务场景比较多,因此后面的内容,我以集成注册中心为核心来讲解示例,通过url配置服务端监控地址就不再论述。

a、 在客户端项目的pom引入eureka-client GAV

 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency>

b、 配置eureka 客户端相关信息

eureka:instance:instance-id: ${spring.application.name}:${spring.cloud.client.ip-address}:${spring.application.instance_id:${random.uuid}}prefer-ip-address: ${PREFER_IP:false}  #是否选择IP注册#   ip-address: ${IP_ADDRESS:localhost}   #指定IP地址注册lease-renewal-interval-in-seconds: 5  #续约更新时间间隔(默认30秒),使得eureka及时剔除无效服务lease-expiration-duration-in-seconds: 10 #续约到期时间(默认90秒)hostname: ${HOSTNAME:${spring.application.name}}metadata-map:ipAddress: ${spring.cloud.client.ip-address}management:address: ${spring.cloud.client.ip-address}client:service-url:defaultZone: ${EUREKA_CLIENT_SERVICEURL_DEFAULTZONE:http://localhost:8761/eureka/}#缩短延迟向服务端注册的时间、默认40sinitial-instance-info-replication-interval-seconds: 10#提高Eureka-Client端拉取Server注册信息的频率,默认30sregistry-fetch-interval-seconds: 5

注: 客户端和服务端集成的eureka地址必须得同一个

客户端和服务端同时配置好注册中心后,我们访问一下服务端监控面板


和用url配置服务端地址的效果一样,到这边大体就差不多了。但是实际使用,没那么简单。我们列举几种场景

场景一:客户端的默认端点不是actuator

因为公司有时候会有等保要求,正常是不能直接暴露actuator端点,所以我们客户端,可能会将端点路径改个名字,比如改成如下

management:endpoints:web:base-path: ${MONINTOR_BASE_PATH:/lyb-geek}exposure:include: "*"

此时通过服务端监控面板访问

会发现爆红了,点击爆红的面板进去

健康检测404,我们可以通过配置注册中心的元数据,示例如下

eureka:instance:metadata-map:management:context-path: ${management.endpoints.web.base-path:/actuator}

此时我们再访问服务端监控面板


发现可以正常访问了。

场景二:客户端的actuator需要认证才能访问

当我们没有通过认证,直接访问服务端监控面板时



会出现401,未授权访问,此时我们在注册中心配置形如下内容

eureka:instance:metadata-map:user.name: ${spring.security.user.name}user.password: ${spring.security.user.password}

访问服务端监控面板

已经可以正常访问

场景三:客户端通过hostName注册到注册中心,服务端监控面板只显示一个实例

这个场景出现在容器化部署,因为此时hostName和port都一样,因此这个客户端就被当成是同一个。此时通过如下配置

eureka:instance:metadata-map:management:address: ${spring.cloud.client.ip-address}

通过配置management.address指定ip

注: 想知道spring-boot-admin可以支持哪些注册中心元数据,可以查看官网
https://docs.spring-boot-admin.com/current/server.html


也看可以查看源码

de.codecentric.boot.admin.server.cloud.discovery.DefaultServiceInstanceConverter

如何为spring-boot-admin集成告警

以集成邮件告警为例,在服务端的POM引入邮件发送的GAV

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId></dependency>

在服务端的application.yml配置邮件发送配置

spring:mail:host: ${MAIL_HOST:邮箱服务器地址}port:username: ${MAIL_USERNAME:邮箱服务器用户名}password: ${MAIL_PWD:邮箱服务器密码}protocol: ${MAIL_PROTOCOL:smtp}default-encoding: UTF-8properties:mail.smtp.auth: truemail.smtp.starttls.enable: truemail.smtp.starttls.required: truemail.smtp.socketFactory.port: ${MAIL_SMTP_SOCKETFACTORY_PORT:465}mail.smtp.socketFactory.class: javax.net.ssl.SSLSocketFactorymail.smtp.socketFactory.fallback: falsemail.smtp.ssl.protocols: ${MAIL_SMTP_SSL_PROTOCOLS:TLSv1}

配置邮件通知接收人和发送人

spring:boot:admin:notify:mail:to: ${NOTIFY_MAIL_TO:邮箱接收人,多个用,隔开}from: ${NOTIFY_MAIL_FROM:邮箱发送人}

当客户端出现异常时,会收到形如下告警

更多告警的玩法可以参考官网
https://docs.spring-boot-admin.com/current/server-notifications.html

总结

spring-boot-admin其实核心就做了一件事,就是把Spring Boot Actuators 可视化。本文就不提供demo了,因为官网文档写得很详细,大部分内容都可以从官网找到https://docs.spring-boot-admin.com/current/。除了那个日志稍微有点坑

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/68965.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

图神经网络和分子表征:4. PAINN

如果说 SchNet 带来了【3D】的火种&#xff0c;DimeNet 燃起了【几何】的火苗&#xff0c;那么 PAINN 则以星火燎原之势跨入 【等变】时代。 在 上一节 中&#xff0c;我们提到&#xff0c; PAINN 在看到 DimeNet 取得的成就之后&#xff0c;从另一个角度解决了三体几何问题&a…

无涯教程-Android - 系统架构

Android操作系统是一堆软件组件&#xff0c;大致分为五个部分和四个主要层&#xff0c;如体系结构图中所示。 Linux内核 底层是Linux-Linux 3.6&#xff0c;带有大约115个补丁&#xff0c;这在设备硬件之间提供了一定程度的抽象&#xff0c;并且包含所有必需的硬件驱动程序&am…

RK3568开发笔记(六):开发板烧写ubuntu固件(支持mipi屏镜像+支持hdmi屏镜像)

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/132686096 红胖子网络科技博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬…

Sentinel 流量控制框架

1. Sentinel 是什么&#xff1f; Sentinel是由阿里中间件团队开源的&#xff0c;面向分布式服务架构的轻量级高可用流量控制组件。 2. 主要优势和特性 轻量级&#xff0c;核心库无多余依赖&#xff0c;性能损耗小。 方便接入&#xff0c;开源生态广泛。 丰富的流量控制场景。 …

AI篇-chatgpt基本用法(文心一言也适用)

目录 &#xff08;1&#xff09;基本规则 &#xff08;2&#xff09;例子1-文章摘要 &#xff08;3&#xff09;例子2-代码生成 &#xff08;4&#xff09;文心一言链接 &#xff08;1&#xff09;基本规则 相比于搜索引擎&#xff0c;ChatGPT的优势在于其高效的想法关联和…

《CTFshow-Web入门》09. Web 81~90

Web 入门 索引web81题解 web82题解原理 web83题解 web84题解 web85题解 web86题解 web87题解原理 web88题解 web89题解 web90题解 ctf - web入门 索引 web81&#xff1a;include() 利用&#xff0c;一句话木马之 Nginx 日志利用。web82~86&#xff1a;include() 利用&#xff…

WebSocket--技术文档--基本概念--《快速了解WebSocket协议》

阿丹&#xff1a; 不断学习新技术&#xff0c;丰富自己了解更多才能扩展更多世界可能。 官网 WebSocket首页、文档和下载 - HTML5开发相关 - OSCHINA - 中文开源技术交流社区 软件简介 WebSocket 是 HTML5 开始提供的一种浏览器与服务器间进行全双工通讯的网络技术。 WebS…

力扣|找出和所对应的两数的下标

从零开始刷力扣&#xff08;bushi 题目放在这&#xff1a; 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出和为目标值target的两个整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一…

linux上vscode中.cpp文件中引入头文件.hpp时报错:找不到头文件(启用错误钵形曲线)

当在.cpp文件中引入系统给定的头文件时&#xff1a;#include < iostream > 或者引入自定义的头文件 &#xff1a;#include <success.hpp> 报错&#xff1a;找不到相应的头文件&#xff0c;即在引入头文件的改行底下标出红波浪线 解决方法为&#xff1a; &#…

linux入门---动静态库的加载

目录标题 为什么会有动态库和静态库静态库的实现动态库的实现动静态库的加载 为什么会有动态库和静态库 我们来模拟一个场景&#xff0c;首先创建两个头文件 根据文件名便可以得知add.h头文件中存放的是加法函数的声明&#xff0c;sub.h头文件中存放的是减法函数的声明&#…

文章预览 安防监控/视频存储/视频汇聚平台EasyCVR播放优化小tips

视频云存储/安防监控EasyCVR视频汇聚平台基于云边端智能协同&#xff0c;可实现视频监控直播、视频轮播、视频录像、云存储、回放与检索、智能告警、服务器集群、语音对讲、云台控制、电子地图、H.265自动转码H.264、平台级联等。为了便于用户二次开发、调用与集成&#xff0c;…

25 Linux可视化-Webmin和bt运维工具

25 Linux可视化-Webmin和bt运维工具 文章目录 25 Linux可视化-Webmin和bt运维工具25.1 Web运行环境简介25.2 Webmin的安装及使用25.2.1 安装webmin25.2.2 Webmin使用演示 25.3 bt(宝塔)的安装及使用25.3.1 安装宝塔25.3.2 宝塔Web登录Linux服务器25.3.3 找回宝塔登录密码 学习视…

Chrome 和 Edge 上出现“status_breakpoint”错误解决办法

文章目录 STATUS_BREAKPOINTSTATUS_BREAKPOINT报错解决办法Chrome浏览器 Status_breakpoint 错误修复- 将 Chrome 浏览器更新到最新版本- 卸载不再使用的扩展程序和应用程序- 安装计算机上可用的任何更新&#xff0c;尤其是 Windows 10- 重启你的电脑。 Edge浏览器 Status_brea…

iOS实时监控与报警器

在现代信息化社会中&#xff0c;即使我们不在电脑前面也能随时获取到最新的数据。而苹果公司提供的iOS推送通知功能为我们带来了一种全新的方式——通过手机接收实时监控和报警信息。 首先让我们了解一下iOS推送通知。它是一个强大且灵活可定制化程度高、适用于各类应用场景&a…

基于沙猫群算法优化的BP神经网络(预测应用) - 附代码

基于沙猫群算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录 基于沙猫群算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍2.沙猫群优化BP神经网络2.1 BP神经网络参数设置2.2 沙猫群算法应用 4.测试结果&#xff1a;5.Matlab代…

论文研读|生成式跨模态隐写发展综述

前言&#xff1a;本文介绍近5年来生成式跨模态隐写领域的相关工作。 相关阅读&#xff1a;生成式文本隐写发展综述 不同于文本隐写&#xff0c;跨模态隐写需要考虑不同模态间的相关性&#xff0c;常见的跨模态场景有&#xff1a;Image-to-Text&#xff08;如图像描述&#xff…

【Python】OpenCV安装

安装起来相当简单&#xff0c;但是看到很多博客的安装过程复杂得很。 pip install opencv-python测试代码&#xff1a; import cv2 as cv img cv.imread("f6759b83f3201997fd7ea1c9b9130a44.jpg")cv.imshow("Display window", img) k cv.waitKey(0) # …

ROS 2官方文档(基于humble版本)学习笔记(二)

ROS 2官方文档&#xff08;基于humble版本&#xff09;学习笔记&#xff08;二&#xff09; 理解节点&#xff08;node&#xff09;ros2 runros2 node list重映射&#xff08;remap&#xff09;ros2 node info 理解话题&#xff08;topic&#xff09;rqt_graphros2 topic listr…

数据库基础

目录 一、数据库是什么&#xff1f; 二、目前主流的数据库 三、数据库基本使用 1.连接服务器 2.服务器、数据库、表关系 3.使用案例 4、数据逻辑存储 四、MySQL基本知识 1、MySQL架构 2、SQL语句分类 3、存储引擎 总结 一、数据库是什么&#xff1f; 数据库是按照数据结构来组…

Spring源码解析-构造函数

1、构造函数概述 构造函数中&#xff0c;主要创建两个对象分别用来读取注解参数和classpath下的文件 AnnotatedBeanDefinitionReader 专门读取注解参数的Reader ClassPathBeanDefinitionScanner 专门读取classpath下的文件&#xff0c;例如yml、properties等。 AnnotationC…