spring boot零配置

spring boot是如何选择tomcat还是Jett作为底层服务器的呢?

springboot通过ServletWebServerApplicationContext的onRefresh()方法,会创建web服务

	protected void onRefresh() {super.onRefresh();try {// 创建web服务createWebServer();}catch (Throwable ex) {throw new ApplicationContextException("Unable to start web server", ex);}}

进入reateWebServer()方法,通过getWebServerFactory()方法获取对应的web服务工厂

	private void createWebServer() {WebServer webServer = this.webServer;ServletContext servletContext = getServletContext();if (webServer == null && servletContext == null) {StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");// 核心拿到web服务工厂ServletWebServerFactory factory = getWebServerFactory();createWebServer.tag("factory", factory.getClass().toString());this.webServer = factory.getWebServer(getSelfInitializer());createWebServer.end();getBeanFactory().registerSingleton("webServerGracefulShutdown",new WebServerGracefulShutdownLifecycle(this.webServer));getBeanFactory().registerSingleton("webServerStartStop",new WebServerStartStopLifecycle(this, this.webServer));}else if (servletContext != null) {try {getSelfInitializer().onStartup(servletContext);}catch (ServletException ex) {throw new ApplicationContextException("Cannot initialize servlet context", ex);}}initPropertySources();}

继续进入getWebServerFactory()方法,发现spring会先从SpringBean容器中的获取所有类型是ServletWebServerFactory.class的bean名称,如果beanNames.length == 0或者>1则会报错,也就是说spring并不会默认去加载tomcat。而是在之前就已经将TomcatServletWebServerFactory放入了spring容器中。

	protected ServletWebServerFactory getWebServerFactory() {// Use bean names so that we don't consider the hierarchyString[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);if (beanNames.length == 0) {throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "+ "ServletWebServerFactory bean.");}if (beanNames.length > 1) {throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "+ "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));}return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);}

明明没有写关于tomcat的配置 tomcat是如何被加载的呢?

进入ServletWebServerFactory

@FunctionalInterface
public interface ServletWebServerFactory {/*** Gets a new fully configured but paused {@link WebServer} instance. Clients should* not be able to connect to the returned server until {@link WebServer#start()} is* called (which happens when the {@code ApplicationContext} has been fully* refreshed).* @param initializers {@link ServletContextInitializer}s that should be applied as* the server starts* @return a fully configured and started {@link WebServer}* @see WebServer#stop()*/WebServer getWebServer(ServletContextInitializer... initializers);}

发现ServletWebServerFactory是一个函数式接口,三个实现分别对应了tomcat,jetty和undertow三个服务器。

进入TomcatServletWebServerFactory类,可以看到有这个类对应的Bean,

跳转过去,可以发现在ServletWebServerFactoryConfiguration这个配置类中,已经将tomcat,jetty和undertow三个服务器对应的配置都写入了进去,具体是否要解析这个Bean是通过项目中是否能正常加载@ConditionalOnClass这个注解里的类来决定的。如果无法加载@ConditionalOnClass注解中的类那么spring就不会去解析这个Bean。

@Configuration(proxyBeanMethods = false)
class ServletWebServerFactoryConfiguration {@Configuration(proxyBeanMethods = false)@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)static class EmbeddedTomcat {@BeanTomcatServletWebServerFactory tomcatServletWebServerFactory(ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,ObjectProvider<TomcatContextCustomizer> contextCustomizers,ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();factory.getTomcatConnectorCustomizers().addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));factory.getTomcatContextCustomizers().addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));factory.getTomcatProtocolHandlerCustomizers().addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));return factory;}}/*** Nested configuration if Jetty is being used.*/@Configuration(proxyBeanMethods = false)@ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)static class EmbeddedJetty {@BeanJettyServletWebServerFactory JettyServletWebServerFactory(ObjectProvider<JettyServerCustomizer> serverCustomizers) {JettyServletWebServerFactory factory = new JettyServletWebServerFactory();factory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList()));return factory;}}/*** Nested configuration if Undertow is being used.*/@Configuration(proxyBeanMethods = false)@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)static class EmbeddedUndertow {@BeanUndertowServletWebServerFactory undertowServletWebServerFactory(ObjectProvider<UndertowDeploymentInfoCustomizer> deploymentInfoCustomizers,ObjectProvider<UndertowBuilderCustomizer> builderCustomizers) {UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();factory.getDeploymentInfoCustomizers().addAll(deploymentInfoCustomizers.orderedStream().collect(Collectors.toList()));factory.getBuilderCustomizers().addAll(builderCustomizers.orderedStream().collect(Collectors.toList()));return factory;}@BeanUndertowServletWebServerFactoryCustomizer undertowServletWebServerFactoryCustomizer(ServerProperties serverProperties) {return new UndertowServletWebServerFactoryCustomizer(serverProperties);}}}

