SpringBoot中的可扩展接口

目录

# 背景

# 可扩展的接口启动调用顺序图

# ApplicationContextInitializer

# BeanDefinitionRegistryPostProcessor

# BeanFactoryPostProcessor

# InstantiationAwareBeanPostProcessor

# SmartInstantiationAwareBeanPostProcessor

# BeanFactoryAware

# ApplicationContextAwareProcessor

# BeanNameAware

# @PostConstruct

# InitializingBean

# FactoryBean

# SmartInitializingSingleton

# CommandLineRunner

# DisposableBean

# ApplicationListener

# 最后


# 背景

Spring的核心思想就是容器,当容器refresh的时候,外部看上去风平浪静,其实内部则是一片惊涛骇浪,汪洋一片。Springboot更是封装了Spring,遵循约定大于配置,加上自动装配的机制。很多时候我们只要引用了一个依赖,几乎是零配置就能完成一个功能的装配。

我非常喜欢这种自动装配的机制,所以在自己开发中间件和公共依赖工具的时候也会用到这个特性。让使用者以最小的代价接入。想要把自动装配玩的转,就必须要了解spring对于bean的构造生命周期以及各个扩展接口。当然了解了bean的各个生命周期也能促进我们加深对spring的理解。业务代码也能合理利用这些扩展点写出更加漂亮的代码。

在网上搜索spring扩展点,发现很少有博文说的很全的,只有一些常用的扩展点的说明。

所以在这篇文章里,我总结了几乎Spring & Springboot所有的扩展接口,以及各个扩展点的使用场景。并且整理出了一个bean在spring内部从被加载到最后初始化完成所有可扩展点的顺序调用图。从而我们也能窥探到bean是如何一步步加载到spring容器中的。

# 可扩展的接口启动调用顺序图

以下是我整理的spring容器中Bean的生命周期内所有可扩展的点的调用顺序,下面会一个个分析

图片

 

# ApplicationContextInitializer

org.springframework.context.ApplicationContextInitializer

这是整个spring容器在刷新之前初始化ConfigurableApplicationContext的回调接口,简单来说,就是在容器刷新之前调用此类的initialize方法。这个点允许被用户自己扩展。用户可以在整个spring容器还没被初始化之前做一些事情。

可以想到的场景可能为,在最开始激活一些配置,或者利用这时候class还没被类加载器加载的时机,进行动态字节码注入等操作。

扩展方式为:

 
public class TestApplicationContextInitializer implements ApplicationContextInitializer {    @Override    public void initialize(ConfigurableApplicationContext applicationContext) {        System.out.println("[ApplicationContextInitializer]");    }}

因为这时候spring容器还没被初始化,所以想要自己的扩展的生效,有以下三种方式:

  • 在启动类中用springApplication.addInitializers(new TestApplicationContextInitializer())语句加入

  • 配置文件配置context.initializer.classes=com.example.demo.TestApplicationContextInitializer

  • Spring SPI扩展,在spring.factories中加入org.springframework.context.ApplicationContextInitializer=com.example.demo.TestApplicationContextInitializer

# BeanDefinitionRegistryPostProcessor

org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor

这个接口在读取项目中的beanDefinition之后执行,提供一个补充的扩展点

使用场景:你可以在这里动态注册自己的beanDefinition,可以加载classpath之外的bean

扩展方式为:

 
public class TestBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {    @Override    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {        System.out.println("[BeanDefinitionRegistryPostProcessor] postProcessBeanDefinitionRegistry");    }
    @Override    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {        System.out.println("[BeanDefinitionRegistryPostProcessor] postProcessBeanFactory");    }}

# BeanFactoryPostProcessor

org.springframework.beans.factory.config.BeanFactoryPostProcessor

这个接口是beanFactory的扩展接口,调用时机在spring在读取beanDefinition信息之后,实例化bean之前。

在这个时机,用户可以通过实现这个扩展接口来自行处理一些东西,比如修改已经注册的beanDefinition的元信息。

扩展方式为:

 
public class TestBeanFactoryPostProcessor implements BeanFactoryPostProcessor {    @Override    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {        System.out.println("[BeanFactoryPostProcessor]");    }}

# InstantiationAwareBeanPostProcessor

org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor

该接口继承了BeanPostProcess接口,区别如下:

