spring 的 IOC 流程(源码流程)

本贴最后更新于 2076 天前,其中的信息可能已经时移世易

先从源码来看 spring 的 IOC 都经历了哪些方法:
1.开始加载 xml 配置文件

ApplicationContext context = new ClassPathXmlApplicationContext("test.xml");

2.ApplicationContext 的构造方法

this(new String[] {configLocation}, true, null);
public ClassPathXmlApplicationContext(  
      String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)  
      throws BeansException {  
  
   super(parent);  
   setConfigLocations(configLocations);  
   if (refresh) {  
      refresh();  
   }  
}

3.父类构造方法
一直跳转到 AbstractApplicationContext 文件

public AbstractApplicationContext(@Nullable ApplicationContext parent) {  
   this();  
   setParent(parent);  
}

其中 this()方法是创建一个新的 AbstractApplicationContext,并生成一个默认的资源模式解析器。因为 parent 为 null,所以 setParent 没有实质作用。

4.设置配置文件路径
返回 ClassPathXmlApplicationContext 查看 setConfigLocations 方法。此方法是将传入的资源路径保存下来。

public void setConfigLocations(@Nullable String... locations) {  
   if (locations != null) {  
      Assert.noNullElements(locations, "Config locations must not be null");  
      this.configLocations = new String[locations.length];  
      for (int i = 0; i < locations.length; i++) {  
         this.configLocations[i] = resolvePath(locations[i]).trim();  
      }  
   }  
   else {  
      this.configLocations = null;  
   }  
}

5.refresh 方法
主要的 IOC 方法。主要看 spring 的 beanfactory 怎么生成的,以及保存了什么。

@Override  
public void refresh() throws BeansException, IllegalStateException {  
   synchronized (this.startupShutdownMonitor) {  
      // 准备此上下文以进行刷新。
  prepareRefresh();  
  
      // 告诉子类刷新内部bean工厂。
  ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();  
  
      // 准备在这种情况下使用的Bean工厂。
  prepareBeanFactory(beanFactory);  
  
      try {  
         // 允许在上下文子类中对bean工厂进行后处理。
  postProcessBeanFactory(beanFactory);  
  
         // 调用工厂处理器在上下文中注册为bean。 
  invokeBeanFactoryPostProcessors(beanFactory);  
  
         // 注册bean处理器,拦截bean创建。
  registerBeanPostProcessors(beanFactory);  
  
         // Initialize message source for this context.  
  initMessageSource();  
  
         // 初始化此上下文的消息源。
  initApplicationEventMulticaster();  
  
         // 在特定上下文子类中初始化其他特殊bean。
  onRefresh();  
  
         // Check for listener beans and register them.  
  registerListeners();  
  
         // 检查侦听器bean并注册它们。
  finishBeanFactoryInitialization(beanFactory);  
  
         // 最后一步:发布相应的事件。
  finishRefresh();  
      }  
  
      catch (BeansException ex) {  
         if (logger.isWarnEnabled()) {  
            logger.warn("Exception encountered during context initialization - " +  
                  "cancelling refresh attempt: " + ex);  
         }  
  
         // Destroy already created singletons to avoid dangling resources.  
  destroyBeans();  
  
         // Reset 'active' flag.  
  cancelRefresh(ex);  
  
         // Propagate exception to caller.  
  throw ex;  
      }  
  
      finally {  
         // Reset common introspection caches in Spring's core, since we  
 // might not ever need metadata for singleton beans anymore...  resetCommonCaches();  
      }  
   }  
}

6.obtainFreshBeanFactory 方法
在 refreshBeanFactory 生成 beanFactory,然后通过 getBeanFactory 获取

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {  
   refreshBeanFactory();  
   ConfigurableListableBeanFactory beanFactory = getBeanFactory();  
   if (logger.isDebugEnabled()) {  
      logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);  
   }  
   return beanFactory;  
}

7.refreshBeanFactory 的实现方法
在 idea 中可以使用 ctrl+alt+b 的快捷键查看方法的实现方法。该方法在 AbstractRefreshableApplicationContext 中,
可以看到,方法中先销毁了之前的 beanFactory(如果有的话),然后生成了一个默认的 DefaultListableBeanFactory。然后将 beanFactory 保存,使得上一步的 getBeanFactory 可以获取。然后看 loadBeanDefinitions 方法。

