Spring源码分析(七)Bean生命周期源码解析5:Bean的销毁

2023-11-01

Bean的销毁过程

应用场景

通过实现DisposableBean接口,或者使用注解@PreDestroy都行:

@Component
public class OrderService implements DisposableBean {

    public void test() {
        System.out.println("orderService test");
    }
    
    @PreDestroy//注解方式,没有数量限制
    public void a(){
        System.out.println("orderService 销毁之前");
    }
    
    @Override
    public void destroy() throws Exception {
        System.out.println("orderService destory");
    }
    
}

Bean销毁是发生在Spring容器关闭过程中的。
在Spring容器关闭时,比如:

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

OrderService userService = (OrderService) context.getBean("orderService");
OrderService.test();

// 容器关闭
context.close();

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 注册关闭钩子也行
ontext.registerShutdownHook(); //jvm实现的,向jvm里注册一个关闭钩子

OrderService userService = (OrderService) context.getBean("orderService");
OrderService.test();

在这里插入图片描述

registerShutdownHook

org.springframework.context.support.AbstractApplicationContext#registerShutdownHook

public void registerShutdownHook() {
    if (this.shutdownHook == null) {
        // No shutdown hook registered yet.
        this.shutdownHook = new Thread(SHUTDOWN_HOOK_THREAD_NAME) {
            @Override
            public void run() {
                synchronized (startupShutdownMonitor) {
                    doClose();
                }
            }
        };
        //注册了一个线程,当jvm正常关闭的时候会调用这个线程
        Runtime.getRuntime().addShutdownHook(this.shutdownHook);
    }
}

源码分析

注册销毁Bean

直接快进看创建Bean实例的方法
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
        throws BeanCreationException {

    // Instantiate the bean.
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {...}
    // 实例化阶段
    if (instanceWrapper == null) {...}
    Object bean = instanceWrapper.getWrappedInstance();
    Class<?> beanType = instanceWrapper.getWrappedClass();
    if (beanType != NullBean.class) {...}

    // BeanDefinition后置处理
    // Allow post-processors to modify the merged bean definition.
    synchronized (mbd.postProcessingLock) {...}

    // Eagerly cache singletons to be able to resolve circular references
    // even when triggered by lifecycle interfaces like BeanFactoryAware.
    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
            isSingletonCurrentlyInCreation(beanName));
    if (earlySingletonExposure) {...}

    // Initialize the bean instance.
    Object exposedObject = bean;
    try {
        //属性填充阶段
        populateBean(beanName, mbd, instanceWrapper);
        //初始化阶段
        exposedObject = initializeBean(beanName, exposedObject, mbd);
    }
    catch (Throwable ex) {...}

    if (earlySingletonExposure) {...}

    // Register bean as disposable.
    try {
        //注册销毁Bean
        registerDisposableBeanIfNecessary(beanName, bean, mbd);
    }
    catch (BeanDefinitionValidationException ex) {...}

    return exposedObject;
}

org.springframework.beans.factory.support.AbstractBeanFactory#registerDisposableBeanIfNecessary

protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
    AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);
    //不是多例,且需要销毁
    //1.判断是否需要销毁
    if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
        if (mbd.isSingleton()) {
            // Register a DisposableBean implementation that performs all destruction
            // work for the given bean: DestructionAwareBeanPostProcessors,
            // DisposableBean interface, custom destroy method.
            //2.注册DisposableBean
            registerDisposableBean(beanName, new DisposableBeanAdapter(
                    bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc));
        }
        else {
            // A bean with a custom scope...
            Scope scope = this.scopes.get(mbd.getScope());
            if (scope == null) {
                throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");
            }
            //2.注册DisposableBean
            scope.registerDestructionCallback(beanName, new DisposableBeanAdapter(
                    bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc));
        }
    }
}

只有多例的情况不会执行销毁。

1. 判断是否需要销毁

org.springframework.beans.factory.support.AbstractBeanFactory#requiresDestruction


