一. 前言

Spring容器主要分为两类BeanFactory和ApplicationContext,后者是基于前者的功能扩展,也就是一个基础容器和一个高级容器的区别。本篇就以BeanFactory基础容器接口的默认实现类XmlBeanFactory启动流程分析来入门Spring源码的学习。

二. 概念要点

1. 概念定义

  • BeanDefinition:Bean元数据描述,Bean在Spring IOC容器中的抽象,是Spring的一个核心概念
  • DefaultListableBeanFactory : Spring IOC容器的实现,可以作为一个独立使用的容器, Spring IOC容器的始祖
  • XmlBeanFactory:继承自DefaultListableBeanFactory,与其不同点在于XmlBeanFactory中使用了自定义的XML读取器XmlBeanDefinitionReader,实现了个性化的BeanDefinitionReader读取
  • ApplicationContext: 高级容器定义接口,基于BeanFactory添加了扩展功能,如ResourceLoader、MessageSource、ApplicationEventPublisher等

2. 糟糕!XmlBeanFactory被废弃了

对Spring有些了解的应该XmlBeanFactory已经过时了。没错,本篇要讲的XmlBeanFactory在Spring3.1这个很久远版本就开始过时了。

@deprecated as of Spring 3.1 in favor of {@link DefaultListableBeanFactory}

取而代之的写法如下

ClassPathResource resource=new ClassPathResource("spring-config.xml");
DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
XmlBeanDefinitionReader beanDefinitionReader=new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.loadBeanDefinitions(resource);

概括DefaultListableBeanFactory + XmlBeanDefinitionReader 取代了 XmlBeanFactory容器的创建和初始化,可以联想到通过组合的方式灵活度是比XmlBeanFactory高的,针对不同的资源读取组合的方式只需替换不同的读取器BeanDefinitionReader就可以了,而XmlBeanFactory中的读取器XmlBeanDefinitionReader限定其只能读取XML格式的文件资源,所以至于XmlBeanFactory被废弃的原因可想而知。

3. XmlBeanFactory?!XML,你会XML解析吗?

<?xml version="1.0" encoding=" UTF-8" standalone="yes"?><root>
<code>0</code>
<message>调用成功</message>
</root>

附上DOM4J解析的代码

String xml="<?xml version=\"1.0\" encoding=\" UTF-8\" standalone=\"yes\"?><root>\n" +
"<code>0</code>\n" +
"<message>调用成功</message>\n" +
"</root>";
Document document = DocumentHelper.parseText(xml);
Element root = document.getRootElement();
String code = root.elementText("code");
String message =root.elementText("message");

XML文档被解析成DOM树,其中Document是整个DOM的根节点,root为根元素,由根元素一层一层向下解析element元素,容器启动解析XML流程就是这样。

三. XmlBeanFactory启动流程分析

XmlBeanFactory容器启动就两行代码

ClassPathResource resource = new ClassPathResource("spring-config.xml");
XmlBeanFactory beanFactory = new XmlBeanFactory(resource);

怎么样?看似简单就证明了Java封装的强大,但背后藏了太多。 这里就送上XmlBeanFactory启动流程图,对应的就是上面的两行代码。

这是啥?!看得头晕的看官老爷们别急着给差评啊(瑟瑟发抖中)。这里精简下,想快速理解的话直接看精简版后的,想深入到细节建议看下上面的时序图。



整个就是bean的加载阶段。通过解析XML中的标签元素生成beanDefinition注册到beanDefinitionMap中。

四. XmlBeanFactory启动源码解析

