Spring之Bean创建过程

news2024/9/27 0:02:53

1. 前言

Spring提供了xml、注解、JavaConfig多种方式来配置bean,不论何种方式,Spring最终都会将bean封装成BeanDefinition对象,Spring创建bean的依据也是通过BeanDefinition来完成的。
当我们调用getBean()方法获取bean实例时,不管是单例bean还是原型bean,首次调用时容器内都不存在可用的bean实例,这时就不得不去创建bean了,我们分析下Spring创建bean的过程。

2. Bean创建概览

Bean生命周期.jpg
Spring Bean的创建过程是非常复杂的,但是整体流程又是清晰的。创建bean的入口函数在AbstractAutowireCapableBeanFactory#createBean(),Spring首先会触发一个扩展点InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation(),如果扩展点返回了可用的bean,这里会进行短路操作,Spring将不再去实例化目标bean,这样做的好处是,扩展点可以在这里返回一个代理对象去避免目标bean被实例化。
如果扩展点没有返回可用的bean对象,Spring会自己去实例化bean,实例化bean的方式有三种,取决于BeanDefinition的配置。Bean实例化完了之后,Spring会通过扩展点的方式来收集Bean的注解属性和方法,比如:@Autowired、@Value、init-method等等,信息收集完毕会通过populateBean()方法对bean进行属性填充,其实就是注入依赖。属性填充完毕,紧接着会对bean进行初始化,触发bean实现的各种Aware接口,生命周期方法,以及BeanPostProcessor扩展点。
以上执行完毕后,此时的bean就已经是一个可用的bean了,可以直接返回了。但是,bean可能还会有destroy-method方法,需要在bean销毁时被调用,所以最后还需要把这一类bean暂存到Map容器中,以便销毁时触发。

3. InstantiationAwareBeanPostProcessor

根据beanName创建bean,首先需要知道bean对应的class。解析class有2种方式,如果mbd存在BeanClass就直接用,否则根据BeanClassName加载class。

/**
* 解析BeanClass
* 1.存在BeanClass
* 2.根据BeanClassName加载Class
*/
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);

紧接着准备MethodOverrides,因为Spring支持lookup-methodreplace-method功能,针对这两类方法Spring统一保存在MethodOverrides里面,如果bean存在MethodOverrides,则需要创建代理对象来增强。
这里的准备阶段,其实是解析MethodOverride是否存在重载方法,如果不存在重载方法,会将overloaded设为false,增强调用时就不用再根据参数类型去判断该调用哪个重载方法了。

/**
 * 准备重写方法,解析是否有重载方法
 * 1.lookup-method
 * 2.replace-method
 * 如果存在MethodOverride,则创建代理对象
 */
try {
    mbdToUse.prepareMethodOverrides();
} catch (BeanDefinitionValidationException ex) {
    throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
            beanName, "Validation of method overrides failed", ex);
}

然后Spring触发扩展点InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation(),如果扩展点返回了可用的bean,Spring将不再对目标bean进行实例化。这里你可以返回一个代理对象,然后自己决定什么时候实例化目标bean。

try {
    /**
     * 触发InstantiationAwareBeanPostProcessor扩展
     * 允许它返回代理对象,来避免目标Bean实例化
     * @see InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation(java.lang.Class, java.lang.String)
     */
    Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
    if (bean != null) {
        /**
         * 如果InstantiationAwareBeanPostProcessor返回了代理对象,则不再实例化目标Bean,短路操作
         */
        return bean;
    }
} catch (Throwable ex) {
    throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
            "BeanPostProcessor before instantiation of bean failed", ex);
}

resolveBeforeInstantiation()其实就是从容器中取出所有的InstantiationAwareBeanPostProcessor实现类,然后依次调用postProcessBeforeInstantiation()方法。如果扩展点返回了bean对象,紧接着会马上触发postProcessAfterInitialization()方法,跳过目标bean的实例化。

protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
    Object bean = null;
    if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            Class<?> targetType = determineTargetType(beanName, mbd);
            if (targetType != null) {
                // 应用InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation处理器
                bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
                if (bean != null) {
                    /**
                     * 如果扩展点返回了可用bean,不再实例化目标bean,紧接着应用after处理器
                     * 应用InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation处理器
                     */
                    bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                }
            }
        }
        mbd.beforeInstantiationResolved = (bean != null);
    }
    return bean;
}

4. createBeanInstance

如果InstantiationAwareBeanPostProcessor扩展点没有返回可用bean,Spring会对目标bean实例化,紧接着调用doCreateBean()方法。
实例化bean的方法是AbstractAutowireCapableBeanFactory#createBeanInstance(),实例化bean的方式有三种:

  • 如果RootBeanDefinition提供了instanceSupplier方法,则直接调用instanceSupplier方法来生成bean对象,避免反射实例化bean的性能消耗。
  • 如果是@Bean方法声明的bean,Spring会从容器中找到factoryBeanName对应的工厂bean,然后反射调用factoryMethodName来创建bean。
  • 通过构造函数实例化。
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
    Class<?> beanClass = resolveBeanClass(mbd, beanName);
    if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                "Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
    }
    Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
    if (instanceSupplier != null) {
        return obtainFromSupplier(instanceSupplier, beanName);
    }
    /**
     * 如果是@Bean方法声明的bean,则调用工厂方法来生成bean
     * 从容器中取出factoryBean,反射调用@Bean方法生成bean
     */
    if (mbd.getFactoryMethodName() != null) {
        return instantiateUsingFactoryMethod(beanName, mbd, args);
    }

    boolean resolved = false;
    boolean autowireNecessary = false;
    if (args == null) {
        synchronized (mbd.constructorArgumentLock) {
            /*
                一个类有多个构造函数,每个构造函数都有不同的参数,
                所以需要根据参数锁定构造函数并进行初始化
             */
            if (mbd.resolvedConstructorOrFactoryMethod != null) {
                resolved = true;
                autowireNecessary = mbd.constructorArgumentsResolved;
            }
        }
    }
    // 如果已经解析过则使用解析好的构造函数 , 不需要再次锁定
    if (resolved) {
        if (autowireNecessary) {
            // 构造函数注入
            return autowireConstructor(beanName, mbd, null, null);
        } else {
            // 无参构造函数
            return instantiateBean(beanName, mbd);
        }
    }

    // 根据参数解析构造函数
    Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
    if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
            mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
        //构造函数自动注入
        return autowireConstructor(beanName, mbd, ctors, args);
    }

    // Preferred constructors for default construction?
    ctors = mbd.getPreferredConstructors();
    if (ctors != null) {
        return autowireConstructor(beanName, mbd, ctors, null);
    }

    // 无需特殊处理:只需使用无参构造函数。
    return instantiateBean(beanName, mbd);
}

5. MergedBeanDefinitionPostProcessor

通过**createBeanInstance()**方法实例化的bean只是一个不可用的bean对象,因为这个时候bean的属性还没有填充,依赖也没有注入,初始化方法也没有被调用,后置处理器也还没有执行。
紧接着,Spring会对bean做一些信息收集,收集的信息包括@Autowired、@Value、@Resource等注解,init-method、destroy-method方法等,将这些信息封装成Member对象,存放到RootBeanDefinition的externallyManagedConfigMembers属性里。
收集工作是通过扩展点MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition()进行的,Spring内置了多个实现类来收集信息,通过名字基本就能看出来它们要收集哪些信息。

  • CommonAnnotationBeanPostProcessor
  • AutowiredAnnotationBeanPostProcessor
  • InitDestroyAnnotationBeanPostProcessor