protected final void refreshBeanFactory() throws BeansException {  
   if (hasBeanFactory()) {  
      destroyBeans();  
      closeBeanFactory();  
   }  
   try {  
      DefaultListableBeanFactory beanFactory = createBeanFactory();  
      beanFactory.setSerializationId(getId());  
      customizeBeanFactory(beanFactory);  
      loadBeanDefinitions(beanFactory);  
      synchronized (this.beanFactoryMonitor) {  
         this.beanFactory = beanFactory;  
      }  
   }  
   catch (IOException ex) {  
      throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);  
   }  
}

8.loadBeanDefinitions 方法
这里有很多的 loadBeanDefinitions 在互相调用,这些 loadBeanDefinitions 的作用就是将最开始传入的资源文件路径加载,封装成 InputSource,方便后续解析文件。

//生成一个xml读取器
@Override  
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {  
   // Create a new XmlBeanDefinitionReader for the given BeanFactory.  
  XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);  
  
   // Configure the bean definition reader with this context's  
 // resource loading environment.  beanDefinitionReader.setEnvironment(this.getEnvironment());  
//传入一个资源加载器,就是AbstractXmlApplicationContext
   beanDefinitionReader.setResourceLoader(this);  
   beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));  
  
   // Allow a subclass to provide custom initialization of the reader,  
 // then proceed with actually loading the bean definitions.  initBeanDefinitionReader(beanDefinitionReader);  
   loadBeanDefinitions(beanDefinitionReader);  
}
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {  
   Resource[] configResources = getConfigResources();  
   if (configResources != null) {  
      reader.loadBeanDefinitions(configResources);  
   }  
//用reader读取最开始传入的资源文件路径
   String[] configLocations = getConfigLocations();  
   if (configLocations != null) {  
      reader.loadBeanDefinitions(configLocations);  
   }  
}
@Override  
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {  
   Assert.notNull(locations, "Location array must not be null");  
   int counter = 0;  
   for (String location : locations) {  
      counter += loadBeanDefinitions(location);  
   }  
   return counter;  
}
@Override  
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {  
   return loadBeanDefinitions(location, null);  
}
public int loadBeanDefinitions(String location, @Nullable Set actualResources) throws BeanDefinitionStoreException {  
   ResourceLoader resourceLoader = getResourceLoader();  
   if (resourceLoader == null) {  
      throw new BeanDefinitionStoreException(  
            "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");  
   }  
  //前面传入了一个资源加载器,可以通过继承树看出AbstractXmlApplicationContext是ResourcePatternResolver的子类
   if (resourceLoader instanceof ResourcePatternResolver) {  
      // Resource pattern matching available.  
  try {  
//将资源文件的路径封装成Resource
         Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);  
         int loadCount = loadBeanDefinitions(resources);  
         if (actualResources != null) {  
            for (Resource resource : resources) {  
               actualResources.add(resource);  
            }  
         }  
         if (logger.isDebugEnabled()) {  
            logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");  
         }  
         return loadCount;  
      }  
      catch (IOException ex) {  
         throw new BeanDefinitionStoreException(  
               "Could not resolve bean definition resource pattern [" + location + "]", ex);  
      }  
   }  
   else {  
      // Can only load single resources by absolute URL.  
  Resource resource = resourceLoader.getResource(location);  
      int loadCount = loadBeanDefinitions(resource);  
      if (actualResources != null) {  
         actualResources.add(resource);  
      }  
      if (logger.isDebugEnabled()) {  
         logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");  
      }  
      return loadCount;  
   }  
}
@Override  
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {  
   Assert.notNull(resources, "Resource array must not be null");  
   int counter = 0;  
   for (Resource resource : resources) {  
      counter += loadBeanDefinitions(resource);  
   }  
   return counter;  
}
@Override  
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {  
   return loadBeanDefinitions(new EncodedResource(resource));  
}
//封装资源文件和inputstream
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {  
   Assert.notNull(encodedResource, "EncodedResource must not be null");  
   if (logger.isInfoEnabled()) {  
      logger.info("Loading XML bean definitions from " + encodedResource.getResource());  
   }  
  
   Set currentResources = this.resourcesCurrentlyBeingLoaded.get();  
   if (currentResources == null) {  
      currentResources = new HashSet<>(4);  
      this.resourcesCurrentlyBeingLoaded.set(currentResources);  
   }  
   if (!currentResources.add(encodedResource)) {  
      throw new BeanDefinitionStoreException(  
            "Detected cyclic loading of " + encodedResource + " - check your import definitions!");  
   }  
   try {  
      InputStream inputStream = encodedResource.getResource().getInputStream();  
      try {  
         InputSource inputSource = new InputSource(inputStream);  
         if (encodedResource.getEncoding() != null) {  
            inputSource.setEncoding(encodedResource.getEncoding());  
         }  
         return doLoadBeanDefinitions(inputSource, encodedResource.getResource());  
      }  
      finally {  
         inputStream.close();  
      }  
   }  
   catch (IOException ex) {  
      throw new BeanDefinitionStoreException(  
            "IOException parsing XML document from " + encodedResource.getResource(), ex);  
   }  
   finally {  
      currentResources.remove(encodedResource);  
      if (currentResources.isEmpty()) {  
         this.resourcesCurrentlyBeingLoaded.remove();  
      }  
   }  
}

