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

Spring boot启动原理及相关组件

优质博文:IT-BLOG-CN

一、Spring Boot应用启动

一个Spring Boot应用的启动通常如下:

@SpringBootApplication
@Slf4j
public class ApplicationMain {public static void main(String[] args) {ConfigurableApplicationContext ctx = SpringApplication.run(ApplicationMain.class, args);}
}

执行如上代码,Spring Boot程序启动成功。事实上启动Spring Boot应用离不开SpringApplication。
所以,我们跟随SpringApplication的脚步,开始从源码角度分析Spring Boot的初始化过程。

btw,可参看例子一节,我对Spring Boot启动的拓展点都做了demo,可参照下面源码分析进行理解。

文档有一句话说了SpringApplication做了什么(目的):

Create an appropriate ApplicationContext instance (depending on your classpath) Register a CommandLinePropertySource to expose command line arguments as Spring properties Refresh the application context, loading all singleton beans Trigger any CommandLineRunner beans

二、SpringApplication构造函数

启动代码先创建SpringApplication示例,在执行run方法:

public static ConfigurableApplicationContext run(Class<?>[] primarySources,String[] args) {return new SpringApplication(primarySources).run(args);
}

如下是SpringApplication的构造函数代码分析。

this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//通过Classloader探测不同web应用核心类是否存在,进而设置web应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath(); 
//找出所有spring.factories中声明的ApplicationContextInitializer并设置,
//ApplicationContextInitializer定义了回调接口,在refresh()前初始化调用(即在prepareContext的applyInitializers方法中调用)
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//找出所有spring.factories中声明的ApplicationListener(细节往后再叙),ApplicationListener继承了
//java.util.EventListener,实现了类似观察者模式的形式,通过实现ApplicationListener、SmartApplicationListener,能够监听Spring上下文的refresh、Prepared等事件或者是自定义事件
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//找出主启动类(有趣的是,是通过new一个runtime异常然后在异常栈里面找出来的)
this.mainApplicationClass = deduceMainApplicationClass();

在构造期间,主要做了:
1、判定应用类型,为后面创建不同类型的spring context做准备。
2、初始化ApplicationContextInitializer和ApplicationListener。
3、找出启动类。

三、run()源码解析

介绍run()方法前,先说说贯穿run方法的ApplicationRunListener,它有助于理解整个run()的运行周期。
写在这里:Spring Application事件机制

run()方法分析如下:

//java.awt.headless,是J2SE的一种模式,用于在缺失显示屏、鼠标或者键盘时的系统配置。
configureHeadlessProperty();
//将spring.factories中的SpringApplicationRunListener接口实现类拖出来,塞到SpringApplicationRunListeners(一个集合)中,统一批量执行
SpringApplicationRunListeners listeners = getRunListeners(args);
//触发runlistener的starting
listeners.starting();
try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);//spring.beaninfo.ignore如果没有设置值,则把它设为true,具体情况具体设置,//如果没用的话,把它设为true可以ignore掉classloader对于不存在的BeanInfo的扫描,提高性能。configureIgnoreBeanInfo(environment);//banner打印。自定义banner挺好玩的Banner printedBanner = printBanner(environment);//根据webApplicationType(一开始推断的应用类型)去新建applicationContextcontext = createApplicationContext();//获取SpringBootExceptionReporter,回调接口类,提供启动时的异常报告exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);//下面会说prepareContext(context, environment, listeners, applicationArguments,printedBanner);refreshContext(context);//do nothingafterRefresh(context, applicationArguments);//计时停止stopWatch.stop();//打日志if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}//启动listeners.started(context);//找出context的ApplicationRunner和CommandLineRunner,用AnnotationAwareOrderComparator排序,并执行callRunners(context, applicationArguments);

下面再分别说说两个方法(prepareEnvironment、refreshContext)的代码。

