转自:http://blog.csdn.net/chjttony/article/details/6259723

1.当spring的IoC容器将Bean定义的资源文件封装为Spring的Resource之后,接下来要做的就是通过Spring的资源加载器(resourceLoader)读入Bean定义资源文件的过程。对于IoC容器来说,Bean定义的载入过程就是将Bean定义资源文件读入进内存并解析转换成Spring所管理的Bean的数据结构的过程。相对于SpringIoC容器定位Bean定义资源文件来说,Bean定义资源文件的载入和解析过程更复杂一些,因此按照程序的运行步骤逐条分析其实现原理。

2.以FileSystemXmlApplicationContext为例分析其载入和解析Bean定义资源文件的过程:

首先从FileSystemXmlApplicationContext的入口构造函数分析起,其代码如下:

  1. //FileSystemXmlApplicationContext IoC容器进行初始化的入口构造函数
  2. public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
  3. throws BeansException {
  4. //调用父类构造方法,为容器设置资源加载器(resourceLoader)
  5. super(parent);
  6. //调用父类AbstractRefreshableConfigApplicationContext的方法,设置//Bean定义的资源文件,完成IoC容器Bean定义资源的定位
  7. setConfigLocations(configLocations);
  8. if (refresh) {
  9. //调用父类AbstractApplicationContext的refresh()
  10. //函数启动载入Bean定义的过程,是Ioc容器载入Bean定义的入口
  11. refresh();
  12. }
  13. }

Spring IoC容器对Bean定义资源的载入是从refresh()函数开始的,FileSystemXmlApplicationContext通过调用其父类AbstractApplicationContext的refresh()函数启动整个IoC容器对Bean定义的载入过程。

3.AbstractApplicationContext的refresh函数载入Bean定义过程:

  1. public void refresh() throws BeansException, IllegalStateException {
  2. synchronized (this.startupShutdownMonitor) {
  3. //调用容器准备刷新的方法,获取容器的当时时间,同时给容器设置同步标识
  4. prepareRefresh();
  5. //告诉子类启动refreshBeanFactory()方法,Bean定义资源文件的载入从
  6. //子类的refreshBeanFactory()方法启动
  7. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
  8. //为BeanFactory配置容器特性,例如类加载器、事件处理器等
  9. prepareBeanFactory(beanFactory);
  10. try {
  11. //为容器的某些子类指定特殊的BeanPost事件处理器
  12. postProcessBeanFactory(beanFactory);
  13. //调用所有注册的BeanFactoryPostProcessor的Bean
  14. invokeBeanFactoryPostProcessors(beanFactory);
  15. //为BeanFactory注册BeanPost事件处理器.
  16. //BeanPostProcessor是Bean后置处理器,用于监听容器触发的事件
  17. registerBeanPostProcessors(beanFactory);
  18. //初始化信息源,和国际化相关.
  19. initMessageSource();
  20. //初始化容器事件传播器.
  21. initApplicationEventMulticaster();
  22. //调用子类的某些特殊Bean初始化方法
  23. onRefresh();
  24. //为事件传播器注册事件监听器.
  25. registerListeners();
  26. //初始化所有剩余的单态Bean.
  27. finishBeanFactoryInitialization(beanFactory);
  28. //初始化容器的生命周期事件处理器,并发布容器的生命周期事件
  29. finishRefresh();
  30. }
  31. catch (BeansException ex) {
  32. //销毁以创建的单态Bean
  33. destroyBeans();
  34. //取消refresh操作,重置容器的同步标识.
  35. cancelRefresh(ex);
  36. throw ex;
  37. }
  38. }
  39. }

refresh()方法主要为IoC容器Bean的生命周期管理提供条件,Spring IoC容器载入Bean定义资源文件从其子类容器的refreshBeanFactory()方法启动,所以整个refresh()中“ConfigurableListableBeanFactory beanFactory =obtainFreshBeanFactory();”这句以后代码的都是注册容器的信息源和生命周期事件,载入过程就是从这句代码启动。