<font color="red">BeanPostProcess接口只在bean的初始化阶段进行扩展(注入spring上下文前后),而InstantiationAwareBeanPostProcessor接口在此基础上增加了3个方法,把可扩展的范围增加了实例化阶段和属性注入阶段。</font>

该类主要的扩展点有以下5个方法,主要在bean生命周期的两大阶段:<font color=red>实例化阶段</font>和<font color=red>初始化阶段</font>,下面一起进行说明,按调用顺序为:

  • postProcessBeforeInstantiation:实例化bean之前,相当于new这个bean之前

  • postProcessAfterInstantiation:实例化bean之后,相当于new这个bean之后

  • postProcessPropertyValues:bean已经实例化完成,在属性注入时阶段触发,@Autowired,@Resource等注解原理基于此方法实现

  • postProcessBeforeInitialization:初始化bean之前,相当于把bean注入spring上下文之前

  • postProcessAfterInitialization:初始化bean之后,相当于把bean注入spring上下文之后

使用场景:这个扩展点非常有用 ,无论是写中间件和业务中,都能利用这个特性。比如对实现了某一类接口的bean在各个生命期间进行收集,或者对某个类型的bean进行统一的设值等等。

扩展方式为:

 
public class TestInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
    @Override    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {        System.out.println("[TestInstantiationAwareBeanPostProcessor] before initialization " + beanName);        return bean;    }
    @Override    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {        System.out.println("[TestInstantiationAwareBeanPostProcessor] after initialization " + beanName);        return bean;    }
    @Override    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {        System.out.println("[TestInstantiationAwareBeanPostProcessor] before instantiation " + beanName);        return null;    }
    @Override    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {        System.out.println("[TestInstantiationAwareBeanPostProcessor] after instantiation " + beanName);        return true;    }
    @Override    public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {        System.out.println("[TestInstantiationAwareBeanPostProcessor] postProcessPropertyValues " + beanName);        return pvs;    }

# SmartInstantiationAwareBeanPostProcessor

org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor

该扩展接口有3个触发点方法:

  • predictBeanType:该触发点发生在postProcessBeforeInstantiation之前(在图上并没有标明,因为一般不太需要扩展这个点),这个方法用于预测Bean的类型,返回第一个预测成功的Class类型,如果不能预测返回null;当你调用BeanFactory.getType(name)时当通过bean的名字无法得到bean类型信息时就调用该回调方法来决定类型信息。

  • determineCandidateConstructors:该触发点发生在postProcessBeforeInstantiation之后,用于确定该bean的构造函数之用,返回的是该bean的所有构造函数列表。用户可以扩展这个点,来自定义选择相应的构造器来实例化这个bean。

  • getEarlyBeanReference:该触发点发生在postProcessAfterInstantiation之后,当有循环依赖的场景,当bean实例化好之后,为了防止有循环依赖,会提前暴露回调方法,用于bean实例化的后置处理。这个方法就是在提前暴露的回调方法中触发。

扩展方式为:

 
public class TestSmartInstantiationAwareBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor {
    @Override    public Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException {        System.out.println("[TestSmartInstantiationAwareBeanPostProcessor] predictBeanType " + beanName);        return beanClass;    }
    @Override    public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException {        System.out.println("[TestSmartInstantiationAwareBeanPostProcessor] determineCandidateConstructors " + beanName);        return null;    }
    @Override    public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {        System.out.println("[TestSmartInstantiationAwareBeanPostProcessor] getEarlyBeanReference " + beanName);        return bean;    }}

# BeanFactoryAware

org.springframework.beans.factory.BeanFactoryAware

这个类只有一个触发点,发生在bean的实例化之后,注入属性之前,也就是Setter之前。这个类的扩展点方法为setBeanFactory,可以拿到BeanFactory这个属性。

使用场景为,你可以在bean实例化之后,但还未初始化之前,拿到 BeanFactory,在这个时候,可以对每个bean作特殊化的定制。也或者可以把BeanFactory拿到进行缓存,日后使用。

扩展方式为:

 
public class TestBeanFactoryAware implements BeanFactoryAware {    @Override    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {        System.out.println("[TestBeanFactoryAware] " + beanFactory.getBean(TestBeanFactoryAware.class).getClass().getSimpleName());    }}

# ApplicationContextAwareProcessor

