【框架源码】Spring源码解析之Bean生命周期流程

news2024/11/21 0:21:26

在这里插入图片描述

观看本文前,我们先思考一个问题,什么是Spring的bean的生命周期?这也是我们在面试的时候,面试官常问的一个问题。

在没有Spring之前,我们创建对象的时候,采用new的方式,当对象不在被使用的时候,由Java的垃圾回收机制回收。

而 Spring 中的对象是 bean,bean 和普通的 Java 对象没啥大的区别,只不过 Spring 不再自己去 new 对象了,而是由 IoC 容器去帮助我们实例化对象并且管理它,我们需要哪个对象,去问 IoC 容器要即可。IoC 其实就是解决对象之间的耦合问题,Spring Bean 的生命周期完全由容器控制。

在这里,我们主要是针对bean 的作用域为singleton的,对于 prototype 的 bean ,Spring 在创建好交给使用者之后则不会再管理后续的生命周期。

那么bean的作用域都有哪些,我们来回顾一下。

singleton : 唯一 bean 实例,Spring 中的 bean 默认都是单例的。

prototype : 每次请求都会创建一个新的 bean 实例。

request : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP request 内有效。

session : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP session 内有效。

global-session: 全局 session 作用域,仅仅在基于 Portlet 的 web 应用中才有意义,Spring5 已经被删除。

Spring Bean 的生命周期核心就分为以下几步:

  • 实例化 Instantiation
  • 属性赋值 Populate
  • 初始化 Initialization
  • 销毁 Destruction

实例化 -> 属性赋值 -> 初始化 -> 销毁

在这里插入图片描述

ok,接下来,我们用一个bean的创建来体验下Spring Bean的生命周期。

首先,我们先来介绍下什么是FactoryBean?

FactoryBean要和BeanFactory区分开来,BeanFactory是一个Bean工厂,创建和管理bean的工厂,是顶级接口,而FactoryBean 用来自定义Bean的创建过程,完成复杂Bean的定义。

Spring中Bean主要有有两种,一个是定义普通Bean,另一个是工厂Bean(FactoryBean)。FactoryBean是一个工厂bean,可以生成某一个类型的Bean实例,通过实现该接口定制实例化bean的逻辑。

FactoryBean在Spring框架中非常重要,Spring自身就提供了70多个FactoryBean接口的实现,通过 getBean()方法返回的不是FactoryBean本身,而是FactoryBean#getObject()方法所返回的对象,相当于FactoryBean#getObject()代理了getBean()方法。

FactoryBean接口源码:

public interface FactoryBean<T> {

  String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";

  //返回由FactoryBean 创建的Bean实例,如果isSingleton()返回true,该实例会被放到Spring容器的单里缓存池中
  @Nullable
  T getObject() throws Exception;

  //返回FactoryBean创建的Bean的类型
  @Nullable
  Class<?> getObjectType();

  //判断Bean的作用域是singleton还是prototype
  default boolean isSingleton() {
    return true;
  }
}

案例实战

  • 配置文件中< bean >的class属性配置的实现类是FactoryBean时,通过getBean()方法返回的不是FactoryBean本身
  • 获取的是FactoryBean#getObject()方法所返回的对象,相当于FactoryBean#getObject()代理了getBean()方法。
  • 创建Order实体类
@Data
public class Order {
		//订单ID
    private int orderId;
		//订单名称
    private String orderName;
		//订单价格
    private int price;
}
  • 创建OrderFactoryBean
public class OrderFactoryBean implements FactoryBean<Order> {

    public OrderFactoryBean() {
        System.out.println("FactoryBean构造方法执行");
    }

    private String orderInfo;
  
  	//设置orderInfo
    public void setOrderInfo(String orderInfo) {
        this.orderInfo = orderInfo;
    }

