主頁 > 後端開發 > Spring決議Xml注冊Bean流程代碼跟蹤

Spring決議Xml注冊Bean流程代碼跟蹤

2020-10-03 14:43:32 後端開發

有道無術,術可求;

有術無道,止于術;

讀原始碼是一個很枯燥的程序,但是Spring原始碼里面有很多值得學習的地方

加油~!!!!!

前言

使用SpringMVC的時候,通常使用下面這行代碼來加載Spring的組態檔

ApplicationContext application = new ClassPathXmlApplicationContext("webmvc.xml"),那么這行代碼到底進行了怎么的操作,接下來就一探究境,看看是如何加載組態檔的

Spring的組態檔

這個組態檔對于已經學會使用SpringMVC的你來說已經再熟悉不過了

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    <bean id="study" >
    </bean>
</beans>

那么Spring是如何進行Bean的注冊的呢?經過這幾天的原始碼查看我寫下了這篇文章來作為筆記,

因為我剛開始看Spring的原始碼,里面有些內容可能理解的不是很到位,有錯誤請指出

原始碼查看

再此之前我先bb幾句,為了方便查看原始碼,可以去GitHub上下載Spring的原始碼匯入到Idea或者是eclipse中這樣查看起來更方便些,同時還可以在上面寫一些注釋

既然使用的是ClassPathXmlApplicationContext("webmvc.xml")那就找到這個類的單參構造器查看跟蹤下原始碼

/**
	 * Create a new ClassPathXmlApplicationContext, loading the definitions
	 * from the given XML file and automatically refreshing the context.
	 * @param configLocation resource location
	 * @throws BeansException if context creation failed
	 * 這個是創建 了 一個 ClassPathXmlApplicationContext,用來從給的XMl檔案中加載規定
	 */
	public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
		this(new String[] {configLocation}, true, null);
	}

這里呼叫的是本類中的另外一個三個引數的構造方法,便進入到了下面這些代碼中

/**
	 * Create a new ClassPathXmlApplicationContext with the given parent,
	 * loading the definitions from the given XML files.
	 * @param configLocations array of resource locations
	 * @param refresh whether to automatically refresh the context,
	 * loading all bean definitions and creating all singletons.
	 * Alternatively, call refresh manually after further configuring the context.
	 * @param parent the parent context
	 * @throws BeansException if context creation failed
	 * @see #refresh()
	 */
	public ClassPathXmlApplicationContext(
			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
			throws BeansException {

		super(parent);
    	//設定組態檔的路徑
		setConfigLocations(configLocations);
		if (refresh) {
      	   //重要的方法,需要進入查看
			refresh();
		}
	}

這里來說下這個方法的引數的意思:

  • configLocations:這個里面保存的是組態檔的路徑
  • Refresh:是否自動重繪背景關系
  • parent:父背景關系

設定資源加載器

要跟蹤下super(parent)這行代碼,在它的父類中(AbstractApplicationContext類里面),有下面的代碼,這段代碼的作 用是獲取一個SpringResource的加載器用來加載資源檔案(這里你可以理解為是為了加載webmvc.xml組態檔做前期的準備)

protected ResourcePatternResolver getResourcePatternResolver() {
	return new PathMatchingResourcePatternResolver(this);
}
//下面的方法在PathMatchingResourcePatternResolver類中,為了查看方便我將這兩個方法寫在了一起
public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
	Assert.notNull(resourceLoader, "ResourceLoader must not be null");
	this.resourceLoader = resourceLoader;
}

在PathMatchingResourcePatternResolver構造方法中就設定了一個資源加載器

設定Bean資訊位置

這個里面有一個setConfigLocations方法,這個里面會設定Bean配置資訊的位置,這個方法的所在的類是AbstractRefreshableConfigApplicationContext,它和CLassPathXmlApplicationContext之間是繼承的關系

@Nullable
private String[] configLocations;
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;
		}
	}

這里面的configLocations的是一個陣列,setConfigLocations方法的引數是一個可變引數,這個方法的作用是將多個路徑放到configLocations陣列中

閱讀refresh

這個方法可以說是一個非常重要的一個方法,這在個方法里面規定了容器的啟動流程,具體的邏輯通過ConfigurableApplicationContext介面的子類實作

@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
      	   //進入到此方法查看
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				//這里面的代碼我洗掉掉了,因為我們本文是看的決議XML創建 Bean的文章,這里的代碼暫時用不到,我就洗掉了,要不然代碼太多了
			}

			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();
			}
		}
	}

Bean的組態檔是在這個方法里面的refreshBeanFactory方法來處理的,這個方法是在AbstractRefreshableApplicationContext類中實作的

