在Spring IoC容器的设计中,我们可以看到两个主要的容器系列,一个是实现BeanFactory接口的简单容器系列,这系列容器只实现了容器的最基本功能,另一个是ApplicationContext应用上下文,它作为容器的高级形态而存在。应用上下文在简单容器的基础上,增加了许多面向框架的特性,同时对应用环境做了许多适配。有了这两种基本的容器系列,基本上可以满足用户对IoC容器使用的大部分需求了。下面,我们就对Spring IoC容器中这两种容器系列的设计与实现进行一个简要的分析。

Spring的IoC容器系列

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

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

在这些Spring提供的基本IoC容器的接口定义和实现的基础上,Spring通过定义BeanDefinition来管理基于Spring的应用中的各种对象以及它们之间的相互依赖关系。BeanDefinition抽象了我们对Bean的定义,是让容器起作用的主要数据类型。我们都知道,在计算机世界里,所有的功能都是建立在通过数据对现实进行抽象的基础上的。IoC容器是用来管理对象依赖关系的,对IoC容器来说,BeanDefinition就是对依赖反转模式中管理的对象依赖关系的数据抽象,也是容器实现依赖反转功能的核心数据结构,依赖反转功能都是围绕对这个BeanDefinition的处理来完成的。这些BeanDefinition就像是容器里装的水,有了这些基本数据,容器才能够发挥作用。

同时,在使用IoC容器时,了解BeanFactory和ApplicationContext之间的区别对我们理解和使用IoC容器也是比较重要的。弄清楚这两种重要容器之间的区别和联系,意味着我们具备了辨别容器系列中不同容器产品的能力。还有一个好处就是,如果需要定制特定功能的容器实现,也能比较方便地在容器系列中找到一款恰当的产品作为参考,不需要重新设计。

Spring IoC容器的设计

在上面,我们了解了IoC容器系列的概况。在Spring中,这个IoC容器是怎样设计的呢?

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

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

1、BeanFactory的应用场景

BeanFactory提供的是最基本的IoC容器的功能,关于这些功能定义,我们可以在接口BeanFactory中看到。

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

用户使用容器时,可以使用转义符 “ & ” 来得到FactoryBean本身,用来区分通过容器来获取FactoryBean产生的对象和获取FactoryBean本身。 举例来说,如果myJdniObject是一个FactoryBean,那么使用 &myJndiObject 得到的是FactoryBean,而不是myJndiObject这个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类型的Bean。对于Singleton属性,用户可以在BeanDefinition中指定。
  • 通过接口方法isTypeMatch来查询指定了名字的Bean的Class类型是否是特定的Class类型。这个Class类型可以由用户指定。
  • 通过接口方法 getType来查询指定名字的Bean的Class类型。
  • 通过接口方法getAliases来查询指定了名字的Bean的所有别名,这些别名都是用户在BeanDefinition中定义的。

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

package org.springframework.beans.factory;import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;public interface BeanFactory {String FACTORY_BEAN_PREFIX = "&";Object getBean(String name) throws BeansException;<T> T getBean(String name, 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;<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);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, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;@NullableClass<?> getType(String name) throws NoSuchBeanDefinitionException;@NullableClass<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;String[] getAliases(String name);
}

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

2、BeanFactory容器的设计原理

BeanFactory接口提供了使用IoC容器的规范。在这个基础上,Spring还提供了符合这个IoC容器接口的一系列容器的实现供开发人员使用。我们以XMLBeanFactory的实现为例来说明简单IoC容器的设计原理。

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

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

仔细阅读XmlBeanFactory的源码,在一开始的注释里会看到对XmlBeanFactory功能的简要说明,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形式的信息的处理实际上是由这个XmlBeanDefintionReader来完成的。

构造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的代码实现就能很容易地理解。在XmlBeanFactory构造方法中需要得到Resource对象。对XmlBeanDefinitionReader对象的初始化,以及使用这个对象来完成对loadBeanDefinitions的调用,就是这个调用启动从Resource中载入BeanDefinitions的过程,LoadBeanDefinitions同时也是IoC容器初始化的重要组成部分。

package org.springframework.beans.factory.xml;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.core.io.Resource;@Deprecated
@SuppressWarnings({"serial", "all"})
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作为基类,DefaultListableBeanFactory是很重要的一个IoC实现,在其他IoC容器中,比如ApplicationContext,其实现的基本原理和XmlBeanFactory一样,也是通过持有或者扩展DefaultListableBeanFactory来获得基本的IoC容器的功能的。