    // 11@测试订单@7999
    @Override
    public Order getObject() throws Exception {
        String[] split = orderInfo.split("@");
        Order order = new Order();
        order.setOrderId(Integer.parseInt(split[0]));
        order.setOrderName(split[1]);
        order.setPrice(Integer.parseInt(split[2]));
        return order;
    }
		//返回当前bean类型
    @Override
    public Class<?> getObjectType() {
        return Order.class;
    }
		//返回当前bean是否为单例
    @Override
    public boolean isSingleton() {
        return FactoryBean.super.isSingleton();
    }
}
  • xml配置文件
    <bean id="order" class="com.lixiang.factory.OrderFactoryBean">
        <property name="orderInfo" value="11@测试订单@7999"/>
    </bean>
  • 主类测试
    public static void main(String[] args) {
        ApplicationContext context =  new ClassPathXmlApplicationContext("classpath*:application.xml");

        Order order = (Order) context.getBean("order");
        System.out.println(order);

        Object obj =  context.getBean("&order");
        System.out.println(obj);
    }

在这里插入图片描述

接下来,我们再来看一下BeanFactoryPostProcessor。

BeanFactoryPostProcessor是Spring框架中一个重要的扩展接口,可以在spring的bean创建之前,可以修改Bean的定义属性,注入第三方数据等。它是在 Spring 容器加载定义 bean 的 XML 文件之后,在 bean 实例化之前执行的,对Bean进行后置处理。它的执行逻辑在BeanFactory初始化时执行,在BeanFactory的后置处理器(BeanPostProcessor)之前执行。

作用:

  • 读取应用程序上下文(ApplicationContext)中的所有Bean定义。
  • 修改定义中的属性值等信息。
  • 对修改后的定义进行处理,使其达到预期效果。
  • 将修改后的Bean定义反馈到容器中,容器将重新执行Bean创建和初始化的过程

接口代码

//ConfigurableListableBeanFactory 可以获取bean定义信息,里面进行修改bean定义
public interface BeanFactoryPostProcessor {
  void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

案例实战

  • 定义一个 OrderBeanFactoryPostProcessor,修改某个 bean 从单例改为多例,然后触发init方法。
public class OrderBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    public OrderBeanFactoryPostProcessor(){
        System.out.println("OrderBeanFactoryPostProcessor构造方法执行");
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        String[] beanStr = beanFactory.getBeanDefinitionNames();
        for (String beanName : beanStr) {
            if ("order".equals(beanName)) {
                BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
                beanDefinition.setScope(ConfigurableBeanFactory.SCOPE_SINGLETON);
            }
        }
    }

    //testInit 初始化方法
    public void testInit(){
        System.out.println("OrderBeanFactoryPostProcessor testInit 方法被调用,执行初始化逻辑");
    }
}

  • 配置xml
<bean id="customFactoryPostProcessor" class="com.lixiang.factory.OrderBeanFactoryPostProcessor" init-method="testInit"></bean>
  • 编写测试主类
	public static void main(String[] args) {
        ApplicationContext context =  new ClassPathXmlApplicationContext("classpath*:application.xml");
        Object obj1 =  context.getBean("&order");
        Object obj2 =  context.getBean("&order");
        System.out.println(obj1);
        System.out.println(obj2);
    }

在这里插入图片描述

Ok,说完BeanFactoryPostProcessor,我们再来看下BeanPostProcessor。

BeanPostProcessor是Spring IOC容器提供的一个扩展机制,用于拦截和修改实例化的Bean对象的过程。默认是对整个Spring容器中【所有的bean】进行处理,如果要对具体某个bean进行处理,通过方法参数判断即可。在容器实例化Bean对象后 (执行构造函数),初始化方法执行【前后】的回调方法,提供Bean实例初始化期间加入自定义逻辑。

案例实战

  • 编写OrderBeanPostProcessor
public class OrderBeanPostProcessor implements BeanPostProcessor {
    public OrderBeanPostProcessor() {
        System.out.println("BeanPostProcessor构造方法执行");
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

        System.out.println("BeanPostProcessor postProcessAfterInitialization 调用 beanName="+beanName);
        if(beanName.equalsIgnoreCase("order")){
            Order order = (Order)bean;
            order.setOrderName("修改后的订单名称");
            return BeanPostProcessor.super.postProcessAfterInitialization(order, beanName);
        }
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}
  • 编写xml配置Order对象属性
    <bean id="orderBeanPostProcessor" class="com.lixiang.factory.OrderBeanPostProcessor"></bean>
    <bean id="order" class="com.lixiang.domain.Order">
        <property name="orderId" value="113726"/>
      	<!-- 这里我们将订单名称设置为测试订单 -->
        <property name="orderName" value="测试订单"/> 
        <property name="price" value="2131213"/>
    </bean>
  • 编写测试代码
	public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath*:application.xml");