AbstractApplicationContext的obtainFreshBeanFactory()方法调用子类容器的refreshBeanFactory()方法,启动容器载入Bean定义资源文件的过程,代码如下:

  1. protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {  
  2. //这里使用了委派设计模式,父类定义了抽象的refreshBeanFactory()方法,具//体实现调用子类容器的refreshBeanFactory()方法          
  3. refreshBeanFactory();  
  4.         ConfigurableListableBeanFactory beanFactory = getBeanFactory();  
  5.         if (logger.isDebugEnabled()) {  
  6.             logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);  
  7.         }  
  8.         return beanFactory;  
  9.     }  

 

4.AbstractApplicationContext子类的refreshBeanFactory()方法:

AbstractApplicationContext类中只抽象定义了refreshBeanFactory()方法,容器真正调用的是其子类AbstractRefreshableApplicationContext实现的refreshBeanFactory()方法,方法的源码如下:

  1. protected final void refreshBeanFactory() throws BeansException {
  2. if (hasBeanFactory()) {//如果已经有容器,销毁容器中的bean,关闭容器
  3. destroyBeans();
  4. closeBeanFactory();
  5. }
  6. try {
  7. //创建IoC容器
  8. DefaultListableBeanFactory beanFactory = createBeanFactory();
  9. beanFactory.setSerializationId(getId());
  10. //对IoC容器进行定制化,如设置启动参数,开启注解的自动装配等
  11. customizeBeanFactory(beanFactory);
  12. //调用载入Bean定义的方法,主要这里又使用了一个委派模式,在当前类中只定//义了抽象的loadBeanDefinitions方法,具体的实现调用子类容器
  13. loadBeanDefinitions(beanFactory);
  14. synchronized (this.beanFactoryMonitor) {
  15. this.beanFactory = beanFactory;
  16. }
  17. }
  18. catch (IOException ex) {
  19. throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
  20. }
  21. }

refresh()方法的作用是:在创建IoC容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在refresh之后使用的是新建立起来的IoC容器。refresh的作用类似于对IoC容器的重启,在新建立好的容器中对容器进行初始化,对Bean定义资源进行载入。

和refreshBeanFactory方法类似,载入Bean定义的方法loadBeanDefinitions也使用了委派模式,在AbstractRefreshableApplicationContext类中只定义了抽象方法,具体的实现调用子类容器中的方法实现。

5.AbstractRefreshableApplicationContext子类的loadBeanDefinitions方法:

AbstractRefreshableApplicationContext中只定义了抽象的loadBeanDefinitions方法,容器真正调用的是其子类AbstractXmlApplicationContext对该方法的实现,AbstractXmlApplicationContext的主要源码如下:

  1. public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {
  2. ……
  3. //实现父类抽象的载入Bean定义方法
  4. @Override
  5. protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
  6. //创建XmlBeanDefinitionReader,即创建Bean读取器,并通过回调设置到容器       //中去,容器使用该读取器读取Bean定义资源
  7. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
  8. //为Bean读取器设置Spring资源加载器,AbstractXmlApplicationContext的
  9. //祖先父类AbstractApplicationContext继承DefaultResourceLoader,因//此,容器本身也是一个资源加载器
  10. beanDefinitionReader.setResourceLoader(this);
  11. //为Bean读取器设置SAX xml解析器
  12. beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
  13. //当Bean读取器读取Bean定义的Xml资源文件时,启用Xml的校验机制
  14. initBeanDefinitionReader(beanDefinitionReader);
  15. //Bean读取器真正实现加载的方法
  16. loadBeanDefinitions(beanDefinitionReader);
  17. }
  18. //Xml Bean读取器加载Bean定义资源
  19. protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
  20. //获取Bean定义资源的定位
  21. Resource[] configResources = getConfigResources();
  22. if (configResources != null) {
  23. //Xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位
  24. //的Bean定义资源
  25. reader.loadBeanDefinitions(configResources);
  26. }
  27. //如果子类中获取的Bean定义资源定位为空,则获取//FileSystemXmlApplicationContext构造方法中setConfigLocations方法设置的资源
  28. String[] configLocations = getConfigLocations();
  29. if (configLocations != null) {
  30. //Xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位
  31. //的Bean定义资源
  32. reader.loadBeanDefinitions(configLocations);
  33. }
  34. }
  35. //这里又使用了一个委托模式,调用子类的获取Bean定义资源定位的方法
  36. //该方法在ClassPathXmlApplicationContext中进行实现,对于我们
  37. //举例分析源码的FileSystemXmlApplicationContext没有使用该方法
  38. protected Resource[] getConfigResources() {
  39. return null;
  40. }   ……
  41. }