protected boolean requiresDestruction(Object bean, RootBeanDefinition mbd) {
    //1.1DisposableBeanAdapter.hasDestroyMethod:是否有销毁方法
    //1.2hasDestructionAwareBeanPostProcessors:判断容器里是否有DestructionAwareBeanPostProcessor
    //1.3DisposableBeanAdapter.hasApplicableProcessors:判断是否有生效的DestructionAwareBeanPostProcessor
    return (bean.getClass() != NullBean.class && (DisposableBeanAdapter.hasDestroyMethod(bean, mbd) ||
            (hasDestructionAwareBeanPostProcessors() && DisposableBeanAdapter.hasApplicableProcessors(
                    bean, getBeanPostProcessorCache().destructionAware))));
}

1.1 判断是否有销毁方法

org.springframework.beans.factory.support.DisposableBeanAdapter#hasDestroyMethod

//是否有销毁方法
public static boolean hasDestroyMethod(Object bean, RootBeanDefinition beanDefinition) {
    //是否是DisposableBean
    //inferDestroyMethodIfNecessary获取销毁方法,是否为null
    return (bean instanceof DisposableBean || inferDestroyMethodIfNecessary(bean, beanDefinition) != null);
}

org.springframework.beans.factory.support.DisposableBeanAdapter#inferDestroyMethodIfNecessary

  • 优先取beanDefinition中指定的销毁方法destroyMethodName
  • 如果destroyMethodName=“(inferred)” 或者 destroyMethodName=null且bean instanceof AutoCloseable,会查找"close"、"shutdown"方法作为销毁方法
//获取销毁方法
private static String inferDestroyMethodIfNecessary(Object bean, RootBeanDefinition beanDefinition) {
    //先从beanDefinition的“缓存”获取(缓存中有说明之前解析过了)
    String destroyMethodName = beanDefinition.resolvedDestroyMethodName;
    if (destroyMethodName == null) {
        //获取beanDefinition指定的销毁方法名
        destroyMethodName = beanDefinition.getDestroyMethodName();
        boolean autoCloseable = (bean instanceof AutoCloseable);
        //销毁方法推断,前提:
        //	destroyMethodName = INFER_METHOD = "(inferred)"
        //  或者 destroyMethodName == null && autoCloseable = true
        //如果满足以上条件会找close或者shutdown方法作为销毁方法
        if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName) ||
                (destroyMethodName == null && autoCloseable)) {
            // Only perform destroy method inference in case of the bean
            // not explicitly implementing the DisposableBean interface
            destroyMethodName = null;
            // 只有在bean没有显式实现DisposableBean接口的情况下才执行destroy方法推断
            if (!(bean instanceof DisposableBean)) {
                if (autoCloseable) {
                    //CLOSE_METHOD_NAME="close"
                    destroyMethodName = CLOSE_METHOD_NAME;
                }
                else {
                    try {
                        //CLOSE_METHOD_NAME="close"
                        destroyMethodName = bean.getClass().getMethod(CLOSE_METHOD_NAME).getName();
                    }
                    catch (NoSuchMethodException ex) {
                        try {
                            //SHUTDOWN_METHOD_NAME="shutdown"
                            destroyMethodName = bean.getClass().getMethod(SHUTDOWN_METHOD_NAME).getName();
                        }
                        catch (NoSuchMethodException ex2) {...}
                    }
                }
            }
        }
        beanDefinition.resolvedDestroyMethodName = (destroyMethodName != null ? destroyMethodName : "");
    }
    return (StringUtils.hasLength(destroyMethodName) ? destroyMethodName : null);
}

1.2 判断容器里是否有DestructionAwareBeanPostProcessor

org.springframework.beans.factory.support.AbstractBeanFactory#hasDestructionAwareBeanPostProcessors

protected boolean hasDestructionAwareBeanPostProcessors() {
    return !getBeanPostProcessorCache().destructionAware.isEmpty();
}

