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项目想记录日志,大概可以分为下面这几种: 用户操作…...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...
【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...
STM32+rt-thread判断是否联网
一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...
Objective-C常用命名规范总结
【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名(Class Name)2.协议名(Protocol Name)3.方法名(Method Name)4.属性名(Property Name)5.局部变量/实例变量(Local / Instance Variables&…...
ServerTrust 并非唯一
NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...
C++.OpenGL (14/64)多光源(Multiple Lights)
多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...
【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...
Qt 事件处理中 return 的深入解析
Qt 事件处理中 return 的深入解析 在 Qt 事件处理中,return 语句的使用是另一个关键概念,它与 event->accept()/event->ignore() 密切相关但作用不同。让我们详细分析一下它们之间的关系和工作原理。 核心区别:不同层级的事件处理 方…...
Linux部署私有文件管理系统MinIO
最近需要用到一个文件管理服务,但是又不想花钱,所以就想着自己搭建一个,刚好我们用的一个开源框架已经集成了MinIO,所以就选了这个 我这边对文件服务性能要求不是太高,单机版就可以 安装非常简单,几个命令就…...
