6. Spring的IoC容器系列

​ IoC容器为开发者管理对象之间的依赖关系提供了很多便利和基础服务。有许多IoC容器供开发者选择,SpringFramework的IoC核心就是其中一个,它是开源的。那具体什么是IoC容器呢?它在Spring框架中到底长什么样?其实对IoC容器的使用者来说,我们经常接触到的BeanFactory和ApplicationContext都可以看成是容器的具体表现形式。如果深入到Spring的实现中去看,我们通常所说的IoC容器,实际上代表着一系列功能各异的容器产品,只是容器的功能有大有小,有各自的特点。我们以水桶为例,在商店中出售的水桶有大有小,制作材料也各不相同,有金属的、塑料的等,总之是各式各样的,但只要能装水,具备水桶的基本特性,那就可以作为水桶来出售,来让用户使用。这在Spring中也是一样,Spring有各式各样的IoC容器的实现供用户选择和使用。使用什么样的容器完全取决于用户的需要,但在使用之前如果能够了解容器的基本情况,那对容器的使用是非常有帮助的,就像我们在购买商品前对商品进行考察和挑选那样。从代码的角度入手,我可以看到关于这一系列容器的设计情况。
图2-1展示了这个容器系列的概况。

图2-1 Spring的IoC容器系列概况

​ 就像商品需要有产品规格说明一样,同样,作为IoC容器,也需要为它的具体实现指定基本的功能规范,这个功能规范的设计表现为接口类BeanFactory,它体现了Spring为提供给用户使用的IoC容器所设定的最基本的功能规范。还是以前面的百货商店出售水桶为例,如果把IoC容器看成一个水桶,那么这个BeanFactory就定义了可以作为水桶的基本功能,比如至少能装水,有个提手等。除了满足基本的功能,为了不同场合的需要,水桶的生产厂家还在这个基础上为用户设计了其他各式各样的水桶产品,以满足不同的用户需求。这些水桶会提供更丰富的功能,有简约型的,有豪华型的,等等。但是,不管是什么水桶,它都需要有一项最基本的功能:能够装水。那对Spring的具体IoC容器实现来说,它需要满足的基本特性是什么呢?它需要满足BeanFactory这个基本的接口定义,所以在图2-1中可以看到这个BeanFactory接口在继承体系中的地位,它是作为一个最基本的接口类出现在Spring的IoC容器体系中的。

​ 在这些Spring提供的基本IoC容器的接口定义和实现的基础上,Spring通过定义BeanDefinition来管理基于Spring的应用中的各种对象以及它们之间的相互依赖关系。BeanDefinition抽象了我们对Bean的定义,是让容器起作用的主要数据类型。我们都知道,在计算机世界里,所有的功能都是建立在通过数据对现实进行抽象的基础上的。IoC容器是用来管理对象依赖关系的,对IoC容器来说,BeanDefinition就是对依赖反转模式中管理的对象依赖关系的数据抽象,也是容器实现依赖反转功能的核心数据结构,依赖反转功能都是围绕对这个BeanDefinition的处理来完成的。这些BeanDefinition就像是容器里装的水,有了这些基本数据,容器才能够发挥作用。在下面的分析中,BeanDefinition的出现次数会很多。
同时,在使用IoC容器时,了解BeanFactory和ApplicationContext之间的区别对我们理解和使用IoC容器也是比较重要的。弄清楚这两种重要容器之间的区别和联系,意味着我们具备了辨别容器系列中不同容器产品的能力。还有一个好处就是,如果需要定制特定功能的容器实现,也能比较方便地在容器系列中找到一款恰当的产品作为参考,不需要重新设计。

6.2 Spring IoC容器的设计

​ 在前面的小节中,我们了解了IoC容器系列的概况。在Spring中,这个IoC容器是怎样设计的呢?我们可以看一下如图2-2所示的IoC容器的接口设计图,这张图描述了IoC容器中的主要接口设计。