DestructionAwareBeanPostProcessor接口有两个方法:
在这里插入图片描述

  • requiresDestruction:判断某一个Bean是否需要销毁
  • postProcessBeforeDestruction:具体销毁要执行的逻辑

1.3 判断是否有生效的DestructionAwareBeanPostProcessor

如果注册过DestructionAwareBeanPostProcessor的话,就调用
org.springframework.beans.factory.support.DisposableBeanAdapter#hasApplicableProcessors
判断注册的DestructionAwareBeanPostProcessor中是否有对当前Bean生效的

public static boolean hasApplicableProcessors(Object bean, List<DestructionAwareBeanPostProcessor> postProcessors) {
    if (!CollectionUtils.isEmpty(postProcessors)) {
        for (DestructionAwareBeanPostProcessor processor : postProcessors) {
            if (processor.requiresDestruction(bean)) {
                //有生效的就true
                return true;
            }
        }
    }
    return false;
}

DestructionAwareBeanPostProcessor应用场景

Spring中提供的@PreDestroy注解就是通过InitDestroyAnnotationBeanPostProcessor实现的。
org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#requiresDestruction

public boolean requiresDestruction(Object bean) {
	//判断当前Bean中是否有销毁方法,即被@PreDestroy标记的方法
    return findLifecycleMetadata(bean.getClass()).hasDestroyMethods();
}

private LifecycleMetadata findLifecycleMetadata(Class<?> clazz) {
    if (this.lifecycleMetadataCache == null) {
        // Happens after deserialization, during destruction...
        // 这里 构建生命周期的元数据
        return buildLifecycleMetadata(clazz);
    }
    // Quick check on the concurrent map first, with minimal locking.
    LifecycleMetadata metadata = this.lifecycleMetadataCache.get(clazz);
    if (metadata == null) {
        synchronized (this.lifecycleMetadataCache) {
            metadata = this.lifecycleMetadataCache.get(clazz);
            if (metadata == null) {
                // 这里 构建生命周期的元数据
                metadata = buildLifecycleMetadata(clazz);
                this.lifecycleMetadataCache.put(clazz, metadata);
            }
            return metadata;
        }
    }
    return metadata;
}

org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#buildLifecycleMetadata
此方法会收集@PostConstruct(初始化前)、@PreDestroy(销毁前)标记的方法

private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {
    if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(this.initAnnotationType, this.destroyAnnotationType))) {...}

    List<LifecycleElement> initMethods = new ArrayList<>();
    List<LifecycleElement> destroyMethods = new ArrayList<>();
    Class<?> targetClass = clazz;

    do {
        final List<LifecycleElement> currInitMethods = new ArrayList<>();
        final List<LifecycleElement> currDestroyMethods = new ArrayList<>();

        //遍历当前类中的所有方法
        ReflectionUtils.doWithLocalMethods(targetClass, method -> {
            //initAnnotationType就是初始化前方法对应的注解类型
            if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
                LifecycleElement element = new LifecycleElement(method);
                currInitMethods.add(element);
                if (logger.isTraceEnabled()) {...}
            }
            //destroyAnnotationType是销毁前方法对应的注解类型
            if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {
                currDestroyMethods.add(new LifecycleElement(method));
                if (logger.isTraceEnabled()) {...}
            }
        });
    	//父类的初始化方法在前面(即先执行父类的初始化方法,再执行子类的)
        initMethods.addAll(0, currInitMethods);
        destroyMethods.addAll(currDestroyMethods);
        targetClass = targetClass.getSuperclass();
    }
    //注意这里有do while,是为了遍历父类
    while (targetClass != null && targetClass != Object.class);

    return (initMethods.isEmpty() && destroyMethods.isEmpty() ? this.emptyLifecycleMetadata :
            new LifecycleMetadata(clazz, initMethods, destroyMethods));
}