        Order order = (Order) context.getBean("order");
        System.out.println(order);

    }

在这里插入图片描述

  • 注意

    • BeanPostProcessor 是在 spring 容器加载了 bean 的定义文件,且实例化 bean 之后执行

    • BeanPostProcessor 的执行顺序是在 BeanFactoryPostProcessor 之后

    • BeanFactoryPostProcessor 和 BeanPostProcessor 都是处理 bean 的生命周期中拓展点,使用场景不同

      • BeanFactoryPostProcessor 作用于 bean 实例化之前,读取配置元数据 BeanDefinition ,且可以修改
      • BeanPostProcessor 作用于 bean 的实例化过程中,可以改变 bean 实例的值

还有几个接口,我们简单了解下,这快就不做演示了。

  • BeanNameAware
BeanNameAware接口是为了让自身Bean能够感知到,获取到自身在Spring容器中的id或name属性。让Bean获取自己在BeanFactory配置中的名字(根据情况是id或者name)。 

Spring自动调用。并且会在Spring自身完成Bean配置之后,且在调用任何Bean生命周期回调(初始化或者销毁)方法之前就调用这个方法。换言之,在程序中使用BeanFactory.getBean(String beanName)之前,Bean的名字就已经设定好了。
  • BeanFactoryAware
BeanFactoryAware接口是Spring框架中的一个接口,用于在Bean实例化后,将BeanFactory实例注入到Bean中。通过实现该接口,Bean可以获取到BeanFactory实例,从而可以在运行时动态获取其他Bean的实例。

具体来说,BeanFactoryAware接口的作用是让Bean能够感知到所在的BeanFactory,从而可以在需要时获取其他Bean的实例。这对于需要动态获取其他Bean的实例的情况非常有用,例如在AOP中需要获取代理对象等。
  • ApplicationContextAware
在spring项目中,类之间的关系是spring容器来管理的,但是一个项目中有些类不受spring容器管理,缺需要使用受spring管理的bean,这时候不能通过正常的方式注入bean,这时候spring给我们提供了ApplicationContextAware接口,我们可以编写一个工具类来实现ApplicationContextAware,当一个类实现ApplicationContextAware接口后,当这个类被spring加载后,就能够在这个类中获取到spring的上下文操作符ApplicationContext,通过ApplicationContext 就能够轻松的获取所有的spring管理的bean。
  • Initializingbean
Initializingbean接口只有一个afterPropertiesSet方法,实现了这个接口的类在初始化Bean时会执行这个方法。所以这个接口的用途就是用来实现初始化数据用的。

public interface InitializingBean {
    void afterPropertiesSet() throws Exception;
}
  • DisposableBean
该接口的作用是:允许一个bean在它的所有必须属性被BeanFactory设置后,来执行初始化的工作,该接口中只有一个方法,afterPropertiesSet。

public interface InitializingBean {
    void afterPropertiesSet() throws Exception;
}

ok,现在我们用Order实现以上的接口。