synchronized (mbd.postProcessingLock) {
    if (!mbd.postProcessed) {
        try {
            /**
             * 触发扩展点
             * 解析bean的 @Autowired @Value @Resource注解
             * init destory-method方法
             * 存放到BD的externallyManagedConfigMembers中
             * @see MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition(org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Class, java.lang.String)
             */
            applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
        } catch (Throwable ex) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                    "Post-processing of merged bean definition failed", ex);
        }
        mbd.postProcessed = true;
    }
}

6. SmartInstantiationAwareBeanPostProcessor

Bean的信息收集完毕,还有一个问题需要解决。Spring是允许Bean之间存在循环依赖的,比如BeanA有一个属性BeanB,BeanB也有一个属性BeanA,两者互相依赖对方。Spring创建BeanA时发现它依赖BeanB,就会去创建BeanB,创建BeanB时又发现它依赖BeanA,此时又会去创建BeanA,循环依赖就此产生。为了打破这个死循环,Spring的解决方案是,不等BeanA完全创建好,就提前暴露到缓存中,这样在创建BeanB时就可以直接依赖这个半成品BeanA了,循环就此打破。
如果允许提前暴露bean,那么Spring还会触发一个扩展点SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference()来获得提前暴露bean的引用,默认是直接原样返回bean。

/**
 * 这个时候bean还没初始化,是否要提前暴露Bean?以便循环依赖
 * 条件:单例 & 允许循环依赖 & bean正在创建中
 */
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
        isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
    if (logger.isTraceEnabled()) {
        logger.trace("Eagerly caching bean '" + beanName +
                "' to allow for resolving potential circular references");
    }
    /**
     * 加入到singletonFactories
     */
    addSingletonFactory(beanName,
            /**
             * 获取提前暴露Bean的引用,默认原样返回bean
             * 触发扩展点,AOP在这里将advice织入bean中
             * @see SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference(java.lang.Object, java.lang.String)
             */
            () -> getEarlyBeanReference(beanName, mbd, bean));
}

7. populateBean

bean实例生成了,bean的信息也收集完毕了,接下来就是对bean进行属性填充了,方法是populateBean()
Spring此时又会触发一个扩展点InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation(),返回值代表是否要继续填充bean,默认返回true。

/**
 * 触发扩展点,返回值代表是否要继续填充bean
 * @see InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation(java.lang.Object, java.lang.String)
 */
boolean continueWithPropertyPopulation = true;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
    for (BeanPostProcessor bp : getBeanPostProcessors()) {
        if (bp instanceof InstantiationAwareBeanPostProcessor) {
            InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
            if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
                continueWithPropertyPopulation = false;
                break;
            }
        }
    }
}
if (!continueWithPropertyPopulation) {
    return;
}

如果需要继续填充bean,则通过InstantiationAwareBeanPostProcessor#postProcessProperties()扩展点来完成的bean的填充。执行完这一步,Bean的属性会完成填充,依赖也会被注入。

/**
 * 触发扩展点,这里会注入@Autowired、@Resource、@Value等属性
 * @see InstantiationAwareBeanPostProcessor#postProcessProperties(org.springframework.beans.PropertyValues, java.lang.Object, java.lang.String) InstantiationAwareBeanPostProcessor#postProcessProperties(org.springframework.beans.PropertyValues, java.lang.Object, java.lang.String)
 * @see InstantiationAwareBeanPostProcessor#postProcessPropertyValues(org.springframework.beans.PropertyValues, java.beans.PropertyDescriptor[], java.lang.Object, java.lang.String)
 */
PropertyDescriptor[] filteredPds = null;
if (hasInstAwareBpps) {
    if (pvs == null) {
        pvs = mbd.getPropertyValues();
    }
    for (BeanPostProcessor bp : getBeanPostProcessors()) {
        if (bp instanceof InstantiationAwareBeanPostProcessor) {
            InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
            PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
            if (pvsToUse == null) {
                if (filteredPds == null) {
                    filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
                }
                // 对所有需要依赖检查的属性进行后处理
                pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                if (pvsToUse == null) {
                    return;
                }
            }
            pvs = pvsToUse;
        }
    }
}

