BeanFactory体系介绍

BeanFactory体系

BeanFactory是Spring最核心的功能,它就是我们常说的Spring IoC容器。

BeanFactory体系下包含许多接口,它们分别代表Spring IoC容器的不同功能:

  • BeanFactory:提供最基础的获取bean信息的方法,如getBean()
  • HierarchicalBeanFactory:提供父子层级Spring容器的基础方法,如getParentBeanFactory()
  • AutowireCapableBeanFactory:提供实例化、自动装配等基础功能,如createBean()
  • ListableBeanFactory:提供枚举所有bean的功能,如getBeansOfType()
  • ConfigurableBeanFactory:提供配置Spring容器的基础功能,是BeanFactory接口的补充。如addBeanPostProcessor()
  • ConfigurableListableBeanFactory:提供获取和修改BeanDefinition、预实例化单例对象的功能。如getBeanDefinition()
  • ApplicationContext:应用层Spring容器的顶级接口。
  • BeanDefinitionRegistry:提供注册BeanDefinition的功能,如registerBeanDefinition()。不是BeanFactory的子接口,但它是BeanFactory体系的核心组成部分。

虽然BeanFactory体系的接口众多,但是它们的核心实现类只有DefaultListableBeanFactory

我们只需要按照DefaultListableBeanFactory的基本使用流程,掌握其中的关键性方法的源码,就能够很好的理解Spring IoC容器。

在对Spring IoC容器有了整体的认识后,再去针对性研究它提供的特性功能,就能够完全掌握Spring IoC容器。

2 DefaultListableBeanFactory

DefaultListableBeanFactory的成员变量很多,这里介绍其中最核心的:

  • beanDefinitionMapBeanDefinition的缓存,keybeanName
  • mergedBeanDefinitions:合并后的BeanDefinition缓存。
  • singletonFactories:单例bean的缓存,保存创建后且依赖注入前的单例对象。
  • earlySingletonObjects:单例bean的缓存,保存依赖注入且回调完的单例对象。
  • singletonObjects:单例bean的缓存,保存最终的单例对象。

DefaultListableBeanFactory中最核心的流程(方法)包括:

  1. 注册BeanDefinition
  2. 创建bean
  3. 获取bean

2.1 注册BeanDefinition

注册BeanDefinitionBeanDefinitionRegistry接口提供的方法,DefaultListableBeanFactory对其进行了实现。

注册BeanDefinition的过程主要是将其保存到beanDefinitionMap缓存中,其中beanName作为key

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

考虑到beanName可能被重复注册多次的情况。一方面,要对此做出限制,主要是通过allowBeanDefinitionOverriding属性;另一方面,在覆盖时需要将旧的beanDefinition衍生出的各种缓存清除,保证数据的一致性。

beanDefinition相关的缓存包括(没有全部列出):

  • mergedBeanDefinitionsmergedBeanDefinitionHolders
  • singletonObjectssingletonFactoriesearlySingletonObjectsregisteredSingletonsdisposableBeansdependentBeanMap

DefaultListableBeanFactory#registerBeanDefinition()

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)  
      throws BeanDefinitionStoreException {  
   // 校验beanDefinition信息的完整性
   if (beanDefinition instanceof AbstractBeanDefinition) {  
      try {  
         ((AbstractBeanDefinition) beanDefinition).validate();  
      }  
      catch (BeanDefinitionValidationException ex) {  
         throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", ex);  
      }  
   }  
  
   // 获取缓存
   BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);  
   // 如果该beanDefinition已经注册
   if (existingDefinition != null) {  
      // 如果beanFactory不允许beanDefinition重载,抛出异常
      if (!isAllowBeanDefinitionOverriding()) {  
         throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);  
      }  
      // 如果beanFactory允许beanDefinition重载,进行覆盖(默认会进行覆盖)
      this.beanDefinitionMap.put(beanName, beanDefinition);  
   }  
   // 如果该beanDefinition未注册
   else {  
      // 如果beanFactory已经创建bean
      if (hasBeanCreationStarted()) {  
         // 加锁,更新beanDefinitionMap缓存
         synchronized (this.beanDefinitionMap) {  
            this.beanDefinitionMap.put(beanName, beanDefinition);  
            List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);  
            updatedDefinitions.addAll(this.beanDefinitionNames);  
            updatedDefinitions.add(beanName);  
            this.beanDefinitionNames = updatedDefinitions;  
            removeManualSingletonName(beanName);  
         }  
      }  
      // 如果beanFactory还没有创建bean,仍处于注册bean阶段
      else {  
         // 直接更新beanDefinitionMap缓存
         this.beanDefinitionMap.put(beanName, beanDefinition);  
         this.beanDefinitionNames.add(beanName);  
         removeManualSingletonName(beanName);  
      }  
      this.frozenBeanDefinitionNames = null;  
   }  
   // 如果beanDefinition已存在,或单例bean已存在,需要清除相关缓存信息:mergedBeanDefinitions、singletonObjects等
   if (existingDefinition != null || containsSingleton(beanName)) {  
      resetBeanDefinition(beanName);  
   }  
   // 如果beanDefinition不存在,并且单例bean不存在,并且beanFactory会缓存所有beanDefinition的元数据
   else if (isConfigurationFrozen()) {  
      // 清除allBeanNamesByType和singletonBeanNamesByType
      clearByTypeCache();  
   }  
}