四、prepareEnvironment

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments) {// Create and configure the environmentConfigurableEnvironment environment = getOrCreateEnvironment();configureEnvironment(environment, applicationArguments.getSourceArgs());//发布environment prepared事件listeners.environmentPrepared(environment);//将获取到的environment中的spring.main配置绑定到SpringApplication中,//使用的是Binder这个spring boot2.0开始有的类bindToSpringApplication(environment);if (!this.isCustomEnvironment) {environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());}//附加的解析器将动态跟踪底层 Environment 属性源的任何添加或删除,//关于ConfigurationPropertySourcesPropertySource和MutablePropertiySource//将在Environment中作进一步讲解ConfigurationPropertySources.attach(environment);return environment;
}

五、prepareContext

private void prepareContext(ConfigurableApplicationContext context,ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments, Banner printedBanner) {//为上下文设置environment(配置、profile)context.setEnvironment(environment);//对application做一些处理,设置一些组件,//比如BeanNameGenerator,ApplicationConversionService(包含一些默认的Converter和formatter)postProcessApplicationContext(context);// 加载并运行ApplicationContextInitializerapplyInitializers(context);listeners.contextPrepared(context);if (this.logStartupInfo) {logStartupInfo(context.getParent() == null);logStartupProfileInfo(context);}// Add boot specific singleton beansConfigurableListableBeanFactory beanFactory = context.getBeanFactory();beanFactory.registerSingleton("springApplicationArguments", applicationArguments);if (printedBanner != null) {beanFactory.registerSingleton("springBootBanner", printedBanner);}if (beanFactory instanceof DefaultListableBeanFactory) {((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);}// Load the sourcesSet<Object> sources = getAllSources();Assert.notEmpty(sources, "Sources must not be empty");//Load beans(其实是由sources构建beanDefinition) into the application context. //构建BeanDefinitionLoader并执行BeanDefinitionLoader.load()load(context, sources.toArray(new Object[0]));//执行contextLoaded事件listeners.contextLoaded(context);
}

六、容器refresh(refreshContext)

