SpringBoot学习05-[SpringBoot的嵌入式Servlet容器]
SpringBoot的嵌入式Servlet容器
- 嵌入式Servlet容器
- servlet容器-嵌入式servlet容器配置修改
- 通过全局配置文件修改修改
- 添加实现了WebServerFactoryCustomizer接口的bean来进行修改
- servlet容器-注册servlet三大组件
- 应该如何注册呢?
- servlet3.0规范提供的注解方式进行注册
- springboot提供的注册方式
- servlet容器-切换到其他servlet容器
- servlet容器-嵌入式servlet容器自动配置原理
- ServletWebServerFactoryAutoConfiguration配置类源码分析
- 为什么可以根据配置依赖自动使用servlet容器?
- 怎么根据配置文件中的server.xxx以及实现了WebServerFactoryCustomizer的类去设置serverlet容器配置?
- 嵌入式servlet容器如何启动?
- servlet容器-使用外部servlet容器
- SpringBoot使用外部servlet
- servlet容器-外部servlet启动SpringBoot原理
- 启动原理
嵌入式Servlet容器
SpringBoot包含对嵌入式Tomcat、Jetty、Undertow等服务器的支持。大多数开发人员使用适当的“”启动器“” 来获取完全配置的实例。默认情况下,嵌入式服务器在port上监听HTTP请求8080
servlet容器-嵌入式servlet容器配置修改
通过全局配置文件修改修改
- 可以通过server.xxx 来进行web服务配置,没有带服务器名称的则是通用配置
- 通常带了具体服务器名称则是单独对该服务器进行设置,比如server.tomcat.xxx就是专门针对tomcat的配置
添加实现了WebServerFactoryCustomizer接口的bean来进行修改
这儿的泛型添加的tomcat的ConfigurableTomcatWebServerFactory
@Component
public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableTomcatWebServerFactory> {@Overridepublic void customize(ConfigurableTomcatWebServerFactory server) {server.setPort(8088);}
}
servlet容器-注册servlet三大组件
- servlet三大组件
- servlet
- 监听器:listenser
- 过滤器:filter
应该如何注册呢?
- servlet3.0规范提供的注解方式进行注册
- springboot提供的注册方式
servlet3.0规范提供的注解方式进行注册
@WebServlet:注册servlet的注解
@WebListener:注册监听器的注解
@WebFilter:注册过滤器的注解
- 以webservlet进行演示
- 定义一个servlet
@WebServlet(name = "HelloServlet",urlPatterns = "/HelloServlet")
public class HelloServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.getWriter().println("hello servlet");}
}
- 在springboot启动类上加上@ServletComponentScan,让springboot可以扫描到serverlet的bean
@SpringBootApplication
@ServletComponentScan
public class MyApplication {public static void main(String[] args) {SpringApplication.run(MyApplication.class,args);}
}
springboot提供的注册方式
- 自定义一个servlet
public class BeanServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.getWriter().println("bean servlet");}
}
- 定义一个bean注册的配置类
可以使用ServletRegistrationBean、ServletListenerRegistrationBean、FilterRegistrationBean分别来管理servlet、监听器、过滤器
@Configuration
public class RegistryBeanConfigration {@Beanpublic ServletRegistrationBean myServlet(){ServletRegistrationBean<Servlet> registrationBean = new ServletRegistrationBean<>();//设置相应的servletregistrationBean.setServlet(new BeanServlet());//设置名称registrationBean.setName("beanServlet");//添加映射规则registrationBean.addUrlMappings("/beanServlet");return registrationBean;}
}
- 测试
servlet容器-切换到其他servlet容器
srpingboot默认服务器是tomcat服务器
- 排除内嵌tomcat
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><!--排除内嵌tomcat--><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId></exclusion></exclusions></dependency>
- 引入Jetty依赖
<!--引入jetty依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jetty</artifactId></dependency>
- 启动项目
- 测试
servlet容器-嵌入式servlet容器自动配置原理
内嵌servelet自动配置类:ServletWebServerFactoryAutoConfiguration
- 问题:
- 为什么可以根据配置依赖自动使用servlet容器?
- 怎么根据配置文件中的server.xxx以及实现了WebServerFactoryCustomizer的类去设置serverlet容器配置?
- 嵌入式servlet容器如何启动?
ServletWebServerFactoryAutoConfiguration配置类源码分析
//启用配置文件的属性类,那么所有的配置信息都会绑定到ServerProperties.class
@EnableConfigurationProperties(ServerProperties.class)
@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
//只要依赖了任何一个servlet容器,它就会生效
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
//启用配置文件的属性类,那么所有的配置信息都会绑定到ServerProperties.class
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,ServletWebServerFactoryConfiguration.EmbeddedJetty.class,ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
为什么可以根据配置依赖自动使用servlet容器?
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,ServletWebServerFactoryConfiguration.EmbeddedJetty.class,ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
通过@imort导入Embeddel类
每个Embeddel类 中,都配置了相应的@ConditionalOnClass,会根据当前servlet容器启动器依赖判断classpath是否存在对应的类,如果存在就使用对应的sevlet容器,比如tomcat:
@Configuration(proxyBeanMethods = false)//只要添加了tomcat的场景启动器 则该注解才会匹配,如果没有对应的tomcat场景启动器,该注解就不会匹配@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)static class EmbeddedTomcat {
怎么根据配置文件中的server.xxx以及实现了WebServerFactoryCustomizer的类去设置serverlet容器配置?
- ServletWebServerFactoryAutoConfiguration类
@Beanpublic ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties,ObjectProvider<WebListenerRegistrar> webListenerRegistrars,ObjectProvider<CookieSameSiteSupplier> cookieSameSiteSuppliers) {return new ServletWebServerFactoryCustomizer(serverProperties,webListenerRegistrars.orderedStream().collect(Collectors.toList()),cookieSameSiteSuppliers.orderedStream().collect(Collectors.toList()));}
ServletWebServerFactoryCustomizer 也实现了WebServerFactoryCustomizer,说明它也是定制servlet容器的
- ServletWebServerFactoryCustomizer类
根据配置文件中server.xxx来进行定制servlet容器
@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);}if (!CollectionUtils.isEmpty(this.cookieSameSiteSuppliers)) {factory.setCookieSameSiteSuppliers(this.cookieSameSiteSuppliers);}}
- 实现了WebServerFactoryCustomizer接口的类如何进行配置呢?
@Bean@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) {return new TomcatServletWebServerFactoryCustomizer(serverProperties);}
TomcatServletWebServerFactoryCustomizer 也实现了WebServerFactoryCustomizer,说明它也是定制servlet容器的
- TomcatServletWebServerFactoryCustomizer也重写了customize方法
@Overridepublic void customize(TomcatServletWebServerFactory factory) {ServerProperties.Tomcat tomcatProperties = this.serverProperties.getTomcat();if (!ObjectUtils.isEmpty(tomcatProperties.getAdditionalTldSkipPatterns())) {factory.getTldSkipPatterns().addAll(tomcatProperties.getAdditionalTldSkipPatterns());}if (tomcatProperties.getRedirectContextRoot() != null) {customizeRedirectContextRoot(factory, tomcatProperties.getRedirectContextRoot());}customizeUseRelativeRedirects(factory, tomcatProperties.isUseRelativeRedirects());factory.setDisableMBeanRegistry(!tomcatProperties.getMbeanregistry().isEnabled());}
- 怎么让所有的WebServerFactoryCustomizer bean一一调用呢?
- BeanPostProcessorsRegistrar
- 注册WebServerFactoryCustomizerBeanPostProcessor为bean
@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {if (this.beanFactory == null) {return;}registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",//注册了WebServerFactoryCustomizerBeanPostProcessor beanWebServerFactoryCustomizerBeanPostProcessor.class,WebServerFactoryCustomizerBeanPostProcessor::new);registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",ErrorPageRegistrarBeanPostProcessor.class, ErrorPageRegistrarBeanPostProcessor::new);}
- WebServerFactoryCustomizerBeanPostProcessor
实现了BeanPostProcessor,在spring初始化bean的时候会被调用
public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {// 判断当前创建的bean是不是webserverfactofyif (bean instanceof WebServerFactory) {this.postProcessBeforeInitialization((WebServerFactory)bean);}return bean;}
当前对应的Embeddedxxx 启用时,就会在里面配置一个WebServerFactory类型的bean,负责创建对应的容器和启动
@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;}
spring bean初始化前就会调用postProcessBeforeInitialization方法从而执行customize方法
- 调用getCustomizers()方法
- 调用getWebServerFactoryCustomizerBeans()方法,获取所有实现了WebServerFactoryCustomizer接口的bean(获取自定义的、ServletWebServerFactoryCustomizer、TomcatServletWebServerFactoryCustomizer)
- 在invoke方法中循环调用所有实现了WebServerFactoryCustomizer接口 的bean,并调用customize()方法进行一一定制
private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {((LambdaSafe.Callbacks)LambdaSafe.callbacks(WebServerFactoryCustomizer.class, this.getCustomizers(), webServerFactory, new Object[0]).withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)).invoke((customizer) -> {customizer.customize(webServerFactory);});}private Collection<WebServerFactoryCustomizer<?>> getCustomizers() {if (this.customizers == null) {this.customizers = new ArrayList(this.getWebServerFactoryCustomizerBeans());this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE);this.customizers = Collections.unmodifiableList(this.customizers);}return this.customizers;}private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() {return this.beanFactory.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();}
嵌入式servlet容器如何启动?
- TomcatServletWebServerFactory
- 自动配置根据不同的依赖,启动对应一个Enbenddedxxx,然后配置一个对应的servlet工厂类,比如TomcatServletWebServerFactory
- 在springboot应用启动的时候,就会调用refresh方法,onRefresh方法,调用getWebServer,创建servlet容器并且启动
TomcatServletWebServerFactory类
public WebServer getWebServer(ServletContextInitializer... initializers) {if (this.disableMBeanRegistry) {Registry.disableRegistry();}Tomcat tomcat = new Tomcat();File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");tomcat.setBaseDir(baseDir.getAbsolutePath());Iterator var4 = this.serverLifecycleListeners.iterator();while(var4.hasNext()) {LifecycleListener listener = (LifecycleListener)var4.next();tomcat.getServer().addLifecycleListener(listener);}Connector connector = new Connector(this.protocol);connector.setThrowOnFailure(true);tomcat.getService().addConnector(connector);this.customizeConnector(connector);tomcat.setConnector(connector);tomcat.getHost().setAutoDeploy(false);this.configureEngine(tomcat.getEngine());Iterator var8 = this.additionalTomcatConnectors.iterator();while(var8.hasNext()) {Connector additionalConnector = (Connector)var8.next();tomcat.getService().addConnector(additionalConnector);}this.prepareContext(tomcat.getHost(), initializers);//启动方法在getTomcatWebServerreturn this.getTomcatWebServer(tomcat);}
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {return new TomcatWebServer(tomcat, this.getPort() >= 0, this.getShutdown());}
- TomcatWebServer类
在TomcatWebServer中调用initialize方法进行启动
private void initialize() throws WebServerException {logger.info("Tomcat initialized with port(s): " + this.getPortsDescription(false));synchronized(this.monitor) {try {this.addInstanceIdToEngineName();Context context = this.findContext();context.addLifecycleListener((event) -> {if (context.equals(event.getSource()) && "start".equals(event.getType())) {this.removeServiceConnectors();}});this.tomcat.start();this.rethrowDeferredStartupExceptions();try {ContextBindings.bindClassLoader(context, context.getNamingToken(), this.getClass().getClassLoader());} catch (NamingException var5) {}this.startDaemonAwaitThread();} catch (Exception var6) {this.stopSilently();this.destroySilently();throw new WebServerException("Unable to start embedded Tomcat", var6);}}}
servlet容器-使用外部servlet容器
- 外部servlet容器
- 服务器 安装tomcat 配置环境变量
- 部署: war包—>运维—>tomcat webapp startup.sh 启动
- 开发:将开发绑定本地tomcat
- 内嵌servlet容器
- 部署:jar—>运维—java -jar 启动
SpringBoot使用外部servlet
- 修改pom.xml的打包方式,修改为war包形式
设置tomcat依赖设置scope为provided,让内嵌tomcat不参与打包
<!--让它不参与打包部署--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId><scope>provided</scope></dependency>
<type>pom</type> //用在父子类模块中,一般使用在父模块中,此时它相当于一个一个依赖管理(Maven Parent POM)文件
<optional>true</optional> //用在父模块中,此时子模块不会继承父模块的该依赖,true:不传递,false:传递
<scope>provided</scope> //让依赖不参与打包
- 设置tomcat的启动类
目的是tomcat启动的时候调用configure方法来启动springboot的启动类
/*** 当tomcat启动时就会调用configure方法,去启动springboot的启动类*/
public class TomcatStartSpringBoot extends SpringBootServletInitializer {@Overrideprotected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {return builder.sources(MyApplication.class);}
}
- 添加本地tomcat
- 测试-使用本地tomcat启动
servlet容器-外部servlet启动SpringBoot原理
tomcat -->web.xml—filter servlet listener
tomcat不会主动去启动springboot应用,所以tomcat启动的时候肯定调用了SpringBootServletInitializer 的configure方法
public class TomcatStartSpringBoot extends SpringBootServletInitializer {@Overrideprotected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {return builder.sources(MyApplication.class);}
}
启动原理
当servlet容器启动的时候,就会去META-INF/service/文件下去找javax.servlet.ServletContainerInitializer文件
这个文件肯定绑定了一个ServletContainerInitializer
org.springframework.web.SpringServletContainerInitializer
当servlet容器启动的时候就会去该文件夹中找到ServletContainerInitializer的实现类SpringServletContainerInitializer从而创建它实例(SPI机制),并且调用onStartup方法
@HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements ServletContainerInitializer {public SpringServletContainerInitializer() {}public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {List<WebApplicationInitializer> initializers = Collections.emptyList();Iterator var4;if (webAppInitializerClasses != null) {initializers = new ArrayList(webAppInitializerClasses.size());var4 = webAppInitializerClasses.iterator();while(var4.hasNext()) {Class<?> waiClass = (Class)var4.next();if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) {try {((List)initializers).add((WebApplicationInitializer)ReflectionUtils.accessibleConstructor(waiClass, new Class[0]).newInstance());} catch (Throwable var7) {throw new ServletException("Failed to instantiate WebApplicationInitializer class", var7);}}}}if (((List)initializers).isEmpty()) {servletContext.log("No Spring WebApplicationInitializer types detected on classpath");} else {servletContext.log(((List)initializers).size() + " Spring WebApplicationInitializers detected on classpath");AnnotationAwareOrderComparator.sort((List)initializers);var4 = ((List)initializers).iterator();while(var4.hasNext()) {WebApplicationInitializer initializer = (WebApplicationInitializer)var4.next();initializer.onStartup(servletContext);}}}
}
- @HandlesTypes({WebApplicationInitializer.class})
@HandlesTypes({WebApplicationInitializer.class})传入的类为ServletContainerInitializer感兴趣的类
- 容器会自动在classpath中找到WebApplicationInitializer 会传入到onStartup方法的webAppInitializerClasses 参数中,也包括了之前自定义的TomcatStartSpringBoot
@HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements
- 启动WebApplicationInitializer的onStartup方法
循环调用所有WebApplicationInitializer实例的onstartup方法
if (((List)initializers).isEmpty()) {servletContext.log("No Spring WebApplicationInitializer types detected on classpath");} else {servletContext.log(((List)initializers).size() + " Spring WebApplicationInitializers detected on classpath");AnnotationAwareOrderComparator.sort((List)initializers);var4 = ((List)initializers).iterator();while(var4.hasNext()) {WebApplicationInitializer initializer = (WebApplicationInitializer)var4.next();//循环调用所有WebApplicationInitializer实例的onstartup方法initializer.onStartup(servletContext);}
- 调用SpringBootServletInitializer onStartup方法
public void onStartup(ServletContext servletContext) throws ServletException {servletContext.setAttribute("logging.register-shutdown-hook", false);this.logger = LogFactory.getLog(this.getClass());//创建对对对 ,这个方法里就会去调用configure方法WebApplicationContext rootApplicationContext = this.createRootApplicationContext(servletContext);if (rootApplicationContext != null) {servletContext.addListener(new SpringBootContextLoaderListener(rootApplicationContext, servletContext));} else {this.logger.debug("No ContextLoaderListener registered, as createRootApplicationContext() did not return an application context");}}
- createRootApplicationContext
当调用configure方法的时候,就会调用我们自己的configure方法(为了更好的理解才去父类里看),因为TomcatStartSpringBoot是继承了SpringBootServletInitializer又继承了WebApplicationInitializer(我们自己定义的TomcatStartSpringBoot也是一个WebApplicationInitializer)
public class TomcatStartSpringBoot extends SpringBootServletInitializer {@Overrideprotected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {return builder.sources(MyApplication.class);}
}
createRootApplicationContext
调用build方法,就会根据传入的SpringBoot的启动类来构建一个springapplication
- SpringApplicationBuilder
public SpringApplication build(String... args) {this.configureAsChildIfNecessary(args);this.application.addPrimarySources(this.sources);return this.application;}
最后再调用run方法来启动springboot
启动springboot
protected WebApplicationContext run(SpringApplication application) {return (WebApplicationContext)application.run(new String[0]);}
相关文章:

SpringBoot学习05-[SpringBoot的嵌入式Servlet容器]
SpringBoot的嵌入式Servlet容器 嵌入式Servlet容器servlet容器-嵌入式servlet容器配置修改通过全局配置文件修改修改添加实现了WebServerFactoryCustomizer接口的bean来进行修改 servlet容器-注册servlet三大组件应该如何注册呢?servlet3.0规范提供的注解方式进行注…...

查看Oracle是哪个Oracle_home 下启动的
[rootrac1 ~]# ps -ef|grep smon root 413 24903 0 22:30 pts/0 00:00:00 grep --colorauto smon root 27165 1 0 22:11 ? 00:00:09 /u01/app/19.0.0/grid/bin/osysmond.bin grid 27784 1 0 22:12 ? 00:00:00 asm_smon_ASM1 oracl…...

重温react-06(初识函数组件和快速生成格式的插件使用方式)
开始 函数组件必然成为未来发展的趋势(个人见解),总之努力的去学习,才能赚更多的钱.加油呀! 函数组件的格式 import React from reactexport default function LearnFunction01() {return (<div>LearnFunction01</div>) }以上是函数式组件的组基本的方式 快捷生…...

【高考志愿】仪器科学与技术
目录 一、专业介绍 1.1 专业概述 1.2 专业方向 1.3 主要课程 二、专业技能与素质培养 三、就业前景 四、个人发展规划建议 五、仪器科学与技术专业排名 六、总结 一、专业介绍 1.1 专业概述 仪器科学与技术专业是一门综合性极强的学科,它融合了测量、控制…...

Elasticsearch的Mapping
Elasticsearch的Mapping Mapping是什么 Mapping定义了ES的索引结构、字段类型、分词器等,是索引的一部分。类似于关系型数据库中“表结构”的概念,在 Mapping 里也包含了一些属性,比如字段名称、类型、字段使用的分词器、是否评分、是否创建…...

【vocabulary in use (elementary)】6 Health and Illness
very well / fine 很好 ill sick 生病 I feel terrible 感觉很差 headache 头疼 toothache 牙疼 dentist medicine 药 pills 片药 caps 胶囊 aspirin 阿司匹林 antibiotic 抗生素 vitamin 维生素 painkiller 止痛药 dentist 牙医 got a cold 感冒 for many years 很多年 all th…...

探囊取物之多形式注册页面(基于BootStrap4)
基于BootStrap4的注册页面,支持手机验证码注册、账号密码注册 低配置云服务器,首次加载速度较慢,请耐心等候;演练页面可点击查看源码 预览页面:http://www.daelui.com/#/tigerlair/saas/preview/ly4gax38ub9j 演练页…...

【C++进阶学习】第五弹——二叉搜索树——二叉树进阶及set和map的铺垫
二叉树1:深入理解数据结构第一弹——二叉树(1)——堆-CSDN博客 二叉树2:深入理解数据结构第三弹——二叉树(3)——二叉树的基本结构与操作-CSDN博客 二叉树3:深入理解数据结构第三弹——二叉树…...

【RabbitMQ实战】Springboot 整合RabbitMQ组件,多种编码示例,带你实践 看完这一篇就够了
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、对RabbitMQ管理界面深入了解1、在这个界面里面我们可以做些什么? 二、编码练习(1)使用direct exchange(直连型交换机)&a…...

【你也能从零基础学会网站开发】理解DBMS数据库管理系统架构,从用户到数据到底经历了什么
🚀 个人主页 极客小俊 ✍🏻 作者简介:程序猿、设计师、技术分享 🐋 希望大家多多支持, 我们一起学习和进步! 🏅 欢迎评论 ❤️点赞💬评论 📂收藏 📂加关注 其实前面我们也…...

Vue.js 中的API接口封装实战与详解
在开发Web应用的过程中,我们常常需要和服务器进行数据交互,这就涉及到了API接口的调用。在Vue.js项目中,为了提高代码复用性、可维护性和降低错误率,我们将API接口进行合理的封装显得尤为重要。本文将详细介绍如何在Vue.js项目中实…...

职场内卷、不稳定、没前景……怎么破?
经济下行期,大家普遍反映混职场艰难。 再深究下,发现造成职场艰难的原因主要有三个: 1.内卷:狼多肉少 2.不稳定:裁员总是不期而遇 3.没前景:明知过几年会被优化,但无法改变,死气沉沉…...

LeetCode 算法:将有序数组转换为二叉搜索树 c++
原题链接🔗:将有序数组转换为二叉搜索树 难度:简单⭐️ 题目 给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 平衡 二叉搜索树。 示例 1: 输入:nums [-10,-3,0,5,9]…...

智慧公厕系统改变了人们对服务区公厕的看法
在过去,服务区公厕常常给人留下脏乱差的印象,成为人们在长途旅行途中不愿停留的地方。然而,随着智慧科技的不断发展和应用,智慧公厕系统的出现改变了人们对服务区公厕的看法,为公共卫生设施的提升注入了新的活力。 一、…...

终极指南:RNNS、Transformers 和 Diffusion 模型
一、说明 作为广泛使用这些工具和模型的人,我的目标是解开 RNN、Transformer 和 Diffusion 模型的复杂性和细微差别,为您提供详细的比较,为您的特定需求提供正确的选择。 无论您是在构建语言翻译系统、生成高保真图像,还是处理时间…...

WPF UI 3D 基本概念 点线三角面 相机对象 材质对象与贴图 3D地球 光源 变形处理 动作交互 辅助交互插件 系列三
WPF UI交互专题 平面图形 Path Drawing 绘图 渐变 Brush 矩阵 Transform 变形 阴影效果 模糊效果 自定义灰度去色效果 系列二-CSDN博客 1软件中的3D基本概念 WPF 中 3D 功能的设计初衷并非提供功能齐全的游戏开发平台。 WPF 中的 3D 图形内容封装在 Viewport3D 元素中&#x…...

分子AI预测赛Task2笔记
下面所述比较官方的内容都来自官方文档 Task2:赛题深入解析 - 飞书云文档 (feishu.cn) 赛题背景 强调了人工智能在科研领域&…...

剖析DeFi交易产品之UniswapV4:创建池子
本文首发于公众号:Keegan小钢 创建池子的底层函数是 PoolManager 合约的 initialize 函数,其代码实现并不复杂,如下所示: function initialize(PoolKey memory key, uint160 sqrtPriceX96, bytes calldata hookData)externalover…...

速盾:cdn内容分发服务有哪些优势?
CDN(Content Delivery Network)是指内容分发网络,是一种将网络内容分发到全球各个地点的技术和架构。在现代互联网架构中,CDN已经变得非常重要。CDN通过将内容分发到靠近用户的服务器上,提供高速、高效的服务。下面是C…...

如何利用React和Python构建强大的网络爬虫应用
如何利用React和Python构建强大的网络爬虫应用 引言: 网络爬虫是一种自动化程序,用于通过互联网抓取网页数据。随着互联网的不断发展和数据的爆炸式增长,网络爬虫越来越受欢迎。本文将介绍如何利用React和Python这两种流行的技术,…...

炎黄数智人:招商局集团推出AI数字员工“招小影”
引言 在全球数字化浪潮的推动下,招商局集团开启了一项具有里程碑意义的项目。招商局集团将引入AI数字员工“招小影”,这一举措不仅彰显了招商局集团在智能化转型方面的坚定决心,也为企业管理模式的创新注入了新的活力。 “招小影”是一款集成…...

【开发篇】明明配置跨域声明,为什么却仍可以发送HTTP请求
一、问题 在SpringBoot项目中,明确指定仅允许指定网站跨域访问: 为什么开发人员却仍旧可以通过HTTP工具调用接口? 二、为什么 在回答这个问题之前,我们首先要了解一下什么是CORS! 1、什么是CORS CORS的全称为跨域资源…...

单片机中有FLASH为啥还需要EEROM?
在开始前刚好我有一些资料,是我根据网友给的问题精心整理了一份「单片机的资料从专业入门到高级教程」, 点个关注在评论区回复“888”之后私信回复“888”,全部无偿共享给大家!!! 一是EEPROM操作简单&…...

Qt的源码目录集合(V5.12.12版本)
目录 1.QObject实现源码 2.qml中的ListModel实现源码 3.qml中的JS运行时的环境和数据类型源码 1.QObject实现源码 .\Qt\Qt5.12.12\5.12.12\Src\qtbase\src\corelib\kernel\qobject.h .\Qt\Qt5.12.12\5.12.12\Src\qtbase\src\corelib\kernel\qobject.cpp .\Qt\Qt5.12.12\5…...

记因hive配置文件参数运用不当导致 sqoop MySQL导入数据到hive 失败的案例
sqoop MySQL导入数据到hive报错 ERROR tool.ImportTool: Encountered IOException running import job: java.io.IOException: Hive exited with status 64 报错解释: 这个错误表明Sqoop在尝试导入数据到Hive时遇到了问题,导致Hive进程异常退出。状态码…...

自动化邮件通知:批处理脚本的通讯增强
自动化邮件通知:批处理脚本的通讯增强 引言 批处理脚本在自动化任务中扮演着重要角色,无论是在系统管理、数据处理还是日常任务调度中。然而,批处理脚本的自动化能力可以通过集成邮件通知功能得到显著增强。当脚本执行完毕或在执行过程中遇…...

236、二叉树的最近公共祖先
前提: 所有 Node.val 互不相同 。p ! qp 和 q 均存在于给定的二叉树中。 代码如下: class Solution { public:TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {if (root q || root p || root NULL) return root;TreeN…...

WebStorm 2024 for Mac JavaScript前端开发工具
Mac分享吧 文章目录 效果一、下载软件二、开始安装1、双击运行软件(适合自己的M芯片版或Intel芯片版),将其从左侧拖入右侧文件夹中,等待安装完毕2、应用程序显示软件图标,表示安装成功3、打开访达,点击【文…...

【Redis7】零基础篇
1 课程概述 2 Redis入门概述 2.1 是什么 Redis是基于内存的KV键值对内存数据库 Redis:Remote Dictionary Server(远程字典服务)是完全开源的,使用ANSIC语言编写遵守BSD协议,是一个高性能的Key-Value数据库提供了丰富的数据结构,…...

[ROS 系列学习教程] 建模与仿真 - 使用 ros_control 控制差速轮式机器人
ROS 系列学习教程(总目录) 本文目录 一、差速轮式机器人二、差速驱动机器人运动学模型三、对外接口3.1 输入接口3.2 输出接口 四、控制器参数五、配置控制器参数六、编写硬件抽象接口七、控制机器人移动八、源码 ros_control 提供了多种控制器,其中 diff_drive_cont…...