initAnnotationType和destroyAnnotationType的注解类型是在子类CommonAnnotationBeanPostProcessor的构造中设置的:
在这里插入图片描述

2. 注册DisposableBean

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#registerDisposableBean

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

就是存到disposableBeans的map
在这里插入图片描述

其中用到适配器模式,目的:兼容各种设置销毁方法的场景(比如通过实现DisposableBean接口、使用@PreDestroy注解方式…),最后统一调用org.springframework.beans.factory.support.DisposableBeanAdapter#destroy就可以了。
在这里插入图片描述
在这里插入图片描述

总结

在Bean创建过程中,在最后(初始化之后),有一个步骤会去判断当前创建的Bean是不是DisposableBean:

  1. 当前Bean是否实现了DisposableBean接口
  2. 或者,当前Bean是否实现了AutoCloseable接口
  3. BeanDefinition中是否指定了destroyMethod
  4. 调用DestructionAwareBeanPostProcessor.requiresDestruction(bean)进行判断
    a. ApplicationListenerDetector中直接使得ApplicationListener是DisposableBean
    b. InitDestroyAnnotationBeanPostProcessor中使得拥有@PreDestroy注解了的方法就是DisposableBean
  5. 把符合上述任意一个条件的Bean适配成DisposableBeanAdapter对象,并存入disposableBeans中(一个LinkedHashMap)

销毁方法执行时机

看下容器关闭方法:
org.springframework.context.support.AbstractApplicationContext#close

public void close() {
    synchronized (this.startupShutdownMonitor) {
        doClose();
        // If we registered a JVM shutdown hook, we don't need it anymore now:
        // We've already explicitly closed the context.
        if (this.shutdownHook != null) {...}
    }
}
protected void doClose() {
    // Check whether an actual close attempt is necessary...
    if (this.active.get() && this.closed.compareAndSet(false, true)) {
        if (logger.isDebugEnabled()) {...}

        if (!NativeDetector.inNativeImage()) {...}

        try {
            // Publish shutdown event.
            // 发布ContextClosedEvent事件
            publishEvent(new ContextClosedEvent(this));
        }
        catch (Throwable ex) {...}

        // Stop all Lifecycle beans, to avoid delays during individual destruction.
        if (this.lifecycleProcessor != null) {
            try {
                //Spring容器生命周期
                this.lifecycleProcessor.onClose();
            }
            catch (Throwable ex) {...}
        }

        // Destroy all cached singletons in the context's BeanFactory.
        // 销毁Bean
        destroyBeans();

        // Close the state of this context itself.
        closeBeanFactory();

        // Let subclasses do some final clean-up if they wish...
        onClose();

        // Reset local application listeners to pre-refresh state.
        if (this.earlyApplicationListeners != null) {...}

        // Switch to inactive.
        this.active.set(false);
    }
}

销毁Bean

org.springframework.context.support.AbstractApplicationContext#destroyBeans

protected void destroyBeans() {
    //销毁所有单例Bean(不管有没有定义销毁方法)
    getBeanFactory().destroySingletons();
}

org.springframework.beans.factory.support.DefaultListableBeanFactory#destroySingletons

public void destroySingletons() {
	//调用父类
    super.destroySingletons();
    updateManualSingletonNames(Set::clear, set -> !set.isEmpty());
    clearByTypeCache();
}

父类方法
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroySingletons

public void destroySingletons() {
    if (logger.isTraceEnabled()) {...}
    //标记单例Bean正在销毁中
    synchronized (this.singletonObjects) {...}

    String[] disposableBeanNames;
    synchronized (this.disposableBeans) {
        //收集之前注册的有定义销毁方法的Bean
        disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
    }
    for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
        //遍历处理所有定义过销毁方法的Bean
        destroySingleton(disposableBeanNames[i]);
    }

    this.containedBeanMap.clear();
    this.dependentBeanMap.clear();//某个Bean被哪些Bean依赖了的Map
    this.dependenciesForBeanMap.clear();//某个Bean依赖了哪些Bean的Map

    clearSingletonCache();//清空单例池等Map
}

