mini-spring|向虚拟机注册钩子,实现Bean对象的初始化和销毁方法

news2025/1/15 16:55:27

目标

当我们的类创建的 Bean 对象,交给 Spring 容器管理以后,这个类对象就可以被赋予更多的使用能力。我们还希望可以在 Bean 初始化过程,执行一些操作。比如帮我们做一些数据的加载执行,链接注册中心暴漏RPC接口以及在Web程序关闭时执行链接断开,内存销毁等操作。如果说没有Spring我们也可以通过构造函数、静态方法以及手动调用的方式实现,但这样的处理方式终究不如把诸如此类的操作都交给 Spring 容器来管理更加合适。 因此你会看到到 spring.xml 中有如下操作:
在这里插入图片描述
需要满足用户可以在 xml 中配置初始化和销毁的方法,也可以通过实现类的方式处理,比如我们在使用 Spring 时用到的 InitializingBean, DisposableBean 两个接口。
第三种方法是在方法上加注解PostConstruct和PreDestroy 这种方法暂时不做实现

分析

对于这样在 Bean 容器初始化过程中额外添加的处理操作,无非就是预先执行了一个定义好的接口方法或者是反射调用类中xml中配置的方法,最终按照接口定义实现,就会有 Spring 容器在处理的过程中进行调用。
整体设计结构如下图:
在这里插入图片描述
在 spring.xml 配置中添加 init-method、destroy-method 两个注解,在配置文件加载的过程中,把注解配置一并定义到 BeanDefinition 的属性当中。这样在 initializeBean 初始化操作的工程中,就可以通过反射的方式来调用配置在 Bean 定义属性当中的方法信息了。另外如果是接口实现的方式,那么直接可以通过 Bean 对象调用对应接口定义的方法即可,((InitializingBean) bean).afterPropertiesSet(),两种方式达到的效果是一样的。
除了在初始化做的操作外,destroy-method 和 DisposableBean 接口的定义,都会在 Bean 对象初始化完成阶段,执行注册销毁方法的信息到 DefaultSingletonBeanRegistry 类中的 disposableBeans 属性里,这是为了后续统一进行操作。这里还有一段适配器的使用,因为反射调用和接口直接调用,是两种方式。所以需要使用适配器进行包装,具体代码在DisposableBeanAdapter 中实现-关于销毁方法需要在虚拟机执行关闭之前进行操作,所以这里需要用到一个注册钩子(执行关闭)的操作,如:Runtime.getRuntime().addShutdownHook(new Thread(() -> System.out.println(“close!”))); 这段代码你可以执行测试,另外你可以使用手动调用 ApplicationContext.close 方法关闭容器

实现

项目结构
src
├── main
│ └── java
│ └── cn.bugstack.springframework
│ ├── beans
│ │ ├── factory
│ │ │ ├── factory
│ │ │ │ ├── AutowireCapableBeanFactory.java
│ │ │ │ ├── BeanDefinition.java(实体类)
初始化和销毁
│ │ │ │ ├── BeanFactoryPostProcessor.java
│ │ │ │ ├── BeanPostProcessor.java
│ │ │ │ ├── BeanReference.java
│ │ │ │ ├── ConfigurableBeanFactory.java(接口) 定义了 destroySingletons 销毁方法
│ │ │ │ └── SingletonBeanRegistry.java
│ │ │ ├── support
│ │ │ │ ├── AbstractAutowireCapableBeanFactory.java(抽象类)
继承关系 继承AbstractBeanFactory
实现AutowireCapableBeanFactory接口
主要方法
CreateBean():创建Bean 调用registerDisposableBeanIfNecessary
initializeBean():初始化Bean,调用PostProcessor Before 处理,执行初始化方法invokeInitMethods,执行 BeanPostProcessor After 处理