2.2 创建bean

创建bean的底层方法位于AbstractAutowireCapableBeanFactory#createBean()

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {  
   RootBeanDefinition mbdToUse = mbd;  
   // 解析bean对应的beanClass对象,用于后面的实例化
   Class<?> resolvedClass = resolveBeanClass(mbd, beanName);  
   if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {  
      mbdToUse = new RootBeanDefinition(mbd);  
      mbdToUse.setBeanClass(resolvedClass);  
   }  
  
   // Prepare method overrides:校验lookup方法是否存在
   try {  
      mbdToUse.prepareMethodOverrides();  
   }  
   catch (BeanDefinitionValidationException ex) {  
      throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),  
            beanName, "Validation of method overrides failed", ex);  
   }  
  
   try {  
      // 触发InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation()和BeanPostProcessor#postProcessAfterInitialization()方法
      Object bean = resolveBeforeInstantiation(beanName, mbdToUse);  
      // 如果通过InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation()方法实例化对象,就直接返回了
      if (bean != null) {  
         return bean;  
      }  
   }  
   catch (Throwable ex) {  
      throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,  
            "BeanPostProcessor before instantiation of bean failed", ex);  
   }  
  
   try {  
      // 使用BeanFactory默认策略实例化对象:使用instanceSupplier、factoryMethodName或构造函数
      Object beanInstance = doCreateBean(beanName, mbdToUse, args);
      return beanInstance;  
   }  
   catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
      throw ex;  
   }  
   catch (Throwable ex) {  
      throw new BeanCreationException(  
            mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);  
   }  
}

2.2.1 解析beanClass

为了创建bean,首先要知道它的beanClass是什么。

解析常规beanClass的底层方法位于AbstractBeanDefinition#resolveBeanClass()。简单来说,它会根据beanClassName加载对应的类对象,并且缓存起来:

public Class<?> resolveBeanClass(@Nullable ClassLoader classLoader) throws ClassNotFoundException {  
   String className = getBeanClassName();  
   if (className == null) {  
      return null;  
   }  
   Class<?> resolvedClass = ClassUtils.forName(className, classLoader);  
   this.beanClass = resolvedClass;  
   return resolvedClass;  
}

2.2.2 实例化前的回调

在创建bean之前,会先触发InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation()BeanPostProcessor#postProcessAfterInitialization()方法回调:

protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {  
   Object bean = null;  
   if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {  
      // Make sure bean class is actually resolved at this point.  
      if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {  
         Class<?> targetType = determineTargetType(beanName, mbd);  
         if (targetType != null) {  
            // InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation()回调
            bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);  
            if (bean != null) {  
               // BeanPostProcessor#postProcessAfterInitialization()
               bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);  
            }  
         }  
      }  
      mbd.beforeInstantiationResolved = (bean != null);  
   }  
   return bean;  
}

在这个阶段,会触发常见的InstantiationAwareBeanPostProcessor实现类如下:

  • AbstractAutoProxyCreator:将符合条件的bean用AOP代理封装起来,并指定代理的拦截器。

如果在这个阶段创建了bean,那么会直接返回,不会继续执行后续创建bean操作。

2.2.3 创建bean

AbstractAutowireCapableBeanFactory#doCreateBean()会根据RootBeanDefinition的信息进行创建对象。

简单来说,包括以下步骤:

  1. 通过instanceSupplier、工厂方法和构造函数等方式创建对象。‘
  2. 触发MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition()回调。
  3. InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation()回调。
  4. InstantiationAwareBeanPostProcessor#postProcessProperties()回调
  5. 依赖注入。
  6. 触发BeanNameAwareBeanClassLoaderAwareBeanFactoryAware回调。
  7. 触发BeanPostProcessor#postProcessBeforeInitialization()回调。
  8. 触发InitializingBean#afterPropertiesSet()回调。
  9. 触发initMethod回调。
  10. 触发BeanPostProcessor#postProcessAfterInitialization()回调。