protected void clearSingletonCache() {
    synchronized (this.singletonObjects) {
        this.singletonObjects.clear();//单例池
        //后面和循环依赖有关,先不看
        this.singletonFactories.clear();
        this.earlySingletonObjects.clear();
        this.registeredSingletons.clear();
        this.singletonsCurrentlyInDestruction = false;
    }
}

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroySingleton

public void destroySingleton(String beanName) {
    // Remove a registered singleton of the given name, if any.
    // 先从单例池中移除掉
    removeSingleton(beanName);

    // Destroy the corresponding DisposableBean instance.
    DisposableBean disposableBean;
    synchronized (this.disposableBeans) {
        //将自己从销毁BeanMap中移除
        disposableBean = (DisposableBean) this.disposableBeans.remove(beanName);
    }
	//执行销毁逻辑
    destroyBean(beanName, disposableBean);
}

//先从单例池中移除掉
protected void removeSingleton(String beanName) {
    synchronized (this.singletonObjects) {
        this.singletonObjects.remove(beanName);
        this.singletonFactories.remove(beanName);
        this.earlySingletonObjects.remove(beanName);
        this.registeredSingletons.remove(beanName);
    }
}

执行销毁逻辑
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroyBean

protected void destroyBean(String beanName, @Nullable DisposableBean bean) {
    // dependentBeanMap表示某Bean被哪些Bean依赖了
    // 所以现在要销毁某个Bean时,如果这个Bean还被其他Bean依赖了,那么也得销毁其他Bean
    // Trigger destruction of dependent beans first...
    Set<String> dependencies;
    synchronized (this.dependentBeanMap) {
        // Within full synchronization in order to guarantee a disconnected Set
        dependencies = this.dependentBeanMap.remove(beanName);
    }
    //dependencies就是依赖当前Bean的Bean,需要先销毁它们,才能销毁自己
    if (dependencies != null) {
        if (logger.isTraceEnabled()) {...}
        for (String dependentBeanName : dependencies) {
            //销毁其他依赖当前Bean的Bean
            destroySingleton(dependentBeanName);
        }
    }

    // Actually destroy the bean now...
    if (bean != null) {
        try {
            //执行destroy方法
            //这个bean就是DisposableBeanAdapter
            bean.destroy();
        }
        catch (Throwable ex) {...}
    }

    // Trigger destruction of contained beans...
    // 如果这个disposableBean还包含了inner beans,将这些Bean从单例池中移除掉
    Set<String> containedBeans;
    synchronized (this.containedBeanMap) {...}
    if (containedBeans != null) {
        for (String containedBeanName : containedBeans) {
            destroySingleton(containedBeanName);
        }
    }

    // Remove destroyed bean from other beans' dependencies.
    // 当前Bean已经销毁了,从其他bean的依赖项中删除
    synchronized (this.dependentBeanMap) {
        for (Iterator<Map.Entry<String, Set<String>>> it = this.dependentBeanMap.entrySet().iterator(); it.hasNext();) {
            Map.Entry<String, Set<String>> entry = it.next();
            Set<String> dependenciesToClean = entry.getValue();
            dependenciesToClean.remove(beanName);
            if (dependenciesToClean.isEmpty()) {
                it.remove();
            }
        }
    }

    // Remove destroyed bean's prepared dependency information.
    // dependenciesForBeanMap:某个Bean依赖了哪些Bean的Map,这个信息也可以移除了
    this.dependenciesForBeanMap.remove(beanName);
}

总结