图2-2 IoC容器的接口设计图
下面对接口关系做一些简要的分析,可以依据以下内容来理解这张接口设计图。

  • 从接口BeanFactory到HierarchicalBeanFactory,再到ConfigurableBeanFactory,是一条主要的BeanFactory设计路径。在这条接口设计路径中,BeanFactory接口定义了基本的IoC容器的规范。在这个接口定义中,包括了getBean()这样的IoC容器的基本方法(通过这个方法可以从容器中取得Bean)。而HierarchicalBeanFactory接口在继承了BeanFactory的基本接口之后,增加了getParentBeanFactory()的接口功能,使BeanFactory具备了双亲IoC容器的管理功能。在接下来的ConfigurableBeanFactory接口中,主要定义了一些对BeanFactory的配置功能,比如通过setParentBeanFactory()设置双亲IoC容器,通过addBeanPostProcessor()配置Bean后置处理器,等等。通过这些接口设计的叠加,定义了BeanFactory就是简单IoC容器的基本功能。关于BeanFactory简单IoC容器的设计,我们会在后面的内容中详细介绍。
  • 第二条接口设计主线是,以ApplicationContext应用上下文接口为核心的接口设计,这里涉及的主要接口设计有,从BeanFactory到ListableBeanFactory,再到ApplicationContext,再到我们常用的WebApplicationContext或者ConfigurableApplicationContext接口。我们常用的应用上下文基本上都是ConfigurableApplicationContext或者WebApplicationContext的实现。在这个接口体系中,ListableBeanFactory和HierarchicalBeanFactory两个接口,连接BeanFactory接口定义和ApplicationConext应用上下文的接口定义。在ListableBeanFactory接口中,细化了许多BeanFactory的接口功能(它可以枚举所有的bean实例等),比如定义了getBeanDefinitionNames()接口方法;对于HierarchicalBeanFactory接口,我们在前文中已经提到过;对于ApplicationContext接口,它通过继承MessageSource、ResourceLoader、ApplicationEventPublisher接口,在BeanFactory简单IoC容器的基础上添加了许多对高级容器的特性的支持。
  • 这里涉及的是主要接口关系,而具体的IoC容器都是在这个接口体系下实现的,比如DefaultListableBeanFactory,这个基本IoC容器的实现就是实现了ConfigurableBeanFactory,从而成为一个简单IoC容器的实现。像其他IoC容器,比如XmlBeanFactory,都是在DefaultListableBeanFactory的基础上做扩展,同样地,ApplicationContext的实现也是如此。
  • 这个接口系统是以BeanFactory和ApplicationContext为核心的。而BeanFactory又是IoC容器的最基本接口,在ApplicationContext的设计中,一方面,可以看到它继承了BeanFactory接口体系中的ListableBeanFactory、AutowireCapableBeanFactory、HierarchicalBeanFactory等BeanFactory的接口,具备了BeanFactory IoC容器的基本功能;另一方面,通过继承MessageSource、ResourceLoadr、ApplicationEventPublisher这些接口,BeanFactory为ApplicationContext赋予了更高级的IoC容器特性。对于ApplicationContext而言,为了在Web环境中使用它,还设计了WebApplicationContext接口,而这个接口通过继承ThemeSource接口来扩充功能。

1. BeanFactory的应用场景

​ BeanFactory提供的是最基本的IoC容器的功能,关于这些功能定义,我们可以在接口BeanFactory中看到。
​ BeanFactory接口定义了IoC容器最基本的形式,并且提供了IoC容器所应该遵守的最基本的服务契约,同时,这也是我们使用IoC容器所应遵守的最底层和最基本的编程规范,这些接口定义勾画出了IoC的基本轮廓。很显然,在Spring的代码实现中,BeanFactory只是一个接口类,并没有给出容器的具体实现,而我们在图2-1中看到的各种具体类,比如DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等都可以看成是容器附加了某种功能的具体实现,也就是容器体系中的具体容器产品。下面我们来看看BeanFactory是怎样定义IoC容器的基本接口的。

​ 用户使用容器时,可以使用Ctril + 左击来得到FactoryBean本身,用来区分通过容器来获取FactoryBean产生的对象和获取FactoryBean本身。关于具体的FactoryBean的设计和实现模式,我们会在后面的章节中介绍。

​ 注意理解上面这段话需要很好地区分FactoryBean和BeanFactory这两个在Spring中使用频率很高的类,它们在拼写上非常相似。一个是Factory,也就是IoC容器或对象工厂;一个是Bean。在Spring中,所有的Bean都是由BeanFactory(也就是IoC容器)来进行管理的。但对FactoryBean而言,这个Bean不是简单的Bean,而是一个能产生或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似。

​ BeanFactory接口设计了getBean方法,这个方法是使用IoC容器API的主要方法,通过这个方法,可以取得IoC容器中管理的Bean,Bean的取得是通过指定名字来索引的。如果需要在获取Bean时对Bean的类型进行检查,BeanFactory接口定义了带有参数的getBean方法,这个方法的使用与不带参数的getBean方法类似,不同的是增加了对Bean检索的类型的要求。