参考XmlBeanFactory的实现,我们以编程的方式使用DefaultListableBeanFactory。从中我们可以看到IoC容器使用的一些基本过程。尽管我们在应用中使用IoC容器时很少会使用这样原始的方式,但是了解一下这个基本过程,对我们了解IoC容器的工作原理是非常有帮助的。因为这个编程式使用容器的过程,很清楚的揭示了在IoC容器实现中的那么关键的类(比如Resource、DefaultListableBeanFactory和BeanDefinitionReader)之间的相互关系,例如她们是如何把IoC容器的功能解耦的,又是如何结合在一起为IoC容器服务的,等等。下面的代码中,可以看到编程式使用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容器,这些功能为ApplicationContext提供了以下BeanFactory不具备的新特性。

  • 支持不同的信息源。我们看到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的实现为例来说明Application容器的设计原理。

在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中的实现代码如下:

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

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

以上就是Spring IoC容器的设计,下面开始我们来说说Spring IoC容器的初始化过程。

【Spring】IoC容器系列的设计与实现:BeanFactory和ApplicationContext相关推荐

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

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

  2. Spring IoC容器总结(未完)

    在面向对象系统中,对象封装了数据和对数据的处理,对象的依赖关系常常体现在对数据和方法的依赖上.这些依赖关系可以通过把对象的依赖注入交给框架或IOC容器来完成,这种从具体对象手中交出控制的做法是非常有价 ...

  3. spring ioc容器创建过程-整体流程

    文章目录 一.spring ioc 创建总览 二.BeanFactory和ApplicationContext 三.初始化DefaultListableBeanFactory 四.总结: 前面写了四篇 ...

  4. [Spring 深度解析]第6章 Spring的IoC容器系列

    6. Spring的IoC容器系列 ​ IoC容器为开发者管理对象之间的依赖关系提供了很多便利和基础服务.有许多IoC容器供开发者选择,SpringFramework的IoC核心就是其中一个,它是开源 ...

  5. Spring IOC 容器源码分析系列文章导读

    1. 简介 前一段时间,我学习了 Spring IOC 容器方面的源码,并写了数篇文章对此进行讲解.在写完 Spring IOC 容器源码分析系列文章中的最后一篇后,没敢懈怠,趁热打铁,花了3天时间阅 ...

  6. Spring IOC 容器源码分析系列文章导读 1

    1. 简介 Spring 是一个轻量级的企业级应用开发框架,于 2004 年由 Rod Johnson 发布了 1.0 版本.经过十几年的迭代,现在的 Spring 框架已经非常成熟了.Spring ...

  7. Spring IoC容器设计与实现——IoC容器的依赖注入

    Spring IoC容器设计与实现--IoC容器的初始化过程 依赖注入的过程是用户第一次向IoC容器索要Bean时触发的,当然也有例外,就是可以通过控制lazy-init属性来让容器完成对Bean的预 ...

  8. [Spring实战系列](6)配置Spring IOC容器的Bean

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/SunnyYoona/article/details/50619900 1. 简介 Spring提供了 ...

  9. spring bean加载过程_Spring源码剖析3:Spring IOC容器的加载过程

    本文转自五月的仓颉 https://www.cnblogs.com/xrq730 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https ...

最新文章

  1. 计算机ui答辩,KGUT1027 班级UI设计第一阶段成长答辩开始啦
  2. 1、深入理解计算机系统 笔记,系统综述
  3. java启动100线程_Java启动新线程的几种方式(Runnable、Callable、CompletableFuture)
  4. 优化SQL查询:如何写出高性能SQL语句
  5. git gui怎么拉取项目代码_Git GUI使用方法【转】
  6. 【渝粤题库】陕西师范大学400013 组织行为学 作业(专升本)
  7. tex中让公式和文字在一行_1行代码搞定LaTeX公式编写,这个4.6M的Python小插件,堪称论文必备神器...
  8. 科普:为什么 String hashCode 方法选择数字31作为乘子
  9. 装饰效果(最大连续字段和)
  10. 床头千万不要有电磁波
  11. matlab太阳影子定位,基于MATLAB软件的太阳影子定位
  12. 开源的网页防篡改监控工具推荐——WGCLOUD
  13. 6个实例电路,详解雷击浪涌的防护
  14. windows常用快捷键与快捷指令
  15. pig的安装、pig的数据模型和pig的常用命令
  16. 姜小白的Python日记day1 初识Python
  17. Docker入门教程 Part 1 基础概念 - 镜像、容器、仓库
  18. Spring创建Bean的流程
  19. ubuntu: 查看cuda版本
  20. 数说故事「行业热点快报」连锁餐饮行业企业动态

热门文章

  1. 成功的10000小时定律
  2. 【Python常用函数合集】clip函数、range函数等
  3. 每天15分钟中度或快步走以燃烧100卡路里的热量
  4. Udacity 人工智能编程基础 - 课程目录
  5. 物联网的那些事----------01无线通信技术介绍
  6. php微信摇一摇开发文档,摇一摇关注的JSAPI
  7. 计算机usb无法读取u盘启动,U盘无法识别的USB设备怎么办解决
  8. win10怎么不能运行c语言,Win10系统打开游戏出现程序无法正常启动0xc0000142解决方法...
  9. 求解高维优化问题的改进正弦余弦算法
  10. 医学影像分割论文合集