主頁 > 後端開發 > 死磕Spring之IoC篇 - BeanDefinition 的加載階段(XML 檔案)

死磕Spring之IoC篇 - BeanDefinition 的加載階段(XML 檔案)

2021-02-24 06:12:13 後端開發

該系列文章是本人在學習 Spring 的程序中總結下來的,里面涉及到相關原始碼,可能對讀者不太友好,請結合我的原始碼注釋 Spring 原始碼分析 GitHub 地址 進行閱讀

Spring 版本:5.1.14.RELEASE

開始閱讀這一系列文章之前,建議先查看《深入了解 Spring IoC(面試題)》這一篇文章

該系列其他文章請查看:《死磕 Spring 之 IoC 篇 - 文章導讀》

BeanDefinition 的加載階段(XML 檔案)

上一篇文章 《Bean 的“前身”》 對 BeanDefinition 進行了介紹,Bean 是根據 BeanDefinition 配置元資訊物件生成的,我們在 Spring 中通常以這兩種方式定義一個 Bean:面向資源(XML、Properties)面向注解,那么 Spring 是如何將這兩種方式定義的資訊轉換成 BeanDefinition 物件的,接下來會先分析面向資源(XML、Properties)這種方式 Spring 是如何處理的

下來熟悉一段代碼:

dependency-lookup-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       https://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd">
	<!-- <context:component-scan base-package="org.geekbang.thinking.in.spring.ioc.overview" /> -->

    <bean id="user" >
        <property name="id" value="https://www.cnblogs.com/lifullmoon/p/1"/>
        <property name="name" value="https://www.cnblogs.com/lifullmoon/p/小馬哥"/>
    </bean>
</beans>
// 創建 BeanFactory 容器
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
// XML 組態檔 ClassPath 路徑
String location = "classpath:/META-INF/dependency-lookup-context.xml";
// 加載配置
int beanDefinitionsCount = reader.loadBeanDefinitions(location);
System.out.println("Bean 定義加載的數量:" + beanDefinitionsCount);
// 依賴查找
System.out.println(beanFactory.getBean("user"));;

這段代碼是 Spring 中編程式使用 IoC 容器,我們可以看到 IoC 容器的使用程序大致如下:

  1. 創建 BeanFactory 物件(底層 IoC 容器)
  2. 創建 BeanDefinitionReader 物件(資源決議器),關聯第 1 步創建的 BeanFactory
  3. 通過 BeanDefinitionReader 加載 XML 組態黨澩,決議出所有的 BeanDefinition 物件
  4. 進行依賴查找

上面的第 3 步會決議 Resource 資源,將 XML 檔案中定義的 Bean 決議成 BeanDefinition 配置元資訊物件,并往 BeanDefinitionRegistry 注冊中心注冊,此時并沒有生成對應的 Bean 物件,需要通過依賴查找獲取到 Bean,當然,我們在實際場景中一般不會這樣使用 Spring,這些作業都會有 Spring 來完成,接下來我們一起來看看 Sping 是如何加載 XML 檔案的

BeanDefinitionReader 體系結構

org.springframework.beans.factory.support.BeanDefinitionReader 介面的類圖如下所示:

總覽:

  • org.springframework.beans.factory.support.BeanDefinitionReader 介面,BeanDefinition 讀取器

  • org.springframework.beans.factory.support.AbstractBeanDefinitionReader 抽象類,提供通用的實作,具體的資源加載邏輯在由子類實作

  • org.springframework.beans.factory.xml.XmlBeanDefinitionReader,XML 檔案資源決議器,決議出 BeanDefinition 配置元資訊物件并注冊

  • org.springframework.beans.factory.support.PropertiesBeanDefinitionReader,Properties 檔案資源決議器

BeanDefinitionReader 介面

org.springframework.beans.factory.support.BeanDefinitionReader 介面,BeanDefinition 讀取器,定義了加載資源的方法,代碼如下:

public interface BeanDefinitionReader {

	/** 回傳 BeanDefinition 注冊中心 */
	BeanDefinitionRegistry getRegistry();

	/** 回傳 Resource 資源加載器,默認為 PathMatchingResourcePatternResolver */
	@Nullable
	ResourceLoader getResourceLoader();

	/** 回傳類加載器 */
	@Nullable
	ClassLoader getBeanClassLoader();

	/** 回傳 Bean 的名稱生成器,默認為 DefaultBeanNameGenerator */
	BeanNameGenerator getBeanNameGenerator();


	/** 從 Resource 資源中加載 BeanDefinition 并回傳數量 */
	int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;

	int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException;
    
	int loadBeanDefinitions(String location) throws BeanDefinitionStoreException;