@ConditionalOnClass注解底层原理

spring boot是如何知道哪些类是被@Component注解还有@Bean注解注释的呢?

介绍@ConditionalOnClass注解之前,首先想一个问题,spring是如何知道哪些类是被@Component注解还有@Bean注解注释的呢?也就是说spring在配置完包扫描路径后是如何将扫描范围的所有需要加载的Bean放入spring容器中的呢?

将扫描路径下的所有类都加载到JVM中然后再判断类是否有@Component或者@Bean注解,然后舍弃掉其他的普通类吗?

这样做的话就会导致加载了很多不需要的类,不仅违反了只加载需要的类到jvm中规则,而且也会影响性能。

那么spring是如何解决这个问题的呢?

ASM(字节码框架)

答案是spring通过ASM(字节码框架),先加载类的字节码文件,根据java字节码规范来查看这些类是否有@Component或者@Bean注解,只加载有这些注解的类到jvm中。通过ASM框架,spring解决了会加载不必要的类这个问题。

具体的asm框架的使用可以参考asm的官方地址

ASM

明明没有导入相关的依赖为什么被@ConditionalOnClass注解注释的类在spring运行时没有报错呢?

回到@ConditionalOnClass注解上来在之前的spring加载服务器的讨论中可以看到,ServletWebServerFactoryConfiguration这个配置类中将tomcat,jetty和undertow三个服务器对应的配置都写入了进去,但是只引入了tomcat相关的依赖,为什么spring运行时没有抛出类不存在的问题呢?

这个问题也在ASM中解决

@ConditionalOnClass注解的底层也是通过ASM来解决的。spring在通过ASM读取字节码文件时,会先尝试加载@ConditionalOnClass注解中的类,如果加载失败那么就会修改字节码文件,移除@ConditionalOnClass注解注释的方法,从而保证程序的继续向下运行。

为什么springboot启动的的服务器默认端口是8080

从spring的run方法上开始,run方法会调用refresh()方法,refresh()内部调用onRefresh()方法,然后找到ServletWebServerApplicationContext实现类的onRefresh()方法,进入reateWebServer()方法,在进入factory.getWebServer(getSelfInitializer()),找到TomcatServletWebServerFactory实现类(因为只有tomcat的相关依赖,spring容器只有TomcatServletWebServerFactory这一个Bean),可以看到这里就是对tomcat服务器做的初始化操作。