│           │   │   │   ├── AbstractBeanDefinitionReader.java
│           │   │   │   ├── AbstractBeanFactory.java
│           │   │   │   ├── BeanDefinitionReader.java
│           │   │   │   ├── BeanDefinitionRegistry.java
│           │   │   │   ├── CglibSubclassingInstantiationStrategy.java
│           │   │   │   ├── DefaultListableBeanFactory.java
│           │   │   │   ├── **DefaultSingletonBeanRegistry.java**
实现destroySingletons 销毁方法( AbstractBeanFactory.java的父类)
│           │   │   │   ├── **DisposableBeanAdapter.java**
**描述:**销毁方法适配器
**继承关系:** 实现DisposableBean接口
│           │   │   │   ├── InstantiationStrategy.java
│           │   │   │   └── SimpleInstantiationStrategy.java  
│           │   │   ├── support
│           │   │   │   └── **XmlBeanDefinitionReader.java**
│           │   │   ├── BeanFactory.java
│           │   │   ├── ConfigurableListableBeanFactory.java
│           │   │   ├── **DisposableBean.java**
│           │   │   ├── HierarchicalBeanFactory.java
│           │   │   ├── **InitializingBean.java(接口)** 定义初始化方法
│           │   │   └── ListableBeanFactory.java
│           │   ├── BeansException.java
│           │   ├── PropertyValue.java
│           │   └── PropertyValues.java 
│           ├── context
│           │   ├── support
│           │   │   ├── **AbstractApplicationContext.java(抽象类)**
继承关系:实现 ConfigurableApplicationContext接口
│           │   │   ├── AbstractRefreshableApplicationContext.java 
│           │   │   ├── AbstractXmlApplicationContext.java 
│           │   │   └── ClassPathXmlApplicationContext.java 
│           │   ├── ApplicationContext.java 
│           │   └── **ConfigurableApplicationContext.java(接口)**
主要描述:虚拟机关闭钩子注册调用销毁
继承关系:继承ApplicationContext 
主要方法:
refresh():
registerShutdownHook():注册虚拟机钩子的方法
close():手动执行关闭虚拟机钩子的方法
│           ├── core.io
│           │   ├── ClassPathResource.java 
│           │   ├── DefaultResourceLoader.java 
│           │   ├── FileSystemResource.java 
│           │   ├── Resource.java 
│           │   ├── ResourceLoader.java 
│           │   └── UrlResource.java
│           └── utils
│               └── ClassUtils.java
└── test
    └── java
        └── cn.bugstack.springframework.test
            ├── bean
            │   ├── UserDao.java
            │   └── UserService.java
            └── ApiTest.java
 **类图**
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/06dbcaa151f8444f9f9b032eb6d70007.png)

以上整个类图结构描述出来的就是本次新增 Bean 实例化过程中的初始化方法和销毁方法。一共实现了两种方式的初始化和销毁方法,xml配置和定义接口,所以这里既有 InitializingBean、DisposableBean 也有需要 XmlBeanDefinitionReader 加载 spring.xml 配置信息到 BeanDefinition 中。另外接口 ConfigurableBeanFactory 定义了 destroySingletons 销毁方法,并由 AbstractBeanFactory 继承的父类 DefaultSingletonBeanRegistry 实现 ConfigurableBeanFactory 接口定义的 destroySingletons 方法。这种方式的设计可能数程序员是没有用过的,都是用的谁实现接口谁完成实现类,而不是把实现接口的操作又交给继承的父类处理。所以这块还是蛮有意思的,是一种不错的隔离分层服务的设计方式
最后就是关于向虚拟机注册钩子,保证在虚拟机关闭之前,执行销毁操作。Runtime.getRuntime().addShutdownHook(new Thread(() -> System.out.println(“close!”)));

定义初始化和销毁方法的接口

InitializingBean

public interface InitializingBean {

    /**
     * Bean 处理了属性填充后调用
     * 
     * @throws Exception
     */
    void afterPropertiesSet() throws Exception;

}

DisposableBean

public interface DisposableBean {

    void destroy() throws Exception;

}

InitializingBean、DisposableBean,两个接口方法还是比较常用的,在一些需要结合 Spring 实现的组件中,经常会使用这两个方法来做一些参数的初始化和销毁操作。比如接口暴漏、数据库数据读取、配置文件加载等等。

Bean属性定义新增初始化和销毁

BeanDefinition

public class BeanDefinition {

    private Class beanClass;

    private PropertyValues propertyValues;

    private String initMethodName;
    
    private String destroyMethodName;
    
    // ...get/set
}