按照XmlBeanFactory启动流程的先后顺序整理的关键性代码索引列表,其中一级索引为类,二级索引对应其类下的方法。符号 ---▷表示接口的实现。建议可以观察方法索引的参数变化(资源转换)来分析整个流程,来加深对流程的理解。

  1. XmlBeanFactory

    1. XmlBeanFactory(Resource resource)
  2. XmlBeanDefinitionReader

    1. loadBeanDefinitions(Resource resource)
    2. doLoadBeanDefinitions(InputSource inputSource, Resource resource)
    3. registerBeanDefinitions(Document doc, Resource resource)
  3. DefaultBeanDefinitionDocumentReader ---▷ BeanDefinitionDocumentReader

    1. registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
    2. doRegisterBeanDefinitions(Element root)
    3. parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)
    4. parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate)
    5. processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)
  4. BeanDefinitionParserDelegate

    1. parseBeanDefinitionElement(Element ele)
    2. decorateBeanDefinitionIfRequired( Element ele, BeanDefinitionHolder originalDef,...)
  5. DefaultListableBeanFactory ---▷ BeanDefinitionRegistry

    1. registerBeanDefinition(String beanName, BeanDefinition beanDefinition)

好了,当你心里对这个流程有个大概的样子之后,这里就可以继续走下去。建议还是很模糊的同学自重,避免走火入魔想对我人身攻击的。下面就每个方法重要的点一一解析,严格按照方法的执行先后顺序来说明。

1.1 XmlBeanFactory(Resource resource)

功能概述: XmlBeanFactory的构造方法,整个容器启动的入口,完成bean工厂的实例化和BeanDefinition加载(解析和注册)。

/**
* XmlBeanFactory
**/
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
} public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
// 加载(解析注册)BeanDefinition
this.reader.loadBeanDefinitions(resource);
}

知识点:

  1. Resource:Resource接口是Spring资源访问策略的抽象,,而具体的资源访问方式由其实现类完成,如类路径资源(ClassPathResource)、文件(FileSystemResource)、URL资源(UrlResource)、InputStream资源(InputStreamResource)、Byte数组(ByteArrayResource),根据不同的类型的资源使用对应的访问策略,明明白白的策略模式。

2.1 loadBeanDefinitions(Resource resource)

功能概述: 上面根据ClassPathResource资源访问策略拿到了资源Resource,这里将Resource进行特定的编码处理,然后将编码后的Resource转换成SAX解析XML文件所需要的输入源InputSource。

/**
* XmlBeanDefinitionReader
**/
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
// 把从XML文件读取的Resource资源进行编码处理
return loadBeanDefinitions(new EncodedResource(resource));
} /**
* XmlBeanDefinitionReader
**/
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
...
// 完成resource->inputStream->inputSource转换,使用SAX方式接收输入源InputSource解析XML
try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
// 执行加载BeanDefinition
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
...
}

知识点:

  1. XML解析两种方式:SAX(Simple API for XML)和DOM(Document Object Model),区别在于SAX按照顺序逐行读取,查找到符合条件即停止,只能读不能修改,适合解析大型XML;DOM则是一次性读取到内存建立树状结构,占用内存,不仅能读还能修改XML。Spring采用的SAX方式来解析XML。

2.2 doLoadBeanDefinitions(InputSource inputSource, Resource resource)

功能概述: 获取DOM Document对象,XmlBeanDefinitionReader本身没有对文档读取的能力,而是委托给DocumentLoader的实现类DefaultDocumentLoader去读取输入源InputResource从而得到Document对象。获得Document对象后,接下来就是BeanDefinition的解析和注册。

/**
* XmlBeanDefinitionReader
**/
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
...
try {
// inputSource->DOM Document
Document doc = doLoadDocument(inputSource, resource);
int count = registerBeanDefinitions(doc, resource);
return count;
}
...
}

知识点:

  1. Document是DOM的根节点,提供对文档数据访问的入口。

2.3 registerBeanDefinitions(Document doc, Resource resource)

功能概述: 解析DOM Document成容器的内部数据接口BeanDefinition并注册到容器内部。

/**
* XmlBeanDefinitionReader
**/
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// 已注册的BeanDefinition的数量
int countBefore = getRegistry().getBeanDefinitionCount();
// DOM Document->BeanDefinition,注册BeanDefinition至容器
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
// 返回此次注册新增的BeanDefinition数量
return getRegistry().getBeanDefinitionCount() - countBefore;
}

