【Spring】(四)Bean 的作用域和生命周期

文章目录

  • 前言
  • 一、Bean 的作用域
    • 1.1 被修改的 Bean 案例
    • 1.2 作用域的定义
    • 1.3 Bean 的六种作用域
    • 1.4 Bean 作用域的设置
  • 二、Spring 的执行流程 和 Bean 的生命周期
    • 2.1 Spring 的执行流程
    • 2.2 Bean 的生命周期
    • 2.3 Bean 生命周期的演示


前言

Bean 是 Spring 框架中的一个核心概念,它是指由 Spring 容器管理的对象实例。在使用 Spring 进行开发时,我们通常会定义各种各样的 Bean,用于承载应用程序的不同功能和组件。然而,很多开发者可能只关注了 Bean 的定义和使用方式,而忽略了 Bean 的作用域和生命周期,这两者对于一个应用程序的性能、稳定性和可维护性都至关重要。

在本文中,我将深入讨论 Bean 的作用域和生命周期,并解释它们对于 Spring 应用程序的影响。我会尽量用简单明了的语言来阐述这些概念,以便读者能够轻松理解和应用到自己的开发实践中。

一、Bean 的作用域

初次看到 Bean 的作用域这个名词,可能会令我们一头雾水,但是没关系,接下来的一个简单案例将会告诉我们什么是 Bean 的作用域。

1.1 被修改的 Bean 案例

现在有一个公共的 Bean对象,可以供 A 和 B 两个用户使用,但是 A 在使用这个 Bean 对象的时候却悄悄的对这个 Bean 的数据进行了修改,那么会不会导致 B 用户在使用这个 Bean 对象的时候发生预期之外的结果呢?

创建一个 UserBean 的类,它的作用是通过 @Bean 注解的方式将 User 对象存储到 Spring 容器中,其中 User 包含一个 idname 属性:

@Component
public class UserBeans {
    @Bean
    public User getUser(){
        User user = new User();
        user.setId(123);
        user.setName("张三");
        return user;
    }
}

然后创建一个 UserController1 类,通过 @Controller 注解将其存储到 Spring 容器中,然后使用属性注解获取 Spring 容器中的 Bean 对象。另外创建一个 printUser 方法,里面创建一个临时引用 myUser 指向 Bean 对象,然后对这个 Bean 的数据进行修改:

@Controller
public class UserController1 {

    @Autowired
    private User user;

    public void printUser(){
        System.out.println("user: " +  user);

        User myUser = user;

        myUser.setName("李四");
        System.out.println("myUser: " + myUser);
        System.out.println("user: " +  user);
    }
}

另外再创建一个 UserController2,同样使用 @Controller 注解将其存储到 Spring 容器中,然后使用属性注解获取 Spring 容器中的 Bean 对象。另外创建一个 printUser 方法,只打印获取到的 Bean 对象:

@Controller
public class UserController2 {

	@Resource
    private User user;
    
    public void printUser(){
        System.out.println("UserController2: user -> " +  user);
    }
}

在启动类中的 main 方法分别通过 ApplicationContext 获取 UserController1UserController2,然后分别执行其中的方法,观察修改 Bean 对象后产生的影响。

public static void main(String[] args) {
   ApplicationContext context
           = new ClassPathXmlApplicationContext("spring-config.xml");

   UserController1 userController1
           = context.getBean("userController1", UserController1.class);

   UserController2 userController2
           = context.getBean("userController2", UserController2.class);

   userController1.printUser();
   System.out.println("=========");
   userController2.printUser();
}

执行的结果如下:

从运行的结果来看,当 UserController1 修改了 Bean 在数据,此时通过 UserController2 获取的 Bean 的数据也被修改了,那么就说明一个 Bean 在 Spring 中的储存只有一份,并且是单例模式的。因此 Spring 容器中的 Bean 的默认作用域就是单例模式(singleton)的

1.2 作用域的定义

作用域(Scope)是在编程中用于描述变量或标识符在程序中可访问的范围。换句话说,它规定了变量在哪些部分可以被引用和使用。作用域是一个重要的概念,因为它可以帮助程序员避免命名冲突和理解变量在代码中的生命周期

在 Spring 框架中,作用域(Scope)就是用来定义 Bean 对象的生命周期和可见性规则作用域决定了在不同的上下文环境中,Spring 容器如何管理和提供 Bean 对象。例如,在定义一个 Bean 的时候,我们可以指定其作用域,从而决定它在应用程序中的行为表现。