在 BeanDefinition 新增加了两个属性:initMethodName、destroyMethodName,这两个属性是为了在 spring.xml 配置的 Bean 对象中,可以配置 init-method=“initDataMethod” destroy-method=“destroyDataMethod” 操作,最终实现接口的效果是一样的。只不过一个是接口方法的直接调用,另外是一个在配置文件中读取到方法反射调用

执行 Bean 对象的初始化方法

AbstractAutowireCapableBeanFactory

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {

    private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
        Object bean = null;
        try {
            bean = createBeanInstance(beanDefinition, beanName, args);
            // 给 Bean 填充属性
            applyPropertyValues(beanName, bean, beanDefinition);
            // 执行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置处理方法
            bean = initializeBean(beanName, bean, beanDefinition);
        } catch (Exception e) {
            throw new BeansException("Instantiation of bean failed", e);
        }

        // ...

        addSingleton(beanName, bean);
        return bean;
    }
	//初始化方法
    private Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {
        // 1. 执行 BeanPostProcessor Before 处理
        Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);

        // 执行 Bean 对象的初始化方法
        try {
            invokeInitMethods(beanName, wrappedBean, beanDefinition);
        } catch (Exception e) {
            throw new BeansException("Invocation of init method of bean[" + beanName + "] failed", e);
        }

        // 2. 执行 BeanPostProcessor After 处理
        wrappedBean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
        return wrappedBean;
    }

    private void invokeInitMethods(String beanName, Object bean, BeanDefinition beanDefinition) throws Exception {
        // 1. 实现接口 InitializingBean
        if (bean instanceof InitializingBean) {
            ((InitializingBean) bean).afterPropertiesSet();
        }

        // 2. 配置信息 init-method {判断是为了避免二次执行销毁}
        String initMethodName = beanDefinition.getInitMethodName();
        if (StrUtil.isNotEmpty(initMethodName)) {
            Method initMethod = beanDefinition.getBeanClass().getMethod(initMethodName);
            if (null == initMethod) {
                throw new BeansException("Could not find an init method named '" + initMethodName + "' on bean with name '" + beanName + "'");
            }
            initMethod.invoke(bean);
        }
    }

}

1.抽象类 AbstractAutowireCapableBeanFactory 中的 createBean 是用来创建 Bean 对象的方法,在这个方法中我们之前已经扩展了BeanFactoryPostProcessor、BeanPostProcessor 操作,这里我们继续完善执行 Bean 对象的初始化方法的处理动作。
2.在方法 invokeInitMethods 中,主要分为两块来执行实现了 InitializingBean 接口的操作,处理 afterPropertiesSet 方法。另外一个是判断配置信息 init-method 是否存在,执行反射调用initMethod.invoke(bean)。这两种方式都可以在 Bean 对象初始化过程中进行处理加载 Bean 对象中的初始化操作,让使用者可以额外新增加自己想要的动作。

定义销毁方法适配器(接口和配置)

??这里的适配器模式如何理解
DisposableBeanAdapter

public class DisposableBeanAdapter implements DisposableBean {

    private final Object bean;
    private final String beanName;
    private String destroyMethodName;

    public DisposableBeanAdapter(Object bean, String beanName, BeanDefinition beanDefinition) {
        this.bean = bean;
        this.beanName = beanName;
        this.destroyMethodName = beanDefinition.getDestroyMethodName();
    }
	//**适配器模式:实现目标接口并持有原始类的实例来实现对原始类的封装和转换**
    @Override
    public void destroy() throws Exception {
        // 1. 实现接口 DisposableBean
        if (bean instanceof DisposableBean) {
            ((DisposableBean) bean).destroy();
        }

        // 2. 配置信息 destroy-method {判断是为了避免二次执行销毁}
        if (StrUtil.isNotEmpty(destroyMethodName) && !(bean instanceof DisposableBean && "destroy".equals(this.destroyMethodName))) {
            Method destroyMethod = bean.getClass().getMethod(destroyMethodName);
            if (null == destroyMethod) {
                throw new BeansException("Couldn't find a destroy method named '" + destroyMethodName + "' on bean with name '" + beanName + "'");
            }
            destroyMethod.invoke(bean);
        }
        
    }

}