知识点:

  1. BeanDefinitionDocumentReader是接口,实际完成对DOM Document解析的是其默认实现类DefaultBeanDefinitionDoucumentReade。
  2. createReaderContext(resource)创建XmlReaderContext上下文,包含了XmlBeanDefinitionReader读取器和NamespaceHandlerResolver 命名空间解析器。

3.1 registerBeanDefinitions(Document doc, XmlReaderContext readerContext)

功能概述: 在此之前一直 是XML加载解析的准备阶段,在获取到Document对象之后就开始真正的解析了。

/**
* DefaultBeanDefinitionDocumentReader
**/
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
doRegisterBeanDefinitions(doc.getDocumentElement());
}

3.2 doRegisterBeanDefinitions(Element root)

功能概述: 解析Element,DefaultBeanDefinitionDoucumentReader同样不具有解析Element的能力,委托给BeanDefinitionParserDelegate执行。

/**
* DefaultBeanDefinitionDocumentReader
**/
protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
...
// 解析前处理,留给子类实现
preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
// 解析后处理,留给子类实现
postProcessXml(root); this.delegate = parent;
}

知识点:

  1. preProcessXml和postProcessXml里代码是空的,这两个方法是面向子类设计的,设计模式中的模板方法,也可以说是Spring的一个扩展点,后面有机会可以深入下细节。

3.3 parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)

功能概述: 判别XML中Bean的声明标签是默认的还是自定义的,执行不同的解析逻辑。对于根节点或者子节点是默认命名空间采用parseDefaultElement,否则使用parseCustomElement对自定义命名空间解析。

/**
* DefaultBeanDefinitionDocumentReader
**/
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate){
// 对beans的处理
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
// 对默认的Bean标签解析
parseDefaultElement(ele, delegate);
}
else {
// 对自定义的Bean标签解析
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}

知识点:

  1. Spring中XML有两大类Bean的声明标签

    1. 默认声明标签:

            <bean id="userService" class="com.fly4j.service.impl.UserServiceImpl"/>
    2. 自定义声明标签: ·

            <tx: annotation-driven />
  2. 怎么区分是默认命名空间还是自定义命名空间?

    通过node.getNamespaceURI()获取命名空间并和Spring中固定的命名空间http://www.springframework.org/schema/beans进行比对,如果一致则默认,否则自定义。

3.4 parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate)

功能概述: 上面说到Spring标签包括默认标签和自定义标签两种。解析这两种方式分别不同。以下就默认标签进行说明BeanDefinitionParseDelegate对Bean标签元素的解析过程。

/**
* DefaultBeanDefinitionDocumentReader
**/
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
// import标签
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
// alias标签
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
// bean标签
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// beans标签
doRegisterBeanDefinitions(ele);
}
}

知识点:

  1. Spring的4种默认标签举例:

    1. import

       <import resource="spring-config.xml"/>
    2. alias

      <bean id="userService" class="com.fly4j.service.impl.UserServiceImpl"></bean>
      <alias name="userService" alias="user" />
    3. bean

      <beans>
      <bean id="userService" class="com.fly4j.service.impl.UserServiceImpl"></bean>
      </beans>
    4. beans

      <beans>
      <bean id="userService" class="com.fly4j.service.impl.UserServiceImpl"></bean>
      </beans>

3.5 processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)

功能概述: 在4中默认标签当中,其中核心的是对bean标签的解析。以下就对bean标签的解析来看解析过程。

/**
* DefaultBeanDefinitionDocumentReader
**/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate){
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); // 参考4.1源码
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); // 参考4.2源码
try {
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); // 参考5.1源码
}
...
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}

流程解读:

  1. DefaultBeanDefinitionDocumentReader委托BeanDefinitionParseDelegate的parseBeanDefinitionElement方法进行标签元素的解析。解析后返回BeanDefinitionHolder实例bdHolder,bdHolder实例包含了配置文件中的id、name、alias之类的属性。
  2. 返回的bdHolder不为空时,标签元素如果有自定义属性和自定义子节点,还需要再次对以上两个标签解析。具体逻辑参考4.2小节源码。
  3. 解析完成后,对bdHolder进行注册,使用BeanDefinitionReaderUtils.registerBeanDefinition()方法。具体逻辑参考5.1小节源码。
  4. 发出响应事件,通知相关监听器,bean已经解析完成。