8.doLoadBeanDefinitions 方法

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)  
      throws BeanDefinitionStoreException {  
   try {  
//使用前面封装的资源文件和inputSource读取资源文件,并封装为Document对象
      Document doc = doLoadDocument(inputSource, resource);  
      return registerBeanDefinitions(doc, resource);  
   }  
   catch (BeanDefinitionStoreException ex) {  
      throw ex;  
   }  
   catch (SAXParseException ex) {  
      throw new XmlBeanDefinitionStoreException(resource.getDescription(),  
            "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);  
   }  
   catch (SAXException ex) {  
      throw new XmlBeanDefinitionStoreException(resource.getDescription(),  
            "XML document from " + resource + " is invalid", ex);  
   }  
   catch (ParserConfigurationException ex) {  
      throw new BeanDefinitionStoreException(resource.getDescription(),  
            "Parser configuration exception parsing XML from " + resource, ex);  
   }  
   catch (IOException ex) {  
      throw new BeanDefinitionStoreException(resource.getDescription(),  
            "IOException parsing XML document from " + resource, ex);  
   }  
   catch (Throwable ex) {  
      throw new BeanDefinitionStoreException(resource.getDescription(),  
            "Unexpected exception parsing XML document from " + resource, ex);  
   }  
}

9.registerBeanDefinitions 方法

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {  
//创建一个BeanDefinition读取器
   BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();  
   int countBefore = getRegistry().getBeanDefinitionCount();  
   documentReader.registerBeanDefinitions(doc, createReaderContext(resource));  
   return getRegistry().getBeanDefinitionCount() - countBefore;  
}
@Override  
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {  
   this.readerContext = readerContext;  
   logger.debug("Loading bean definitions");  
   Element root = doc.getDocumentElement();  
   doRegisterBeanDefinitions(root);  
}

10.doRegisterBeanDefinitions

protected void doRegisterBeanDefinitions(Element root) {  
   // Any nested  elements will cause recursion in this method. In  
 // order to propagate and preserve  default-* attributes correctly, // keep track of the current (parent) delegate, which may be null. Create // the new (child) delegate with a reference to the parent for fallback purposes, // then ultimately reset this.delegate back to its original (parent) reference. // this behavior emulates a stack of delegates without actually necessitating one.  BeanDefinitionParserDelegate parent = this.delegate;  
   this.delegate = createDelegate(getReaderContext(), root, parent);  
  
   if (this.delegate.isDefaultNamespace(root)) {  
      String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);  
      if (StringUtils.hasText(profileSpec)) {  
         String[] specifiedProfiles = StringUtils.tokenizeToStringArray(  
               profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);  
         if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {  
            if (logger.isInfoEnabled()) {  
               logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +  
                     "] not matching: " + getReaderContext().getResource());  
            }  
            return;  
         }  
      }  
   }  
  
   preProcessXml(root);  