public class Order implements BeanNameAware, BeanFactoryAware,
        ApplicationContextAware, InitializingBean, DisposableBean {

    private int orderId;

    private String orderName;

    private int price;

    public Order() {
        System.out.println("Order - 构造方法执行,创建对象");
    }

    public Order(int orderId, String orderName, int price) {
        this.orderId = orderId;
        this.orderName = orderName;
        this.price = price;
    }

    public int getOrderId() {
        return orderId;
    }

    public void setOrderId(int orderId) {
        this.orderId = orderId;
    }

    public String getOrderName() {
        return orderName;
    }

    public void setOrderName(String orderName) {
        this.orderName = orderName;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Order{" +
                "orderId=" + orderId +
                ", orderName='" + orderName + '\'' +
                ", price=" + price +
                '}';
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("Order - BeanFactoryAware - setBeanFactory 方法被调用");
    }

    @Override
    public void setBeanName(String s) {
        System.out.println("Order - BeanNameAware - setBeanName 方法被调用");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("Order - DisposableBean - destroy 方法被调用");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("Order - InitializingBean - afterPropertiesSet 方法被调用");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("Order - ApplicationContextAware - setApplicationContext 方法被调用");
    }
}

创建Order对象,查看方法执行调用链。

在这里插入图片描述

方法调用链图解

在这里插入图片描述

源码分析目标,关键就在于AbstractApplicationContext 类 的 refresh 方法

在这里插入图片描述

OK,接下来我们来看看refresh的源码分析,基本上整一个spring生命周期都这这个方法里执行,我们来看看这个核心方法里面都做了什么

public void refresh() throws BeansException, IllegalStateException {
    //采用对象锁,保证多线程环境下,在容器正在启动/关闭时, 另一个启动/关闭操作会被阻塞
    synchronized (this.startupShutdownMonitor) {
      //统计Spring应用程序的启动时间,并输出到log
      StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

      //1.调用子类覆盖的方法prepareRefresh(),为当前的工厂进行启动数据准备工作
      prepareRefresh();

      /**
       * 2.调用子类覆盖的方法obtainFreshBeanFactory(),创建新的BeanFactory, 默认实现是DefaultListableBeanFactory
       * 解析xml, 生成BeanDefinition 并注册到 BeanDefinitionRegistry
       *
       */
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      //3.beanFactory准备工作,调用子类覆盖的方法prepareBeanFactory(),对新的BeanFactory进行各种后置处理
      prepareBeanFactory(beanFactory);

      try {
        //4.beanFactory准备工作完成后,继续进行的【后置处理】
        postProcessBeanFactory(beanFactory);

        StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");

        //5.BeanFactoryPostProcessor
        //Spring 启动时查找所有实现 BeanFactoryPostProcessor 接口的类,并逐一调用其 postProcessBeanFactory 方法
        invokeBeanFactoryPostProcessors(beanFactory);

        //6.BeanPostProcessor
        //注册 BeanPostProcessor,负责为该 Bean 对象创建代理对象
        registerBeanPostProcessors(beanFactory);
        beanPostProcess.end();

        //7.初始化信息源,用来支持国际化
        initMessageSource();

        //8.初始化事件广播器,用来支持了Spring事件机制
        initApplicationEventMulticaster();

        //9.留给子类实现的初始化操作,即调用自定义的回调方法
        onRefresh();

        //10.注册实现了ApplicationListener接口的类
        registerListeners();

        /**
         * 11.实例化所有剩余的单例Bean(非懒加载的单例bean),注意这里要区分单例和非单例Bean,主要下面的工作
         *    填充属性
         *    调用初始化方法 afterPropertiesSet、init-method方法
         *    调用BeanPostProcessor 后置处理器 postProcessBeforeInitialization和postProcessAfterInitialization执行
         */
        finishBeanFactoryInitialization(beanFactory);

        //12.事件发布,发送ContextRefreshedEvent事件
        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.
        // 销毁已经创建的单例bean
        destroyBeans();

        // 将 context 的 active 属性重置为 false
        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...
        // 重置Spring IOC中的共享缓存,避免单例bean的引用问题
        resetCommonCaches();
        //更新contextRefresh的状态
        contextRefresh.end();
      }
    }
  }

总结

  • 首先根据配置情况调用 Bean 构造方法或工厂方法实例化 Bean。

  • 然后利用反射创建对象设置Bean 中所有属性值的配置注入。

  • 如果 Bean 实现了 BeanNameAware 接口,则 Spring 调用 Bean 的 setBeanName() 方法传入当前 Bean 的 id 值。

  • 如果 Bean 实现了 BeanFactoryAware 接口,则 Spring 调用 setBeanFactory() 方法传入当前工厂实例的引用。

  • 如果 Bean 实现了 ApplicationContextAware 接口,则 Spring 调用 setApplicationContext() 方法传入当前 ApplicationContext 实例的引用。

  • 如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的预初始化方法 postProcessBeforeInitialzation() 对 Bean 进行加工操作,此处非常重要,Spring 的 AOP 就是利用它实现的。

  • 如果 Bean 实现了 InitializingBean 接口,则 Spring 将调用 afterPropertiesSet() 方法,如果在配置文件中通过 init-method 属性指定了初始化方法,则调用该初始化方法。

  • 如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的初始化方法 postProcessAfterInitialization()。此时,Bean 已经可以被应用系统使用了。

  • 如果在 中指定了该 Bean 的作用范围为 scope=“singleton”,则将该 Bean 放入 Spring IoC 的缓存池中,将触发 Spring 对该 Bean 的生命周期管理;如果在 中指定了该 Bean 的作用范围为 scope=“prototype”,则将该 Bean 交给调用者,调用者管理该 Bean 的生命周期,Spring 不再管理该 Bean。

  • 如果 Bean 实现了 DisposableBean 接口,则 Spring 会调用 destory() 方法将 Spring 中的 Bean 销毁;如果在配置文件中通过 destory-method 属性指定了 Bean 的销毁方法,则 Spring 将调用该方法对 Bean 进行销毁。

ok,那么至此,Spring Bean的整一个生命周期全部解析完成。
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【网络】UDP/TCP网络程序

目录 UDP网络程序 简单通信版本(UDP) 准备工作&#xff08;接口学习、分析&#xff09; 整体代码&#xff08;Server.hpp/Server.cpp/Client.hpp/Client.cpp&#xff09; 添加“婴儿版”业务逻辑 英译汉翻译 my_shell 聊天室 linux和windows通信 TCP网络程序 简单通…

AB32VG1:SDK_AB53XX_V061(3)IO口复用功能的补充资料

文章目录 1.IO口功能复用表格2.功能映射寄存器 FUNCTION03.功能映射寄存器 FUNCTION14.功能映射寄存器 FUNCTION2 AB5301A的官方数据手册很不完善&#xff0c;没有开放出来。我通过阅读源码补充了一些关于IO口功能复用寄存器的资料。 官方寄存器文档&#xff1a;《 AB32VG1_Re…

chatgpt赋能python:Python怎么截屏Windows

Python怎么截屏Windows Python是一种高级编程语言&#xff0c;具有快速开发、易于学习、可移植性强等优点&#xff0c;因此在实现Windows屏幕截图方面也是一种非常强大的工具。 什么是Windows屏幕截图&#xff1f; Windows屏幕截图是将当前屏幕或窗口的图像保存成文件或剪贴…

Android BlueToothBLE入门(二)——设备的连接和通讯(附Demo源码地址)

学更好的别人&#xff0c; 做更好的自己。 ——《微卡智享》 本文长度为7870字&#xff0c;预计阅读12分钟 前言 接《Android BlueToothBLE入门&#xff08;一&#xff09;——低功耗蓝牙介绍》上篇&#xff0c;这篇文章主要就是来做Demo实现Android两台设备的数据通讯。 实现效…

chatgpt赋能python:Python如何快速处理数据

Python如何快速处理数据 在当今数据爆炸的时代&#xff0c;数据处理已经成为一项非常重要的任务。因此&#xff0c;如何快速、高效地处理数据就成为了每个数据科学家、数据工程师以及数据分析师的必备技能之一。而Python正是其中的佼佼者。 为什么选择Python进行数据处理 Py…

Spring事物失效的八大场景

1.方法内的自调用&#xff1a;spring事物是基于aop的&#xff0c;只要使用代理对象调用某个方法时&#xff0c;spring事物才能生效&#xff0c;而在一个方法内使用this.xxx()时。this并不是代理对象&#xff0c;所以会失效&#xff08;实际上是transaction注解失效&#xff09;…

用程序控制对文本的复制和粘贴pyperclip模块

【小白从小学Python、C、Java】 【等级考试500强双证书考研】 【Python-数据分析】 用程序控制对文本的复制和粘贴 pyperclip模块 选择题 关于下列代码说法错误的是&#xff1f; import pyperclip print(【执行】pyperclip.copy("Python 太强大了&#xff01;")) p…

读发布!设计与部署稳定的分布式系统(第2版)笔记01_生产环境的生存法则

1. 系统“应该”做什么 1.1. 添加所需特性 2. 系统“不应该”做什么 2.1. 崩溃 2.2. 停止响应 2.3. 丢失数据 2.4. 侵犯隐私 2.5. 损失金钱 2.6. 摧毁公司 2.7. “杀死”客户 3. QA部门的测试 3.1. 团队的大部分工作是想方设法地通过测试 3.2. 做了敏捷、务实和自动…

【设计模式与范式:行为型】57 | 观察者模式(下):如何实现一个异步非阻塞的EventBus框架?

上一节课中&#xff0c;我们学习了观察者模式的原理、实现、应用场景&#xff0c;重点介绍了不同应用场景下&#xff0c;几种不同的实现方式&#xff0c;包括&#xff1a;同步阻塞、异步非阻塞、进程内、进程间的实现方式。 同步阻塞是最经典的实现方式&#xff0c;主要是为了…

GreenPlum分布式集群部署实战

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&am…

GC演变过程、三色标记法、大白话讲解G1

文章目录 GC演变过程并发垃圾回收需要解决的问题怎么确定一个垃圾?并发收集存在的问题 三色标记法CMS垃圾收集器G1垃圾收集器介绍,主要特点优点使用注意点 GC演变过程 在Java中,垃圾收集一直是一个非常重要的组成部分, 到目前为止,垃圾收集器已经有十种了, 在不停的优化. 那为…

GoogleTest之Actions的用法

目录 返回值Actions的组合验证复杂参数mock副作用改变mock对象的行为设置返回类型的默认值使用自定义函数作为Actions 通用示例 namespace mock_action { class Foo { public:virtual ~Foo() {}virtual int& GetBar() 0; // 1virtual int GetPointerValue() 0; //…

Linux CentOS7虚拟机配置静态IP并允许上网的配置方法

文章目录 前言一、开启本地电脑VMnet8二、Linux配置静态IP1. NAT模式设置2. 开启虚拟机登录root用户3. 执行命令设置静态IP4. 重启网卡① 重启网卡 (正常)② 重启网卡 (异常)③ 解决方式&#xff1a;禁用NetworkManager 5. 查看ip6. 本地电脑cmd窗口ping虚拟机7. 虚拟机ping本地…

Golang每日一练(leetDay0095) 第一个错误的版本、完全平方数

目录 278. 第一个错误的版本 First Bad Version &#x1f31f; 279. 完全平方数 Perfect Squares &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Rust每日一练 专栏 Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日…

springboot的数据访问和数据视图

当使用 Spring Boot 进行数据访问时&#xff0c;我们可以选择使用 MyBatis 或 JPA&#xff08;Java Persistence API&#xff09;来实现增删改查操作。下面我将分别给出使用这两种方式整合数据访问的详细步骤和示例&#xff0c;同时结合 Thymeleaf 实现数据展现。 方式一: 使用…

AI实战营:语义分割与MMSegmentation

目录 OpenMMLab图像分割算法库MMSegmentation 深度学习下的语义分割模型 全卷积网络Fully Convolutional Network 201 ​编辑 上下文信息与PSPNet模型 空洞卷积与DeepLab模型 语义分割算法总结 语义分割 前沿算法 SegFormer K-Net MaskFormer Mask2Former Seg…

PySide2 or PyQt5???该如何抉择???

1. 区别 Qt库里面有非常强大的图形界面开发库&#xff0c;但是Qt库是C语言开发的&#xff0c;PySide2、PyQt5可以让我们通过Python语言使用Qt。 但是 PySide2、PyQt5 这两者有什么区别呢&#xff1f; 可以形象地这样说&#xff1a; PySide2 是Qt的 亲儿子 &#xff0c; PyQt5 …

面向对象程序设计|静态友元

题目一&#xff1a;复数运算 题目描述&#xff1a; 复数类的声明如下 要求如下&#xff1a; 1. 实现复数类和友元函数addCom和outCom&#xff1b; 2. 参考addCom函数为复数类增加一个友元函数minusCom&#xff0c;用于实现两个复数的减法&#xff1b; 3. 在main函数中&…

待办事项JS:DHTMLX To Do List 1.2 cRACK

DHTMLX To Do List用于有效任务管理的DHTMLX JavaScript 待办事项列表 使用 JavaScript/HTML 5 中的待办事项列表来管理您的任务并确定其优先级。将组件连接到 DHTMLX 甘特图&#xff0c;并允许用户以简单直观的方式快速组织他们的业务流程。 DHTMLX JavaScript 待办事项列表的…

chatgpt赋能python:Python建模块最佳实践

Python建模块最佳实践 Python是一种灵活、易于使用的编程语言&#xff0c;因其强大的模块化支持和丰富的第三方模块而备受推崇。本文将介绍Python建模块的最佳实践&#xff0c;以便帮助开发人员创建可重用、可维护和易于测试的Python模块。 基本概念 在Python中&#xff0c;…