1.3 Bean 的六种作用域

Spring 容器在初始化一个 Bean 的实例的时候,同时会指定该实例的作用域,如果我们不修改要指定的作用域,Spring 就会默认指定一个默认的作用域。以下是 Spring 中的六种作用域,其中最后四种是基于 Spring MVC 生效的,因此本文先不讨论。

  1. 单例作用域(singleton):这是 Spring 容器默认的作用域。在整个应用程序的生命周期中,只会创建一个该类型的 Bean 实例,并且所有对该 Bean 的引用都会执行同一个对象。这种作用域适用于哪些无状态、线程安全的 Bean 对象,例如工具类、配置类等。

  2. 原型作用域(prototype):每次请求 Bean 对象的时候,Spring 容器都会创建一个新的 Bean 实例,因此在不同的请求中,得到的是不同的对象实例。原型作用域适用于那先状态较多,需要频繁创建和销毁的对象,比如某些与会话相关的 Bean。

  3. 请求作用域(request):请求作用域是在 Web 应用中常用的作用域。它表示每次 HTTP 请求都会创建一个新的 Bean 实例,该实例仅在当前请求的处理过程中有效。当请求结束后,该 Bean 会被销毁。这样的作用域通常用于存储和处理与单个请求相关的数据。

  4. 会话作用域(session):会话作用域是在 Web 应用中基于用户会话的作用域。每个 HTTP 会话(Session)对应一个 Bean 实例,该实例在整个会话的生命周期内有效。这种作用域适用于需要在整个会话期间保持状态的对象,比如用户登录信息等。

  5. 全局作用域(application):全局作用域是指在整个 Web 应用程序的生命周期内只创建一个 Bean 实例。该作用域的 Bean 在整个应用中可见,适用于那些在整个应用中需要共享的状态信息。

  6. HTTP WebSocket 作用域(websocket): HTTP WebSocket 作用域是基于 WebSocket 连接的作用域。每个 WebSocket 连接对应一个 Bean 实例,该实例在 WebSocket 连接的整个生命周期内有效。

单例作用域(singleton)和全局作用域(application)之间的区别:

单例作用域和全局作用域都只创建一个 Bean 对象,那么它们之间有什么区别呢?

1. 定义位置:

  • 单例作用域是 Spring Core 的一部分,在整个 Spring IoC 容器中生效。它适用于任何类型的 Spring 应用,不仅限于 Web 应用。
  • 全局作用域是 Spring Web 的一部分,在 Servlet 容器中生效。它是专门为 Web 应用设计的,通过 Spring Web 库提供支持。

2. 作用范围:

  • 单例作用域只保证在 Spring IoC 容器中,每个 Bean 只会有一个实例。在整个应用程序中,不同的 Spring IoC 容器可能会有不同的实例。
  • 全局作用域确保在整个 Web 应用程序中,每个 Bean 只会有一个实例。无论是在同一个 Servlet 容器中还是不同的 Servlet 容器中,都只有一个实例。

3. 应用场景:

  • 单例作用域适用于那些无状态、线程安全的 Bean,例如工具类、配置类等。由于单例 Bean 在整个应用中只有一个实例,因此在性能和资源利用方面有一定的优势。
  • 全局作用域适用于那些在整个 Web 应用中需要共享状态信息的 Bean,比如全局的配置对象、全局缓存等。通过全局作用域,我们可以确保这些对象在整个应用中只有一个实例,而不受多个 Servlet 容器的影响。

4. 管理容器:

  • 单例作用域的管理是 Spring IoC 容器负责的,它由 Spring Core 提供的 IoC 容器管理,与 Web 应用无关。
  • 全局作用域的管理需要由 Spring Web 库提供的 Servlet 容器来管理,它与 Web 应用的生命周期相关。

总结来说,单例作用域适用于整个 Spring IoC 容器,保证每个 Bean 在容器中只有一个实例;而全局作用域适用于整个 Web 应用,保证每个 Bean 在整个应用中只有一个实例。选择合适的作用域取决于具体的应用需求和设计考虑。

1.4 Bean 作用域的设置

在 Spring 中,设置 Bean 作用域可以通过两种方法:XML 配置和注解配置