org.springframework.context.support.ApplicationContextAwareProcessor

该类本身并没有扩展点,但是该类内部却有6个扩展点可供实现 ,这些类触发的时机在bean实例化之后,初始化之前

图片

可以看到,该类用于执行各种驱动接口,在bean实例化之后,属性填充之后,通过执行以上红框标出的扩展接口,来获取对应容器的变量。所以这里应该来说是有6个扩展点,这里就放一起来说了

  • EnvironmentAware:用于获取EnviromentAware的一个扩展类,这个变量非常有用, 可以获得系统内的所有参数。当然个人认为这个Aware没必要去扩展,因为spring内部都可以通过注入的方式来直接获得。

  • EmbeddedValueResolverAware:用于获取StringValueResolver的一个扩展类, StringValueResolver用于获取基于String类型的properties的变量,一般我们都用@Value的方式去获取,如果实现了这个Aware接口,把StringValueResolver缓存起来,通过这个类去获取String类型的变量,效果是一样的。

  • ResourceLoaderAware:用于获取ResourceLoader的一个扩展类,ResourceLoader可以用于获取classpath内所有的资源对象,可以扩展此类来拿到ResourceLoader对象。

  • ApplicationEventPublisherAware:用于获取ApplicationEventPublisher的一个扩展类,ApplicationEventPublisher可以用来发布事件,结合ApplicationListener来共同使用,下文在介绍ApplicationListener时会详细提到。这个对象也可以通过spring注入的方式来获得。

  • MessageSourceAware:用于获取MessageSource的一个扩展类,MessageSource主要用来做国际化。

  • ApplicationContextAware:用来获取ApplicationContext的一个扩展类,ApplicationContext应该是很多人非常熟悉的一个类了,就是spring上下文管理器,可以手动的获取任何在spring上下文注册的bean,我们经常扩展这个接口来缓存spring上下文,包装成静态方法。同时ApplicationContext也实现了BeanFactory,MessageSource,ApplicationEventPublisher等接口,也可以用来做相关接口的事情。

# BeanNameAware

org.springframework.beans.factory.BeanNameAware

可以看到,这个类也是Aware扩展的一种,触发点在bean的初始化之前,也就是postProcessBeforeInitialization之前,这个类的触发点方法只有一个:setBeanName

使用场景为:用户可以扩展这个点,在初始化bean之前拿到spring容器中注册的的beanName,来自行修改这个beanName的值。

扩展方式为:

 
public class NormalBeanA implements BeanNameAware{    public NormalBeanA() {        System.out.println("NormalBean constructor");    }
    @Override    public void setBeanName(String name) {        System.out.println("[BeanNameAware] " + name);    }}

# @PostConstruct

javax.annotation.PostConstruct

这个并不算一个扩展点,其实就是一个标注。其作用是在bean的初始化阶段,如果对一个方法标注了@PostConstruct,会先调用这个方法。这里重点是要关注下这个标准的触发点,这个触发点是在postProcessBeforeInitialization之后,InitializingBean.afterPropertiesSet之前。

使用场景:用户可以对某一方法进行标注,来进行初始化某一个属性

扩展方式为:

 
public class NormalBeanA {    public NormalBeanA() {        System.out.println("NormalBean constructor");    }
    @PostConstruct    public void init(){        System.out.println("[PostConstruct] NormalBeanA");    }}

# InitializingBean

org.springframework.beans.factory.InitializingBean

这个类,顾名思义,也是用来初始化bean的。InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候都会执行该方法。这个扩展点的触发时机在postProcessAfterInitialization之前。

使用场景:用户实现此接口,来进行系统启动的时候一些业务指标的初始化工作。

扩展方式为:

 
public class NormalBeanA implements InitializingBean{    @Override    public void afterPropertiesSet() throws Exception {        System.out.println("[InitializingBean] NormalBeanA");    }}

# FactoryBean

org.springframework.beans.factory.FactoryBean

一般情况下,Spring通过反射机制利用bean的class属性指定支线类去实例化bean,在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在bean中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。FactoryBean接口对于Spring框架来说占用重要的地位,Spring自身就提供了70多个FactoryBean的实现。它们隐藏了实例化一些复杂bean的细节,给上层应用带来了便利。从Spring3.0开始,FactoryBean开始支持泛型,即接口声明改为FactoryBean<T>的形式