private void refreshContext(ConfigurableApplicationContext context) {refresh(context);if (this.registerShutdownHook) {try {context.registerShutdownHook();}catch (AccessControlException ex) {// Not allowed in some environments.}}
}

refreshContext会做两件事,
1、应用上下文刷新
2、注册shutdown钩子

我们来看看ServletWebServer的刷新。

// ServletWebServerApplicationContext 
public final void refresh() throws BeansException, IllegalStateException {try {super.refresh();}catch (RuntimeException ex) {//停止webserverstopAndReleaseWebServer();throw ex;}
}org.springframework.context.support.AbstractApplicationContext refresh()
public void refresh() throws BeansException, IllegalStateException {// 单线程执行synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing.// 1、设置Spring容器的启动时间,撤销关闭状态,开启活跃状态。2、初始化属性源信息(Property)3、验证环境信息里一些必须存在的属性prepareRefresh();// Tell the subclass to refresh the internal bean factory.// 如果是RefreshtableApplicationContext会做了很多事情:// 1、让子类刷新内部beanFactory ,创建IoC容器(DefaultListableBeanFactory--ConfigurableListableBeanFactory 的实现类)// 2、加载解析XML文件(最终存储到Document对象中)// 3、读取Document对象,并完成BeanDefinition的加载和注册工作ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context.//从Spring容器获取BeanFactory(Spring Bean容器)并进行相关的设置为后续的使用做准备://1、设置classloader(用于加载bean),设置表达式解析器(解析bean定义中的一些表达式),添加属性编辑注册器(注册属性编辑器)//2、添加ApplicationContextAwareProcessor这个BeanPostProcessor。取消ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware、EnvironmentAware这5个接口的自动注入。因为ApplicationContextAwareProcessor把这5个接口的实现工作做了//3、设置特殊的类型对应的bean。BeanFactory对应刚刚获取的BeanFactory;ResourceLoader、ApplicationEventPublisher、ApplicationContext这3个接口对应的bean都设置为当前的Spring容器//4、注入一些其它信息的bean,比如environment、systemProperties等prepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses.postProcessBeanFactory(beanFactory);// Invoke factory processors registered as beans in the context. 执行beanfactoryPostProcessorinvokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation. // 注册beanPostProcessorregisterBeanPostProcessors(beanFactory);// Initialize message source for this context. 初始化messageSourceinitMessageSource();// Initialize event multicaster for this context.initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.onRefresh();// Check for listener beans and register them.registerListeners();// Instantiate all remaining (non-lazy-init) singletons.finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.finishRefresh();} catch (BeansException ex) {if (logger.isWarnEnabled()) {logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex);}// Destroy already created singletons to avoid dangling resources.destroyBeans();// Reset 'active' flag.cancelRefresh(ex);// Propagate exception to caller.throw ex;}finally {// Reset common introspection caches in Spring's core, since we// might not ever need metadata for singleton beans anymore...resetCommonCaches();}}
}

七、postProcessBeanFactory()

设置BeanFactory之后再进行后续的一些BeanFactory操作。

不同的Context会进行不同的操作。 比如,AnnotationConfigServletWebServerApplicationContext

protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {// 父类实现,会注册web应用特有的factory scope, super.postProcessBeanFactory(beanFactory);//查看basePackages属性,如果设置了会使用ClassPathBeanDefinitionScanner去扫描basePackages包下的bean并注if (this.basePackages != null && this.basePackages.length > 0) {this.scanner.scan(this.basePackages);}// 查看annotatedClasses属性,如果设置了会使用AnnotatedBeanDefinitionReader去注册这些beanif (!this.annotatedClasses.isEmpty()) {this.reader.register(ClassUtils.toClassArray(this.annotatedClasses));}
}

八、invokeBeanFactoryPostProcessors()

/*** Instantiate and invoke all registered BeanFactoryPostProcessor beans,* respecting explicit order if given.* <p>Must be called before singleton instantiation.*/
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {//执行AbstractContext持有的BeanFactory后置处理器//这些处理器是之前ContextInitializerPostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)// 如果通过-javaagent参数设置了LTW的织入器类包,那么增加LTW的BeanProcessor。if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));}
}

从容器中找出BeanDefinitionRegistryPostProcessor、BeanFactoryPostProcessor(二者的区别是,一个使用BeanDefinitionRegistry作处理,一个使用BeanFactory做处理), 并按一定的规则顺序执行。

ConfigurationClassPostProcessor的优先级为最高,它会对项目中的@Configuration注解修饰的类(@Component、@ComponentScan、@Import、@ImportResource修饰的类也会被处理)进行解析,解析完成之后把这些bean注册到BeanFactory中。 需要注意的是这个时候注册进来的bean还没有实例化。

ConfigurationClassPostProcessor的流程之后会独立进行分析。

九、registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory)方法

···java
/**

  • Instantiate and invoke all registered BeanPostProcessor beans,
  • respecting explicit order if given.
  • Must be called before any instantiation of application beans.

*/
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
//委派PostProcessorRegistrationDelegate去做
PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}

从Spring容器中按一定顺序(PriorityOrdered、Ordered、非PriorityOrdered非Ordered)找出实现了BeanPostProcessor接口的bean,并设置到BeanFactory的属性中。之后bean被实例化的时候会调用这个BeanPostProcessor。## 十、initMessageSource()
初始化一些国际化相关的属性。Spring boot的国际化配置可阅读MessageSourceAutoConfiguration。 默认情况会设置一个DelegatingMessageSource,是一个空实现,因为ApplicationContext接口拓展了MessageSource接口,所以Spring容器都有getMessage方法, 可是,在实现上又允许空MessageSource,所以,通过一个DelegatingMessageSource去适配。## 十一、initApplicationEventMulticaster()
Initialize event multicaster for this context. 初始化事件广播器。默认实现是SimpleApplicationEventMulticaster。onRefresh()
模板方法,给不同的Spring应用容器去实例化一些特殊的类。比如,AnnotationConfigServletWebServerApplicationContext、AnnotationConfigReactiveWebServerApplicationContext会去创建web server(createWebServer())。 spring boot的mvc内置支持有tomcat、Undertow、jetty三种server,而reactive web server则内置支持tomcat、jetty、netty三种。
java
// Unlike Jetty, all Tomcat threads are daemon threads. We create a
// blocking non-daemon to stop immediate shutdown
startDaemonAwaitThread();

btw,如果是tomcat server的话,spring boot会启动多一个线程防止退出。

十二、registerListeners()

把BeanFactory的ApplicationListener拿出来塞到事件广播器里。

如果ApplicationContext的earlyApplicationEvents属性有值,则广播该属性持有的early事件。

十三、finishBeanFactoryInitialization(beanFactory)

实例化BeanFactory中已经被注册但是未实例化的所有实例(懒加载的不需要实例化)。

比如invokeBeanFactoryPostProcessors方法中根据各种注解解析出来的类,在这个时候都会被初始化。

十四、finishRefresh()

// ReactiveWebServerApplicationContext@Overrideprotected void finishRefresh() {super.finishRefresh();WebServer webServer = startReactiveWebServer();if (webServer != null) {publishEvent(new ReactiveWebServerInitializedEvent(webServer, this));}}// AbstractApplicationContext/*** Finish the refresh of this context, invoking the LifecycleProcessor's* onRefresh() method and publishing the* {@link org.springframework.context.event.ContextRefreshedEvent}.*/protected void finishRefresh() {// Clear context-level resource caches (such as ASM metadata from scanning).// 容器完成刷新,清除资源缓存clearResourceCaches();// Initialize lifecycle processor for this context.// 初始化lifeCycleProcessor, 默认实现是DefaultLifeCycleProcessor,实现了BeanFactoryAware接口,通过BeanFactory找出LifeCycle bean// 可通过自定义实现LifeCycle接口的Bean,来监听容器的生命周期。initLifecycleProcessor();// Propagate refresh to lifecycle processor first.//粗发生命周期处理器的onRefresh方法,顺带一说,在程序正常退出时,会粗发shutdownHook,那时会粗发生命周期处理器的onClose方法getLifecycleProcessor().onRefresh();// Publish the final event.// 广播ContextRefreshed事件publishEvent(new ContextRefreshedEvent(this));// Participate in LiveBeansView MBean, if active.// 将ApplicationContext注册到Spring tool suite里LiveBeansView.registerApplicationContext(this);}

十五、resetCommonCaches()

// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();

最后会在finally执行resetCommonCaches(),清除一些Spring core、beans加载和解析的Bean信息缓存(因为对于singleton bean来说已经不需要了)。

十六、流程整理

最后,按照启动阶段整理一幅全景图。
在这里插入图片描述

十七、例子

在github里,我把Spring Boot应用启动的拓展组件(自定义的应用初始器、监听器、事件、ApplicationRunner)都写了例子,可参照阅读。 代码在这 | spring-boot-none-startup

日志如下:

2020-05-20 18:30:11.625  INFO 81568 --- [           main] n.t.d.s.b.s.n.s.r.SimpleRunListener      : environmentPrepared, env:StandardEnvironment {activeProfiles=[dev], defaultProfiles=[default], propertySources=[MapPropertySource {name='systemProperties'}, OriginAwareSystemEnvironmentPropertySource {name='systemEnvironment'}, RandomValuePropertySource {name='random'}, OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/application-dev.yml]'}, OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/application.yml]'}]}.   ____          _            __ _ _/\ / ___'_ __ _ _(_)_ __  __ _    