4.1 registerBeanDefinition(String beanName, BeanDefinition beanDefinition)

功能概述: 通过BeanDefinitionParseDelegate对Bean标签的解析,解析得到id和name这些信息封装到BeanDefinitionHolder并返回。

/**
* BeanDefinitionParseDelegate
**/
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
/**
* BeanDefinitionParseDelegate
**/
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// 解析id属性
String id = ele.getAttribute(ID_ATTRIBUTE);
// 解析name属性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
} String beanName = id;
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
} if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
} AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
...
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
}

4.2 decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd)

功能概述: 4.1是对默认标签的属性解析,那如果标签有自定义属性和自定义子节点,这时就要通过decorateBeanDefinitionIfRequired解析这些自定义属性和自定义子节点。

/**
* BeanDefinitionParseDelegate
**/
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
Element ele, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) { BeanDefinitionHolder finalDefinition = originalDef; // Decorate based on custom attributes first.
// 首先对自定义属性解析和装饰
NamedNodeMap attributes = ele.getAttributes();
for (int i = 0; i < attributes.getLength(); i++) {
Node node = attributes.item(i);
finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
} // Decorate based on custom nested elements.
// 装饰标签下的自定义子节点
NodeList children = ele.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node node = children.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
}
}
return finalDefinition;
}

5.1 registerBeanDefinition(String beanName, BeanDefinition beanDefinition)

功能概述: 通过beanName注册BeanDefinition至BeanDefinitionMap中去,至此完成Bean标签的解析,转换成Bean在容器中的数据结构BeanDefinition.

/**
* BeanDefinitionReaderUtils
**/
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException { // Register bean definition under primary name.
// 使用beanName注册BeanDefinition
String beanName = definitionHolder.getBeanName(); registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any.
// 使用别名注册BeanDefiniion
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
} /**
* BeanDefinitionParseDelegate
**/
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
...
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
...
this.beanDefinitionMap.put(beanName, beanDefinition);
}else{
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);
}
}
else {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
...
}

五. 结语

总结Spring IOC基础容器XmlBeanFactory的启动流程概括如下:

  1. 执行XmlFactoryBean构造方法,执行加载BeanDefinition方法。
  2. 使用XmlBeanDefinitionReader读取器将资源Resource解析成DOM Document对象。
  3. 使用DefaultBeanDefinitionDocumentReader读取器从Document对象解析出 Element。
  4. 通过BeanDefinitionParserDelegate将Element转换成对应的BeanDefinition。
  5. 实现BeanDefinitionRegistry注册器将解析好的BeanDefinition注册到容器中的BeanDefitionMap里去

本篇就XmlBeanFactory容器启动流程分析和源码解析两个角度来对Spring IOC容器有个基础的认识。有了这篇基础,下篇就开始对Spring的扩展容器ApplicationContext进行分析。最后希望大家看完都有所收获,可以的话给个关注,感谢啦。

六. 附录

附上我编译好的Spring源码,版本是当前最新版本5.3.0,欢迎star

spring-framework-5.3.0编译源码