public WebServer getWebServer(ServletContextInitializer... initializers) {if (this.disableMBeanRegistry) {Registry.disableRegistry();}Tomcat tomcat = new Tomcat();File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");tomcat.setBaseDir(baseDir.getAbsolutePath());Connector connector = new Connector(this.protocol);connector.setThrowOnFailure(true);tomcat.getService().addConnector(connector);customizeConnector(connector);tomcat.setConnector(connector);tomcat.getHost().setAutoDeploy(false);configureEngine(tomcat.getEngine());for (Connector additionalConnector : this.additionalTomcatConnectors) {tomcat.getService().addConnector(additionalConnector);}prepareContext(tomcat.getHost(), initializers);return getTomcatWebServer(tomcat);}

进入customizeConnector(connector),spring就是在这里设置的tomcat 服务的端口信息

	protected void customizeConnector(Connector connector) {// 设置tomcat服务的端口号int port = Math.max(getPort(), 0);connector.setPort(port);if (StringUtils.hasText(getServerHeader())) {connector.setProperty("server", getServerHeader());}if (connector.getProtocolHandler() instanceof AbstractProtocol) {customizeProtocol((AbstractProtocol<?>) connector.getProtocolHandler());}invokeProtocolHandlerCustomizers(connector.getProtocolHandler());if (getUriEncoding() != null) {connector.setURIEncoding(getUriEncoding().name());}// Don't bind to the socket prematurely if ApplicationContext is slow to startconnector.setProperty("bindOnInit", "false");if (getHttp2() != null && getHttp2().isEnabled()) {connector.addUpgradeProtocol(new Http2Protocol());}if (getSsl() != null && getSsl().isEnabled()) {customizeSsl(connector);}TomcatConnectorCustomizer compression = new CompressionConnectorCustomizer(getCompression());compression.customize(connector);for (TomcatConnectorCustomizer customizer : this.tomcatConnectorCustomizers) {customizer.customize(connector);}}

进入getPort()方法,就能发现spring在关于WebServer的port默认值就是8080(tomcat,jetty和undertow公共父类,所以不管是tomcat还是jetty和undertow他们的默认端口号都是8080)

那么spring boot是如何通过配置文件来修改port的呢

 BeanPostProcessor

什么是BeanPostProcessor

有时候,我们希望Spring容器在创建bean的过程中,能够使用我们自己定义的逻辑,对创建的bean做一些处理,或者执行一些业务。而实现方式有多种,比如自定义bean的初始化话方法等,而BeanPostProcessor接口也是用来实现类似的功能的。

  如果我们希望容器中创建的每一个bean,在创建的过程中可以执行一些自定义的逻辑,那么我们就可以编写一个类,并让他实现BeanPostProcessor接口,然后将这个类注册到一个容器中。容器在创建bean的过程中,会优先创建实现了BeanPostProcessor接口的bean,然后,在创建其他bean的时候,会将创建的每一个bean作为参数,调用BeanPostProcessor的方法。而BeanPostProcessor接口的方法,即是由我们自己实现的。下面就来具体介绍一下BeanPostProcessor的使用。

springboot中配置文件对于BeanPostProcessor的使用

想要成功获取修改后的post的值,就要在getPort()这个方法被执行之前就要改变this.port对应的值,首先我们知道目前 AbstractConfigurableWebServerFactory这个类的真正实现是TomcatServletWebServerFactory这个类(因为spring容器中只有这一个匹配的Bean),又因为TomcatServletWebServerFactory这个类是一个Bean,那么根据spring设置的生命周期,就可以在Bean实例化后,spring会调用BeanPostProcessor这个spring提供的后置处理器来重新设置port变量

spring boot自己实现创建的WebServerFactoryCustomizerBeanPostProcessor类就实现了BeanPostProcessor接口,他的postProcessBeforeInitialization()方法调用了postProcessBeforeInitialization(),

@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if (bean instanceof WebServerFactory) {postProcessBeforeInitialization((WebServerFactory) bean);}return bean;}

在postProcessBeforeInitialization内部就设置调用了WebServerFactory接口的customize()方法,

	private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {LambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory).withLogger(WebServerFactoryCustomizerBeanPostProcessor.class).invoke((customizer) -> customizer.customize(webServerFactory));}

最终调用的spring容器中的ServletWebServerFactoryCustomizer类的customize() 方法,拿到this.serverProperties中的属性来重新填充包括port属性值在内的配置值。

	@Overridepublic void customize(ConfigurableServletWebServerFactory factory) {PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();map.from(this.serverProperties::getPort).to(factory::setPort);map.from(this.serverProperties::getAddress).to(factory::setAddress);map.from(this.serverProperties.getServlet()::getContextPath).to(factory::setContextPath);map.from(this.serverProperties.getServlet()::getApplicationDisplayName).to(factory::setDisplayName);map.from(this.serverProperties.getServlet()::isRegisterDefaultServlet).to(factory::setRegisterDefaultServlet);map.from(this.serverProperties.getServlet()::getSession).to(factory::setSession);map.from(this.serverProperties::getSsl).to(factory::setSsl);map.from(this.serverProperties.getServlet()::getJsp).to(factory::setJsp);map.from(this.serverProperties::getCompression).to(factory::setCompression);map.from(this.serverProperties::getHttp2).to(factory::setHttp2);map.from(this.serverProperties::getServerHeader).to(factory::setServerHeader);map.from(this.serverProperties.getServlet()::getContextParameters).to(factory::setInitParameters);map.from(this.serverProperties.getShutdown()).to(factory::setShutdown);for (WebListenerRegistrar registrar : this.webListenerRegistrars) {registrar.register(factory);}}

而ServerProperties类读取的就是配置文件中的以 server开头的属性值,这样spring boot就完成了对通过配置文件来改变默认属性值。如果不写相应的配置文件也会有对应的默认配置(即零配置)