Xml Bean读取器(XmlBeanDefinitionReader)调用其父类AbstractBeanDefinitionReader的 reader.loadBeanDefinitions方法读取Bean定义资源。

由于我们使用FileSystemXmlApplicationContext作为例子分析,因此getConfigResources的返回值为null,因此程序执行reader.loadBeanDefinitions(configLocations)分支。

6. AbstractBeanDefinitionReader读取Bean定义资源:

AbstractBeanDefinitionReader的loadBeanDefinitions方法源码如下:

  1. //重载方法,调用下面的loadBeanDefinitions(String, Set<Resource>);方法
  2. public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
  3. return loadBeanDefinitions(location, null);
  4. }
  5. public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
  6. //获取在IoC容器初始化过程中设置的资源加载器
  7. ResourceLoader resourceLoader = getResourceLoader();
  8. if (resourceLoader == null) {
  9. throw new BeanDefinitionStoreException(
  10. "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
  11. }
  12. if (resourceLoader instanceof ResourcePatternResolver) {
  13. try {
  14. //将指定位置的Bean定义资源文件解析为Spring IoC容器封装的资源
  15. //加载多个指定位置的Bean定义资源文件
  16. Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
  17. //委派调用其子类XmlBeanDefinitionReader的方法,实现加载功能
  18. int loadCount = loadBeanDefinitions(resources);
  19. if (actualResources != null) {
  20. for (Resource resource : resources) {
  21. actualResources.add(resource);
  22. }
  23. }
  24. if (logger.isDebugEnabled()) {
  25. logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
  26. }
  27. return loadCount;
  28. }
  29. catch (IOException ex) {
  30. throw new BeanDefinitionStoreException(
  31. "Could not resolve bean definition resource pattern [" + location + "]", ex);
  32. }
  33. }
  34. else {
  35. //将指定位置的Bean定义资源文件解析为Spring IoC容器封装的资源
  36. //加载单个指定位置的Bean定义资源文件
  37. Resource resource = resourceLoader.getResource(location);
  38. //委派调用其子类XmlBeanDefinitionReader的方法,实现加载功能
  39. int loadCount = loadBeanDefinitions(resource);
  40. if (actualResources != null) {
  41. actualResources.add(resource);
  42. }
  43. if (logger.isDebugEnabled()) {
  44. logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
  45. }
  46. return loadCount;
  47. }
  48. }
  49. //重载方法,调用loadBeanDefinitions(String);
  50. public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
  51. Assert.notNull(locations, "Location array must not be null");
  52. int counter = 0;
  53. for (String location : locations) {
  54. counter += loadBeanDefinitions(location);
  55. }
  56. return counter;
  57. }

loadBeanDefinitions(Resource...resources)方法和上面分析的3个方法类似,同样也是调用XmlBeanDefinitionReader的loadBeanDefinitions方法。

从对AbstractBeanDefinitionReader的loadBeanDefinitions方法源码分析可以看出该方法做了以下两件事:

首先,调用资源加载器的获取资源方法resourceLoader.getResource(location),获取到要加载的资源。

其次,真正执行加载功能是其子类XmlBeanDefinitionReader的loadBeanDefinitions方法。

7.资源加载器获取要读入的资源:

XmlBeanDefinitionReader通过调用其父类DefaultResourceLoader的getResource方法获取要加载的资源,其源码如下:

  1. //获取Resource的具体实现方法
  2. public Resource getResource(String location) {
  3. Assert.notNull(location, "Location must not be null");
  4. //这里除了带有classpath标识的Resource
  5. if (location.startsWith(CLASSPATH_URL_PREFIX)) {
  6. return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
  7. }
  8. else {
  9. try {
  10. //这里处理URL标识的Resource定位
  11. URL url = new URL(location);
  12. return new UrlResource(url);
  13. }
  14. catch (MalformedURLException ex) {
  15. //如果既不是classpath标识,又不是URL标识的Resource定位,则调用
  16. //容器本身的getResourceByPath方法获取Resource
  17. return getResourceByPath(location);
  18. }
  19. }
  20. }

FileSystemXmlApplicationContext容器提供了getResourceByPath方法的实现,就是为了处理既不是classpath标识,又不是URL标识的Resource定位这种情况。

现在,Bean定义的Resource得到了,下面我们继续跟随程序执行方向,分析XmlBeanDefinitionReader的loadBeanDefinitions方法。

8. XmlBeanDefinitionReader加载Bean定义资源:

  1. //XmlBeanDefinitionReader加载资源的入口方法
  2. public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
  3. //将读入的XML资源进行特殊编码处理
  4. return loadBeanDefinitions(new EncodedResource(resource));
  5. }
  6. //这里是载入XML形式Bean定义资源文件方法
  7. public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
  8. Assert.notNull(encodedResource, "EncodedResource must not be null");
  9. if (logger.isInfoEnabled()) {
  10. logger.info("Loading XML bean definitions from " + encodedResource.getResource());
  11. }
  12. //这里是获取线程局部变量
  13. Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
  14. if (currentResources == null) {
  15. currentResources = new HashSet<EncodedResource>(4);
  16. this.resourcesCurrentlyBeingLoaded.set(currentResources);
  17. }
  18. if (!currentResources.add(encodedResource)) {
  19. throw new BeanDefinitionStoreException(
  20. "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
  21. }
  22. try {
  23. //将资源文件转换为IO输入流
  24. InputStream inputStream = encodedResource.getResource().getInputStream();
  25. try {
  26. InputSource inputSource = new InputSource(inputStream);
  27. if (encodedResource.getEncoding() != null) {
  28. inputSource.setEncoding(encodedResource.getEncoding());
  29. }
  30. //具体读取过程的方法
  31. return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
  32. }
  33. finally {
  34. inputStream.close();
  35. }
  36. }
  37. catch (IOException ex) {
  38. throw new BeanDefinitionStoreException(
  39. "IOException parsing XML document from " + encodedResource.getResource(), ex);
  40. }
  41. finally {
  42. currentResources.remove(encodedResource);
  43. if (currentResources.isEmpty()) {
  44. this.resourcesCurrentlyBeingLoaded.remove();
  45. }
  46. }
  47. }
  48. //从特定XML文件中实际载入Bean定义资源的方法
  49. protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
  50. throws BeanDefinitionStoreException {
  51. try {
  52. int validationMode = getValidationModeForResource(resource);
  53. //将XML文件转换为DOM对象,解析过程由documentLoader实现
  54. Document doc = this.documentLoader.loadDocument(
  55. inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
  56. //这里是启动对Bean定义解析的详细过程,该解析过程会用到Spring的Bean
  57. //配置规则
  58. return registerBeanDefinitions(doc, resource);
  59. }
  60. catch (BeanDefinitionStoreException ex) {
  61. throw ex;
  62. }
  63. catch (SAXParseException ex) {
  64. throw new XmlBeanDefinitionStoreException(resource.getDescription(),
  65. "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
  66. }
  67. catch (SAXException ex) {
  68. throw new XmlBeanDefinitionStoreException(resource.getDescription(),
  69. "XML document from " + resource + " is invalid", ex);
  70. }
  71. catch (ParserConfigurationException ex) {
  72. throw new BeanDefinitionStoreException(resource.getDescription(),
  73. "Parser configuration exception parsing XML from " + resource, ex);
  74. }
  75. catch (IOException ex) {
  76. throw new BeanDefinitionStoreException(resource.getDescription(),
  77. "IOException parsing XML document from " + resource, ex);
  78. }
  79. catch (Throwable ex) {
  80. throw new BeanDefinitionStoreException(resource.getDescription(),
  81. "Unexpected exception parsing XML document from " + resource, ex);
  82. }
  83. }

通过源码分析,载入Bean定义资源文件的最后一步是将Bean定义资源转换为Document对象,该过程由documentLoader实现。

9. DocumentLoader将Bean定义资源转换为Document对象:

DocumentLoader将Bean定义资源转换成Document对象的源码如下:

  1. //使用标准的JAXP将载入的Bean定义资源转换成document对象
  2. public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
  3. ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
  4. //创建文件解析器工厂
  5. DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
  6. if (logger.isDebugEnabled()) {
  7. logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
  8. }
  9. //创建文档解析器
  10. DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
  11. //解析Spring的Bean定义资源
  12. return builder.parse(inputSource);
  13. }
  14. protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
  15. throws ParserConfigurationException {
  16. //创建文档解析工厂
  17. DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  18. factory.setNamespaceAware(namespaceAware);
  19. //设置解析XML的校验
  20. if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
  21. factory.setValidating(true);
  22. if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
  23. factory.setNamespaceAware(true);
  24. try {
  25. factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
  26. }
  27. catch (IllegalArgumentException ex) {
  28. ParserConfigurationException pcex = new ParserConfigurationException(
  29. "Unable to validate using XSD: Your JAXP provider [" + factory +
  30. "] does not support XML Schema. Are you running on <a href="http://lib.csdn.net/base/java" class='replace_word' title="Java 知识库" target='_blank' style='color:#df3434; font-weight:bold;'>Java </a>1.4 with Apache Crimson? " +
  31. "Upgrade to Apache Xerces (or <a href="http://lib.csdn.net/base/javaee" class='replace_word' title="Java EE知识库" target='_blank' style='color:#df3434; font-weight:bold;'>Java</a> 1.5) for full XSD support.");
  32. pcex.initCause(ex);
  33. throw pcex;
  34. }
  35. }
  36. }
  37. return factory;
  38. }