1. XML 配置
在 XML 配置文件中的 <bean>标签中,可以使用 scope 属性来设置 Bean 的作用域。例如,对于原型作用域的 Bean,可以这样配置:

<bean id="myBean" class="com.spring.demo.MyBean" scope="prototype">
    <!-- Bean 的属性配置 -->
</bean>

其中,scope指定的是作用域的名称,如:prototype、singleton。

2. 注解配置

使用注解配置 Bean 的作用域更加的简洁。在 Spring 中,可以使用 @Scope 注解来指定 Bean 的作用域,例如对刚才的 UserBeans 类指定原型作用域:

@Component
public class UserBeans {
    @Bean(name = {"user1"})
    // @Scope("prototype") // 原型模式
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) // 使用全局变量设置
    public User getUser(){
        User user = new User();
        user.setId(123);
        user.setName("张三");
        return user;
    }
}

此时,@Scope中的参数可以说作用域的名称,如:prototype;也可以通过ConfigurableBeanFactory类来指定。ConfigurableBeanFactory类的源码:


可以发现ConfigurableBeanFactory类也是对作用域名的封装。

当作用域设置为原型模型的时候,再次运行刚才修改 Bean 内容的代码:

此时就能够发现,两个 UserController 获取到的都是一个全新的 Bean 对象,即使一个修改也不会对另一个造成影响。

二、Spring 的执行流程 和 Bean 的生命周期

2.1 Spring 的执行流程

Spring 执行流程:

  1. 启动 Spring 容器:

    • Spring 容器在启动时会读取配置文件或者扫描注解来获取 Bean 的定义信息。
  2. 实例化 Bean(分配内存空间,从无到有):

    • Spring 容器根据配置信息或者注解的定义,实例化 Bean 对象。实例化过程可能会涉及构造函数的调用和依赖对象的创建。
  3. Bean 注册到 Spring 容器(存操作):

    • 实例化后的 Bean 被注册到 Spring 容器中,容器将其纳入管理范围,并为每个 Bean 分配一个唯一的标识符(通常是 Bean 的名称或者 ID)。
  4. Bean 初始化(初始化操作):

    • 如果 Bean 的定义中配置了初始化方法(例如使用 init-method 属性或者 @PostConstruct 注解),Spring 容器会在实例化之后调用该初始化方法,用于执行一些初始化逻辑。
  5. 将 Bean 装配到需要的类中(取操作):

    • 当其他类需要使用某个 Bean 时,Spring 容器会根据依赖注入的配置,将对应的 Bean 自动注入到需要的类中。这样,其他类就可以直接使用该 Bean 的实例,而不需要关心 Bean 对象的创建和管理过程。
  6. 使用 Bean:

    • 现在,Bean 已经被装配到需要的类中,可以在其他类中直接使用它了。
  7. Bean 销毁(销毁操作):

    • 如果 Bean 的定义中配置了销毁方法(例如使用 destroy-method 属性或者 @PreDestroy 注解),Spring 容器会在容器关闭时调用该销毁方法,用于执行一些清理操作。

需要注意的是,Bean 的生命周期在 Spring 容器的控制下,开发者无需手动管理 Bean 的创建和销毁过程,这就是 Spring IoC(控制反转)的核心思想。通过依赖注入,我们能够更专注于业务逻辑的实现,而不需要关心对象的创建和管理。

2.2 Bean 的生命周期