在Spring容器关闭过程时:

  1. 首先发布ContextClosedEvent事件
  2. 调用lifecycleProcessor的onClose()方法
  3. 销毁单例Bean
    1. 遍历disposableBeans
      1. 把每个disposableBean从单例池中移除
      2. 调用disposableBean的destroy()
      3. 如果这个disposableBean还被其他Bean依赖了,那么也得销毁其他Bean
      4. 如果这个disposableBean还包含了inner beans,将这些Bean从单例池中移除掉
        (inner bean参考:https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-inner-beans)
    2. 清空manualSingletonNames,是一个Set,存的是用户手动注册的单例Bean的beanName
    3. 清空allBeanNamesByType,是一个Map,key是bean类型,value是该类型所有的beanName数组
    4. 清空singletonBeanNamesByType,和allBeanNamesByType类似,只不过只存了单例Bean

这里涉及到一个设计模式:适配器模式
在销毁时,Spring会找出实现了DisposableBean接口的Bean。

但是我们在定义一个Bean时,如果这个Bean实现了DisposableBean接口,或者实现了AutoCloseable接口,或者在BeanDefinition中指定了destroyMethodName,那么这个Bean都属于“DisposableBean”,这些Bean在容器关闭时都要调用相应的销毁方法。

所以,这里就需要进行适配,将实现了DisposableBean接口、或者AutoCloseable接口等适配成实现了DisposableBean接口,所以就用到了DisposableBeanAdapter。

会把实现了AutoCloseable接口的类封装成DisposableBeanAdapter,而DisposableBeanAdapter实现了DisposableBean接口。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring源码分析(七)Bean生命周期源码解析5:Bean的销毁 的相关文章