8. initializeBean

属性填充,依赖注入完,接下来就是bean的初始化。
首先会调用invokeAwareMethods()方法来触发Bean实现的各类Aware接口。

private void invokeAwareMethods(final String beanName, final Object bean) {
    if (bean instanceof Aware) {
        if (bean instanceof BeanNameAware) {
            ((BeanNameAware) bean).setBeanName(beanName);
        }
        if (bean instanceof BeanClassLoaderAware) {
            ClassLoader bcl = getBeanClassLoader();
            if (bcl != null) {
                ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
            }
        }
        if (bean instanceof BeanFactoryAware) {
            ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
        }
    }
}

然后触发扩展点BeanPostProcessor#postProcessBeforeInitialization()

Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
    /**
     * 触发扩展点
     * @see BeanPostProcessor#postProcessBeforeInitialization(java.lang.Object, java.lang.String)
     */
    wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}

如果bean实现了InitializingBean接口,Spring紧接着会触发InitializingBean#afterPropertiesSet()扩展点,再执行init-method。

protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
            throws Throwable {
        //判断该bean 如果实现了InitializingBean接口,则调用bean的afterPropertiesSet方法
        boolean isInitializingBean = (bean instanceof InitializingBean);
        // 执行afterPropertiesSet方法
        if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
            if (logger.isTraceEnabled()) {
                logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
            }
            if (System.getSecurityManager() != null) {
                try {
                    AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
                        ((InitializingBean) bean).afterPropertiesSet();
                        return null;
                    }, getAccessControlContext());
                } catch (PrivilegedActionException pae) {
                    throw pae.getException();
                }
            } else {
                ((InitializingBean) bean).afterPropertiesSet();
            }
        }
        // 执行initMethod方法
        if (mbd != null && bean.getClass() != NullBean.class) {
            String initMethodName = mbd.getInitMethodName();
            //判断是否指定了init-method方法,如果指定了init-method方法,则再调用制定的init-method
            if (StringUtils.hasLength(initMethodName) &&
                    !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                    !mbd.isExternallyManagedInitMethod(initMethodName)) {
                //调用init-method方法中指定的方法, 是通过反射实现
                invokeCustomInitMethod(beanName, bean, mbd);
            }
        }
    }

initMethods执行完毕,接着触发BeanPostProcessor#postProcessAfterInitialization()扩展点对bean做最后的扩展。

if (mbd == null || !mbd.isSynthetic()) {
    /**
     * 触发扩展点
     * @see BeanPostProcessor#postProcessAfterInitialization(java.lang.Object, java.lang.String)
     */
    wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;

9. registerDisposableBean

到此为止,bean就创建完毕了,是一个完整的可用的bean了。最后一步,是判断bean是否存在destroy-method,如果有还需要把bean注册到Map容器中,以便bean销毁时调用。

protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
    AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);
    if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
        if (mbd.isSingleton()) {
            registerDisposableBean(beanName,
                    new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
        } else {
            Scope scope = this.scopes.get(mbd.getScope());
            if (scope == null) {
                throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");
            }
            scope.registerDestructionCallback(beanName,
                    new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
        }
    }
}

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

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

相关文章

jmeter使用教程之登录接口(工作日记)

首先我们打开jmeter 快捷按钮&#xff1a;winr 会弹出快捷运行弹框&#xff0c;我们输入cmd 后点击回车 会弹出一个控制窗口&#xff0c;我们输入jmeter&#xff0c;然后回车 首次进入jmeter&#xff0c;页面显示空白页且默认英文 我们可以切换语言 【Options - Choose Lan…

AI前沿 | 利用训练好的模型库进行一键抠图(实例演示)