	int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException;
}

AbstractBeanDefinitionReader 抽象類

org.springframework.beans.factory.support.AbstractBeanDefinitionReader 抽象類,實作了 BeanDefinitionReader 和 EnvironmentCapable 介面,代碼如下:

public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader, EnvironmentCapable {

	private final BeanDefinitionRegistry registry;

	@Nullable
	private ResourceLoader resourceLoader;

	@Nullable
	private ClassLoader beanClassLoader;

	private Environment environment;

	private BeanNameGenerator beanNameGenerator = new DefaultBeanNameGenerator();

	protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		this.registry = registry;

		// Determine ResourceLoader to use.
		if (this.registry instanceof ResourceLoader) {
			this.resourceLoader = (ResourceLoader) this.registry;
		}
		else {
			this.resourceLoader = new PathMatchingResourcePatternResolver();
		}

		// Inherit Environment if possible
		if (this.registry instanceof EnvironmentCapable) {
			this.environment = ((EnvironmentCapable) this.registry).getEnvironment();
		}
		else {
			this.environment = new StandardEnvironment();
		}
	}

	@Override
	public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
		Assert.notNull(resources, "Resource array must not be null");
		int count = 0;
		for (Resource resource : resources) {
			count += loadBeanDefinitions(resource);
		}
		return count;
	}

	@Override
	public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(location, null);
	}

	public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
		// 獲得 ResourceLoader 物件
		ResourceLoader resourceLoader = getResourceLoader();
		if (resourceLoader == null) {
			throw new BeanDefinitionStoreException(
					"Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
		}

		if (resourceLoader instanceof ResourcePatternResolver) {
			// Resource pattern matching available.
			try {
				// 獲得 Resource 陣列,因為 Pattern 模式匹配下,可能有多個 Resource ,例如說,Ant 風格的 location
				Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
				// 加載 BeanDefinition 們
				int count = loadBeanDefinitions(resources);
				if (actualResources != null) {
					// 添加到 actualResources 中
					Collections.addAll(actualResources, resources);
				}
				if (logger.isTraceEnabled()) {
					logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
				}
				return count;
			}
			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 resource = resourceLoader.getResource(location);
			// 加載 BeanDefinition 們
			int count = loadBeanDefinitions(resource);
			if (actualResources != null) {
				// 添加到 actualResources 中
				actualResources.add(resource);
			}
			if (logger.isTraceEnabled()) {
				logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
			}
			return count;
		}
	}

	@Override
	public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
		Assert.notNull(locations, "Location array must not be null");
		int count = 0;
		for (String location : locations) {
			count += loadBeanDefinitions(location);
		}
		return count;
	}
    
    // ... 省略相關代碼
}

在實作的方法中,最終都會呼叫 int loadBeanDefinitions(Resource resource) 這個方法,該方法在子類中實作

XmlBeanDefinitionReader

org.springframework.beans.factory.xml.XmlBeanDefinitionReader,XML 檔案資源決議器,決議出 BeanDefinition 配置元資訊物件并注冊

建構式

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
	/**
	 * 禁用驗證模式
	 */
	public static final int VALIDATION_NONE = XmlValidationModeDetector.VALIDATION_NONE;

	/**
	 * 自動獲取驗證模式
	 */
	public static final int VALIDATION_AUTO = XmlValidationModeDetector.VALIDATION_AUTO;

	/**
	 * DTD 驗證模式
	 */
	public static final int VALIDATION_DTD = XmlValidationModeDetector.VALIDATION_DTD;

	/**
	 * XSD 驗證模式
	 */
	public static final int VALIDATION_XSD = XmlValidationModeDetector.VALIDATION_XSD;

	/** Constants instance for this class. */
	private static final Constants constants = new Constants(XmlBeanDefinitionReader.class);

	/**
	 * 驗證模式,默認為自動模式,
	 */
	private int validationMode = VALIDATION_AUTO;

	private boolean namespaceAware = false;

	private Class<? extends BeanDefinitionDocumentReader> documentReaderClass = DefaultBeanDefinitionDocumentReader.class;

	/**
	 * 決議程序中例外處理器
	 */
	private ProblemReporter problemReporter = new FailFastProblemReporter();

	private ReaderEventListener eventListener = new EmptyReaderEventListener();

	private SourceExtractor sourceExtractor = new NullSourceExtractor();

	@Nullable
	private NamespaceHandlerResolver namespaceHandlerResolver;

	private DocumentLoader documentLoader = new DefaultDocumentLoader();

	@Nullable
	private EntityResolver entityResolver;

	private ErrorHandler errorHandler = new SimpleSaxErrorHandler(logger);

	/**
	 * XML 驗證模式探測器
	 */
	private final XmlValidationModeDetector validationModeDetector = new XmlValidationModeDetector();

	/**
	 * 當前執行緒,正在加載的 EncodedResource 集合,
	 */
	private final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded = new NamedThreadLocal<>(
        "XML bean definition resources currently being loaded");

	/**
	 * Create new XmlBeanDefinitionReader for the given bean factory.
	 * @param registry the BeanFactory to load bean definitions into,
	 * in the form of a BeanDefinitionRegistry
	 */
	public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
		super(registry);
	}
}