可能你会想这里怎么有一个适配器的类呢,因为销毁方法有两种甚至多种方式,目前有实现接口 DisposableBean、配置信息 destroy-method,两种方式。而这两种方式的销毁动作是由 AbstractApplicationContext 在注册虚拟机钩子后看,虚拟机关闭前执行的操作动作。
那么在销毁执行时不太希望还得关注都销毁那些类型的方法,它的使用上更希望是有一个统一的接口进行销毁,所以这里就新增了适配类,做统一处理。

创建Bean时注册销毁方法对象

AbstractAutowireCapableBeanFactory

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {

    private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
        Object bean = null;
        try {
            bean = createBeanInstance(beanDefinition, beanName, args);
            // 给 Bean 填充属性
            applyPropertyValues(beanName, bean, beanDefinition);
            // 执行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置处理方法
            bean = initializeBean(beanName, bean, beanDefinition);
        } catch (Exception e) {
            throw new BeansException("Instantiation of bean failed", e);
        }

        // 注册实现了 DisposableBean 接口的 Bean 对象
        registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);

        addSingleton(beanName, bean);
        return bean;
    }

    protected void registerDisposableBeanIfNecessary(String beanName, Object bean, BeanDefinition beanDefinition) {
        if (bean instanceof DisposableBean || StrUtil.isNotEmpty(beanDefinition.getDestroyMethodName())) {
            registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, beanDefinition));
        }
    }

}

DefaultSingletonBeanRegistry

public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {

	private Map<String, Object> singletonObjects = new HashMap<>();

	private final Map<String, DisposableBean> disposableBeans = new HashMap<>();

	@Override
	public Object getSingleton(String beanName) {
		return singletonObjects.get(beanName);
	}

	protected void addSingleton(String beanName, Object singletonObject) {
		singletonObjects.put(beanName, singletonObject);
	}

	public void registerDisposableBean(String beanName, DisposableBean bean) {
		disposableBeans.put(beanName, bean);
	}

	public void destroySingletons() {
		ArrayList<String> beanNames = new ArrayList<>(disposableBeans.keySet());
		for (String beanName : beanNames) {
			DisposableBean disposableBean = disposableBeans.remove(beanName);
			try {
				disposableBean.destroy();
			} catch (Exception e) {
				throw new BeansException("Destroy method on bean with name '" + beanName + "' threw an exception", e);
			}
		}
	}
}

1.在创建 Bean 对象的实例的时候,需要把销毁方法保存起来,方便后续执行销毁动作进行调用。
2.那么这个销毁方法的具体方法信息,会被注册到 DefaultSingletonBeanRegistry 中新增加的 Map<String, DisposableBean> disposableBeans 属性中去,因为这个接口的方法最终可以被类 AbstractApplicationContext 的 close 方法通过 getBeanFactory().destroySingletons() 调用
在注册销毁方法的时候,会根据是接口类型和配置类型统一交给 DisposableBeanAdapter 销毁适配器类来做统一处理。实现了某个接口的类可以被 instanceof 判断或者强转后调用接口方法

虚拟机关闭钩子注册调用销毁方法

ConfigurableApplicationContext

public interface ConfigurableApplicationContext extends ApplicationContext {

    void refresh() throws BeansException;

    void registerShutdownHook();

    void close();

}

AbstractApplicationContext

public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {

    // ...

    @Override
    public void registerShutdownHook() {
        Runtime.getRuntime().addShutdownHook(new Thread(this::close));
    }

    @Override
    public void close() {
        getBeanFactory().destroySingletons();
    }

}

由AbstractApplication抽象类实现ConfigurableApplicationContex接口Context这里主要体现了关于注册钩子和关闭的方法实现,上文提到过的 Runtime.getRuntime().addShutdownHook,可以尝试验证。在一些中间件和监控系统的设计中也可以用得到,比如监测服务器宕机,执行备机启动操作。

测试

UserDAO

public class UserDao {

    private static Map<String, String> hashMap = new HashMap<>();

    public void initDataMethod(){
        System.out.println("执行:init-method");
        hashMap.put("10001", "小傅哥");
        hashMap.put("10002", "八杯水");
        hashMap.put("10003", "阿毛");
    }

    public void destroyDataMethod(){
        System.out.println("执行:destroy-method");
        hashMap.clear();
    }