​ 用户可以通过BeanFactory接口方法中的getBean来使用Bean名字,从而在获取Bean时,如果需要获取的Bean是prototype类型的,用户还可以为这个prototype类型的Bean生成指定构造函数的对应参数。这使得在一定程度上可以控制生成prototype类型的Bean。有了BeanFactory的定义,用户可以执行以下操作:

  • 通过接口方法containsBean让用户能够判断容器是否含有指定名字的Bean。

  • 通过接口方法isSingleton来查询指定名字的Bean是否是Singleton类型的Bean。对于Singleton属性,用户可以在BeanDefinition中指定。

  • 通过接口方法isPrototype来查询指定名字的Bean是否是prototype类型的。与Singleton属性一样,这个属性也可以由用户在BeanDefinition中指定。

  • 通过接口方法isTypeMatch来查询指定了名字的Bean的Class类型是否是特定的Class类型。这个Class类型可以由用户来指定。

  • 通过接口方法getType来查询指定名字的Bean的Class类型。

  • 通过接口方法getAliases来查询指定了名字的Bean的所有别名,这些别名都是用户在BeanDefinition中定义的。

    这些定义的接口方法勾画出了IoC容器的基本特性。因为这个BeanFactory接口定义了IoC容器,所以下面给出它定义的全部内容供大家参考,如代码清单2-1所示。

代码清单2-1 BeanFactory接口

public interface BeanFactory {String FACTORY_BEAN_PREFIX = "&";Object getBean(String name) throws BeansException;<T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;Object getBean(String name, Object... args) throws BeansException;<T> T getBean(Class<T> requiredType) throws BeansException;<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;boolean containsBean(String name);boolean isSingleton(String name) throws NoSuchBeanDefinitionException;boolean isPrototype(String name) throws NoSuchBeanDefinitionException;boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;Class<?> getType(String name) throws NoSuchBeanDefinitionException;String[] getAliases(String name);}

​ 可以看到,这里定义的只是一系列的接口方法,通过这一系列的BeanFactory接口,可以使用不同的Bean的检索方法,很方便地从IoC容器中得到需要的Bean,从而忽略具体的IoC容器的实现,从这个角度上看,这些检索方法代表的是最为基本的容器入口。

2. BeanFactory容器的设计原理

​ BeanFactory接口提供了使用IoC容器的规范。在这个基础上,Spring还提供了符合这个IoC容器接口的一系列容器的实现供开发人员使用。我们以XmlBeanFactory的实现为例来说明简单IoC容器的设计原理。如图2-3所示为XmlBeanFactory设计的类继承关系。

图2-3 XmlBeanFactory设计的类继承关系

​ 可以看到,作为一个简单IoC容器系列最底层实现的XmlBeanFactory,与我们在Spring应用中用到的那些上下文相比,有一个非常明显的特点:它只提供最基本的IoC容器的功能。理解这一点有助于我们理解ApplicationContext与基本的BeanFactory之间的区别和联系。我们可以认为直接的BeanFactory实现是IoC容器的基本形式,而各种ApplicationContext的实现是IoC容器的高级表现形式。关于ApplicationContext的分析,以及它与BeanFactory相比的增强特性都会在下面进行详细的分析。

​ 让我们好好地看一下图2-3中的继承关系,从中可以清楚地看到类之间的联系,它们都是IoC容器系列的组成部分。在设计这个容器系列时,我们可以从继承体系的发展上看到IoC容器各项功能的实现过程。如果要扩展自己的容器产品,建议读者最好在继承体系中检查一下,看看Spring是不是已经提供了现成的或相近的容器实现供我们参考。下面就从我们比较熟悉的XmlBeanFactory的实现入手进行分析,来看看一个基本的IoC容器是怎样实现的。

​ 仔细阅读XmlBeanFactory的源码,在一开始的注释里会看到对XmlBeanFactory功能的简要说明,从代码的注释还可以看到,这是Rod Johnson在2001年就写下的代码,可见这个类应该是Spring的元老类了。XmlBeanFactory继承自DefaultListableBeanFactory这个类,后者非常重要,是我们经常要用到的一个IoC容器的实现,比如在设计应用上下文ApplicationContext时就会用到它。我们会看到这个DefaultListableBeanFactory实际上包含了基本IoC容器所具有的重要功能,也是在很多地方都会用到的容器系列中的一个基本产品。