( ( )___ | '_ | '| | ’ / ` |
/ )| |)| | | | | || (| | ) ) ) )
’ |
| .__|| ||| |, | / / / /
=========|
|==============|
/=///_/
:: Spring Boot :: (v2.1.3.RELEASE)

2020-05-20 18:30:11.832  INFO 81568 --- [           main] n.t.d.s.b.s.n.s.r.SimpleRunListener      : contextPrepared, ctx:org.springframework.context.annotation.AnnotationConfigApplicationContext@1d730606, started on Thu May 01 08:00:00 CST 1970
2020-05-20 18:30:11.838  INFO 81568 --- [           main] n.t.d.s.b.startup.none.ApplicationMain   : Starting ApplicationMain on DESKTOP-OLDGHC1 with PID 81568 ( started by teash in )
2020-05-20 18:30:11.838  INFO 81568 --- [           main] n.t.d.s.b.startup.none.ApplicationMain   : The following profiles are active: dev
2020-05-20 18:30:11.894  INFO 81568 --- [           main] n.t.d.s.b.s.n.s.r.SimpleRunListener      : contextLoaded, context: org.springframework.context.annotation.AnnotationConfigApplicationContext@1d730606, started on Thu May 01 08:00:00 CST 1970
2020-05-20 18:30:12.404  INFO 81568 --- [           main] .s.b.s.n.s.SimpleApplicationContextAware : SimpleApplicationContextAware and send SimpleAppEvent
2020-05-20 18:30:12.441  INFO 81568 --- [           main] n.t.d.s.b.s.n.s.e.SimpleEventListener    : event: net.teaho.demo.spring.boot.startup.none.spring.event.SimpleAppEvent[source=event source], source: event source
2020-05-20 18:30:12.444  INFO 81568 --- [           main] n.t.d.s.b.s.n.config.BeanConfiguration   : [net.teaho.demo.spring.boot.startup.none.spring.spi.DemoSpringLoaderImpl@c96a4ea]
2020-05-20 18:30:12.484  INFO 81568 --- [           main] n.t.d.s.b.s.n.s.l.LoggingLifeCycle       : In Life cycle bean start().
2020-05-20 18:30:12.496  INFO 81568 --- [           main] n.t.d.s.b.startup.none.ApplicationMain   : Started ApplicationMain in 1.573 seconds (JVM running for 3.195)
2020-05-20 18:30:12.496  INFO 81568 --- [           main] n.t.d.s.b.s.n.s.r.SimpleRunListener      : started, context: org.springframework.context.annotation.AnnotationConfigApplicationContext@1d730606, started on Mon May 25 18:30:11 CST 2020
2020-05-20 18:30:12.497  INFO 81568 --- [           main] n.t.d.s.b.s.n.s.r.EchoApplicationRunner  : EchoApplicationRunner running, args:org.springframework.boot.DefaultApplicationArguments@45673f68
2020-05-20 18:30:12.497  INFO 81568 --- [           main] n.t.d.s.b.s.n.s.r.EchoCommandLineRunner  : EchoCommandLineRunner running
2020-05-20 18:30:12.497  INFO 81568 --- [           main] n.t.d.s.b.s.n.s.r.SimpleRunListener      : running, context: org.springframework.context.annotation.AnnotationConfigApplicationContext@1d730606, started on Mon May 25 18:30:11 CST 2020
2020-05-20 18:30:12.500  INFO 81568 --- [       Thread-3] n.t.d.s.b.s.n.s.l.LoggingLifeCycle       : In Life cycle bean stop().

相关文章:

Spring boot启动原理及相关组件

优质博文&#xff1a;IT-BLOG-CN 一、Spring Boot应用启动 一个Spring Boot应用的启动通常如下&#xff1a; SpringBootApplication Slf4j public class ApplicationMain {public static void main(String[] args) {ConfigurableApplicationContext ctx SpringApplication.…...

【Linux】信号处理以及补充知识

目录 一、信号被处理的时机&#xff1a; 1、理解&#xff1a; 2、内核态与用户态&#xff1a; 1、概念&#xff1a; 2、重谈地址空间&#xff1a; 3、处理时机&#xff1a; 补充知识&#xff1a; 1、sigaction&#xff1a; 2、函数重入&#xff1a; 3、volatile&…...

微服务——网关、网关登录校验、OpenFeign传递共享信息、Nacos共享配置以及热更新、动态路由

之前学习了Nacos&#xff0c;用于发现并注册、管理项目里所有的微服务&#xff0c;而OpenFeign简化微服务之间的通信&#xff0c;而为了使得前端可以使用微服务项目里的每一个微服务的接口&#xff0c;就应该将所有微服务的接口管理起来方便前端调用&#xff0c;所以有了网关。…...

【leetcode hot 100 19】删除链表的第N个节点

解法一&#xff1a;将ListNode放入ArrayList中&#xff0c;要删除的元素为num list.size()-n。如果num 0则将头节点删除&#xff1b;否则利用num-1个元素的next删除第num个元素。 /*** Definition for singly-linked list.* public class ListNode {* int val;* Lis…...

comctl32!ListView_OnSetItem函数分析LISTSUBITEM结构中的image表示图标位置

第一部分&#xff1a; BOOL ListView_SetSubItem(LV* plv, const LV_ITEM* plvi) { LISTSUBITEM lsi; BOOL fChanged FALSE; int i; int idpa; HDPA hdpa; if (plvi->mask & ~(LVIF_DI_SETITEM | LVIF_TEXT | LVIF_IMAGE | LVIF_STATE)) { …...

数据结构——多项式问题(顺序存储结构or链式存储结构)

补充&#xff1a;malloc函数&#xff1a; malloc 函数是 C 语言标准库中的一个重要函数&#xff0c;位于 <stdlib.h> 头文件中&#xff0c;主要用于在程序运行时动态分配内存。以下将详细介绍其用法。 前面的返回值指针可以自己定义&#xff0c;如 &#xff08;int*&am…...

【学习方法】技术开发者的提问智慧:如何高效获得解答?

技术开发者的提问智慧&#xff1a;如何高效获得解答&#xff1f; 在技术开发过程中&#xff0c;每个人都会遇到无法解决的问题。此时&#xff0c;我们通常会向团队、社区或论坛求助。然而&#xff0c;为什么有些人的问题能迅速得到解答&#xff0c;而有些人的问题却石沉大海&a…...

记录小白使用 Cursor 开发第一个微信小程序(一):注册账号及下载工具(250308)

文章目录 记录小白使用 Cursor 开发第一个微信小程序&#xff08;一&#xff09;&#xff1a;注册账号及下载工具&#xff08;250308&#xff09;一、微信小程序注册摘要1.1 注册流程要点 二、小程序发布流程三、下载工具 记录小白使用 Cursor 开发第一个微信小程序&#xff08…...

vue2项目修改浏览器显示的网页图标

1.准备一个新的图标文件&#xff0c;通常是. ico格式&#xff0c;也可以是. Png、. Svg等格式 2.将新的图标文件(例如&#xff1a;faviconAt.png)放入项目的public文件夹中。如下图 public文件夹中的所有文件都会在构建时原样复制到最终的输出目录(通常是dist) 3. 修改vue项目…...

spring boot3.4.3+MybatisPlus3.5.5+swagger-ui2.7.0

使用 MyBatis-Plus 操作 books 表。我们将实现以下功能&#xff1a; 创建实体类 Book。 创建 Mapper 接口 BookMapper。 创建 Service 层 BookService 和 BookServiceImpl。 创建 Controller 层 BookController。 配置 MyBatis-Plus 和数据库连接。 1. 项目结构 src ├─…...

【网络安全工程】任务10:三层交换机配置

CSDN 原创主页&#xff1a;不羁https://blog.csdn.net/2303_76492156?typeblog三层交换机是指在OSI&#xff08;开放系统互连&#xff09;模型中的第三层网络层提供路由功能的交换机。它不仅具备二层交换机的交换功能&#xff0c;还能实现路由功能&#xff0c;提供更为灵活的网…...

侯捷 C++ 课程学习笔记:C++内存管理机制

内存管理从平地到万丈高楼 内存管理入门&#xff08;Memory Management 101&#xff09; 需要具有动态分配并使用memory&#xff08;存储&#xff08;器&#xff09;&#xff0c;&#xff08;计算机的&#xff09;内存&#xff09;&#xff0c;使用过C标准库的容器&#xff0…...

JVM常用概念之本地内存跟踪

问题 Java应用启动或者运行过程中报“内存不足&#xff01;”&#xff0c;我们该怎么办? 基础知识 对于一个在本地机器运行的JVM应用而言&#xff0c;需要足够的内存来存储机器代码、堆元数据、类元数据、内存分析等数据结构&#xff0c;来保证JVM应用的成功启动以及未来平…...

【鸿蒙开发】Hi3861学习笔记- 软件定时器示例

00. 目录 文章目录 00. 目录01. 定时器概述02. 定时器API03. 定时器常用API3.1 osTimerNew3.2 osTimerDelete3.3 osTimerStart3.4 osTimerStop 04. 程序示例05. 附录 01. 定时器概述 软件定时器&#xff0c;是基于系统Tick时钟中断且由软件来模拟的定时器&#xff0c;当经过设…...

在Html5中仿Matlab自定义色带生成实践

目录 前言 一、RGB的相关知识 1、RGB的基本原理 2、RGB的数值表示 3、应用场景 二、ColorMap生成实战 1、外部库介绍 2、相关API 3、实例生成 三、总结 前言 在现代网页开发与数据可视化领域&#xff0c;色彩的表现力对于信息传达和视觉体验起着至关重要的作用。色带&…...

贪心算法--

1.柠檬水找零 link:860. 柠檬水找零 - 力扣&#xff08;LeetCode&#xff09; code class Solution { public:bool lemonadeChange(vector<int>& bills) {// 贪心算法&#xff0c; 优先花出大面额bill&#xff0c; 尽可能保护小面额billint five 0, ten 0;// 不…...

【学习方法一】

学习方法一 一、通用高效学习法二、学科专项方法三、工具与技术辅助四、习惯与心理策略五、避免常见误区总结六、进阶学习策略七、解决学习痛点八、场景化学习法九、资源与工具推荐十、个性化学习调整十一、长期学习心态十二、常见问题QA十三、应对特殊挑战的学习法十四、健康与…...

k8s启动时calico-kube-controllers与coredns组件一直是pending状态

症状&#xff1a; k8s执行kubectl get po -n kube-system时&#xff0c;以下组件一直>是pending状态&#xff1a; calico-kube-controllerscoredns 当执行 kubectl get po -n kube-system 发现 calico-kube-controllers 和 coredns 一直处于 Pending 状态时&#xff0c;通常…...

ngx_openssl_create_conf

ngx_openssl_create_conf 声明在 src\event\ngx_event_openssl.c static void *ngx_openssl_create_conf(ngx_cycle_t *cycle); 定义在 src\event\ngx_event_openssl.c static void * ngx_openssl_create_conf(ngx_cycle_t *cycle) {ngx_openssl_conf_t *oscf;oscf ngx_…...

如何选择国产串口屏?

目录 1、迪文 2、淘晶驰 3、广州大彩 4、金玺智控 5、欣瑞达 6、富莱新 7、冠显 8、有彩 串口屏&#xff0c;顾名思义&#xff0c;就是通过串口通信接口&#xff08;如RS232、RS485、TTL UART等&#xff09;与主控设备进行通信的显示屏。其核心功能是显示信息和接收输入…...

matlab慕课学习3.1

3.1顺序结构程序 于20250306 3.1.1程序和程序设计 程序是用某种计算机能够理解并且能够执行的语言来描述的解决问题的方法和步骤。 3.1.2程序的三种基本结构 1.顺序结构 2.选择结构 3.循环结构 3.1.3脚本文件和函数文件 脚本文件是可在命令行窗口直接执行的文件&#xff0…...

EB-Cable许可管理系统的功能和特点

在数字化时代&#xff0c;软件许可管理已成为企业日常运营中不可或缺的一部分。EB-Cable许可管理系统作为一款专为电缆管理而设计的软件解决方案&#xff0c;为企业提供了全面、高效且灵活的许可管理功能。本文将详细介绍EB-Cable许可管理系统的功能和特点&#xff0c;帮助您快…...

cesium地图设置3d,2d,2.5d动态切换

通过修改cesium实例vw的scene的显示模式&#xff0c;来切换最终的显示模式。 Cesium.SceneMode总共有四个变量值&#xff0c;分别如下&#xff1a;NameTypeDescriptionMORPHINGnumber在3d与2d之间切换变体 between mode, e.g., 3D to 2D.COLUMBUS_VIEWnumber2.5d模式&#xff0…...

Mac如何查看 IDEA 的日志文件

在 macOS 上&#xff0c;IntelliJ IDEA 的日志文件通常存储在用户目录下的 .IntelliJIdea<版本号> 文件夹中。以下是查看日志文件的具体步骤&#xff1a; 1. 找到日志文件的位置 日志文件通常位于以下路径&#xff1a; ~/Library/Logs/IntelliJIdea<版本号> 其…...

linux 软件安装(下)

七、ElasticSearch安装 官网地址&#xff1a;Elasticsearch&#xff1a;官方分布式搜索和分析引擎 | Elastic ​ 官网下载地址&#xff1a;Past Releases of Elastic Stack Software | Elastic 7.1、linux安装 1、上传安装包 altp # 打开sftp窗口 # 上传es安装包 put e:/sof…...

MongoDB 自动化部署

部署在容器中&#xff0c;并且自动创建所需用户和权限等 # 启动 mongoDBsudo docker run -dit --name china_fish_mongo \ -p 27017:27017 \ -v /data/project1/db/mongo/config/mongod.conf:/etc/mongod.conf \ -v /data/project1/db/mongo/data:/data/db \ -v /data/project1…...

程序化广告知识入门与Python基础数据处理实践

程序化广告知识入门与Python基础数据处理实践 大家好&#xff01;我写这一系列博客的初衷是想和大家一起学习进步。在技术飞速发展的今天&#xff0c;数据处理能力愈发重要&#xff0c;Python作为强大的数据处理工具&#xff0c;掌握它能为我们的职业发展和技术提升带来极大帮…...

【数据结构】二叉搜索树、平衡搜索树、红黑树

二叉搜索树&#xff08;Binary Search Tree&#xff09; 二叉搜索树是一种特殊的二叉树&#xff0c;它用来快速搜索某个值&#xff0c;对于每个节点都应该满足以下条件&#xff1a; 若该节点有左子树&#xff0c;那么左子树中所有节点的值都应该小于该节点的值。若该节点有右…...

密码学(终极版)

加密 & 解密 备注&#xff1a;密码学领域不存在完全不能破解的密码&#xff0c;但是如果一个密码需要很久很久&#xff0c;例如一万年才能破解&#xff0c;就认为这个密码是安全的了。 对称加密 非对称加密 公钥加密、私钥解密 私钥签名、公钥认证 非对称的底层原理是…...

经销商管理系统选型解析:8款产品详评

本文主要介绍了以下8款经销商管理系统&#xff1a;1.纷享销客&#xff1b; 2.用友T6经销商管理系统&#xff1b; 3.金蝶经销商管理系统&#xff1b; 4.鼎捷经销商管理系统&#xff1b; 5.浪潮经销商管理系统&#xff1b; 6.销售易&#xff1b; 7.SAP Business One Distributor …...