loadBeanDefinitions 方法

loadBeanDefinitions(Resource resource) 方法,決議 Resource 資源的入口,方法如下:

@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
    return loadBeanDefinitions(new EncodedResource(resource));
}

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
    Assert.notNull(encodedResource, "EncodedResource must not be null");
    if (logger.isTraceEnabled()) {
        logger.trace("Loading XML bean definitions from " + encodedResource);
    }

    // <1> 獲取當前執行緒正在加載的 Resource 資源集合,添加當前 Resource,防止重復加載
    Set<EncodedResource> 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 {
        // <2> 從 Resource 資源獲取 InputStream 流物件(支持編碼)
        InputStream inputStream = encodedResource.getResource().getInputStream();
        try {
            InputSource inputSource = new InputSource(inputStream);
            if (encodedResource.getEncoding() != null) {
                inputSource.setEncoding(encodedResource.getEncoding());
            }
            // <3> 【核心】執行加載 Resource 資源程序,決議出 BeanDefinition 進行注冊
            return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
        } finally {
            // 關閉流
            inputStream.close();
        }
    } catch (IOException ex) {
        throw new BeanDefinitionStoreException(
                "IOException parsing XML document from " + encodedResource.getResource(), ex);
    } finally {
        // <4> 從當前執行緒移除當前加載的 Resource 物件
        currentResources.remove(encodedResource);
        if (currentResources.isEmpty()) {
            this.resourcesCurrentlyBeingLoaded.remove();
        }
    }
}

將 Resource 封裝成 EncodedResource 物件,目的是讓資源物件可設定編碼

  1. 獲取當前執行緒正在加載的 Resource 資源集合,添加當前 Resource,防止重復加載
  2. 從 Resource 資源獲取 InputStream 流物件(支持編碼)
  3. 【核心】呼叫 doLoadBeanDefinitions(InputSource inputSource, Resource resource) 方法,執行加載 Resource 資源程序,決議出 BeanDefinition 進行注冊
  4. 從當前執行緒移除當前加載的 Resource 物件

doLoadBeanDefinitions 方法

doLoadBeanDefinitions(InputSource inputSource, Resource resource) 方法,執行加載 Resource 資源程序,決議出 BeanDefinition 進行注冊,方法如下:

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
        throws BeanDefinitionStoreException {
    try {
        // <1> 獲取 XML Document 實體
        Document doc = doLoadDocument(inputSource, resource);
        // <2> 根據 Document 實體,決議出 BeanDefinition 們并注冊,回傳注冊數量
        int count = registerBeanDefinitions(doc, resource);
        if (logger.isDebugEnabled()) {
            logger.debug("Loaded " + count + " bean definitions from " + resource);
        }
        return count;
    }
    // 省略 catch 各種例外
}
  1. 呼叫 doLoadDocument(InputSource inputSource, Resource resource) 方法,獲取 XML Document 實體
  2. 呼叫 registerBeanDefinitions(Document doc, Resource resource) 方法,根據 Document 實體,決議出 BeanDefinition 們并注冊,回傳注冊數量

doLoadDocument 方法

doLoadDocument(InputSource inputSource, Resource resource) 方法,獲取 Resource 資源對應的 XML Document 實體,方法如下:

protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
    // <3> 通過 DefaultDocumentLoader 根據 Resource 獲取一個 Document 物件
    return this.documentLoader.loadDocument(inputSource,
            getEntityResolver(), // <1> 獲取 `org.xml.sax.EntityResolver` 物體決議器,ResourceEntityResolver
            this.errorHandler,
            getValidationModeForResource(resource), isNamespaceAware()); // <2> 獲取 XML 檔案驗證模式,保證 XML 檔案的正確性
}
  1. 獲取 org.xml.sax.EntityResolver 物體決議器,ResourceEntityResolver,根據 publicId 和 systemId 獲取對應的 DTD 或 XSD 檔案,用于對 XML 檔案進行驗證,這個類比較關鍵,在后續文章會講到
  2. 獲取 XML 檔案驗證模式,保證 XML 檔案的正確性,通常情況下都是 XSD 模式
    1. 獲取指定的驗證模式,如果手動指定,則直接回傳,通常情況下不會
    2. 從 Resource 資源中獲取驗證模式,根據 XML 檔案的內容進行獲取,如果包含 DOCTYPE 內容則為 DTD 模式,否則為 XSD 模式
    3. 如果還沒有獲取到驗證模式,則默認為 XSD 模式
  3. 通過 DefaultDocumentLoader 根據 Resource 獲取一個 Document 物件
    1. 創建 DocumentBuilderFactory 物件 factory,開啟校驗
    2. 根據 factory 創建 DocumentBuilder 物件 builder,設定 EntityResolver(第 1 步創建的)、ErrorHandler 屬性
    3. 通過 builderinputSource(Resource 資源)進行決議,回傳一個 Document 物件