​ 在Spring中,实际上是把DefaultListableBeanFactory作为一个默认的功能完整的IoC容器来使用的。XmlBeanFactory在继承了DefaultListableBeanFactory容器的功能的同时,增加了新的功能,这些功能很容易从XmlBeanFactory的名字上猜到。它是一个与XML相关的BeanFactory,也就是说它是一个可以读取以XML文件方式定义的BeanDefinition的IoC容器。

​ 这些实现XML读取的功能是怎样实现的呢?对这些XML文件定义信息的处理并不是由XmlBeanFactory直接完成的。在XmlBeanFactory中,初始化了一个XmlBeanDefinitionReader对象,有了这个Reader对象,那些以XML方式定义的BeanDefinition就有了处理的地方。我们可以看到,对这些XML形式的信息的处理实际上是由这个XmlBeanDefinitionReader来完成的。

​ 构造XmlBeanFactory这个IoC容器时,需要指定BeanDefinition的信息来源,而这个信息来源需要封装成Spring中的Resource类来给出。Resource是Spring用来封装I/O操作的类。比如,我们的BeanDefinition信息是以XML文件形式存在的,那么可以使用像“ClassPathResource res = new ClassPathResource(“beans.xml”);”这样具体的ClassPathResource来构造需要的Resource,然后将Resource作为构造参数传递给XmlBeanFactory构造函数。这样,IoC容器就可以方便地定位到需要的BeanDefinition信息来对Bean完成容器的初始化和依赖注入过程。
XmlBeanFactory的功能是建立在DefaultListableBeanFactory这个基本容器的基础上的,并在这个基本容器的基础上实现了其他诸如XML读取的附加功能。对于这些功能的实现原理,看一看XmlBeanFactory的代码实现就能很容易地理解。如代码清单2-2所示,在XmlBeanFactory构造方法中需要得到Resource对象。对XmlBeanDefinitionReader对象的初始化,以及使用这个对象来完成对loadBeanDefinitions的调用,就是这个调用启动从Resource中载入BeanDefinitions的过程,LoadBeanDefinitions同时也是IoC容器初始化的重要组成部分。

代码清单2-2 XmlBeanFactory的实现

public class XmlBeanFactory extends DefaultListableBeanFactory {private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);public XmlBeanFactory(Resource resource) throws BeansException {this(resource, null);}public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {super(parentBeanFactory);this.reader.loadBeanDefinitions(resource);}
}

​ 我们看到XmlBeanFactory使用了DefaultListableBeanFactory作为基类,DefaultListable-BeanFactory是很重要的一个IoC实现,在其他IoC容器中,比如ApplicationContext,其实现的基本原理和XmlBeanFactory一样,也是通过持有或者扩展DefaultListableBeanFactory来获得基本的IoC容器的功能的。
​ 参考XmlBeanFactory的实现,我们以编程的方式使用DefaultListableBeanFactory。从中我们可以看到IoC容器使用的一些基本过程。尽管我们在应用中使用IoC容器时很少会使用这样原始的方式,但是了解一下这个基本过程,对我们了解IoC容器的工作原理是非常有帮助的。因为这个编程式使用容器的过程,很清楚揭示了在IoC容器实现中的那些关键的类(比如Resource、DefaultListableBeanFactory和BeanDefinitionReader)之间的相互关系,例如它们是如何把IoC容器的功能解耦的,又是如何结合在一起为IoC容器服务的,等等。在代码清单2-3中可以看到编程式使用IoC容器的过程。

代码清单2-3 编程式使用IoC容器

ClassPathResource res = new ClassPathResource("beans.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(res);

​ 这样,我们就可以通过factory对象来使用DefaultListableBeanFactory这个IoC容器了。在使用IoC容器时,需要如下几个步骤:

  • 1)创建IoC配置文件的抽象资源,这个抽象资源包含了BeanDefinition的定义信息。
  • 2)创建一个BeanFactory,这里使用DefaultListableBeanFactory。
  • 3)创建一个载入BeanDefinition的读取器,这里使用XmlBeanDefinitionReader来载入XML文件形式的BeanDefinition,通过一个回调配置给BeanFactory。
  • 4)从定义好的资源位置读入配置信息,具体的解析过程由XmlBeanDefinitionReader来完成。完成整个载入和注册Bean定义之后,需要的IoC容器就建立起来了。这个时候就可以直接使用IoC容器了。