Bean 的生命周期是指一个 Bean 实例从被创建到被销毁的整个过程。在 Spring 容器中,Bean 的生命周期主要包含以下阶段:

  1. 实例化:这个阶段 Spring 容器会根据配置信息和注解创建 Bean 实例。这是 “从无到有” 的过程,即分配内存空间并调用构造方法来创建 Bean 实例。

  2. 设置属性(Bean 的注入和装配):在实例化后,Spring 容器会根据配置文件或注解,将属性值注入到 Bean 实例中。这是依赖注入(Dependency Injection)的过程,通过属性或构造方法来设置 Bean 的属性值。

  3. Bean 初始化:在属性赋值完成后,Spring 容器会执行以下步骤来初始化 Bean:

    • 各种通知:如果 Bean 实现了相应的 Aware 接口,Spring 会通过回调方式通知 Bean 相应的状态,例如 BeanNameAwareBeanFactoryAwareApplicationContextAware 等。
    • 初始化前置方法:执行 BeanPostProcessor 初始化前置方法,如 postProcessBeforeInitialization
    • 初始化方法(XML方式和注解方式):如果 Bean 定义了初始化方法(@PostConstruct 注解或实现了 InitializingBean 接口),Spring 容器会在设置属性后调用该方法进行进一步的初始化。
    • 初始化后置方法:如果 Bean 定义了初始化后置方法,如 BeanPostProcessor 接口实现类的 postProcessAfterInitialization 方法,Spring 容器会在 Bean 初始化完成后调用该方法。
  4. 使用 Bean:在初始化完成后,Bean 实例就可以被应用程序使用了。它会被注入到其他类中,或者通过 Spring 容器获取并调用它的方法。

  5. 销毁 Bean 对象:在容器关闭或者程序结束时,Spring 容器会执行以下步骤来销毁 Bean:

    • 销毁前置方法:如果 Bean 定义了销毁前置方法(destroy-method),Spring 容器会在 Bean 销毁前调用该方法。
    • 销毁方法(XML方式和注解方式):如果 Bean 定义了销毁方法(@PreDestroy 注解或实现了 DisposableBean 接口),Spring 容器会在 Bean 销毁时调用该方法进行清理操作。

2.3 Bean 生命周期的演示

下面的代码演示了一个完整的 Bean 生命周期过程,并展示了 Spring 中 Bean 的各个阶段的回调方法。让我们来看一下 Bean 生命周期的执行流程:


@Component
public class BeanComponent implements BeanNameAware, BeanPostProcessor {
    @Override
    public void setBeanName(String s) {
        System.out.println("执行了通知,Bean name -> " + s);
    }

    // xml 的初始化方法
    public void myInit(){
        System.out.println("XML 方式初始化");
    }

    @PostConstruct
    public void doPostConstruct(){
        System.out.println("注解 的初始化方法");
    }

    public void sayHi(){
        System.out.println("do sayHi()");
    }

    @PreDestroy
    public void preDestroy(){
        System.out.println("do PreDestroy");
    }

    // 前置方法
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("do postProcessBeforeInitialization");
        return bean;
    }

    // 后置方法
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("do postProcessAfterInitialization");
        return bean;
    }
}

首先需要在 spring-config.xml配置文件中增加以下内容:

<!-- XML 配置方式 -->
    <bean id="beanComponent"
          class="com.spring.demo.component.BeanComponent" 
          scope="prototype" 
          init-method="myInit" ></bean>

模拟 Bean 的生命周期:

  1. 实例化 Bean:

    • Spring 容器读取配置文件或扫描注解,发现了 BeanComponent 的定义,并实例化了该 Bean。此时,还没有调用 Bean 的构造方法。
  2. 设置属性(Bean 的注入和装配):

    • 如果有需要,Spring 容器会将相应的属性注入到 BeanComponent 实例中。
  3. Bean 初始化:

    • 执行各种通知:因为 BeanComponent 实现了 BeanNameAware 接口,所以 setBeanName 方法会被调用,打印输出 执行了通知,Bean name -> beanComponent
    • 初始化前置方法:如果是 XML 配置方式,myInit 方法会被调用,打印输出 XML 方式初始化
    • 初始化方法(@PostConstruct 注解):doPostConstruct 方法会被调用,打印输出 注解 的初始化方法
    • 初始化后置方法:postProcessAfterInitialization 方法会被调用,打印输出 do postProcessAfterInitialization
  4. 使用 Bean:

    • 在初始化完成后,BeanComponent 实例可以被应用程序使用,例如调用 sayHi() 方法,打印输出 do sayHi()
  5. 销毁 Bean 对象:

    • 在容器关闭或程序结束时,preDestroy 方法会被调用,打印输出 do PreDestroy

需要注意的是,在上述示例中,使用了 BeanPostProcessor 接口来实现前置和后置方法。这个接口提供了在 Bean 初始化前后对 Bean 进行额外处理的能力。在实际应用中,可以通过实现 BeanPostProcessor 接口来自定义一些特定的处理逻辑,例如 AOP 的代理生成。

启动类的main方法:

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        BeanComponent beanComponent= context.getBean("beanComponent", BeanComponent.class);
        beanComponent.sayHi();
    }

执行结果:

这里发现执行了两次通知,第一次通常是因为在创建 Spring 上下文的时候会执行 Bean 的实例化;第二次执行通常是因为采用的是原型模式,当执行getBean的会创建一个新的 Bean 对象。

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

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

相关文章

从支付或退款之回调处理的设计,看一看抽象类的使用场景

一、背景 抽象类&#xff0c;包含抽象方法和实例方法&#xff0c;抽象方法待继承类去实例化&#xff0c;正是利用该特性&#xff0c;以满足不同支付渠道的差异化需求。 我们在做多渠道支付的时候&#xff0c;接收支付或退款的回调报文&#xff0c;然后去处理。这就意味着&…

JVM运行时五大数据区域详解

前言&#xff1a; java虚拟机再执行Java程序的时候把它所拥有的内存区域划分了若干个数据区域。这些区域有着不同的功能&#xff0c;各司其职。这些区域不但功能不同&#xff0c;创建、销毁时间也不同。有些区域为线程私有&#xff0c;如&#xff1a;每个线程都有自己的程序计数…

kubernetes基于helm部署gitlab

kubernetes基于helm部署gitlab 这篇博文介绍如何在 Kubernetes 中使用helm部署 GitLab。 先决条件 已运行的 Kubernetes 集群负载均衡器&#xff0c;为ingress-nginx控制器提供EXTERNAL-IP&#xff0c;本示例使用metallb默认存储类&#xff0c;为gitlab pods提供持久化存储&…

“算法详解”系列第3卷贪心算法和动态规划出版

“算法详解”系列图书共有4卷&#xff0c;目前1到3卷已经出版。最新出版的是第3卷—贪心算法和动态规划。 算法详解 卷3 贪心算法和动态规划 “算法详解”系列图书共有4卷&#xff0c;本书是第3卷—贪心算法和动态规划。其中贪心算法主要包括调度、最小生成树、集群、哈夫曼编…

centos7实现负载均衡

目录 一、基于 CentOS 7 构建 LVS-DR 集群。 1.1 配置lvs负载均衡服务 1.1.1 下载ipvsadm 1.1.2 增加vip 1.1.3 配置ipvsadm 1.2 配置rs1 1.2.1 编写测试页面 1.2.2 手工在RS端绑定VIP、添加路由 1.2.3 抑制arp响应 1.3 配置rs2 1.4 测试 二、配置nginx负载…

Jmeter命令行运行实例讲解

1. 简介 使用非 GUI 模式&#xff0c;即命令行模式运行 JMeter 测试脚本能够大大缩减所需要的系统资 本文介绍windows下以命令行模式运行的方法。 1.1. 命令介绍 jmeter -n -t <testplan filename> -l <listener filename> 示例&#xff1a; jmeter -n -t test…

【数学建模】-- Matlab中图的最短路径

前言&#xff1a; 图的基本概念&#xff1a; 若想简单绘制图可以利用此网站&#xff1a; 左上角Undirected/Directed是无向图/有向图 左边 0-index &#xff0c;1-index为0下标&#xff0c;1下标。 Node Count为节点个数 Graph Data&#xff1a;最初尾节点的名称&#xff…

【什么是应变波齿轮又名谐波驱动?机器人应用的完美齿轮组!?】

什么是应变波齿轮又名谐波驱动&#xff1f;机器人应用的完美齿轮组&#xff01;&#xff1f; 1. 什么是应变波齿轮&#xff1f;2. 工作原理3. 应变波齿轮 – 谐波驱动 3D 模型4. 3D 打印应变波齿轮 – 谐波驱动5. 总结 在本教程中&#xff0c;我们将学习什么是应变波齿轮&#…

第五次作业 运维高级 构建 LVS-DR 集群和配置nginx负载均衡

1、基于 CentOS 7 构建 LVS-DR 群集。 LVS-DR模式工作原理 首先&#xff0c;来自客户端计算机CIP的请求被发送到Director的VIP。然后Director使用相同的VIP目的IP地址将请求发送到集群节点或真实服务器。然后&#xff0c;集群某个节点将回复该数据包&#xff0c;并将该数据包…

ResUNet原理与实现

简述 ResNet是一种非常成功的深度卷积神经网络结构&#xff0c;其具有较强的特征表达能力和较浅的网络深度&#xff0c;使得其在图像分类等任务中表现出了出色的性能。因此&#xff0c;将ResNet作为encoder替换U-Net原始结构&#xff0c;可以使U-Net在图像分割任务中获得更好的…