/*** {@link ConfigurationProperties @ConfigurationProperties} for a web server (e.g. port* and path settings).** @author Dave Syer* @author Stephane Nicoll* @author Andy Wilkinson* @author Ivan Sopov* @author Marcos Barbero* @author Eddú Meléndez* @author Quinten De Swaef* @author Venil Noronha* @author Aurélien Leboulanger* @author Brian Clozel* @author Olivier Lamy* @author Chentao Qu* @author Artsiom Yudovin* @author Andrew McGhie* @author Rafiullah Hamedy* @author Dirk Deyne* @author HaiTao Zhang* @author Victor Mandujano* @author Chris Bono* @author Parviz Rozikov* @since 1.0.0*/
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {/*** Server HTTP port.*/private Integer port;/*** Network address to which the server should bind.*/private InetAddress address;

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

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

相关文章

PyTorch 之 Dataset 类入门学习

PyTorch 之 Dataset 类入门学习 Dataset 类简介 PyTorch 中的 Dataset 类是一个抽象类&#xff0c;用来表示数据集。通过继承 Dataset 类可以进行自定义数据集的格式、大小和其它属性&#xff0c;供后续使用&#xff1b; 可以看到官方封装好的数据集也是直接或间接的继承自 …

6.Gin 路由详解 - GET POST 请求以及参数获取示例

6.Gin 路由详解 - GET POST 请求以及参数获取示例 GET POST 请求以及参数获取示例 Get 请求&#xff1a;获取 Quary 参数 // 获取query参数示例&#xff1a;GET /user?uid20&namejack&page1 r.GET("/user", func(c *gin.Context) {// 获取参数// Query获取参…

[机缘参悟-119] :一个IT人的反思:反者道之动;弱者,道之用 VS 恒者恒强,弱者恒弱的马太效应

目录 前言&#xff1a; 一、道家的核心思想 二、恒者恒强&#xff0c;弱者恒弱的马太效应 三、马太效应与道家思想的统一 3.1 大多数的理解 3.2 个人的理解 四、矛盾的对立统一 前言&#xff1a; 马太效应和强弱互转的道家思想&#xff0c;都反应了自然规律和社会规律&…

SVN 修改版本库地址url路径

一、win11用户 1. win11系统右链菜单比较优秀&#xff0c;如果菜单中选择“TortoiseSVN”找不到“重新定位”&#xff0c;如下图所示&#xff0c;则需要添加右键菜单&#xff1a; 2.添加右键菜单&#xff1a;选择“TortoiseSVN”&#xff0c;点击设置&#xff0c;如下图所示&a…

Vue 项目实战——如何在页面中展示 PDF 文件以及 PDFObject 插件实战

文章目录 &#x1f4cb;前言&#x1f3af;使用 HTML 标签&#x1f9e9; embed 标签&#x1f9e9; object标签&#x1f9e9; iframe标签&#x1f9e9;完整代码 &#x1f3af;使用 PDFObject 插件&#x1f9e9;为什么使用 PDFObject 插件&#xff08;AI翻译&#xff09;&#x1f…

【微服务】SaaS云智慧工地管理平台源码

智慧工地系统是一种利用人工智能和物联网技术来监测和管理建筑工地的系统。它可以通过感知设备、数据处理和分析、智能控制等技术手段&#xff0c;实现对工地施工、设备状态、人员安全等方面的实时监控和管理。 一、智慧工地让工程施工智能化 1、内容全面&#xff0c;多维度数…

华为云IoT与OpenHarmony深度协同,加速设备上鸿即上云【云驻共创】

本次专题论坛探讨了华为云IoT与Open Harmony的深度协同、边缘屏蔽硬件差异、实现智慧隧道全方位智能化管理&#xff0c;以及华为云与Open Harmony生态的合作。同时也介绍了华为云物联网卡平台、HTTP2协议以及华为物联网在交通领域的应用。 一&#xff0e;华为云IoT与Open Harm…

数学几百年重大错误:将两异函数误为同一函数

黄小宁 因各实数都可是数轴上点的坐标所以数集A可形象化为数轴上的点集A&#xff0c;从而使x∈R变换为实数yxδ的几何意义可是&#xff1a;一维空间“管道”g内R轴上的质点x∈R(x是点的坐标)运动到新的位置yxδ还在管道g内&#xff08;设各点只作位置改变而没别的改变即变位前…

Vue学习

1。 搭框架 依赖等 创建vue项目 vue create 项目名称 vue create [options] <app-name>使用vite npm init vitelatest <app-name>-- --template vue 目录调整1 apiutilsvenderimages、styles 配置文件 jsconfig.json 配置之后路径可以直接使用 / {"comp…

mysql 查询

-- 多表查询select * from tb_dept,tb_emp; 内来链接 -- 内连接 -- A 查询员工的姓名 &#xff0c; 及所属的部门名称 &#xff08;隐式内连接实现&#xff09;select tb_emp.name,tb_dept.name from tb_emp,tb_dept where tb_emp.idtb_emp.id;-- 推荐使用select a.name,b.n…

【精选】OpenCV多视角摄像头融合的目标检测系统:全面部署指南&源代码

1.研究背景与意义 随着计算机视觉和图像处理技术的快速发展&#xff0c;人们对于多摄像头拼接行人检测系统的需求日益增加。这种系统可以利用多个摄像头的视角&#xff0c;实时监测和跟踪行人的活动&#xff0c;为公共安全、交通管理、视频监控等领域提供重要的支持和帮助。 …

TS类型全解

使用TypeScript开发的程序更安全&#xff0c;常见的错误都能检查出来。TS能让程序员事半功倍。而原因在于TS的“类型安全”&#xff08;借助类型避免程序做无效的事情&#xff09;。 图 运行程序的过程 但是TS不会直接编译成字节码&#xff0c;而是编译成JavaScript代码。TS编…

【C+进阶之路】第六篇:C++11

文章目录 一、【C】C11&#xff08;1&#xff09;二、【C】C11&#xff08;2&#xff09; 一、【C】C11&#xff08;1&#xff09; 【C】C11&#xff08;1&#xff09; 二、【C】C11&#xff08;2&#xff09; 【C】C11&#xff08;2&#xff09; &#x1f339;&#x1f33…

3.计算机网络

1.重点概念 MSL&#xff08;Maximum segment lifetime&#xff09;&#xff1a;TCP 报⽂最⼤⽣存时间。它是任何 TCP 报⽂在⽹络上存在的 最⻓时间&#xff0c;超过这个时间报⽂将被丢弃。实际应⽤中常⽤的设置是 30 秒&#xff0c;1 分钟和 2 分钟。 TTL&#xff08;Time to …

LeetCode算法心得——打家劫舍(记忆化搜索)

大家好&#xff0c;我是晴天学长&#xff0c;准备开始深入动态规划啦&#xff0c;先从记忆化搜索开始&#xff0c;需要的小伙伴可以关注支持一下哦&#xff01;后续会继续更新的。&#x1f4aa;&#x1f4aa;&#x1f4aa; 1) .打家劫舍 你是一个专业的小偷&#xff0c;计划偷窃…

RK3568平台开发系列讲解(Linux系统篇)kernel config 配置解析

🚀返回专栏总目录 文章目录 一、图形化界面的操作二、Kconfig 语法简介三、.config 配置文件介绍四、deconfig 配置文件沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 Linux 内核可以通过输入“make menuconfig”来打开图形化配置界面,menuconfig 是一套图形化的配…

Javaweb之Axios的详细解析

1.3 Axios 上述原生的Ajax请求的代码编写起来还是比较繁琐的&#xff0c;所以接下来我们学习一门更加简单的发送Ajax请求的技术Axios 。Axios是对原生的AJAX进行封装&#xff0c;简化书写。Axios官网是&#xff1a;https://www.axios-http.cn 1.3.1 Axios的基本使用 Axios的…

map的基础定义及运用

Map 1 使用 1 声明 /*声明map*/map<int, string> myMap {{1, "Apple"}, {2, "Banana"}, {3, "Orange"}};2 插入元素 myMap.insert(make_pair(4, "Graphes"));3 通过访问键查找和访问元素 cout << myMap[2] <<…

[SCTF 2021]rceme

文章目录 前置知识可变参数绕过create_function注入无字母数字RCE动态链接库so绕过disable_functions利用php原生类进行文件读取 解题过程 前置知识 可变参数绕过 PHP 在用户自定义函数中支持可变数量的参数列表。在 PHP 5.6 及以上的版本中&#xff0c;由 … 语法实现&#x…

2023 年爆肝将近 20 万字讲解最新 JavaEE 全栈工程师基础教程(更新中)

1. Java 语言基本概述 Java 是一种广泛使用的编程语言&#xff0c;由 James Gosling 在 Sun Microsystems&#xff08;现在是 Oracle Corporation 的一部分&#xff09;于 1995 年发表。Java 是一种静态类型的、类基础的、并发性的、面向对象的编程语言。Java 广泛应用于企业级…