3. ApplicationContext的应用场景

​ 上一节中我们了解了IoC容器建立的基本步骤。理解这些步骤之后,可以很方便地通过编程的方式来手工控制这些配置和容器的建立过程了。但是,在Spring中,系统已经为用户提供了许多已经定义好的容器实现,而不需要开发人员事必躬亲。相比那些简单拓展BeanFactory的基本IoC容器,开发人员常用的ApplicationContext除了能够提供前面介绍的容器的基本功能外,还为用户提供了以下的附加服务,可以让客户更方便地使用。所以说,ApplicationContext是一个高级形态意义的IoC容器,如图2-4所示,可以看到ApplicationContext在BeanFactory的基础上添加的附加功能,这些功能为ApplicationContext提供了以下BeanFactory不具备的新特性。

图2-4 ApplicationContext的接口关系

  • 支持不同的信息源。我们看到ApplicationContext扩展了MessageSource接口,这些信息源的扩展功能可以支持国际化的实现,为开发多语言版本的应用提供服务。
  • 访问资源。这一特性体现在对ResourceLoader和Resource的支持上,这样我们可以从不同地方得到Bean定义资源。这种抽象使用户程序可以灵活地定义Bean定义信息,尤其是从不同的I/O途径得到Bean定义信息。这在接口关系上看不出来,不过一般来说,具体ApplicationContext都是继承了DefaultResourceLoader的子类。因为DefaultResourceLoader是AbstractApplicationContext的基类,关于Resource在IoC容器中的使用,后面会有详细的讲解。
  • 支持应用事件。继承了接口ApplicationEventPublisher,从而在上下文中引入了事件机制。这些事件和Bean的生命周期的结合为Bean的管理提供了便利。
  • 在ApplicationContext中提供的附加服务。这些服务使得基本IoC容器的功能更丰富。因为具备了这些丰富的附加功能,使得ApplicationContext与简单的BeanFactory相比,对它的使用是一种面向框架的使用风格,所以一般建议在开发应用时使用ApplicationContext作为IoC容器的基本形式。

4. ApplicationContext容器的设计原理

​ 在ApplicationContext容器中,我们以常用的FileSystemXmlApplicationContext的实现为例来说明ApplicationContext容器的设计原理。
​ 在FileSystemXmlApplicationContext的设计中,我们看到ApplicationContext应用上下文的主要功能已经在FileSystemXmlApplicationContext的基类AbstractXmlApplicationContext中实现了,在FileSystemXmlApplicationContext中,作为一个具体的应用上下文,只需要实现和它自身设计相关的两个功能。
​ 一个功能是,如果应用直接使用FileSystemXmlApplicationContext,对于实例化这个应用上下文的支持,同时启动IoC容器的refresh()过程。这在FileSystemApplicationContext的代码实现中可以看到,代码如下:

public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)throws BeansException {super(parent);setConfigLocations(configLocations);if (refresh) {refresh();}
}

​ 这个refresh()过程会牵涉IoC容器启动的一系列复杂操作,同时,对于不同的容器实现,这些操作都是类似的,因此在基类中将它们封装好。所以,我们在FileSystemXml的设计中看到的只是一个简单的调用。关于这个refresh()在IoC容器启动时的具体实现,是后面要分析的主要内容,这里就不详细展开了。

​ 另一个功能是与FileSystemXmlApplicationContext设计具体相关的功能,这部分与怎样从文件系统中加载XML的Bean定义资源有关。
​ 通过这个过程,可以为在文件系统中读取以XML形式存在的BeanDefinition做准备,因为不同的应用上下文实现对应着不同的读取BeanDefinition的方式,在FileSystemXmlApplicationContext中的实现代码如下:

protected Resource getResourceByPath(String path) {if (path.startsWith("/")) {path = path.substring(1);}return new FileSystemResource(path);}

可以看到,调用这个方法,可以得到FileSystemResource的资源定位。