python爬虫实战(1)--爬取新闻数据

想要每天看到新闻数据又不想占用太多时间去整理&#xff0c;萌生自己抓取新闻网站的想法。 1. 准备工作 使用python语言可以快速实现&#xff0c;调用BeautifulSoup包里面的方法 安装BeautifulSoup pip install BeautifulSoup完成以后引入项目 2. 开发 定义请求头&#xf…

【Windows】Windows开机密码重置

文章目录 前言一、问题描述二、操作步骤2.1 安装DaBaiCai_d14_v6.0_2207_Online.exe2.2 插入U盘2.3 打开大白菜&#xff0c;点击“一键制作USB启动盘”2.4 等待进度条走完2.5 重启电脑&#xff0c;开机按“F12”或者“F8”&#xff08;具体百度一下&#xff0c;对应品牌电脑开机…

Java 成功实现通过网址URL截图保存

Java 实现通过网址URL截图 1.DjNativeSwing方式 &#xff08;不好用&#xff09;2.phantomjs方式 &#xff08;截图还是有瑕疵&#xff09;3.selenium方式 &#xff08;满意&#xff0c;成功实现&#xff09;maven 引入下载相关浏览器chrome下载相关浏览器chromedriver驱动后端…

代码随想录算法训练营第53天|动态规划part11|123. 买卖股票的最佳时机 III、188.买卖股票的最佳时机IV

代码随想录算法训练营第53天&#xff5c;动态规划part11&#xff5c;123. 买卖股票的最佳时机 III、 188.买卖股票的最佳时机IV 123. 买卖股票的最佳时机 III 123. 买卖股票的最佳时机 III 思路&#xff1a; 相比买股票的最佳时机II&#xff0c;限制了买股票的次数&#xf…

Oracle 开发篇+Java调用OJDBC访问Oracle数据库

标签&#xff1a;JAVA语言、Oracle数据库、Java访问Oracle数据库释义&#xff1a;OJDBC是Oracle公司提供的Java数据库连接驱动程序 ★ 实验环境 ※ Oracle 19c ※ OJDBC8 ※ JDK 8 ★ Java代码案例 package PAC_001; import java.sql.Connection; import java.sql.ResultSet…

gitblit windows部署

1.官网下载 往死慢&#xff0c;我是从百度找的1.9.1&#xff0c;几乎就是最新版 http://www.gitblit.com/ 2.解压 下载下来是一个zip压缩包&#xff0c;直接解压即可 3.配置 3.1.配置资源库路径 找到data文件下的gitblit.properties文件&#xff0c;用Notepad打开 **注意路…

Android Ble蓝牙App(三)特性和属性

Ble蓝牙App&#xff08;三&#xff09;特性使用 前言正文一、获取属性列表二、属性适配器三、获取特性名称四、特性适配器五、加载特性六、显示特性和属性七、源码 前言 在上一篇中我们完成了连接和发现服务两个动作&#xff0c;那么再发现服务之后要做什么呢&#xff1f;发现服…

【二】数据库系统

数据库系统的分层抽象DBMS 数据的三个层次从 数据 到 数据的结构----模式数据库系统的三级模式&#xff08;三级视图&#xff09;数据库系统的两层映像数据库系统的两个独立性数据库系统的标准结构 数据模型从 模式 到 模式的结构----数据模型三大经典数据模型 数据库的演变与发…

windows使用/服务(13)戴尔电脑怎么设置通电自动开机

戴尔pc机器通电自启动 1、将主机显示器键盘鼠标连接好后&#xff0c;按主机电源键开机 2、在开机过程中按键盘"F12",进入如下界面&#xff0c;选择“BIOS SETUP” 3、选择“Power Management” 4、选择“AC Recovery”&#xff0c;点选“Power On”&#xff0c;点击“…

uniapp 格式化时间刚刚,几分钟前,几小时前,几天前…

效果如图&#xff1a; 根目录下新建utils文件夹&#xff0c;文件夹下新增js文件&#xff0c;文件内容&#xff1a; export const filters {dateTimeSub(data) {if (data undefined) {return;}// 传进来的data必须是日期格式&#xff0c;不能是时间戳//将字符串转换成时间格式…