Spring源码解析02:Spring IOC容器之XmlBeanFactory启动流程分析和源码解析的更多相关文章

  1. Spring源码解析 | 第二篇:Spring IOC容器之XmlBeanFactory启动流程分析和源码解析

    一. 前言 Spring容器主要分为两类BeanFactory和ApplicationContext,后者是基于前者的功能扩展,也就是一个基础容器和一个高级容器的区别.本篇就以BeanFactory基 ...

  2. Spring MVC启动流程分析

    本文是Spring MVC系列博客的第一篇,后续会汇总成贴子. Spring MVC是Spring系列框架中使用频率最高的部分.不管是Spring Boot还是传统的Spring项目,只要是Web项目 ...

  3. SpringBoot启动流程分析(六):IoC容器依赖注入

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  4. SpringBoot启动流程分析(四):IoC容器的初始化过程

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  5. Spring 的IOC容器之XML方式

    1. Spring 入门 1.1 概述 Spring 是一个分层的 JavaEE 轻量级开源框架; Spring 的核心是控制反转(IOC)和面向切面(AOP); 1.2 特点 方便解耦,简化开发; ...

  6. Spring Boot(五)启动流程分析

    学习过springboot的都知道,在Springboot的main入口函数中调用SpringApplication.run(DemoApplication.class,args)函数便可以启用Spr ...

  7. Spring Boot 应用程序启动流程分析

    SpringBoot 有两个关键元素: @SpringBootApplicationSpringApplication 以及 run() 方法 SpringApplication 这个类应该算是 Sp ...

  8. Spring Cloud Eureka(五):Eureka Server 启动流程分析

    启用EurekaServer @SpringBootApplication @EnableEurekaServer public class EurekaApplication { public st ...

  9. Spring Boot启动流程分析

    引言 早在15年的时候就开始用spring boot进行开发了,然而一直就只是用用,并没有深入去了解spring boot是以什么原理怎样工作的,说来也惭愧.今天让我们从spring boot启动开始 ...

随机推荐

  1. SpringCloud-Config 配置中心

    概述 分布式系统面临的问题 微服务意味着要将单体应用中的业务拆分成一个个的子服务,这些服务都需要必要的配置信息才能运行,如果有上百个微服务,上百个配置文件,管理起来是非常困难的,这时候,一套集中式的. ...

  2. 深入分析Redis的主从复制机制

    一.前言   最近由于疫情影响,时间比较多,所以开始学习之前一直想学,但是却没时间学的Redis.这两天研究了一下Redis的持久化以及主从复制机制,现在已经很晚了,就不多废话了.这篇博客就来谈一谈R ...

  3. 天池Docker学习赛笔记

    容器的基本概念 什么是容器? 容器就是一个视图隔离.资源可限制.独立文件系统的进程集合.所谓"视图隔离"就是能够看到部分进程以及具有独立的主机名等:控制资源使用率则是可以对于内存大 ...

  4. 设置共享内存大小 【windows】

    hMapFile = CreateFileMapping( INVALID_HANDLE_VALUE, // use paging file NULL, // default security PAG ...

  5. 梁国辉获Yes评分表系统3.0计算机软件著作权

    梁国辉获Yes评分表系统3.0计算机软件著作权 Liang Guohui won the Yes score system 3 computer software copyright 登记证书如下 R ...

  6. 【集群实战】NFS网络文件共享服务3-相关知识补充(showmount,exports,rpc)

    1. showmount命令说明 showmount命令一般用于从NFS客户端检查NFS服务器端共享目录的情况. 参数说明: -e,--exports 显示NFS服务器输出的目录列表 [root@we ...

  7. 【shell】Shell变量基础及深入

    1. 什么是变量 变量就是用一个固定的字符串(也可能是字符数字等的组合),替代更多更复杂的内容,这个内容里可能还会包含变量和路径,字符串等其他内容. 变量的定义是存在内存中. x=1 y=2 2. 变 ...

  8. Netty(七):EventLoop学习前导——Reactor模式

    了解Netty的人多少都会知道Netty的高性能的一个原因就是它是基于事件驱动的,而这一事件的原型就是Reactor模式. 所以在学习EventLoop前,很有必要先搞懂Reactor模式. 本文目录 ...

  9. 参加Windows7深圳社区发布会

    昨天下午参加了深圳DotNet俱乐部组织的Windows7深圳社区发布会. 开场:朱兴林,俱乐部简介及Win7的发布情况 Session 1,万洪,Windows7的新特性 Session 2,张善友 ...

  10. DeepWalk论文精读:(1)解决问题&相关工作

    模块1 1. 研究背景 随着互联网的发展,社交网络逐渐复杂化.多元化.在一个社交网络中,充斥着不同类型的用户,用户间产生各式各样的互动联系,形成大小不一的社群.为了对社交网络进行研究分析,需要将网络中 ...