上述程序目的就是獲取到 Resource 資源對應的 Document 物件,需要經過校驗和決議兩個程序

registerBeanDefinitions 方法

registerBeanDefinitions(Document doc, Resource resource) 方法,根據 Document 實體,決議出 BeanDefinition 們并注冊,回傳注冊數量,方法如下:

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    // <1> 創建 BeanDefinitionDocumentReader 物件
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    // <2> 獲取已注冊的 BeanDefinition 數量
    int countBefore = getRegistry().getBeanDefinitionCount();
    // <3> 創建 XmlReaderContext 物件(讀取 Resource 資源的背景關系物件)
    // <4> 根據 Document、XmlReaderContext 決議出所有的 BeanDefinition 并注冊
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    // <5> 計算新注冊的 BeanDefinition 數量
    return getRegistry().getBeanDefinitionCount() - countBefore;
}
  1. 創建 DefaultBeanDefinitionDocumentReader 物件 documentReader
  2. 獲取已注冊的 BeanDefinition 數量
  3. 創建 XmlReaderContext 物件(讀取 Resource 資源的背景關系物件),注意這里會初始化一個 DefaultNamespaceHandlerResolver 物件,用于處理自定義標簽(XML 檔案),比較關鍵,在后續文章會講到
  4. 根據 Document、XmlReaderContext 決議出所有的 BeanDefinition 并注冊,呼叫 DefaultBeanDefinitionDocumentReader#registerBeanDefinitions(Document doc, XmlReaderContext readerContext) 方法
  5. 計算新注冊的 BeanDefinition 數量并回傳

拓展:DTD 與 XSD 的區別?

DTD(Document Type Definition),即檔案型別定義,為 XML 檔案的驗證機制,屬于 XML 檔案中組成的一部分,DTD 是一種保證 XML 檔案格式正確的有效驗證方式,它定義了相關 XML 檔案的元素、屬性、排列方式、元素的內容型別以及元素的層次結構,其實 DTD 就相當于 XML 中的 “詞匯”和“語法”,我們可以通過比較 XML 檔案和 DTD 檔案 來看檔案是否符合規范,元素和標簽使用是否正確,

DTD 在一定的階段推動了 XML 的發展,但是它本身存在著一些缺陷

  1. 它沒有使用 XML 格式,而是自己定義了一套格式,相對決議器的重用性較差;而且 DTD 的構建和訪問沒有標準的編程介面,導致決議器很難簡單的決議 DTD 檔案
  2. DTD 對元素的型別限制較少;同時其他的約束力也比較弱
  3. DTD 擴展能力較差
  4. 基于正則運算式的 DTD 檔案的描述能力有限

XSD(XML Schemas Definition),即 XML Schema 語言,針對 DTD 的缺陷由 W3C 在 2001 年推出,XML Schema 本身就是一個 XML 檔案,使用的是 XML 語法,因此可以很方便的決議 XSD 檔案,相對于 DTD,XSD 具有如下優勢

  1. XML Schema 基于 XML,沒有專門的語法
  2. XML Schema 可以像其他 XML 檔案一樣決議和處理
  3. XML Schema 比 DTD 提供了更豐富的資料型別
  4. XML Schema 提供可擴充的資料模型
  5. XML Schema 支持綜合命名空間
  6. XML Schema 支持屬性組

總結

我們在 Spring 中通常以這兩種方式定義一個 Bean:面向資源(XML、Properties)面向注解,對于第一種方式如果定義的是一個 XML 檔案,Spring 會通過 XmlBeanDefinitionReader 加載該 XML 檔案,獲取該 Resource 資源的 org.w3c.dom.Document 物件,這個程序會經過校驗、決議兩個步驟

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

標籤:Java

上一篇:電影院售票管理系統

下一篇:(九) MyBatis從入門到入土——延遲加載、鑒別器以及繼承

標籤雲
其他(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