//开始解析Document中的Element信息
   parseBeanDefinitions(root, this.delegate);  
   postProcessXml(root);  
  
   this.delegate = parent;  
}

11.parseBeanDefinitions

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {  
   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)) { 
//             使用默认的命名空间解析element 
               parseDefaultElement(ele, delegate);  
            }  
            else {  
               delegate.parseCustomElement(ele);  
            }  
         }  
      }  
   }  
   else {  
      delegate.parseCustomElement(root);  
   }  
}

12.parseDefaultElement

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {  
//注册import标签信息
   if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {  
      importBeanDefinitionResource(ele);  
   }  
//注册alias标签信息
   else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {  
      processAliasRegistration(ele);  
   }  
//注册Bean标签信息
   else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {  
      processBeanDefinition(ele, delegate);  
   }  
//解析beans标签信息
   else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {  
      // recurse  
  doRegisterBeanDefinitions(ele);  
   }  
}

13.processBeanDefinition

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {  
//这里已经将配置文件的信息封装为BeanDefinitionHolder,该类保存了BeanDefinition,BeanName和BeanAlices
   BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);  
   if (bdHolder != null) {  
      bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);  
      try {  
         // Register the final decorated instance.  
  BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());  
      }  
      catch (BeanDefinitionStoreException ex) {  
         getReaderContext().error("Failed to register bean definition with name '" +  
               bdHolder.getBeanName() + "'", ele, ex);  
      }  
      // Send registration event.  
  getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));  
   }  
}

14.registerBeanDefinition

public static void registerBeanDefinition(  
      BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)  
      throws BeanDefinitionStoreException {  
  
   // Register bean definition under primary name.  
  String beanName = definitionHolder.getBeanName();  
   registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());  
  
   // Register aliases for bean name, if any.  
  String[] aliases = definitionHolder.getAliases();  
   if (aliases != null) {  
      for (String alias : aliases) {  
         registry.registerAlias(beanName, alias);  
      }  
   }  
}

15.registerBeanDefinition

@Override  
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)  
      throws BeanDefinitionStoreException {  
  
   Assert.hasText(beanName, "Bean name must not be empty");  
   Assert.notNull(beanDefinition, "BeanDefinition must not be null");  
  
   if (beanDefinition instanceof AbstractBeanDefinition) {  
      try {  
         ((AbstractBeanDefinition) beanDefinition).validate();  
      }  
      catch (BeanDefinitionValidationException ex) {  
         throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,  
               "Validation of bean definition failed", ex);  
      }  
   }  
  
   BeanDefinition oldBeanDefinition;  
  
   oldBeanDefinition = this.beanDefinitionMap.get(beanName);  
   if (oldBeanDefinition != null) {  
      if (!isAllowBeanDefinitionOverriding()) {  
         throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,  
               "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +  
               "': There is already [" + oldBeanDefinition + "] bound.");  
      }  
      else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {  
         // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE  
  if (this.logger.isWarnEnabled()) {  
            this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +  
                  "' with a framework-generated bean definition: replacing [" +  
                  oldBeanDefinition + "] with [" + beanDefinition + "]");  
         }  
      }  
      else if (!beanDefinition.equals(oldBeanDefinition)) {  
         if (this.logger.isInfoEnabled()) {  
            this.logger.info("Overriding bean definition for bean '" + beanName +  
                  "' with a different definition: replacing [" + oldBeanDefinition +  
                  "] with [" + beanDefinition + "]");  
         }  
      }  
      else {  
         if (this.logger.isDebugEnabled()) {  
            this.logger.debug("Overriding bean definition for bean '" + beanName +  
                  "' with an equivalent definition: replacing [" + oldBeanDefinition +  
                  "] with [" + beanDefinition + "]");  
         }  
      }  