@Override
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);
  }
}

這里有一個方法是loadBeanDefinitions(beanFactory)在這個方法里面就開始決議組態檔了,進入這個方法

@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());
  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);
  //Bean讀取器實作加載的方法
  loadBeanDefinitions(beanDefinitionReader);
}

進入到loadBeanDefinitions(XmlBeanDefinitionReader reader)方法

XML Bean讀取器加載Bean配置資源

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
 //獲娶Bean配置資源的位置
  Resource[] configResources = getConfigResources();
  if (configResources != null) {
    reader.loadBeanDefinitions(configResources);
  }
  String[] configLocations = getConfigLocations();
  if (configLocations != null) {
    reader.loadBeanDefinitions(configLocations);
  }
}

但是本文的教程是通過ClassPathXmlApplicationContext來舉的例子,getConfigResources()方法回傳的是空的,就執行下面的分支

說點和本文有關也有可能沒有關系的話

當代碼看到這里,學習過設計模式的同鞋可能會發現我們看過的這些代碼里也涉及到了委派模式策略模式因為Spring框架中使用到了很多的設計模式,所以說在看一些框架原始碼的時候,我們盡可能的先學習下設計模式,不管是對于看原始碼來說或者是對于在公司中作業都是啟到了很重要的作用,在作業中使用了設計模式對于以后系統的擴展或者是維護來說都是比較方便的,當然學習設計模式也是沒有那么的簡單,或許你看了關于設計模式的視頻或者是一些書籍,但是在作業中如果是想很好的運用出來,還是要寫很多的代碼和常用設計模式的,

學習設計模式也是投入精力的,Scott Mayer在《Effective C++》也說過:C++新手和老手的區別就是前者手背上有很多的傷疤,

回到正文

這個方法是在AbstractBeanDefinitionReader類中的方法,里面的代碼我會做適當的刪減

public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
		ResourceLoader resourceLoader = getResourceLoader();
		//resourceLoader的判空 處理....

		if (resourceLoader instanceof ResourcePatternResolver) {
			// Resource pattern matching available.
			try {
        		 //將對應位置的配置資訊決議成Resource資源
				Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
        		  //委派呼叫子類的XmlBeanDefinitionReader的方法
				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 {
			//......代碼被我洗掉了,因為這里的代碼不會說到
		}
	}

上面int loadCount = loadBeanDefinitions(resources);代碼是通過委派模式來呼叫的XmlBeanDefinitionReader方法,那就來看下這個類中的代碼

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		//....
		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();
			}
		}
		//...
	}

再來查看下doLoadBeanDefinitions方法,這個里面有具體的讀取流程

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
		try {
      	   //將XML檔案轉換成了DOm物件
			Document doc = doLoadDocument(inputSource, resource);
      	    //此方法中有對Bean定義決議的詳細程序
			return registerBeanDefinitions(doc, resource);
		}
		//...
	}

doLoadDocument方法最后呼叫的是DefaultDocumentLoader類中的loadDocument方法

@Override
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
                             ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
 //創建檔案的決議工廠
  DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
  if (logger.isDebugEnabled()) {
    logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
  }
  //創建檔案的決議器
  DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
  //決議Bean配置的資訊
  return builder.parse(inputSource);
}

loadDocument方法先看到這里,如果想要深入的看,自已可以跟蹤下代碼,接下來再看registerBeanDefinitions代碼

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
  //拿到BeanDefinitionDocumentReader來對XML格式的組態檔進行決議
  BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();

  int countBefore = getRegistry().getBeanDefinitionCount();
  //具體的決議程序是實作類DefaultBeanDefinititonDocumentReader完成的
  documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
  return getRegistry().getBeanDefinitionCount() - countBefore;
}

到這里先來總結下Bean配置載入的程序:

  • 調手XML決議器將配置資訊轉成檔案的物件
  • 在完成XML決議后,將按SPring IOC 的定義規則對檔案物件決議

DefaultBeanDefinititonDocumentReader類中看下registerBeanDefinitions

@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
  this.readerContext = readerContext;
  logger.debug("Loading bean definitions");
  //獲取到Root元素
  Element root = doc.getDocumentElement();、
   //注冊Bean的定義
  doRegisterBeanDefinitions(root);   //進入方法
}
protected void doRegisterBeanDefinitions(Element root) {
  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);
  //在檔案的Root元素開始對檔案決議
  parseBeanDefinitions(root, this.delegate);  //進入這個方法
  postProcessXml(root);

  this.delegate = parent;
}

到了下面的這一步就是通過Bean規則決議檔案元素了

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);
		}
		else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
			// recurse
			doRegisterBeanDefinitions(ele);
		}
	}

