SpringBoot系列-1启动流程
背景
本文作为SpringBoot系列的开篇,介绍SpringBoot的启动流程,包括Spring容器和Tomcat启动过程。SpringBoot作为流行的微服务框架,其是基于约定和自动装配机制对Spring的封装和增强。
由于前面的Spring系列对Spring容器已经进行了较为细致的梳理,相同内容不进行重复说明。
1.案例
添加SpringBoot和web依赖:
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.2.RELEASE</version>
</parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
</dependencies>
启动类:
@SpringBootApplication
// 标注[1]
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}
}
Note:
按规范,一般将main
方法所在的类命名为artifactId+Application.
2.启动过程
跟踪SpringApplication.run(DemoApplication.class, args)
:
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {return new SpringApplication(primarySources).run(args);
}
从逻辑上可以拆分为两个部分:构造SpringApplication对象,调用SpringApplication对象的run方法.
2.1 构造SpringApplication对象
new SpringApplication(primarySources), primarySources参数为传入的DemoApplication.class对象;
说明:传入的primarySources不一定为main方法所在类,只需要保证为@SpringBootApplication注解的类即可。
经过public SpringApplication(Class<?>… primarySources) {this(null, primarySources);}进入:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {// resourceLoader为null;this.resourceLoader = resourceLoader;// 主配置类,此时为DemoApplication.classthis.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));// web类型为SERVLETthis.webApplicationType = WebApplicationType.deduceFromClasspath();// 使用SPI机制加载ApplicationContextInitializer和ApplicationListener类型的对象,并保存到initializers和listeners属性中;[标注1]setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));// 推算出main方法所在类(通过构造异常对象获取), 此时为DemoApplication.classthis.mainApplicationClass = deduceMainApplicationClass();
}
Note:
getSpringFactoriesInstances
方法逻辑如下:
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {ClassLoader classLoader = getClassLoader();// 从spring.factories文件中加载指定名称的类型,此时为ApplicationContextInitializer和ApplicationListenerSet<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));// 通过反射创建对象List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);// 排序(继承了Ordered接口的使用getOrder获取,使用@Order注解的根据注解的值)AnnotationAwareOrderComparator.sort(instances);return instances;
}
ApplicationContextInitializer包括:
// spring-boot包中的spring.factories文件:
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer// spring-boot-autoconfiguration包中的spring.factories文件:
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
ApplicationListener包括:
// spring-boot包中的spring.factories文件:
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener// spring-boot-autoconfiguration包中的spring.factories文件:
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
2.2 执行run方法
删除计时、Banner打印、日志打印、异常逻辑后:
public ConfigurableApplicationContext run(String... args) {// 对java.awt.headless参数设置[Ignore]this.configureHeadlessProperty();// 使用SPI机制获取SpringApplicationRunListeners,// 内部仅包含一个EventPublishingRunListener类型的监听器SpringApplicationRunListeners listeners = this.getRunListeners(args);listeners.starting();// 对启动方法的入参进行包装ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);// 创建环境变量 [标注1]ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);// spring.beaninfo.ignore属性设置[Ignore]this.configureIgnoreBeanInfo(environment);// 根据Servlet类型创建Spring容器 [标注2]ConfigurableApplicationContext context = this.createApplicationContext();// 刷新前的预处理 [标注3]this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);// 刷新容器 标注[4]this.refreshContext(context);// 容器刷新后处理,预留扩展接口,此时逻辑为空this.afterRefresh(context, applicationArguments);listeners.started(context);// 调用run方法 [标注5]this.callRunners(context, applicationArguments);// 返回AnnotationConfigServletWebServerApplicationContext类型的Spring容器return context;}
Note1: 创建环境变量
prepareEnvironment
方法用户构造环境变量,类型为StandardServletEnvironment,包括:
[1] systemProperties保存系统属性,如java.version, user.name;
[2] systemEnvironment保存环境变量,如JAVA_HOME, GRADLE_HOME;
[3] applicationConfig保存application.yml配置文件信息 …
Note2: 创建Spring容器
protected ConfigurableApplicationContext createApplicationContext() {
// 省略部分代码 ...switch (this.webApplicationType) {case SERVLET:contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");break;case REACTIVE:contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");break;default:contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");}return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
}
webApplicationType
为SERVLET类型,通过反射构造一个AnnotationConfigServletWebServerApplicationContext类型的对象返回。
Note3: 刷新前的预处理
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {// 为Spring容器设置环境变量context.setEnvironment(environment);// 对Spring容器的beanNameGenerator(默认启动流程为空)、resourceLoader(默认启动流程为空)、addConversionService组件进行设置postProcessApplicationContext(context);// 调用initializers的initialize方法,在new SpringApplication阶段通过SPI获取的ApplicationContextInitializer对象列表applyInitializers(context);listeners.contextPrepared(context);ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();// 将启动入参和printedBanner注册到IOC中beanFactory.registerSingleton("springApplicationArguments", applicationArguments);if (printedBanner != null) {beanFactory.registerSingleton("springBootBanner", printedBanner);}// SpringBoot不支持循环依赖if (beanFactory instanceof DefaultListableBeanFactory) {((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);}// lazyInitialization为false, 不进行懒加载(LazyInitializationBeanFactoryPostProcessor会将容器中的BeanDefinition的lazyInit属性设置为true,从而实现懒加载)if (this.lazyInitialization) {context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());}// 向容器导入主配置类,此时为DemoApplication[标注3-1]Set<Object> sources = getAllSources();load(context, sources.toArray(new Object[0]));listeners.contextLoaded(context);
}
跟踪load
方法进入:
class BeanDefinitionLoader {// ...private int load(Class<?> source) {// ...this.annotatedReader.register(source);}
}
public class AnnotatedBeanDefinitionReader {// ...public void registerBean(Class<?> beanClass) {doRegisterBean(beanClass, null, null, null, null);}
}
doRegisterBean
方法表示向IOC容器中注册一个beanClass类型的对象,此时为DemoApplication. registerBean
方法实现逻辑的解读是Spring导入Bean对象的内容,本文不进行深究。
Note4: 刷新容器
this.refreshContext(context);
内容如下
private void refreshContext(ConfigurableApplicationContext context) {this.refresh((ApplicationContext)context);if (this.registerShutdownHook) {try {context.registerShutdownHook();} catch (AccessControlException var3) {}}
}
逻辑可以分为两个部分:
调用this.refresh((ApplicationContext)context);
刷新容器,调用context.registerShutdownHook()
注册关闭时的勾子函数。context.registerShutdownHook()是通过Runtime.getRuntime().addShutdownHook
方法向JVM注册钩子函数,在当JVM关闭时执行。
这里需要执行的逻辑包括:注销Bean、关闭Bean工厂、清理缓存等。
进入refresh
方法:
public void refresh() throws BeansException, IllegalStateException {
//... 省略部分与主体逻辑无关的代码this.prepareRefresh();ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();this.prepareBeanFactory(beanFactory);this.postProcessBeanFactory(beanFactory);this.invokeBeanFactoryPostProcessors(beanFactory);this.registerBeanPostProcessors(beanFactory);this.initMessageSource();this.initApplicationEventMulticaster();this.onRefresh();this.registerListeners();this.finishBeanFactoryInitialization(beanFactory);this.finishRefresh();
}
上述流程在 Spring系列-1 启动流程 中以及进行介绍过, 重复内容这里不进行说明。
此处刷新的Spring容器类型为AnnotationConfigServletWebServerApplicationContext,重写了onRefresh
方法:
protected void onRefresh() {// 空方法super.onRefresh();try {this.createWebServer();} catch (Throwable var2) {throw new ApplicationContextException("Unable to start web server", var2);}
}
即onRefresh()的核心逻辑在this.createWebServer()
方法中,该方法用于创建并启动web容器,逻辑如下:
private void createWebServer() {// this.webServer为nullWebServer webServer = this.webServer;// servletContext为nullServletContext servletContext = getServletContext();if (webServer == null && servletContext == null) {//获取web容器工厂 [标注4-1]ServletWebServerFactory factory = getWebServerFactory();// 构造web容器 [标注4-2]this.webServer = factory.getWebServer(getSelfInitializer());// 向IOC注册webServerGracefulShutdown和webServerStartStop对象 [标注4-3]getBeanFactory().registerSingleton("webServerGracefulShutdown",new WebServerGracefulShutdownLifecycle(this.webServer));getBeanFactory().registerSingleton("webServerStartStop",new WebServerStartStopLifecycle(this, this.webServer));} else if (servletContext != null) {// Ignore...}//占位符替换:替换环境变量中名称"servletContextInitParams"的资源->容器对象initPropertySources();
}
Note 4-1:getWebServerFactory()
用于获取容器工厂
spring-boot-autoconfigure包中定义了ServletWebServerFactoryConfiguration配置类,定义了Tomcat、Jetty、Undertow容器工厂配置类:
@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(...) {TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();//...return factory;}}@Configuration(proxyBeanMethods = false)@ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)static class EmbeddedJetty {@BeanJettyServletWebServerFactory JettyServletWebServerFactory(...) {JettyServletWebServerFactory factory = new JettyServletWebServerFactory();//...return factory;}}//...
}
其中EmbeddedTomcat的注入条件是IOC容器中还没有注入ServletWebServerFactory对象,以及类路径中存在Tomcat.class
类;而Tomcat.class
以及Tomcat相关依赖定义在spring-boot-starter-tomcat包中,在引入spring-boot-starter-web依赖时,会自动引入spring-boot-starter-tomcat依赖,即springboot默认使用Tomcat容器。
SpringBoot也可以使用Jetty容器启动,pom依赖需要进行调整,删除spring-boot-starter-web中引入的tomcat依赖,并引入jetty依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId></exclusion></exclusions>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jetty</artifactId><version>2.6.4</version>
</dependency>
本文后续以SpringBoot默认的Tomcat为web容器进行介绍。
Note 4-2: this.webServer = factory.getWebServer(getSelfInitializer())
构造web容器
该步骤包含的内容较多,可以拆成两个部分:
(1) 参数部分:是一个lambda表达式,将被作为参数传入到Tomcat类中,在Tomcat启动时被调用;
(2) 函数部分:负责构造并启动web容器。
参数部分:
private void selfInitialize(ServletContext servletContext) throws ServletException {// prepareWebApplicationContext用于实现web容器上下文(ApplicationContextFacade类型)与Spring容器相互指向prepareWebApplicationContext(servletContext);// 将servletContext包装成ServletContextScope对象,作为Scope对象添加到容器的中以及设置到servletContext的application属性registerApplicationScope(servletContext);WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);for (ServletContextInitializer beans : getServletContextInitializerBeans()) {beans.onStartup(servletContext);}}
WebApplicationContextUtils.registerEnvironmentBeans
方法将servletContext以"servletContext"
为BeanName注册到IOC中,并从servletContext中获取InitParameter和Attribute信息分别以"contextParameters"
和""为beanName注册到IOC中。 getServletContextInitializerBeans()
方法从IOC中获取ServletContextInitializer、Filter、Servlet对象, 封装为RegistrationBean对象,并调用这些对象的onStartup
方法。以DispatcherServletRegistrationBean为例进行介绍:
public final void onStartup(ServletContext servletContext) throws ServletException {// "servlet dispatcherServlet"String description = getDescription();register(description, servletContext);}register(description, servletContext)方法包含两个主要步骤addRegistration和configure:protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {// "dispatcherServlet"String name = getServletName();// this.servlet就是DispatcherServlet对象,将其注册到serverContext容器上下文中return servletContext.addServlet(name, this.servlet);}protected void configure(ServletRegistration.Dynamic registration) {super.configure(registration);String[] urlMapping = StringUtils.toStringArray(this.urlMappings);if (urlMapping.length == 0 && this.alwaysMapUrl) {urlMapping = DEFAULT_MAPPINGS;}// 注册urlMapping, 默认是 "/"if (!ObjectUtils.isEmpty(urlMapping)) {registration.addMapping(urlMapping);}// 设置loadOnStartup, 默认是-1,表示懒加载registration.setLoadOnStartup(this.loadOnStartup);if (this.multipartConfig != null) {registration.setMultipartConfig(this.multipartConfig);}}
继续进入factory.getWebServer(getSelfInitializer())
方法:
public WebServer getWebServer(ServletContextInitializer... initializers) {if (this.disableMBeanRegistry) {Registry.disableRegistry();} // Ignore// 创建Tomcat对象,并设置service、connector、engine、Host// 这个Tomcat来自org.apache.catalina.startup包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);}// 给Host添加一个Context, 该Context被封装后就是前文提到的web容器上下文// initializers属性被保存在Context中, 在tonmcat启动后调用initializers的onStartUp方法(上文涉及的lambda表达式).prepareContext(tomcat.getHost(), initializers);// 构造、启动并返回web容器[标注3]return getTomcatWebServer(tomcat);
}
getTomcatWebServer(tomcat)
方法逻辑如下:
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {new TomcatWebServer(tomcat, getPort() >= 0, Shutdown.IMMEDIATE);
}
其中:getPort()
得到的端口来源为Spring配置文件, 因此getPort()>0
; Shutdown.IMMEDIATE
为枚举变量。
public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {this.tomcat = tomcat;// truethis.autoStart = autoStart;// 传入的Shutdown.IMMEDIATE标志立即关闭(而非优雅关闭),因此this.gracefulShutdown设置为nullthis.gracefulShutdown = (shutdown == Shutdown.GRACEFUL) ? new GracefulShutdown(tomcat) : null;initialize();
}
initialize
方法中只有两处逻辑需要注意:
private void initialize() throws WebServerException {// ... // 启动Tomcatthis.tomcat.start();// ... // 开启一个非守护线程,因为Tomcat所有线程为守护线程,否则会直接退出进程startDaemonAwaitThread();
}
Note 4-3:
// 向IOC注册webServerGracefulShutdown和webServerStartStop对象 [标注3]
getBeanFactory().registerSingleton("webServerGracefulShutdown",new WebServerGracefulShutdownLifecycle(this.webServer));
getBeanFactory().registerSingleton("webServerStartStop",new WebServerStartStopLifecycle(this, this.webServer));
将得到的webServer对象进行包装并注册到IOC中。其中: 可通过WebServerGracefulShutdownLifecycle对象优雅地关闭容器,WebServerStartStopLifecycle可以进行容器的启停。优雅关闭指等待所有正在处理的请求完成后再关闭,以确保所有连接都被正确地关闭。
Note5: 执行runner的run方法
this.callRunners(context, applicationArguments);
逻辑如下:
private void callRunners(ApplicationContext context, ApplicationArguments args) {List<Object> runners = new ArrayList();// 从IOC容器获取所有的ApplicationRunner类型的Bean对象runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());// 从IOC容器获取所有的CommandLineRunner类型的Bean对象runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());// 按照Ordered接口和@Order注解进行排序AnnotationAwareOrderComparator.sort(runners);// 按顺序调用ApplicationRunner或CommandLineRunner的run方法 [标注5-1]for (Object runner : runners) {if (runner instanceof ApplicationRunner) {this.callRunner((ApplicationRunner)runner, args);}if (runner instanceof CommandLineRunner) {this.callRunner((CommandLineRunner)runner, args);}}
}
Note:5-1
调用ApplicationRunner和CommandLineRunner时,有try-catch异常保护,不会因为某个runner执行异常而影响其他runner执行,传入runner的参数就是容器启动时传给SpringApplication的参数,即main
方法的参数。
相关文章:
SpringBoot系列-1启动流程
背景 本文作为SpringBoot系列的开篇,介绍SpringBoot的启动流程,包括Spring容器和Tomcat启动过程。SpringBoot作为流行的微服务框架,其是基于约定和自动装配机制对Spring的封装和增强。 由于前面的Spring系列对Spring容器已经进行了较为细致的…...

【记】一次common模块导入无效的bug
首先Maven clean install无用 然后idea清除缓存重启无用 pom.xml文件重载无效 正确解决路径: 1.检查common模块的父工程导入和自身模块的声明是否正确 默认是继承父工程的groupid,可以不用再声明 2.检查子工程是否引入正确的common,org不要…...

1.Netty概述
原生NIO存在的问题(Netty要解决的问题) 虽然JAVA NIO 和 JAVA AIO框架提供了多路复用IO/异步IO的支持,但是并没有提供给上层“信息格式”的良好封装。JAVA NIO 的 API 使用麻烦,需要熟练掌握 ByteBuffer、Channel、Selector等 , 所以用这些API实现一款真正的网络应…...

YOLO目标检测——真实道路车辆检测数据集【含对应voc、coco和yolo三种格式标签】
实际项目应用:自动驾驶技术研发、交通安全监控数据集说明:真实道路车辆检测数据集,真实场景的高质量图片数据,数据场景丰富标签说明:使用lableimg标注软件标注,标注框质量高,含voc(xml)、coco(j…...

【Solidity】Solidity中的基本数据类型和复合数据类型
1. 基本数据类型 1.1 整数类型 Solidity支持有符号整数和无符号整数,可以指定位数和范围。以下是一些整数类型的示例: int:有符号整数,可以是正数或负数。2,-45,2023 uint:无符号整数&#x…...
Flutter Set存储自定义对象时 如何保证唯一
在Flutter中,Set和List是两种不同的集合类型,List中存储的元素可以重复,Set中存储的元素不可重复。 如果你想在Set中存储自定义对象,你需要确保对象的唯一性。 这可以通过在自定义类中实现hashCode方法和equals方法来实现。 has…...

Docker容器中执行throttle.sh显示权限报错:RTNETLINK answers: Operation not permitted
在模拟通信环境时,我执行了一下命令: bash ./throttle.sh wan但是,出现了权限的报错:RTNETLINK answers: Operation not permitted 解决方案说简单也挺简单,只需要两步完成。但是其实又蛮繁琐,因为需要将…...

【Linux】jdk、tomcat、MySQL环境搭建的配置安装,Linux更改后端端口
一、作用 工具的组合为开发者和系统管理员提供了构建和运行Java应用程序以及存储和管理数据的完整环境。 JDK(Java Development Kit):JDK是Java开发工具包,它提供了开发和运行Java应用程序所需的工具和库。通过安装JDK,…...

【WinForm详细教程七】WinForm中的DataGridView控件
文章目录 1.主要属性DataSource行(Row 相关属性)列(Column 相关属性)单元格(Cell 相关属性)逻辑删除AllowUserToAddRowsAllowUserToDeleteRowsAllowUserToOrderColumns其他布局和行为属性 2.控件中的行、列…...

SpringCloudTencent(上)
SpringCloudTencent 1.PolarisMesh介绍2.北极星具备的功能3.北极星包含的组件4.功能特性1.服务管理1.服务注册2.服务发现3.健康检查 2.配置管理 5.代码实战1.环境准备2.服务注册与发现3.远程调用 1.PolarisMesh介绍 1.北极星是腾讯开源的服务治理平台,致力于解决分…...
linux硬盘挂载(linux 修改某个磁盘挂载到新目录)
文章目录 什么是硬盘挂载linux 修改某个磁盘挂载到新目录 什么是硬盘挂载 在Linux操作系统中,挂载硬盘是将硬盘的分区或者整个硬盘与文件系统关联起来,使得我们可以通过文件系统访问硬盘中的数据。 确认硬盘信息 sudo fdisk -l该命令会列出所有已连接…...
hdlbits系列verilog解答(always块case语句)-33
文章目录 一、问题描述二、verilog源码三、仿真结果一、问题描述 Verilog 中的 case 语句几乎等同于 if-elseif-else 序列,该序列将一个表达式与其他表达式列表进行比较。它的语法和功能与 C 中的 switch 语句不同。 always @(*) begin // This is a combinational circuit …...

3D医学三维技术影像PACS系统源码
一、系统概述 3D医学影像PACS系统,它集影像存储服务器、影像诊断工作站及RIS报告系统于一身,主要有图像处理模块、影像数据管理模块、RIS报告模块、光盘存档模块、DICOM通讯模块、胶片打印输出等模块组成, 具有完善的影像数据库管理功能,强大…...

python 之softmx 函数
文章目录 总的介绍小应用 总的介绍 Softmax函数是一个常用的激活函数,通常用于多类别分类问题中。它将一个实数向量转换为概率分布。这个函数的输出是一个概率分布,表示输入样本属于每个可能类别的概率。 给定一个具有 (K) 个不同数值的实数向量 z (z1…...
第3章_基本select语句
文章目录 SQL概述SQL背景知识SQL分类 SQL语言的规则与规范SQL语言的规则SQL大小写规范注释命令规则(暂时了解)数据导入指令 基本的select语句select ...select ... from列的别名去除重复行空值参与运算着重号查询常数 显示表结构讲课代码课后练习 SQL概述…...
GPT3.5+文心一言+chatGLM 计算和代码生成能力简单对比
chatGLM3刚发布(10.27),打算尝试一下其code和计算能力。 共选取三个问题,难度从中等,偏困难,到困难。测试内容是正好手头上在做的事想让LLM来完成(偷懒),之前都是直接使…...
手搓一个ubuntu自动安装python3.9的sh脚本
#!/bin/bash# Step 1: 更新系统软件包 sudo apt update sudo apt upgrade -y sudo apt install -y software-properties-common# Step 2: 安装Python 3.9的依赖项 sudo apt install -y build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libread…...
volte使用方法 nodejs版本切换
Volta 一种轻松管理 JavaScript 命令行工具的方法。 文档 https://docs.volta.sh/guide/ 源码 https://github.com/volta-cli/volta 命令行 安装版本 此方法运行完会配置为默认版本 volta install node 安装最新版本的node volta install node14 安装指定版本的node volta i…...

Oracle安全基线检查
一、账户安全 1、禁止SYSDBA用户远程连接 用户具备数据库超级管理员(SYSDBA)权限的用户远程管理登录SYSDBA用户只能本地登录,不能远程。REMOTE_LOGIN_PASSWORDFILE函数的Value值为NONE。这意味着禁止共享口令文件,只能通过操作系统认证登录Oracle数据库。 1)检查REMOTE…...

@Slf4j将日志记录到磁盘和数据库
文章目录 1、背景介绍2、存本地2.1、配置文件2.2、使用 3、存数据库3.1、配置文件改造3.2、过滤器编写3.3、表准备3.4、添加依赖3.5、测试 4、优化4.1、日志定期删除 1、背景介绍 现在我一个SpringBoot项目想记录日志,大概可以分为下面这几种: 用户操作…...

JavaSec-RCE
简介 RCE(Remote Code Execution),可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景:Groovy代码注入 Groovy是一种基于JVM的动态语言,语法简洁,支持闭包、动态类型和Java互操作性,…...

Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...

CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
Fabric V2.5 通用溯源系统——增加图片上传与下载功能
fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...
虚拟电厂发展三大趋势:市场化、技术主导、车网互联
市场化:从政策驱动到多元盈利 政策全面赋能 2025年4月,国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》,首次明确虚拟电厂为“独立市场主体”,提出硬性目标:2027年全国调节能力≥2000万千瓦࿰…...
JS手写代码篇----使用Promise封装AJAX请求
15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...