使用场景:用户可以扩展这个类,来为要实例化的bean作一个代理,比如为该对象的所有的方法作一个拦截,在调用前后输出一行log,模仿ProxyFactoryBean的功能。

扩展方式为:

 
public class TestFactoryBean implements FactoryBean<TestFactoryBean.TestFactoryInnerBean> {
    @Override    public TestFactoryBean.TestFactoryInnerBean getObject() throws Exception {        System.out.println("[FactoryBean] getObject");        return new TestFactoryBean.TestFactoryInnerBean();    }
    @Override    public Class<?> getObjectType() {        return TestFactoryBean.TestFactoryInnerBean.class;    }
    @Override    public boolean isSingleton() {        return true;    }
    public static class TestFactoryInnerBean{
    }}

# SmartInitializingSingleton

org.springframework.beans.factory.SmartInitializingSingleton

这个接口中只有一个方法afterSingletonsInstantiated,其作用是是 在spring容器管理的所有单例对象(非懒加载对象)初始化完成之后调用的回调接口。其触发时机为postProcessAfterInitialization之后。

使用场景:用户可以扩展此接口在对所有单例对象初始化完毕后,做一些后置的业务处理。

扩展方式为:

 
public class TestSmartInitializingSingleton implements SmartInitializingSingleton {    @Override    public void afterSingletonsInstantiated() {        System.out.println("[TestSmartInitializingSingleton]");    }}

# CommandLineRunner

org.springframework.boot.CommandLineRunner

这个接口也只有一个方法:run(String... args),触发时机为整个项目启动完毕后,自动执行。如果有多个CommandLineRunner,可以利用@Order来进行排序。

使用场景:用户扩展此接口,进行启动项目之后一些业务的预处理。

扩展方式为:

 
public class TestCommandLineRunner implements CommandLineRunner {
    @Override    public void run(String... args) throws Exception {        System.out.println("[TestCommandLineRunner]");    }}

# DisposableBean

org.springframework.beans.factory.DisposableBean

这个扩展点也只有一个方法:destroy(),其触发时机为当此对象销毁时,会自动执行这个方法。比如说运行applicationContext.registerShutdownHook时,就会触发这个方法。

扩展方式为:

 
public class NormalBeanA implements DisposableBean {    @Override    public void destroy() throws Exception {        System.out.println("[DisposableBean] NormalBeanA");    }}

# ApplicationListener

org.springframework.context.ApplicationListener

准确的说,这个应该不算spring&springboot当中的一个扩展点,ApplicationListener可以监听某个事件的event,触发时机可以穿插在业务方法执行过程中,用户可以自定义某个业务事件。但是spring内部也有一些内置事件,这种事件,可以穿插在启动调用中。我们也可以利用这个特性,来自己做一些内置事件的监听器来达到和前面一些触发点大致相同的事情。

接下来罗列下spring主要的内置事件:

  • ContextRefreshedEvent

    ApplicationContext 被初始化或刷新时,该事件被发布。这也可以在ConfigurableApplicationContext接口中使用 refresh()方法来发生。此处的初始化是指:所有的Bean被成功装载,后处理Bean被检测并激活,所有Singleton Bean 被预实例化,ApplicationContext容器已就绪可用。

  • ContextStartedEvent

    当使用 ConfigurableApplicationContext (ApplicationContext子接口)接口中的 start() 方法启动 ApplicationContext时,该事件被发布。你可以调查你的数据库,或者你可以在接受到这个事件后重启任何停止的应用程序。

  • ContextStoppedEvent

    当使用 ConfigurableApplicationContext接口中的 stop()停止ApplicationContext 时,发布这个事件。你可以在接受到这个事件后做必要的清理的工作

  • ContextClosedEvent

    当使用 ConfigurableApplicationContext接口中的 close()方法关闭 ApplicationContext 时,该事件被发布。一个已关闭的上下文到达生命周期末端;它不能被刷新或重启

  • RequestHandledEvent

    这是一个 web-specific 事件,告诉所有 bean HTTP 请求已经被服务。只能应用于使用DispatcherServlet的Web应用。在使用Spring作为前端的MVC控制器时,当Spring处理用户请求结束后,系统会自动触发该事件

# 最后