AbstractAutowireCapableBeanFactory#doCreateBean()

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {  
   BeanWrapper instanceWrapper = null;  
   if (mbd.isSingleton()) {  
      // 如果factoryBeanInstanceCache有,直接取
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);  
   }  
   if (instanceWrapper == null) {  
      /* 创建bean:
         1、instanceSupplier#get()方法创建
         2、factoryMethod工厂方法创建
         3、有参构造函数创建
         4、无参构造函数创建
      */
      instanceWrapper = createBeanInstance(beanName, mbd, args);  
   }  
   
   Object bean = instanceWrapper.getWrappedInstance();  
   Class<?> beanType = instanceWrapper.getWrappedClass();  
   if (beanType != NullBean.class) {  
      mbd.resolvedTargetType = beanType;  
   }  
  
   // 触发MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition()回调
   synchronized (mbd.postProcessingLock) {  
      if (!mbd.postProcessed) {  
         try {  
            applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);  
         }  
         catch (Throwable ex) {  
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,  
                  "Post-processing of merged bean definition failed", ex);  
         }  
         mbd.postProcessed = true;  
      }  
   }  
  
   // 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) {  
      // 如果是提前暴露的单例bean:缓存单例对象:singletonFactories和registeredSingletons
      addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));  
   }  
  
   // 实例化bean
   Object exposedObject = bean;  
   try {  
      /* 
         1、InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation()回调
         2、InstantiationAwareBeanPostProcessor#postProcessProperties()回调
         3、依赖注入
      */ 
      populateBean(beanName, mbd, instanceWrapper);  
      /*
	     1、触发BeanNameAware、BeanClassLoaderAware和BeanFactoryAware回调
	     2、触发BeanPostProcessor#postProcessBeforeInitialization()回调
	     3、触发InitializingBean#afterPropertiesSet()回调
	     4、触发initMethod回调
	     5、触发BeanPostProcessor#postProcessAfterInitialization()回调
      */
      exposedObject = initializeBean(beanName, exposedObject, mbd);  
   }  
   catch (Throwable ex) {  
      
   }  
   // 如果是提前暴露的单例bean
   if (earlySingletonExposure) {  
      // 从缓存中获取:singletonObjects、earlySingletonObjects、singletonFactories
      Object earlySingletonReference = getSingleton(beanName, false);  
      if (earlySingletonReference != null) {  
         if (exposedObject == bean) {  
            exposedObject = earlySingletonReference;  
         }  
         else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {  
            // 清除依赖bean的缓存
            String[] dependentBeans = getDependentBeans(beanName);  
            Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);  
            for (String dependentBean : dependentBeans) {  
               if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {  
                  actualDependentBeans.add(dependentBean);  
               }  
            }  
            if (!actualDependentBeans.isEmpty()) {  
               throw new BeanCurrentlyInCreationException();  
            }  
         }  
      }  
   }  
  
   // Register bean as disposable.  
   try {  
      registerDisposableBeanIfNecessary(beanName, bean, mbd);  
   }  
   catch (BeanDefinitionValidationException ex) {  
      throw new BeanCreationException(  
            mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);  
   }  
  
   return exposedObject;  
}

需要注意的是,由于存在循环依赖的情况:

  1. 在创建bean后,依赖注入前,会将未完全实例化的bean信息缓存到singletonFactories
  2. 在依赖注入并且执行完回调方法之后,会将完全实例化的bean信息缓存到earlySingletonObjects中,并移除singletonFactories中的缓存。

2.3 获取bean

获取bean的底层方法位于AbstractBeanFactory#doGetBean()方法,主要包括以下步骤:

  1. 解析beanName
  2. 从三级缓存中获取bean,如果存在则直接返回。
  3. 合并BeanDefinition
  4. 根据作用域创建bean
protected <T> T doGetBean(  
      String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)  
      throws BeansException {  
  
   // 获取最原始的beanName:别名处理、"&"前缀(factoryBean)
   String beanName = transformedBeanName(name);  
   Object beanInstance;  
  
   // 依次从获取singletonObjects、earlySingletonObjects和singletonFactories三级缓存中获取(解决循环依赖)
   Object sharedInstance = getSingleton(beanName);  
   if (sharedInstance != null && args == null) {    
      // 已存在,直接返回
      beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);  
   }  
   else {  
      // Fail if we're already creating this bean instance:  
      // We're assumably within a circular reference.      
      if (isPrototypeCurrentlyInCreation(beanName)) {  
         throw new BeanCurrentlyInCreationException(beanName);  
      }  
  
      // 先从父容器中获取
      BeanFactory parentBeanFactory = getParentBeanFactory();  
      if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {  
         // Not found -> check parent.  
         String nameToLookup = originalBeanName(name);  
         if (parentBeanFactory instanceof AbstractBeanFactory) {  
            return ((AbstractBeanFactory) parentBeanFactory).doGetBean(  
                  nameToLookup, requiredType, args, typeCheckOnly);  
         }  
         else if (args != null) {  
            // Delegation to parent with explicit args.  
            return (T) parentBeanFactory.getBean(nameToLookup, args);  
         }  
         else if (requiredType != null) {  
            // No args -> delegate to standard getBean method.  
            return parentBeanFactory.getBean(nameToLookup, requiredType);  
         }  
         else {  
            return (T) parentBeanFactory.getBean(nameToLookup);  
         }  
      }  
      // 标记已创建
      if (!typeCheckOnly) {  
         markBeanAsCreated(beanName);  
      }  
      try {
         // 合并BeanDefinition
         RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);  
         checkMergedBeanDefinition(mbd, beanName, args);  
  
         // Guarantee initialization of beans that the current bean depends on.  
         String[] dependsOn = mbd.getDependsOn();  
         if (dependsOn != null) {  
            for (String dep : dependsOn) {  
               if (isDependent(beanName, dep)) {  
                  throw new BeanCreationException(mbd.getResourceDescription(), beanName,  
                        "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");  
               }  
               // 注册依赖的bean
               registerDependentBean(dep, beanName);  
               try {  
                  // 创建依赖的bean
                  getBean(dep);  
               }  
               catch (NoSuchBeanDefinitionException ex) {  
                  throw new BeanCreationException(mbd.getResourceDescription(), beanName,  
                        "'" + beanName + "' depends on missing bean '" + dep + "'", ex);  
               }  
            }  
         }  
  
         // 创建单例bean,保存到singletonObjects、registeredSingletons缓存,从singletonFactories、earlySingletonObjects移除
         if (mbd.isSingleton()) {  
            sharedInstance = getSingleton(beanName, () -> {  
               try {  
                  return createBean(beanName, mbd, args);  
               }  
               catch (BeansException ex) {             
                  destroySingleton(beanName);  
                  throw ex;  
               }  
            });  
            beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);  
         }  
         // 创建prototype的bean
         else if (mbd.isPrototype()) {  
            // It's a prototype -> create a new instance.  
            Object prototypeInstance = null;  
            try {  
               beforePrototypeCreation(beanName);  
               prototypeInstance = createBean(beanName, mbd, args);  
            }  
            finally {  
               afterPrototypeCreation(beanName);  
            }  
            beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);  
         }  
         // 创建自定义作用域的bean
         else {  
            String scopeName = mbd.getScope();  
            if (!StringUtils.hasLength(scopeName)) {  
               throw new IllegalStateException("No scope name defined for bean '" + beanName + "'");  
            }  
            Scope scope = this.scopes.get(scopeName);  
            if (scope == null) {  
               throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");  
            }  
            try {  
               // 根据自定义作用域的规则创建bean
               Object scopedInstance = scope.get(beanName, () -> {  
                  beforePrototypeCreation(beanName);  
                  try {  
                     return createBean(beanName, mbd, args);  
                  }  
                  finally {  
                     afterPrototypeCreation(beanName);  
                  }  
               });  
               beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);  
            }  
            catch (IllegalStateException ex) {  
               throw new ScopeNotActiveException(beanName, scopeName, ex);  
            }  
         }  
      }  
      catch (BeansException ex) {
         cleanupAfterBeanCreationFailure(beanName);  
         throw ex;  
      }  
      finally {  
         beanCreation.end();  
      }  
   }  
  
   return adaptBeanInstance(name, beanInstance, requiredType);  
}
本文转载于网络 如有侵权请联系删除