//将前面封装的BeanDefinitionHolder中的beanName和beanDefinition保存到一个ConcurrentHashMap中
      this.beanDefinitionMap.put(beanName, beanDefinition);  
   }  
   else {  
      if (hasBeanCreationStarted()) {  
         // Cannot modify startup-time collection elements anymore (for stable iteration)  
  synchronized (this.beanDefinitionMap) {  
            this.beanDefinitionMap.put(beanName, beanDefinition);  
            List updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);  
            updatedDefinitions.addAll(this.beanDefinitionNames);  
            updatedDefinitions.add(beanName);  
            this.beanDefinitionNames = updatedDefinitions;  
            if (this.manualSingletonNames.contains(beanName)) {  
               Set updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);  
               updatedSingletons.remove(beanName);  
               this.manualSingletonNames = updatedSingletons;  
            }  
         }  
      }  
      else {  
         // Still in startup registration phase  
  this.beanDefinitionMap.put(beanName, beanDefinition);  
         this.beanDefinitionNames.add(beanName);  
         this.manualSingletonNames.remove(beanName);  
      }  
      this.frozenBeanDefinitionNames = null;  
   }  
  
   if (oldBeanDefinition != null || containsSingleton(beanName)) {  
      resetBeanDefinition(beanName);  
   }  
}