随机推荐

  • 微前端qiankun使用+踩坑

    背景 项目使用qiankun 改造的背景 项目A 项目B 项目C 项目A和项目B具有清晰的服务边界 从服务类型的角度能够分为两个项目 在公司项目一体化的背景下 所有的项目又应该是一个项目 项目B研发启动的时候 1 由于开发时间紧张 2 项目
  • Android项目工程结构介绍

    Android项目工程结构介绍 1 gradle和 idea Android Studio自动生成的文件 打包的时候一般会删掉再进行打包 2 app 项目的代码资源都在其中 也是我们工作的核心目录 build 编译生成文件 生成的apk就在
  • Scroller与computeScroll处理滑动

    背景 最近在纯手写一个 slidingMenu 里面用到了 Scroller与computeScroll处理滑动 由于我也是第一次遇到这种东西 我这暴脾气 实在忍不住要记住一下 以供大家参考 更重要的是方便自己以后回忆 知识点讲解 实现滚动
  • 01_08_桶排序(Bucket Sort)

    桶排序 Bucket Sort 桶排序 Bucket Sort 介绍 是一种排序算法 适用于数据范围较小且分布均匀的浮点数数据 它将待排序序列划分为若干个桶 区间 对每个桶中的元素进行排序 然后按顺序合并所有桶的元素得到最终有序序列 桶排序
  • RFID 复杂事件检测算法-毕业论文

    摘 要 本论文首先介绍了RFID技术的概念 工作原理 发展过程 应用背景等信息 然后对本系统所需的硬件条件 即RFID阅读器的特性和配置等信息进行说明 接下来介绍了基于RFID的仓储管理系统的开发背景 探讨了数据库的功能特点 做出了系统需求
  • shell grep 详解说明,实战造就英雄,苦练成就神话

    shell grep 详解说明 当您使用Shell中的grep命令时 它允许您在文本文件或标准输入中搜索匹配某个模式的行 并输出结果 下面是grep命令的详细说明和参数介绍表格 参数 描述 i 忽略大小写进行匹配 默认情况下 grep区分大
  • 制作cmd小游戏_小伙利用Python自制一个推箱子小游戏!

    导语 月初更波python制作小游戏系列吧用python写了个推箱子小游戏 在这里分享给大家 让我们愉快地开始吧 小伙利用Python自制一个推箱子小游戏 开发工具 Python版本 3 6 4 相关模块 pygame模块 以及一些Pyth
  • [转]QT中窗口刷新事件的学习总结

    QT中窗口刷新事件的学习总结 一 主要理解一下几个方法和属性 1 QWidget QScrollView viewport const 2 void QWidget paintEvent QPaintEvent 虚 保护 3 void QW
  • Hive 窗口函数大全

    目录 窗口函数概述 窗口序列函数 row number dense rank 窗口边界 滑动窗口 lag 获取上一行数据 lead 获取下一行数据 窗口专用计算函数 sum累加函数 max最大值 min最小值 avg平均值 count累计次
  • vue的组件通信方法(9种)

    1 传 在 组件的标签上定义属性 组件通过props来进 接收 可以通过数组或者对象的 式接收 如果 组件没有传递属性 组件可以default来设置默认值 父传子的使用场景 封装列表的时候 把数据传到子组件渲染 2 传 组件通过this e
  • 深度学习(30)—— DeformableDETR(1)

    深度学习 30 DeformableDETR 1 原本想在一篇文章中就把理论和debug的过程都呈现 但是发现内容很多 所以就分开两篇 照常先记录理论学习过程 然后是实践过程 注 大家一定不要看过理论就完事儿了 去扣代码 看人家怎么完成的
  • qt5.6.0 opengl —— 纹理贴图

    对于CUBE这个例子 之前分析了它的框架 至于图怎么弄上去的还怎么细看 现在尝试弄了一下 首先分析它怎么对应的 原本是一张图 怎么分成六面的 于是像高中时代那样开始了计算理解 这样就清楚多了 一张图 划分为6个块 一个面一块 至于归一化 可
  • k8s如何对外公布一个应用程序

    一 Kubernetes Service 服务 概述 事实上 Pod 容器组 有自己的 生命周期 opens new window 当 worker node 节点 故障时 节点上运行的 Pod 容器组 也会消失 然后 Deployment
  • Spring Security快速入门

    Spring Security是一个框架 提供 认证 authentication 授权 authorization 和 保护 以抵御常见的攻击 它对保护命令式和响应式应用程序有一流的支持 是保护基于Spring的应用程序的事实标准 spr
  • Java中的集合及深拷贝与浅拷贝

    Java中的集合及深拷贝与浅拷贝 Java是一种面向对象的编程语言 其中集合是常用的数据结构之一 具有方便快捷的特点 在Java开发中 我们常常需要对集合进行复制 拷贝 操作 但是 拷贝操作并不是简单的复制 而应该分为浅拷贝和深拷贝两种不同
  • MySQL学习笔记1:MySQL字符集和字符集编码

    MySQL学习笔记索引 MySQL学习笔记1 MySQL字符集和字符集编码 MySQL学习笔记2 如何避免数据库乱码 MySQL学习笔记3 排序规则和排序规则的影响 MySQL学习笔记4 排序规则的修改 文章目录 一 基本概念 二 mysq
  • 【学习笔记】应用与编排管理:Deployment

    学习笔记 应用与编排管理 Deployment 需求来源 背景问题 Deployment 管理部署发布的控制器 架构设计 管理模式 Deployment 控制器 ReplicaSet 控制器 发布模拟 spec 字段解析 升级策略字段解析
  • 闭包使用的3种情景

    定义 通俗讲 闭包是函数里面再定义一个函数 里层函数能访问到外层函数的局部变量 也就是说闭包是一个能访问外层函数局部变量的函数 常用情景有以下3种 1 在window下有个全局变量a 在函数checkScope内部有个局部变量a 需求 在w
  • Parkour World 游戏内测攻略详解

    Parkour World 是一款将赛博朋克风格与跑酷元素结合的运动类PC端游戏 玩家通过在虚拟世界中进行跑酷 从而获取相应的奖励 Parkour World结合区块链技术 采用保值NFT以及独特的3token Arb Hood Yuri
  • Spring源码分析(七)Bean生命周期源码解析5:Bean的销毁

    Bean的销毁过程 应用场景 通过实现DisposableBean接口 或者使用注解 PreDestroy都行 Component public class OrderService implements DisposableBean pu