    public String queryUserName(String uId) {
        return hashMap.get(uId);
    }

}

UserService
实现InitializingBean, DisposableBean接口

public class UserService implements InitializingBean, DisposableBean {

    private String uId;
    private String company;
    private String location;
    private UserDao userDao;

    @Override
    public void destroy() throws Exception {
        System.out.println("执行:UserService.destroy");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("执行:UserService.afterPropertiesSet");
    }

    // ...get/set
}

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

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

相关文章

vSphere高可用架构---HA简介

1.高可用性 2.不同级别的高可用&#xff1a; 1&#xff09;应用程序级别&#xff0c;2&#xff09;操作系统级别&#xff0c;3&#xff09;虚拟化级别&#xff0c;4&#xff09;物理层级别 不同级别的高可用举例&#xff1a; 应用程序级别的高可用性。例如&#xff1a;Oracl…

高温应用中GaN HEMT大信号建模的ASM-HEMT

来源&#xff1a;An ASM-HEMT for Large-Signal Modeling of GaN HEMTs in High-Temperature Applications&#xff08;JEDS 23年&#xff09; 摘要 本文报道了一种用于模拟高温环境下氮化镓高电子迁移率晶体管&#xff08;GaN HEMT&#xff09;的温度依赖性ASM-HEMT模型。我…

机器学习.线性回归

斯塔1和2是权重项&#xff0c;斯塔0是偏置项&#xff0c;在训练过程中为了使得训练结果更加精确而做的微调&#xff0c;不是一个大范围的因素&#xff0c;核心影响因素是权重项 为了完成矩阵的运算&#xff0c;在斯塔0后面乘x0&#xff0c;使得满足矩阵的转换&#xff0c;所以在…

UE5 文字游戏(1) 仅UI截图转换为texture2d(适用于window端)

目录 需求 思路 1.截图并读取到本地 2.本地读取图片并转换为纹理2d 效果展示 找了好多的解决办法&#xff0c;都不管用。这个算是折中的。 需求 将当前的用户控件&#xff08;ui&#xff09;截图下来&#xff0c;并赋值到一个texture2d上。 我的需求&#xff1a;文字游戏…

matlab 三质量-弹簧系统受激振力

1、内容简介 略 44-可以交流、咨询、答疑 建立系统运动方程&#xff0c;研究固有频率和对应主振型 2、内容说明 略 三质量&#xff0d;弹簧系统受激振力&#xff0c;并不考虑各自的阻尼。建立系统运动方程。 解&#xff1a;由于阻尼对固有频率没有影响&#xff0c;故本文不…

力扣1290. 二进制链表转整数

Problem: 1290. 二进制链表转整数 文章目录 题目描述思路复杂度Code 题目描述 思路 1.记录一个变量res初始化为0&#xff0c;指针p指向链表头&#xff1b; 2.循环每次res res * 2 p -> val;p p -> next;&#xff08;充分利用二进制数的特性&#xff1b;其中利用指针先…

Camera sensor调试与bringup帧率计算

Camera sensor调试 前言DVP并行接口sensor调试方法硬件调试出图调试错误调试地址无响应问题获取帧缓存失败 DVP Wrapper调试 MIPI 串行差分接口sensor调试方法硬件调试MIPI错误调试PCB设计要求ISP时钟大小要求CSI Controller配置时钟部分其他部分 Sensor HS-PREPARE配置 Camera…

L1、L2、Smooth L1 loss

L1 loss 均绝对误差&#xff08;Mean Absolute Error&#xff0c;MAE&#xff09;&#xff0c;公式如下 优点&#xff1a;因为梯度不变&#xff0c;对离群点不敏感 缺点&#xff1a;因为梯度不变&#xff0c;不管是误差小还是大&#xff0c;梯度都一样&#xff0c;不利于模型…

Hikvision SPON IP网络对讲广播系统命令执行漏洞

声明 本文仅用于技术交流&#xff0c;请勿用于非法用途 由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;文章作者不为此承担任何责任。 1.漏洞描述 Hikvision Intercom Broadcasting System是中国海康威视&a…

深入理解Java中的Reader类:一步步剖析