我们从这些spring&springboot的扩展点当中,大致可以窥视到整个bean的生命周期。在业务开发或者写中间件业务的时候,可以合理利用spring提供给我们的扩展点,在spring启动的各个阶段内做一些事情。以达到自定义初始化的目的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/80302.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ARM体系结构学习笔记:CPU并不直接访问内存

CPU并不直接访问内存 原因: 寄存器可以更快的进行访问存取指令集 LDR STR 寻址模式 Pre-index [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KFSzzeZB-1692235692358)(https://cdn.jsdelivr.net/gh/nzcv/picgo/20220117071415.png)] Post-index …

Docker资源控制

目录 一、CPU 资源控制 1.设置CPU使用率上限 2.设置CPU资源占用比&#xff08;设置多个容器时才有效&#xff09; 3.设置容器绑定指定的CPU 二、对内存使用的限制 三、对磁盘IO配额控制&#xff08;blkio&#xff09;的限制 一、CPU 资源控制 cgroups&#xff0c;是一个非常强…

Spring源码深度解析二(AOP)

书接上文 9. AOP源码深度剖析 概述 AOP&#xff08;Aspect Orient Programming&#xff09;&#xff1a;面向切面编程&#xff1b; 用途&#xff1a;用于系统中的横切关注点&#xff0c;比如日志管理&#xff0c;事务管理&#xff1b; 实现&#xff1a;利用代理模式&#x…

【实战】十一、看板页面及任务组页面开发(二) —— React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(二十四)

文章目录 一、项目起航&#xff1a;项目初始化与配置二、React 与 Hook 应用&#xff1a;实现项目列表三、TS 应用&#xff1a;JS神助攻 - 强类型四、JWT、用户认证与异步请求五、CSS 其实很简单 - 用 CSS-in-JS 添加样式六、用户体验优化 - 加载中和错误状态处理七、Hook&…

2023+HuggingGPT: Solving AI Tasks with ChatGPT and itsFriends in Hugging Face

摘要&#xff1a; 语言是llm(例如ChatGPT)连接众多AI模型(例如hugs Face)的接口&#xff0c;用于解决复杂的AI任务。在这个概念中&#xff0c;llms作为一个控制器&#xff0c;管理和组织专家模型的合作。LLM首先根据用户请求规划任务列表&#xff0c;然后为每个任务分配专家模…

LeetCode 热题 100(四):48. 旋转图像、240. 搜索二维矩阵 II、234. 回文链表

一.48. 旋转图像 题目要求&#xff1a;就是一个顺时针的旋转过程。 思路&#xff1a;观察矩阵&#xff0c;得出翻转前第i行的第J个元素 等于 翻转后倒数第i列的第J个元素&#xff0c;举例说明&#xff0c;第1行第2个元素为“2”&#xff0c;翻转后到了 倒数第1列的第2个元素…

git 回滚相关问题

原本用as自带的git执行回滚任务&#xff0c; 但是提交之后发现并没有成功&#xff0c; 后面通过命令行的方式重新回滚并且提交上去&#xff0c;就可以了 说明as的git还是有点小瑕疵&#xff0c;还是命令行最稳妥 相关博文&#xff1a; git代码回滚操作_imkaifan的博客-CSDN博…

数据采集器丨高效传输、集中管理

随时科技的发展&#xff0c;数据采集器成为各行各业中的常用重要仪器&#xff0c;为人们对各种不同数据的采集处理&#xff0c;提供了无可比拟的便利。 数据采集器能够接入不同种类的RS485设备&#xff0c;洞察世间万象&#xff0c;探测大千世界的细微脉动&#xff0c;并将监测…

SNMP简单介绍

SNMP SNMP是广泛应用于TCP/IP网络的网络管理标准协议&#xff0c;该协议能够支持网络管理系统&#xff0c;用以监测连接到网络上的设备是否有任何引起管理上关注的情况。SNMP采用轮询机制&#xff0c;提供最基本的功能集&#xff0c;适合小型、快速、低价格的环境使用&#xf…

Springboot 实践(3)配置DataSource及创建数据库

前文讲述了利用MyEclipse2019开发工具&#xff0c;创建maven工程、加载springboot、swagger-ui功能。本文讲述创建数据库&#xff0c;为项目配置数据源&#xff0c;实现数据的增删改查服务&#xff0c;并通过swagger-ui界面举例调试服务控制器 创建数据库 项目使用MySQL 8.0.…

感觉和身边其他人有差距怎么办?

虽然清楚知识需要靠时间沉淀&#xff0c;但在看到自己做不出来的题别人会做&#xff0c;自己写不出的代码别人会写时还是会感到焦虑怎么办&#xff1f; 你是否也因为自身跟周围人的差距而产生过迷茫&#xff0c;这份迷茫如今是被你克服了还是仍旧让你感到困扰&#xff1f; 下…

关于docker-compose up -d在文件下无法运行的原因以及解决方法

一、确认文件下有docker-compose.yml文件 二、解决方法 检查 Docker 服务是否运行&#xff1a; 使用以下命令检查 Docker 服务是否正在运行&#xff1a; systemctl status docker 如果 Docker 未运行&#xff0c;可以使用以下命令启动它&#xff1a; systemctl start docker …

【C语言】字符串函数的介绍二( strcmp、strncpy、strncat、strncmp)

前言 上篇文章我们介绍了strlen、strcpy、stract这三个函数&#xff0c;接下来我们会学习新的函数&#xff0c;话不多说&#xff0c;让我们直接开始吧 上一篇文章 strcmp 引入&#xff1a; 在进行字符串比较时&#xff0c;不可以直接使用符号&#xff0c; 这是在比较二者的…

五、修改官方FreeRTOS例程(STM32F1)

1、官方源码下载 (1)进入FreeRTOS官网&#xff1a;FreeRTOS官网 (2)下载FreeRTOS。(选择带示例的下载) 2、删减目录 (1)下载后解压的FreeRTOS文件如下图所示。 (2)删除下图中红框勾选的文件。 FreeRTOS-Plus&#xff0c;FreeRTOS的生态文件&#xff0c;非必需的。tools&…

Docker vs. Kubernetes:选择合适的场景

在决定使用 Docker 还是 Kubernetes 之前&#xff0c;让我们看看一些实际的场景&#xff0c;以便更好地理解它们的适用性。 使用 Docker 的场景 假设您正在开发一个微服务应用程序&#xff0c;其中每个微服务都需要一些特定的依赖项和环境。在这种情况下&#xff0c;Docker 是一…

C语言中常见的一些语法概念和功能

常用代码&#xff1a; 程序入口&#xff1a;int main() 函数用于定义程序的入口点。 输出&#xff1a;使用 printf() 函数可以在控制台打印输出。 输入&#xff1a;使用 scanf() 函数可以接收用户的输入。 条件判断&#xff1a;使用 if-else 语句可以根据条件执行不同的代码…

Spring-4-掌握Spring事务传播机制

今日目标 能够掌握Spring事务配置 Spring事务管理 1 Spring事务简介【重点】 1.1 Spring事务作用 事务作用&#xff1a;在数据层保障一系列的数据库操作同成功同失败 Spring事务作用&#xff1a;在数据层或业务层保障一系列的数据库操作同成功同失败 1.2 案例分析Spring…

从零开始,快速打造租车服务小程序的分享

随着移动互联网的发展&#xff0c;小程序成为了企业推广和服务的重要手段之一。租车服务行业也不例外&#xff0c;通过打造一款租车服务小程序&#xff0c;企业可以更好地与用户进行互动和交流&#xff0c;提供更方便快捷的租车服务。本文将介绍如何利用第三方制作平台/工具快速…

Linux/centos上如何配置管理NFS服务器?

Linux/centos上如何配置管理NFS服务器&#xff1f; 1 NFS基础了解1.1 NFS概述1.2 NFS工作流程 2 安装和启动NFS服务2.1 安装NFS服务器2.2 启动NFS服务 3 配置NFS服务器和客户端3.1 配置NFS服务器3.2 配置NFS客户端 4 实际示例4.1 基本要求4.2 案例实现 1 NFS基础了解 NFS&…

openGauss学习笔记-44 openGauss 高级数据管理-存储过程

文章目录 openGauss学习笔记-44 openGauss 高级数据管理-存储过程44.1 语法格式44.2 参数说明44.3 示例 openGauss学习笔记-44 openGauss 高级数据管理-存储过程 存储过程是能够完成特定功能的SQL语句集。用户可以进行反复调用&#xff0c;从而减少SQL语句的重复编写数量&…