相关文章

  • MLX90640 红外热成像仪测温模块开发笔记(五)

    MLX90640红外热成像仪测温模块开发笔记(五)阵列插值-由32*24像素到512*384像素河北稳控科技MLX90640红外热成像仪测温模块MLX90640的32*24=768像素虽然比以往的8*8或者16*8像素提高了很多,但若直接用这些像素还是不能很好的形成热像图,为了使用这些像素点平滑成像就需要对其进行插值,使用更多的像素来绘制图像。看了一些别人的算法,感觉主要就是多项式插值,仅是插值方法的组合方式不同。算法依据比较有代表性的是杭州电子科技大学杨风健等《基于MLX90620的低成本红外热成像系统设计》,使用三次多项式+双线性插值,将原16*4像素扩展为256*64像素。双线性插值的本质就是一次函数(一次多项式)。该文章得到的结论是:(1)双线性插值法计算量小、速度快,但对比度低、细节模糊。(2)三次多项式插值,图像效果较清晰,对比度较高,但计算量较大。(3)先双线性插值再三次多项式插值,效果优于上两种单一插值方法。(4)先三次多项式插值再双线性插值,高低温分布更加明显,图像效果更接趋于真实。同时,该文章还使用了一种对图像质量的评估方法---熵&平均梯度熵,热力学中表征

  • 使用多维存储(全局变量)(二)

    使用多维存储(全局变量)(二)遍历全局变量中的数据有许多方法可以遍历(迭代)存储在全局变量中的数据。$ORDER(下一个/上一个)函数ObjectScript$Order函数允许顺序访问全局中的每个节点。$ORDER函数返回给定级别(下标编号)的下一个下标的值。例如,假设定义了以下全局设置:Set^Data(1)="" Set^Data(1,1)="" Set^Data(1,2)="" Set^Data(2)="" Set^Data(2,1)="" Set^Data(2,2)="" Set^Data(5,1,2)=""复制要查找第一个第一级下标,我们可以使用:SETkey=$ORDER(^Data(""))复制这将返回空字符串(“”)之后的第一个第一级下标。(空字符串用于表示第一个条目之前的下标值;作为返回值,它用于指示没有后续的下标值。)。在本例中,key现在将包含值1。我们可以通过在$ORDER表达式中使用1或键来查找下一个

  • 2022年我应该怎么学习SAP?下篇

    8、哪些人更容易成为SAP顾问?很显然,企业上项目过程中的业务的关键用户、相应it模块人员、以及其他ERP顾问(有宝贵的业务基础)更容易成为SAP顾问。其他的比如SAP普通操作人员跟完全不懂SAP,甚至跟这个行业完全没关系的人,要入行真挺难的。但不管怎么样,既然有目标进入这个行业,现在好好学习,做好准备,迟早有一点会实现自己的理想。自学一样可以入行,一样成为SAP顾问。做任何事情都是看机缘和努力。9、SAP顾问未来的方向在哪里?如果你想往SAP行业一直深耕下去,一直只当SAP顾问从事这个行业,那你得慎重,尤其是ABAP开发人员。很多时候做SAP顾问的同时,应该多多了解一下整个IT行业的环境,有太多的东西值得去学习和了解,如新零售、电商、人工智能、大数据分析、互联网思维、企业规划、中台等等不同的领域。通过对他们的了解,不至于让自己“死读书”一头扎在SAP上面,跟行业趋势脱节。往方案管理,项目管理甚至往管理方向发展都是不错的选择。不必担心失业,SAP这个行业本身有点“越老越吃香”的性质(除非你是猪头三,不思进取)。未来SAP项目会越来越多,现在很多企业在考虑替换ERP为SAP系统,考虑E

  • ApiPost接口调试工具模拟Post上传文件(中文版Postman)

    ApiPost简介:ApiPost是一个支持团队协作,并可直接生成文档的API调试、管理工具。它支持模拟POST、GET、PUT等常见请求,是后台接口开发者或前端、接口测试人员不可多得的工具。ApiPost模拟Post上传文件使用apipost可以方便的模拟Post上传,详细介绍如下1、官方下载安装ApiPost打开Apipost官方链接:https://console.apipost.cn/register?utm_source=100092、注册账号并登陆后,创建一个项目。如图:这两步操作比较简单,没啥说的。我就直接下一步了。3、新建一个接口,url填写我们后台的api地址,在这里我的后台代码如下(PHP):这个后台代码比较简单,就是打印下接受的file数据,相信懂php的你不是问题。4、请求方式选【post】,添加一个参数并类型选择【File】然后发送就可以了。如图:版权声明:本文为腾讯云骑马的少年的原创文章。原文链接:ApiPost接口调试工具模拟Post上传文件(中文版Postman)-云+社区-腾讯云(tencent.com)

  • Doris数据模型

    列可以分为两大类:Key和Value。从业务角度看,Key和Value可以分别对应维度列和指标列。ApacheDoris主要有3种数据模型:明细模型:Duplicate(重复,复制)模型,表中的Key值(类似关系模型中的主键)可以重复,和插入数据行一一对应。聚合模型:Aggregate(聚合,合计)模型,表中key值不重复,对于插入的数据数据按照key值对value值进行聚合函数合并。更新模型:UNIQUE模型,聚合类型的特殊情况,key满足唯一性,最新插入的数据替换掉对应key的数据行。1、明细模型(Duplicate)1.1说明明细模型是DORIS默认使用的数据模型该数据模型不会对导入的数据进行任何处理,保留导入的原始数据明细模型中,可以指定部分的维度列为排序键;而聚合模型和更新模型中,排序键只能是全体维度列事实表中一类事务事实表,用于存储随业务不断产生的数据,一旦产生不再变化。例如交易流水、操作日志、出库入口记录等。这类需求,推荐使用明细模型。1.2样例测试(1)表结构ColumnNameTypeSortKeyCommentvisitor_idINTY访问者IDsession_i

  • Nacos Client 1.4.1 版本踩坑记录

    问题发现 就在这周,我接到MSENacos用户的反馈,说线上Nacos不可用,服务都下线了,日志里面也是一堆报错,我下意识以为线上炸了,赶紧上线排查。本文主要记录这次问题的排查过程,以及解决方案。首先看用户反馈的报错,日志如下:并且用户反馈业务日志也出现了大量的服务地址找不到的报错,说明Nacos服务都下线了。我立刻查看了服务端的监控,发现用户的MSENacos集群并无异常,cpu/内存等指标有下降,并没有异常行为,排除了服务端异常的可能性。随即将视线聚焦在了客户端。老实说,这个报错我第一次见,看异常堆栈,字面意思便是域名解析出问题了。这个报错大概持续了10分钟,立刻让用户在业务节点上使用ping、dig等工具确认域名解析是否正常,测试发现均无异常。继续让用户telnetmse-xx.com8848,发现也能够telnet通。根据这些现象,大概能得出结论:用户的机器上出现了短暂的域名解析问题,导致短时间访问不通MSENacos。但用户继续反馈说,一部分重启以后的机器已经恢复了,但没有重启的机器,竟然还会出现调用报错。不然怎么说重启大法好呢,但也加深了问题的诡异性。正当一筹莫展时,另一用

  • 专业之旅——GitHub 热点速览 Vol.45

    作者:HelloGitHub-小鱼干 从入门到精通需要什么?AI-Expert-Roadmap带你开启专业之旅,和135k+高星项目developer-roadmap一样,AI-Expert-Roadmap这个“后辈”刚开源便获得过4k+star,可见它的专业深得程序员意。同样出色的还有Mastering_Go中文版,HG微博推荐之后便获得大量转发可见它的受欢迎程度。说到专业,高门槛的AI项目PIFuHD本周表现也不俗,凭借这照片变3D模型的professional,一日便获得近1kstar…以下内容摘录自微博@HelloGitHub的GitHubTrending及HackerNews热帖(简称HN热帖),选项标准:新发布|实用|有趣,根据项目release时间分类,发布时间不超过7day的项目会标注New,无该标志则说明项目release超过一周。由于本文篇幅有限,还有部分项目未能在本文展示,望周知?1.本周特推1.1专业之旅:AI-Expert-Roadmap本周star增长数:3350+NewAI-Expert-Roadmap顾名思义是专业AI工程师的Roadmap,包含:数据科

  • 一阶差分序列garch建模_时间序列分析

    参考链接:一阶逻辑解析时间序列的变动 一个时间序列往往是以下几类变化形式的叠加和耦合。 趋势变动:在长时期内按某种规则稳定地呈现出来的持续向上或向下或保持在某一水平。季节变动:在一个年度内重复出现的周期性波动。它是诸如气候条件、生产条件、节假日或人们的风俗习惯等各种因素影响的结果。循环波动:是时间序列呈现出得非固定长度的周期性变动。循环波动的周期可能会持续一段时间,但与趋势不同,它不是朝着单一方向的持续变动,而是涨落相同的交替波动。不规则波动(随机变动):是许多不可控的偶然因素共同作用的结果,致使时间序列产生一种波浪形或震荡式的变动。 随机时序分析的基本概念 随机过程 现象的变化没有确定的形式,没有必然的变化规律。随机过程是随机变量的集合。若一随机系统的样本点是随机函数,则称此函数为样本函数,这一随机系统全部样本函数的集合是一个随机过程。 在研究随机过程时人们透过表面的偶然性描述出必然的内在规律并以概率的形式来描述这些规律,从偶然中悟出必然正是这一学科的魅力所在。 随机变量:简单的随机现象,如某班一天学生出勤人数,是静态的。  随机过程:随机现象的动态变化过程。动态的。如某一时期各个时

  • Shell awk命令

    awk语法结构awk[options]'commands'file(s) option: -F定义字段分割符号 -v定义变量并赋值 command: 1、范围说明或者正则表达式或者{awk命令语句1;awk命令语句2;} 2、范围说明部分可以是BEGIN、END、逻辑表达式或者为空 3、awk命令语句间用分号间隔 4、引用shell变量时需要用双引号引起,命令模式都在单引号''里面 BEGIN{}{}END{} 行处理前行处理行处理后复制字段分割及相关变量$1,$2,$3...$n:awk中用该顺序形式表示files中每行以间隔符号分割的各列的不同字段 $0表示文本本身 NF表示当前记录的字段数(列数) $NF最后一列 $(NF-1)倒数第二列 FNR/NR行号 FILENAME文件名 "\t"制表符 RS/ORS换行符 ""打印字符串 FS/OFS定义间隔符 ~匹配,与==相比不是精确比较 !~不匹配,不精确比较 ==等于,必须全部相等,精确比较 /[0-9][0-9]+/两个或两个以上数字 -F&#

  • LogBack入门实践

    一、简介LogBack是一个日志框架,它是Log4j作者Ceki的又一个日志组件。LogBack,Slf4j,Log4j之间的关系slf4j是TheSimpleLoggingFacadeforJava的简称,是一个简单日志门面抽象框架,它本身只提供了日志FacadeAPI和一个简单的日志类实现,一般常配合Log4j,LogBack,java.util.logging使用。Slf4j作为应用层的Log接入时,程序可以根据实际应用场景动态调整底层的日志实现框架(Log4j/LogBack/JdkLog...);LogBack和Log4j都是开源日记工具库,LogBack是Log4j的改良版本,比Log4j拥有更多的特性,同时也带来很大性能提升。LogBack官方建议配合Slf4j使用,这样可以灵活地替换底层日志框架。LogBack的结构 LogBack分为3个组件,logback-core,logback-classic和logback-access。 其中logback-core提供了LogBack的核心功能,是另外两个组件的基础。 logback-classic则实现了Slf4j的AP

  • 实用技巧!快速教你关闭微信QQ的广告

    微信广告关闭广告现在微信朋友圈都有很多广告?那么怎么去关闭它呢?今天教你怎么关闭微信广告!1.首先,我们打开微信,找到设置中的关于微信2.在关于微信中我们选择最下方的《微信隐私保护指引》3.然后往下滑点击第四条“我们如何使用信息”。注意细看,找到关于广告。4.然后往下滑选择第二条如何管理您看到的广告, 找到标题下面的那两个蓝色的小小的管理二字。5.随后进入腾讯广告个性化管理页面后,我们点击右上角的登陆。 在这里建议选择QQ登录更简单,微信需要扫码选择QQ一键快捷登录之后我们就进入了微信的广告管理页面。选择广告进行关闭一步一步来还是很简单的 在这里我们关闭所有的开关即可关闭广告。 不过呢这里也标注了广告并非是永久关闭, 而只是暂时关闭。 我们也可以今后在广告出现的时候,再重新关闭一次广告。 远离广告从动手做起!

  • 启用Docker虚拟机GPU,加速深度学习

    关于环境配置的文章可以算得上是月经贴了,随便上网一搜,就有大把的文章。但我觉得还是有必要记录一下我最近一次的深度学习环境配置,主要原因在于各种软件在快速更新,对应的安装配置方法也会有一些变化。这篇深度学习环境配置有两个关键词,一个是Docker虚拟机,另一个是GPU加速。开始之前Docker虚拟机首先说一下Docker虚拟机。为什么需要虚拟机?不知道你是否有过这样的经历,在github上看到一个有趣的开源项目,把代码下载下来,按照项目上的说明编译运行,结果发现怎么也不能成功。或者反过来,你开发了一个不错的项目,丢到github,并把编译步骤尽可能详细的写了出来,然而还是有一堆开发者发布issue,说代码编译运行存在问题。你也很无辜啊,明明在我这儿好好的,怎么到了别人那里就状况百出呢?为什么会出现这个状况?主要是软件行业讲究快速迭代,快步向前,软件会不停更新。就拿TensorFlow来说,从发布到现在,不知道更新了多个版本。虽然作为软件开发者会尽力保证向前兼容,但实际上很难做到完美兼容。为了解决这一兼容问题,就有必要使用到虚拟机,现在很多开源项目都会提供一个虚拟机文件,里面包含了所有项目

  • 成为优秀的程序员需要知道的8件事

    1.自我完善尽量发布可以运行的代码。不要指望QA能给你找出所有的程序错误。要经常并且全面深入地测试你的代码,不断找出可以完善的方法。2.公平竞争尝试其他技术、框架、方法和观点。不要总以为只有你的选择才是可行的。别的选择也有可能比你的要强得多。要以开放的心态,来检验其他人的选择。要多多向身边的人学习。说到这里,也给大家推荐一个架构交流学习群:835544715,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,相信对于已经工作和遇到技术瓶颈的码友,在这个群里会有你需要的内容。3.未雨绸缪动手写代码之前要有自己的想法,找出解决问题的方案,要尽量理解要解决的问题。要做原型、从网上查找实例、跟其他也做这个的人讨论或者只是练练手而已。构建你从未做过或用过的东西是个坏习惯。如果开始就有思路,结果可能会更好。4.果断干练不要害怕替换、重写、重构或者放弃垃圾代码。有时候,如果碰到可留可不留的代码,直接扔掉才是上策。永远不要爱上你的代码。要懂得得舍。5.

  • 41个Web开发者都收藏的实用代码

            1.将彻底屏蔽鼠标右键oncontextmenu="window.event.returnValue=false" <tableborderoncontextmenu=return(false)><td>no</table>可用于Table复制        2.取消选取、防止复制<bodyonselectstart="returnfalse">复制        3.JS不允许粘贴onpaste="returnfalse"复制        4.JS防止复制oncopy="returnfalse;"oncut="returnfalse;"复制        5.IE地址栏前换成自己的图标<linkrel="ShortcutIcon"href="favicon.ico">复制在文件的根目录放进去这个图片,后缀修改成ico就可以了        6.可以在收藏夹中显示出你的

  • 移动客户端多音字搜索

    本文首次发表在《程序员》杂志2018年02月期。前言 需求词表方案字表方案客户端索引方案方案优点:实现较为简单可覆盖所有多音字情况方案缺点:索引数据量过大考虑常用汉字一共20777个,其中多音字2659个,多音字占比12.7%,平均每个多音字有2.14个拼音。在微信场景中,联系人的备注和昵称最大字符长度为16个字符,所以我们假设每个昵称的字符为16个汉字,其中,每个汉字的拼音长度为最长度(7个英文字母+1个短拼音英文字母)。一般场景:其中20777个汉字当中,出现在昵称中的概率一样,所以16个字符中,大约会出现3个多音字,得到如下公式:极限场景:昵称中每一个字都是多音字,每个多音字都有4个读音,例如“么么么么么么么么么么么么么么么么”,得到如下公式:从以上两种场景中可以看出,方案三在极限场景中会出现占用超大数据量的情况,所以方案三不可用。索引方案四方案三通过穷举法来列举所有拼音组合,核心在于通过空间换区时间,在所需要的空间过于巨大时,可以采取折中的方案来实现。在汉语中,一个同样意义的实体通过两个不同的词语来表示,称这两个不同的词语为同义词,在数据上表示为(词语A,词语B)=(意义C),

  • 应用程序框架实战二十:映射层超类型

      上一篇介绍了工作单元层超类型的封装演化过程,本文将介绍对EntityFramework映射层超类型的封装。   使用EntityFramework一般需要映射三种类型的对象,即实体、聚合、值对象。   聚合与实体映射的主要区别是:聚合映射单属性标识Id,并需要映射乐观离线锁Version,而实体的标识往往需要映射成复合属性,这样方便物理删除聚合中的实体。EntityFramework通过EntityTypeConfiguration进行实体映射。   值对象以嵌入值模式映射,这需要使用ComplexTypeConfiguration。   封装映射配置并不是必须的,但封装以后可以获得如下好处。   1.辅助记忆。   如果你跟我一样记忆力很差,记不住上面两个类名,那么通过封装一个自定义的类型可以帮助你进行记忆。一旦封装完成,你就可以把系统或第三方的Api扔到一边。   2.划分逻辑结构。   把所有映射代码放到一个方法,不方便阅读,我把它们划分成不同的方法,可以获得更清晰的结构。   3.减少代码冗余。   对于聚合而言,可以把Id标识和Version乐观离线锁封装到层超类型,从而

  • 【算法】并查集模板与练习

    并查集 并查集可以解决“是否畅通问题”,“是否有关系”这一类问题。 模板: #include<bits/stdc++.h> usingnamespacestd; intparent[10010];//祖先,默认-1 voidinit(intn){ for(inti=0;i<n;i++){ parent[i]=-1;//祖先的parent存该集合点数的负值 } } intfind(intx){ /*寻找x的祖先的编号,若自己就是祖先,返回自己,顺便做路径压缩*/ ints; for(s=x;parent[s]>=0;s=parent[s]);//找到了祖先 while(x!=s){ inttmp=parent[x]; parent[x]=s; x=tmp; } returns; } voidUnion(inta,intb){ intr1=find(a),r2=find(b); inttotal=parent[r1]+parent[r2]; if(parent[r1]>parent[r2]){ parent[r1]=r2

  • 编译安装的php 安装pdo_mysql扩展(php版本5.6.29)

    1.进入扩展目录 cd/etc/php-5.6.29/ext/pdo_mysql/复制 注:根据自己情况做适当改变 2.执行phpize /etc/php-5.6.29/scripts/phpize复制 3.编译 ./configure--with-php-config=/etc/php-5.6.29/scripts/php-config--with-pdo-mysql=/usr/bin/mysql复制 --with-php-config=/路径是你已经安装的php的路径下面的bin/php-config--with-pdo-mysql=你的mysql目录(我的mysql为apt-getinstall默认安装的目录) 注:php-config需要赋可执行权限否则会报错找不到php-config 4.make&makeinstall 可能会报错找不到mysql.h文件,默认搜索找不到头文件的位置,这时候需要建立软链接 sudoln-s/usr/include/mysql/*/usr/local/include/复制 执行完毕查看/usr/local/lib/php

  • 安装QTP10.0

    QTP 目录QTP1.QTP简介2.使用win10自带虚拟光驱打开ISO镜像文件3.安装QTP4.汉化 1.QTP简介 ​ QTP是QuickTestProfessional的简称,是一种自动测试工具。使用QTP的目的是想用它来执行重复的自动化测试,主要是用于回归测试和测试同一软件的新版本。因此你在测试前要考虑好如何对应用程序进行测试,例如要测试哪些功能、操作步骤、输入数据和期望的输出数据等。 ​ 下载链接:https://www.veryhuo.com/down/html/221725.html 2.使用win10自带虚拟光驱打开ISO镜像文件 ​ 对于ISO文件,大家都知道它是光盘镜像文件,一般情况下,我们可以通过在光盘上安装虚拟光驱软件来挂载打开ISO镜像文件,或者在系统里安装压缩/解压缩软件对ISO镜像文件进行解压缩,就目前来说,这也是大家经常使用到的两个方法。而对于win10系统,则可以使用自带的”虚拟光驱“(资源管理器)挂载打开ISO镜像文件。 ​ 首先找到QTP10.iso光盘镜像文件,右键它,然后选择打开方式,选择Windows资源管理器。可以看到光盘镜像文件挂载到虚拟

  • JavaScript中解决jQuery和Prototype.js同时引入冲突问题(示例代码)

    1.同时引用jQuery和prototype <head> <metacharset="UTF-8"> <metahttp-equiv="X-UA-Compatible"content="IE=edge"> <metaname="viewport"content="width=device-width,initial-scale=1.0"> <title>Document</title> <!--这里同时引用jQuery和prototype--> <scriptsrc="https://cdn.staticfile.org/jquery/1.8.3/jquery.min.js"></script> <scriptsrc="https://cdn.staticfile.org/prototype/1.7.3/prototype.min.js"></script> </head>复制   解决方法一 在引入jQuery后使用jQuer

  • Nginx七层负载均衡实战

    1.Nginx负载均衡基本概述 ①为什么要使用负载均衡 当我们的Web服务器直接面向用户,往往要承载大量并发请求,单台服务器难以负荷,我使用多台Web服务器组成集群,前端使用Nginx负载均衡,将请求分散的打到我们的后端服务器集群中,实现负载的分发。那么会大大提升系统的吞吐率、请求性能、高容灾。 往往我们接触的最多的是SLB(ServerLoadBalance)负载均衡,实现最多的也是SLB、那么SLB它的调度节点和服务节点通常是在一个地域里面。那么它在这个小的逻辑地域里面决定了他对部分服务的实时性、响应性是非常好的。 所以说当海量用户请求过来以后,它同样是请求调度节点,调度节点将用户的请求转发给后端对应的服务节点,服务节点处理完请求后在转发给调度节点,调度节点最后响应给用户节点。这样也能实现一个均衡的作用,那么Nginx则是一个典型的SLB。 ②负载均衡的叫法 负载均衡 负载 LoadBalance LB 复制 ③公有云中叫法 SLB阿里云负载均衡 QLB青云负载均衡 CLB腾讯云负载均衡 ULBucloud负载均衡 复制 ④常见的负载均衡的软件 Nginx Haproxy LVS

相关推荐

推荐阅读