咦咦咦&#xff0c;各位小可爱&#xff0c;我是你们的好伙伴——bug菌&#xff0c;今天又来给大家普及Java SE相关知识点了&#xff0c;别躲起来啊&#xff0c;听我讲干货还不快点赞&#xff0c;赞多了我就有动力讲得更嗨啦&#xff01;所以呀&#xff0c;养成先点赞后阅读的好…

Android Gradle开发与应用 (二) : Groovy基础语法

1. Groovy是什么 Groovy是基于JVM虚拟机的一种动态语言&#xff0c;语法和Java非常相似&#xff0c;并能够无缝地与Java代码集成和互操作&#xff0c;增加了很多动态类型和灵活的特性。(闭包、DSL) 语法和Java非常相似这个特点&#xff0c;意味着&#xff0c;如果我们完全不懂…

应用配置管理

一、Pod 配置管理 可变配置用 ConfigMap&#xff1b; 敏感信息用 Secret&#xff1b; 身份认证用 ServiceAccount 这几个独立的资源来实现的&#xff1b; 资源配置用 Resources&#xff1b; 安全管控用 SecurityContext&#xff1b; 前置校验用 InitContainers 这几个在 …

nginx之状态页 日志分割 自定义图表 证书

5.1 网页的状态页 基于nginx 模块 ngx_http_stub_status_module 实现&#xff0c;在编译安装nginx的时候需要添加编译参数 --with-http_stub_status_module&#xff0c;否则配置完成之后监测会是提示语法错误注意: 状态页显示的是整个服务器的状态,而非虚拟主机的状态 server{…

吴恩达deeplearning.ai:sigmoid函数的替代方案以及激活函数的选择

以下内容有任何不理解可以翻看我之前的博客哦&#xff1a;吴恩达deeplearning.ai专栏 文章目录 引入——改进下需求预测模型ReLU函数(整流线性单元 rectified linear unit&#xff09;线性激活函数(linear activation function)激活函数的选择实现方式为什么需要激活函数 到现在…

计算机设计大赛 深度学习图像风格迁移

文章目录 0 前言1 VGG网络2 风格迁移3 内容损失4 风格损失5 主代码实现6 迁移模型实现7 效果展示8 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习图像风格迁移 - opencv python 该项目较为新颖&#xff0c;适合作为竞赛课题…

【Redis】安装与部署-Ubuntu

在Ubuntu上部署redis 安装环境&#xff1a;ubuntu 20.04 1.安装Redis&#xff1a; 在终端中运行以下命令来安装Redis&#xff1a; linuxubuntu:/mnt/hgfs/GB28181$ sudo apt install redis-server2.配置Redis&#xff1a; Redis的默认配置文件位于/etc/redis/redis.conf 一…

vue3新特性-defineOptions和defineModel

defineOptions 背景说明&#xff1a; 有 <script setup> 之前&#xff0c;如果要定义 props, emits 可以轻而易举地添加一个与 setup 平级的属性。 但是用了 <script setup> 后&#xff0c;就没法这么干了 setup 属性已经没有了&#xff0c;自然无法添加与其平…

基础复习(IDA调试器)

1.选择IDA调试后端 在顶部有一个下拉菜单&#xff0c;选择调试器后端位置 很多用户实际上使用的是Windows版本的IDA&#xff0c;该IDA可以直接调试Windows下32bit和64bit的程序 2.本地调试启动方法 载入IDA后&#xff0c;程序实际上在对程序内置的一个字符串进行base64解码…

Go编译到linux运行出现 cannot execute binary file

1.初学Go就在windows上写了个"Hello,World!",在windown上编译了一下&#xff0c;生成了可执行文件。运行无问题 go build text.go .\text.exe Hello,World!2.但是按照网上的教程进行生成linux的可执行文件时出现报错 set CGO_ENABLED0 set GOOSlinux set GOARCHam…

Ubuntu22.04.3LTS安装ffmpeg6.x

1.官网ffmpeg下载源码 https://ffmpeg.org/download.html#build-windows 安装 libx264 开发库&#xff08;一个开源的视频压缩库&#xff0c;用于编码视频流为 H.264/MPEG-4 AVC 视频格式&#xff09;。这是编译 FFmpeg 时如果要支持 H.264 编码必须的。 sudo apt install l…