当前位置: 首页 > news >正文

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三大组件应该如何注册呢&#xff1f;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 专业概述 仪器科学与技术专业是一门综合性极强的学科&#xff0c;它融合了测量、控制…...

Elasticsearch的Mapping

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

【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的注册页面&#xff0c;支持手机验证码注册、账号密码注册 低配置云服务器&#xff0c;首次加载速度较慢&#xff0c;请耐心等候&#xff1b;演练页面可点击查看源码 预览页面&#xff1a;http://www.daelui.com/#/tigerlair/saas/preview/ly4gax38ub9j 演练页…...

【C++进阶学习】第五弹——二叉搜索树——二叉树进阶及set和map的铺垫

二叉树1&#xff1a;深入理解数据结构第一弹——二叉树&#xff08;1&#xff09;——堆-CSDN博客 二叉树2&#xff1a;深入理解数据结构第三弹——二叉树&#xff08;3&#xff09;——二叉树的基本结构与操作-CSDN博客 二叉树3&#xff1a;深入理解数据结构第三弹——二叉树…...

【RabbitMQ实战】Springboot 整合RabbitMQ组件,多种编码示例,带你实践 看完这一篇就够了

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、对RabbitMQ管理界面深入了解1、在这个界面里面我们可以做些什么&#xff1f; 二、编码练习&#xff08;1&#xff09;使用direct exchange(直连型交换机)&a…...

【你也能从零基础学会网站开发】理解DBMS数据库管理系统架构,从用户到数据到底经历了什么

&#x1f680; 个人主页 极客小俊 ✍&#x1f3fb; 作者简介&#xff1a;程序猿、设计师、技术分享 &#x1f40b; 希望大家多多支持, 我们一起学习和进步&#xff01; &#x1f3c5; 欢迎评论 ❤️点赞&#x1f4ac;评论 &#x1f4c2;收藏 &#x1f4c2;加关注 其实前面我们也…...

Vue.js 中的API接口封装实战与详解

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

职场内卷、不稳定、没前景……怎么破?

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

LeetCode 算法:将有序数组转换为二叉搜索树 c++

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

智慧公厕系统改变了人们对服务区公厕的看法

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

终极指南:RNNS、Transformers 和 Diffusion 模型

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

WPF UI 3D 基本概念 点线三角面 相机对象 材质对象与贴图 3D地球 光源 变形处理 动作交互 辅助交互插件 系列三

WPF UI交互专题 平面图形 Path Drawing 绘图 渐变 Brush 矩阵 Transform 变形 阴影效果 模糊效果 自定义灰度去色效果 系列二-CSDN博客 1软件中的3D基本概念 WPF 中 3D 功能的设计初衷并非提供功能齐全的游戏开发平台。 WPF 中的 3D 图形内容封装在 Viewport3D 元素中&#x…...

分子AI预测赛Task2笔记

下面所述比较官方的内容都来自官方文档 ‍‌⁠‌‍​​​‌​​⁠​​​​​&#xfeff;​​​&#xfeff;‍‬​​‍⁠‍‍​​‬​&#xfeff;‌​​​‌‍‬​​​​​​‍‌Task2&#xff1a;赛题深入解析 - 飞书云文档 (feishu.cn) 赛题背景 强调了人工智能在科研领域&…...

剖析DeFi交易产品之UniswapV4:创建池子

本文首发于公众号&#xff1a;Keegan小钢 创建池子的底层函数是 PoolManager 合约的 initialize 函数&#xff0c;其代码实现并不复杂&#xff0c;如下所示&#xff1a; function initialize(PoolKey memory key, uint160 sqrtPriceX96, bytes calldata hookData)externalover…...

速盾:cdn内容分发服务有哪些优势?

CDN&#xff08;Content Delivery Network&#xff09;是指内容分发网络&#xff0c;是一种将网络内容分发到全球各个地点的技术和架构。在现代互联网架构中&#xff0c;CDN已经变得非常重要。CDN通过将内容分发到靠近用户的服务器上&#xff0c;提供高速、高效的服务。下面是C…...

如何利用React和Python构建强大的网络爬虫应用

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

炎黄数智人:招商局集团推出AI数字员工“招小影”

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

【开发篇】明明配置跨域声明,为什么却仍可以发送HTTP请求

一、问题 在SpringBoot项目中&#xff0c;明确指定仅允许指定网站跨域访问&#xff1a; 为什么开发人员却仍旧可以通过HTTP工具调用接口&#xff1f; 二、为什么 在回答这个问题之前&#xff0c;我们首先要了解一下什么是CORS&#xff01; 1、什么是CORS CORS的全称为跨域资源…...

单片机中有FLASH为啥还需要EEROM?

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「单片机的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01; 一是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 报错解释&#xff1a; 这个错误表明Sqoop在尝试导入数据到Hive时遇到了问题&#xff0c;导致Hive进程异常退出。状态码…...

自动化邮件通知:批处理脚本的通讯增强

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

236、二叉树的最近公共祖先

前提&#xff1a; 所有 Node.val 互不相同 。p ! qp 和 q 均存在于给定的二叉树中。 代码如下&#xff1a; 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、双击运行软件&#xff08;适合自己的M芯片版或Intel芯片版&#xff09;&#xff0c;将其从左侧拖入右侧文件夹中&#xff0c;等待安装完毕2、应用程序显示软件图标&#xff0c;表示安装成功3、打开访达&#xff0c;点击【文…...

【Redis7】零基础篇

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

[ROS 系列学习教程] 建模与仿真 - 使用 ros_control 控制差速轮式机器人

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