来源&#xff1a;投稿 作者&#xff1a;Struggling cyanobacteria 编辑&#xff1a;学姐 深度学习平台飞浆paddle的环境搭建 ① 效率更高的 gpu 版本的安装 通过 python -m pip install paddlepaddle-gpu -i https://mirror.baidu.com/pypi/simple来进行安装。 paddle.utils…

Dockerfile了解

目录 1.自定义centos&#xff0c;具备vim及ifconfig作用 2.自定义tomcat8 1.自定义centos&#xff0c;具备vim及ifconfig作用 cd /javaxl/software mkdir mydocker vi mycentosDockerfile 向 mycentosDockerfile 编辑内容 FROM centos:7 MAINTAINER zwc "zwcqq.com"…

解决Windows下使用cmd执行Python文件报错 ModuleNotFoundError: No module named ‘XXXX‘

一、问题产生 我在IDEA中可以正常执行的Python文件&#xff0c;想把它配置成每天固定时间执行的定时任务。我写了一个脚本在每天9点时执行Python文件&#xff0c;但是我在cmd中测试执行时遇到以下报错&#xff1a; Traceback (most recent call last):File "D:\dev\code…

AD5328手册翻译不完全(仅供参考)

最近要用到DAC生成数据波形&#xff0c;但是都是英文不好阅读&#xff0c;于是花费点时间翻译了大致数据内容并记录 一、特性 AD5308:16导联TSSOP中的8个缓冲8位DAC A版本&#xff1a;1 LSB INL&#xff0c;B版本&#xff1a;0.75 LSB INR AD5318:16导联TSSOP中的8个缓冲10…

互联网时代,VR全景营销的意义是什么?

互联网时代&#xff0c;很多事项我们都可以在手机上解决&#xff0c;随着互联网的不断发展&#xff0c;年轻人每天花在手机上的时间也在日益增加&#xff0c;这就将压力给到了线下实体店铺&#xff0c;年轻人不喜欢逛街&#xff0c;线下店铺的获客成本就会越来越高。同时也是由…

无人机测深三种方法-激光雷达,测深仪和探地雷达

最近搜素了论文和相关网页&#xff0c;博主总结了一下无人机测深总共有三种办法&#xff1a; &#xff08;1&#xff09;激光雷达&#xff1b; &#xff08;2&#xff09;测深仪&#xff1b; &#xff08;3&#xff09;探地雷达&#xff08;GPR&#xff09;。 1、激光雷达 …

如何在匿名上位机中显示自定义数据波形

匿名上位机相信很多人都用过&#xff0c;以前在调飞控的时候使用过&#xff0c;可以很直观的显示数据的波形&#xff0c;比如飞机姿态等。 最近在调试foc。很多数据在调试过程中&#xff0c;仅仅使用串口打印出来显示是很不直观的&#xff0c;比如正弦波&#xff0c;经典的马鞍…

Python冷知识-优雅的文档属性

Python是一门学习成本低但是使用场景广泛的语言&#xff0c;0基础2周左右就可以进行简单的脚本编写&#xff0c;但也正是因为这个原因&#xff0c;导致很多小伙伴的Python代码比较粗糙&#xff0c;那么我们来聊聊使用文档属性让Python代码更加的优雅。 设置文档属性 作为一名…

JUC并发编程学习笔记(二)Lock接口及线程间通信

2 Lock 接口 2.1 Synchronized 2.1.1 Synchronized 关键字回顾 synchronized 是 Java 中的关键字&#xff0c;是一种同步锁。它修饰的对象有以下几种&#xff1a; 修饰一个代码块&#xff0c;被修饰的代码块称为同步语句块&#xff0c;其作用的范围是大括号{}括起来的代码&…

软件测试中白盒测试的优缺点是什么?