该解析过程调用JavaEE标准的JAXP标准进行处理。

至此Spring IoC容器根据定位的Bean定义资源文件,将其加载读入并转换成为Document对象过程完成。

10.看源代码的个人心得总结:

通过这几天看源码,个人总结一些心得:代码毕竟不是文章,不能从头到尾详细看,个人觉得看源码比较好的方法是:

首先,先实现一个简单例子调用源码,让整个应用能简单跑起来。

然后,在进入代码最开始的地方打一个调试断点,使用Debug工具进行单步调试,直到跑完所有流程。

通过调试就可以理解整个代码的工作流程和调用顺序,有助于理清思路,理解其大概的设计思想。

Spring代码的确实比较复杂,代码中大量使用了设计模式,另外为了解耦合,代码的分工比较明确,对象也非常的多,对于没有分析源码经验的人来说,阅读代码发现其跨度和跳转非常大,难度和挑战比较大。

我也是第一次分析Spring源码,希望和大家一起学习探讨。

Spring技术内幕_IOC容器载入Bean定义资源文件的更多相关文章

  1. Spring技术内幕:SpringIOC原理学习总结

    前一段时候我把Spring技术内幕的关于IOC原理一章看完,感觉代码太多,不好掌握,我特意又各方搜集了一些关于IOC原理的资料,特加深一下印象,以便真正掌握IOC的原理. IOC的思想是:Spring ...

  2. Spring技术内幕:设计理念和整体架构概述(转)

    程序员都很崇拜技术大神,很大一部分是因为他们发现和解决问题的能力,特别是线上出现紧急问题时,总是能够快速定位和解决. 一方面,他们有深厚的技术基础,对应用的技术知其所以然,另一方面,在采坑的过程中不断 ...

  3. 《spring技术内幕》读书笔记(1)——什么是POJO模式

    今天在看<spring技术内幕>,第一章中多次提到了使用POJO来完成开发,就百度了一下,在此保留 1.     什么是POJO POJO的名称有多种,pure old java obje ...

  4. Spring技术内幕总结 - IoC容器的实现

    IoC:Inversion of Control,控制反转,即依赖对象的获得被反转了(DI:dependency inversion,依赖注入)在Spring中,IoC容器是实现这个模式的载体.它可以 ...

  5. 《Spring技术内幕》笔记-第二章 IoC容器的实现

    简单介绍 1,在Spring中,SpringIoC提供了一个主要的JavaBean容器.通过IoC模式管理依赖关系.并通过依赖注入和AOP切面增强了为JavaBean这样子的POJO提供事务管理,生命 ...

  6. Spring技术内幕——深入解析Spring架构与设计原理(一)IOC实现原理

    IOC的基础 下面我们从IOC/AOP开始,它们是Spring平台实现的核心部分:虽然,我们一开始大多只是在这个层面上,做一些配置和外部特性的使用工作,但对这两个核心模块工作原理和运作机制的理解,对深 ...

  7. Spring技术内幕:Spring AOP的实现原理(二)

    **二.AOP的设计与实现 1.JVM的动态代理特性** 在Spring AOP实现中, 使用的核心技术时动态代理.而这样的动态代理实际上是JDK的一个特性.通过JDK的动态代理特性,能够为随意Jav ...

  8. Spring技术内幕阅读笔记(一)

    1.BeanFactory:实现ioc容器的最基本形式.String FACTORY_BEAN_PREFIX = "&";Object getBean(String var ...

  9. 《Spring技术内幕》笔记-第四章 Spring MVC与web环境

    ​上下文在web容器中的启动 1,IoC容器的启动过程 IoC的启动过程就是建立上下文的过程.该上下文是与ServletContext相伴.在Spring中存在一个核心控制分发器,Dispatcher ...

随机推荐

  1. POJ2229 Sumsets

    Sumsets Time Limit: 2000MS   Memory Limit: 200000K Total Submissions: 19024   Accepted: 7431 Descrip ...

  2. firefox浏览器testclient测试接口

  3. JavaScript中的工厂函数

    所谓工厂函数,就是指这些内建函数都是类对象,当你调用他们时,实际上是创建了一个类实例. 在学习jQuery的时候,我们经常会看到“工厂函数”这个概念,那么究竟什么是“工厂函数”呢?我们来看看概念,“所 ...

  4. uwsgi+nginx+django

    http://blog.csdn.net/c465869935/article/details/53242126 http://uwsgi-docs.readthedocs.io/en/latest/ ...

  5. 单片机编译器Keil提供的sprintf有点问题

    AduC70xx系列,Keil提供的sprintf函数不太好用,好像有时会引起内存泄漏,造成不可预知的死机情况出现.

  6. ASP 基础一

    ASP是什么? •ASP代表Active Server Pages(动态服务器页面) •需在IIS中运行的程序 我自己的理解就是UI和逻辑代码同在一个页面中,而缺点就是不易维护.code-Behind ...

  7. Git项目下载部分文件或文件夹

    我们常常要在Github下载一些源码.示例等,但有时候项目库会比较大,而我关心的只是其中很少的一部分内容,由于众所周知的原因,我们下载git库是比较慢的,过大的项目经常会下载失败,所以只下载部分内容就 ...

  8. CentOS 7 - 配置服务实现开机自启动

    新建系统服务描述文件 cd /etc/systemd/system sudo vim myapp.service 添加以下配置: [Unit] # 这里添加你的服务描述 Description=mya ...

  9. 【spring cloud】spring cloud2.X spring boot2.0.4调用feign配置Hystrix Dashboard 和 集成Turbine 【解决:Hystrix仪表盘Unable to connect to Command Metric Stream】【解决:Hystrix仪表盘Loading...】

    环境: <java.version>1.8</java.version><spring-boot.version>2.0.4.RELEASE</spring- ...

  10. MariaDB 库的基本操作(2)

    MariaDB数据库管理系统是MySQL的一个分支,主要由开源社区在维护,采用GPL授权许可MariaDB的目的是完全兼容MySQL,包括API和命令行,MySQL由于现在闭源了,而能轻松成为MySQ ...