[Spring 深度解析]第6章 Spring的IoC容器系列相关推荐

  1. [Spring 深度解析]第5章 Spring之DAO

    第5章 ◄Spring之DAO► ​ 在上一章节中,我们了解了Spring框架中的AOP模块,这一章节我们开始学习Spring框架中的DAO模块. 本章主要涉及的知识点: ​ ● JDBC基本用法:S ...

  2. [Spring 深度解析]第4章 Spring之AOP

    第4章 ◄Spring之AOP► 在上一章节中,我们大致了解了Spring核心容器,了解了IOC思想在Spring中的具体应用Bean容器以及Bean的配置与使用,这一章我们将开始学习AOP在Spri ...

  3. [Spring 深度解析]第2章 Spring基础

    第2章 ◄Spring基础► ​ 在上一章节中,我们学习了Java的注解与反射,在这一章节我们将了解一下Spring框架,并学习Spring框架中重要的编程思想控制反转(IOC).面向切面编程(AOP ...

  4. [Spring 深度解析]第7章 IoC容器的初始化过程

    7. IoC容器的初始化过程 ​ 简单来说,IoC容器的初始化是由前面介绍的refresh()方法来启动的,这个方法标志着IoC容器的正式启动.具体来说,这个启动包括BeanDefinition的Re ...

  5. [Spring 深度解析]第3章 核心容器

    第3章 ◄核心容器► ​ 在上一章节中,我们大致了解了Spring框架,并学习了控制反转(IOC)和面向切面编程(AOP)两个重要的编程思想,这一章我们将开始学习Spring框架中的核心容器. 本章主 ...

  6. [Spring 深度解析]第1章 Java基础

    第1章 ◄Java基础► 在学习Spring之前我们需要对Java基础语法有一定的了解,Java中最重要的两个知识点是注解和反射.注解和反射在Spring框架中应用的最广泛.掌握注解和反射,有助于后面 ...

  7. deque用法深度解析,一篇文章弄懂deque容器各种操作

  8. Spring 的IOC容器系列的设计与实现:BeanFactory 和 ApplicationContext

    在Spring IOC容器的设计中,我们可以看到两个主要的容器系列,一个是实现BeanFactory接口的简单容器系列,这系列容器只实现了容器的最基本的功能,另一个是ApplicationContex ...

  9. 【Spring】IoC容器系列的设计与实现:BeanFactory和ApplicationContext

    在Spring IoC容器的设计中,我们可以看到两个主要的容器系列,一个是实现BeanFactory接口的简单容器系列,这系列容器只实现了容器的最基本功能,另一个是ApplicationContext ...

最新文章

  1. 笔记本通过网线连接并控制工控机
  2. 求助!!让我郁闷纠结恨的状况!!!
  3. Go 源码阅读笔记 text/template/parse
  4. 安卓之页面跳转与传值和按钮事件
  5. 微信公众号开发之准备工作
  6. WCF后续之旅(10): 通过WCF Extension实现以对象池的方式创建Service Instance
  7. python 遍历删除
  8. OpenCV4 DNN模块 Python APIs
  9. 自动登录126邮箱的脚本
  10. jQuery的load()方法
  11. SAP Spartacus Popover Component 显示与否的逻辑判定
  12. “遇事不决,量子力学”?微软亚研院开源时空预测开源工具:FOST,应对各行业共性预测需求!...
  13. [改善Java代码]不推荐使用binarySearch对列表进行检索
  14. 专访黑石集团CEO苏世民:“我们能见他人所不能见”/巴伦独家
  15. 基于RDP瘦客户机协议的简要说明
  16. python音频提取pcm_python 实现录音pcm格式功能
  17. 你问我DataX是谁?对不起,我活在Apache SeaTunnel的时代!
  18. request.getParameterValues()用法
  19. web前端如何才能成为架构师
  20. 一、对文本文件进行数据粒度转换,即将文本文件personnel_data.txt中字段household_register的数据统一成省份,并且输出到文本文档personnel_data_new.tx

热门文章

  1. 你之所以没成为成就,就是因为太刻苦了!
  2. USEARCH11新功能简介
  3. QIIME 2用户文档. 13训练特征分类器Training feature classifiers(2019.7)
  4. Nature: 海洋病毒对环境基因组和潜在的生物地球化学影响
  5. 用polt3画曲面_用SolidWorks建模一个:防滑板曲面造型
  6. pandas自定义设置dataframe每个索引的标签、自定义设置索引的列名称(customize index name and index label)
  7. R语言ggplot2包和lattice包可视化改变x轴和y轴的显示位置实战
  8. 使用聚类算法进行标签传播学习(Clustering for Semi-Supervised Learning)
  9. 循环神经网络(RNN、LSTM、GRU)
  10. tensorflow.python.framework.errors_impl.InvalidArgumentError: 2 root error(s) found.