這里面是一堆的分支陳述句,對應的是決議到那一個節點,『import』『alias』,如果那個都不是將按普通的Bean來處理

這里我們重要看下processBeanDefinition方法

/**
	 * Process the given bean element, parsing the bean definition
	 * and registering it with the registry.
	 */
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
  BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
  if (bdHolder != null) {
    bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
    try {
      //向IOC中注冊決議獲得Bean的定義,Bean定義向IOC注冊的入口
      // 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.
    //完成IOC注冊決議獲到Bean定義后,發送注冊
    getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
  }
}

查看parseBeanDefinitionElement代碼

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
		//獲取bean元素中的id值
		String id = ele.getAttribute(ID_ATTRIBUTE);
		//獲取bean元素中的name值
		String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
		//獲取bean元素中aliases值
		List<String> aliases = new ArrayList<>();
		//將Bean元素中的name屬值存放到別名中
		if (StringUtils.hasLength(nameAttr)) {
			String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
			aliases.addAll(Arrays.asList(nameArr));
		}

		String beanName = id;
		//如果bean元素中沒有配置id屬性,將別名中的第一個值給beanName
		if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
			beanName = aliases.remove(0);
			if (logger.isDebugEnabled()) {
				logger.debug("No XML 'id' specified - using '" + beanName +
						"' as bean name and " + aliases + " as aliases");
			}
		}

		if (containingBean == null) {
			checkNameUniqueness(beanName, aliases, ele);
		}

		AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
		if (beanDefinition != null) {
			if (!StringUtils.hasText(beanName)) {
				try {
					if (containingBean != null) {
						//如果bean元素中沒有配置id 別名 name 沒有子元素 則為決議的bean生成一個唯一的beanName注冊到容器
						beanName = BeanDefinitionReaderUtils.generateBeanName(
								beanDefinition, this.readerContext.getRegistry(), true);
					}
					else {
						//如果bean元素中沒有配置id 別名 name 有子元素 則為決議的bean生成一個唯一的beanName注冊到容器
						beanName = this.readerContext.generateBeanName(beanDefinition);
						// Register an alias for the plain bean class name, if still possible,
						// if the generator returned the class name plus a suffix.
						// This is expected for Spring 1.2/2.0 backwards compatibility.
						String beanClassName = beanDefinition.getBeanClassName();
						if (beanClassName != null &&
								beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
								!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
							aliases.add(beanClassName);
						}
					}
					if (logger.isDebugEnabled()) {
						logger.debug("Neither XML 'id' nor 'name' specified - " +
								"using generated bean name [" + beanName + "]");
					}
				}
				catch (Exception ex) {
					error(ex.getMessage(), ele);
					return null;
				}
			}
			String[] aliasesArray = StringUtils.toStringArray(aliases);
			return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
		}

		return null;
	}

上面的流程完成后,我們再回到了DefaultBeanDefinitionDocumnetReader類中的processBeanDefinition方法

在這個方法里面我們在查看registerBeanDefinition方法

public static void registerBeanDefinition(
			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException {

		// Register bean definition under primary name.
		String beanName = definitionHolder.getBeanName();
		//向IOC容器中注冊definitionHolder   在注冊的時候,真正的完成注冊功能是DefaultListableBeanFactory
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());  //進入這個方法

		// Register aliases for bean name, if any.
		// 如果在決議的程序中拿到了別名,則向IOC注冊別名
		String[] aliases = definitionHolder.getAliases();
		if (aliases != null) {
			for (String alias : aliases) {
				registry.registerAlias(beanName, alias);
			}
		}
	}

查看registerBeanDefinition方法,這個方法在DefaultListableBeanFactory類中,

//在這個Map里面保存了Bean定義物件的映射
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
  throws BeanDefinitionStoreException {

  //....
  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 + "]");
      }
    }
    this.beanDefinitionMap.put(beanName, beanDefinition);
  }
  else {
    if (hasBeanCreationStarted()) {
      // 在這里對啟動的資訊進行鎖定,不可以在修改,保證資料一致
      //這里是注冊的程序
      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;
        if (this.manualSingletonNames.contains(beanName)) {
          Set<String> 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;
  }
  //檢查是否已經注冊過相同的BeanDefinition
  if (oldBeanDefinition != null || containsSingleton(beanName)) {
    resetBeanDefinition(beanName);
  }
}

到這里就完成了IOC容器初始化的作業,

在注冊的流程里面關于子元素(property list)加載沒有寫到,會在另一篇文章中寫到

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/151711.html

標籤:Java

上一篇:java spring動態生成定時任務

下一篇:你還在擔心rpc介面超時嗎

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more