白盒测试的优缺点白盒测试在计算机领域行业用以检查程序的状态&#xff0c;确定实际运行状态与预期状态是否一致&#xff0c;来看看白盒测试的优点和缺点是什么吧。 白盒测试的测试方法有代码检查法、静态结构分析法、静态质量度量法、逻辑覆盖法、基本路径测试法、域测试、符…

Android学习进阶——Dagger 2 使用及原理

概述 Dagger 2 是 Google 开源的一款依赖注入框架&#xff0c;它的前身是 square 的 Dagger 1&#xff0c;Dagger 2 在 Android 中有着较为广泛的应用。 Dagger 2 基于 Java 注解&#xff0c;采用 annotationProcessor&#xff08;注解处理器&#xff09; 在项目编译时动态生…

AS-REPRoasting

讲在前面&#xff1a; 最近笔者发布的几篇文章&#xff0c;大家不难发现&#xff0c;都是和kerberos协议相关的。国内外各类的研究员对于该协议的研究愈发的深入&#xff0c;从该协议产生的漏洞就会越来越多甚至越来越严重。当然这只是笔者的一点拙见。 在介绍了Kerberoastin…

21天Java开发速成篇-Java从入门到大师01快速入门

Java 简介 win10安装 JDK 下载和安装 访问 oracle 官网https://www.oracle.com/ 建议下载解压版,下载完毕后&#xff0c;解压到一个没有中文和空格的目录即可 配置 Path 你可以选择一个喜欢的目录&#xff0c;我的解压目录是 d 盘根目录下&#xff0c;如图所示 其中 b…

创邻Galaxybase入选 2022 爱分析《中国数据智能最佳实践案例》

随着2022年进入收官时间&#xff0c;创邻科技喜报频传。近日&#xff0c;“2022爱分析中国数据智能最佳实践案例”评选结果出炉&#xff0c;经过申报、初评、调研、终评等多轮角逐&#xff0c;创邻科技凭借“中国民生银行万象知识图谱应用平台”成功获评2022中国数据智能最佳实…

FPGA知识汇集-FPGA时序基础理论

对于系统设计工程师来说&#xff0c;时序问题在设计中是至关重要的&#xff0c;尤其是随着时钟频率的提高&#xff0c;留给数据传输的有效读写窗口越来越小&#xff0c;要想在很短的时间限制里&#xff0c;让数据信号从驱动端完整地传送到接收端&#xff0c;就必须进行精确的时…

Kubernetes:minikube操作总结

Kubernetes&#xff1a;minikube操作总结minikube statusminikube nodeminikube status 查询集群状态 $ minikube statusminikube node 新增节点 注意&#xff1a;minikube 仅仅是基于本地 standalone 部署一款简易 k8s 集群&#xff0c;因此添加节点&#xff0c;也仅仅是本…

【多尺度条形池化与通道注意力的图像语义分割】论文精讲

论文&#xff1a;论文链接 出处&#xff1a;中国图象图形学报 目录 1.摘要 2.网络结构 2. 1 改进的条形池化技术 2.2 二阶通道注意力机制 3.结果 1.摘要 目的 针对自然场景下图像语义分割易受物体自身形状多样性、距离和光照等因素影响的问题,本文提出 一种新的基于条形池…

windows搭建gitblit服务器

使用Gitblit搭建属于局域网的Git服务器&#xff0c;方便程序代码的联合开发及程序代码版本管理。 环境&#xff1a;1、Windows 64位操作系统。 2、jdk1.8.0&#xff08;需要java环境&#xff0c;已安装请忽略&#xff09; 3、Git-2.24.1.2-64-bit&#xff08;git工具&#x…

linux系统编程:暂停函数pause

pause函数 该函数功能主要是暂停进程&#xff0c;它的返回值总是-1。 使用方式&#xff1a; &#xff08;1&#xff09;首先使用signal函数提前注册一个中断函数&#xff0c;该函数用于将函数指针和信号做一个绑定; &#xff08;2&#xff09;当程序进行执行pause&#xff0…