终于结束了,可以看到,最后将 beanDefinition 存放在了 beanDefinitionMap 中,而 beanDefinitionMap 就是一个 ConcurrentHashMap 集合。

  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    941 引用 • 1458 回帖 • 151 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • 笔记

    好记性不如烂笔头。

    304 引用 • 777 回帖 • 1 关注
  • 阿里云

    阿里云是阿里巴巴集团旗下公司,是全球领先的云计算及人工智能科技公司。提供云服务器、云数据库、云安全等云计算服务,以及大数据、人工智能服务、精准定制基于场景的行业解决方案。

    89 引用 • 345 回帖
  • IDEA

    IDEA 全称 IntelliJ IDEA,是一款 Java 语言开发的集成环境,在业界被公认为最好的 Java 开发工具之一。IDEA 是 JetBrains 公司的产品,这家公司总部位于捷克共和国的首都布拉格,开发人员以严谨著称的东欧程序员为主。

    180 引用 • 400 回帖
  • Tomcat

    Tomcat 最早是由 Sun Microsystems 开发的一个 Servlet 容器,在 1999 年被捐献给 ASF(Apache Software Foundation),隶属于 Jakarta 项目,现在已经独立为一个顶级项目。Tomcat 主要实现了 JavaEE 中的 Servlet、JSP 规范,同时也提供 HTTP 服务,是市场上非常流行的 Java Web 容器。

    162 引用 • 529 回帖 • 3 关注
  • Hexo

    Hexo 是一款快速、简洁且高效的博客框架,使用 Node.js 编写。

    21 引用 • 140 回帖 • 28 关注
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3168 引用 • 8207 回帖
  • 创业

    你比 99% 的人都优秀么?

    82 引用 • 1398 回帖 • 1 关注
  • 生活

    生活是指人类生存过程中的各项活动的总和,范畴较广,一般指为幸福的意义而存在。生活实际上是对人生的一种诠释。生活包括人类在社会中与自己息息相关的日常活动和心理影射。

    228 引用 • 1450 回帖
  • 大疆创新

    深圳市大疆创新科技有限公司(DJI-Innovations,简称 DJI),成立于 2006 年,是全球领先的无人飞行器控制系统及无人机解决方案的研发和生产商,客户遍布全球 100 多个国家。通过持续的创新,大疆致力于为无人机工业、行业用户以及专业航拍应用提供性能最强、体验最佳的革命性智能飞控产品和解决方案。

    2 引用 • 14 回帖 • 3 关注
  • NGINX

    NGINX 是一个高性能的 HTTP 和反向代理服务器,也是一个 IMAP/POP3/SMTP 代理服务器。 NGINX 是由 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的,第一个公开版本 0.1.0 发布于 2004 年 10 月 4 日。

    311 引用 • 546 回帖 • 33 关注
  • Eclipse

    Eclipse 是一个开放源代码的、基于 Java 的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。

    75 引用 • 258 回帖 • 626 关注
  • 以太坊

    以太坊(Ethereum)并不是一个机构,而是一款能够在区块链上实现智能合约、开源的底层系统。以太坊是一个平台和一种编程语言 Solidity,使开发人员能够建立和发布下一代去中心化应用。 以太坊可以用来编程、分散、担保和交易任何事物:投票、域名、金融交易所、众筹、公司管理、合同和知识产权等等。

    34 引用 • 367 回帖 • 1 关注
  • OkHttp

    OkHttp 是一款 HTTP & HTTP/2 客户端库,专为 Android 和 Java 应用打造。

    16 引用 • 6 回帖 • 54 关注
  • TGIF

    Thank God It's Friday! 感谢老天,总算到星期五啦!

    284 引用 • 4481 回帖 • 654 关注
  • Love2D

    Love2D 是一个开源的, 跨平台的 2D 游戏引擎。使用纯 Lua 脚本来进行游戏开发。目前支持的平台有 Windows, Mac OS X, Linux, Android 和 iOS。

    14 引用 • 53 回帖 • 513 关注
  • Sphinx

    Sphinx 是一个基于 SQL 的全文检索引擎,可以结合 MySQL、PostgreSQL 做全文搜索,它可以提供比数据库本身更专业的搜索功能,使得应用程序更容易实现专业化的全文检索。

    1 引用 • 181 关注
  • RIP

    愿逝者安息!

    8 引用 • 92 回帖 • 291 关注
  • Spark

    Spark 是 UC Berkeley AMP lab 所开源的类 Hadoop MapReduce 的通用并行框架。Spark 拥有 Hadoop MapReduce 所具有的优点;但不同于 MapReduce 的是 Job 中间输出结果可以保存在内存中,从而不再需要读写 HDFS,因此 Spark 能更好地适用于数据挖掘与机器学习等需要迭代的 MapReduce 的算法。

    74 引用 • 46 回帖 • 549 关注
  • Linux

    Linux 是一套免费使用和自由传播的类 Unix 操作系统,是一个基于 POSIX 和 Unix 的多用户、多任务、支持多线程和多 CPU 的操作系统。它能运行主要的 Unix 工具软件、应用程序和网络协议,并支持 32 位和 64 位硬件。Linux 继承了 Unix 以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。

    915 引用 • 931 回帖
  • 30Seconds

    📙 前端知识精选集,包含 HTML、CSS、JavaScript、React、Node、安全等方面,每天仅需 30 秒。

    • 精选常见面试题,帮助您准备下一次面试
    • 精选常见交互,帮助您拥有简洁酷炫的站点
    • 精选有用的 React 片段,帮助你获取最佳实践
    • 精选常见代码集,帮助您提高打码效率
    • 整理前端界的最新资讯,邀您一同探索新世界
    488 引用 • 383 回帖 • 5 关注
  • 正则表达式

    正则表达式(Regular Expression)使用单个字符串来描述、匹配一系列遵循某个句法规则的字符串。

    31 引用 • 94 回帖
  • Flutter

    Flutter 是谷歌的移动 UI 框架,可以快速在 iOS 和 Android 上构建高质量的原生用户界面。 Flutter 可以与现有的代码一起工作,它正在被越来越多的开发者和组织使用,并且 Flutter 是完全免费、开源的。

    39 引用 • 92 回帖 • 7 关注
  • 区块链

    区块链是分布式数据存储、点对点传输、共识机制、加密算法等计算机技术的新型应用模式。所谓共识机制是区块链系统中实现不同节点之间建立信任、获取权益的数学算法 。

    91 引用 • 751 回帖 • 1 关注
  • API

    应用程序编程接口(Application Programming Interface)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。

    76 引用 • 421 回帖
  • 锤子科技

    锤子科技(Smartisan)成立于 2012 年 5 月,是一家制造移动互联网终端设备的公司,公司的使命是用完美主义的工匠精神,打造用户体验一流的数码消费类产品(智能手机为主),改善人们的生活质量。

    4 引用 • 31 回帖 • 10 关注
  • VirtualBox

    VirtualBox 是一款开源虚拟机软件,最早由德国 Innotek 公司开发,由 Sun Microsystems 公司出品的软件,使用 Qt 编写,在 Sun 被 Oracle 收购后正式更名成 Oracle VM VirtualBox。

    10 引用 • 2 回帖 • 5 关注
  • GitLab

    GitLab 是利用 Ruby 一个开源的版本管理系统,实现一个自托管的 Git 项目仓库,可通过 Web 界面操作公开或私有项目。

    46 引用 • 72 回帖 • 1 关注