SpringFramework之IoC容器初始化

来源:https://www.cnblogs.com/boycelee/archive/2020/03/29/12595884.html
-Advertisement-
Play Games

原創文章,轉發請標註https://www.cnblogs.com/boycelee/p/12595884.html [toc] 分析例子 啟動類 Application,使用的是ClassPathXmlApplicationContext來載入xml文件 Bean 配置文件 ​ 在resource ...


原創文章,轉發請標註https://www.cnblogs.com/boycelee/p/12595884.html
目錄

分析例子

啟動類

Application,使用的是ClassPathXmlApplicationContext來載入xml文件

/**
 * @author jianw.li
 * @date 2020/3/16 11:53 PM
 * @Description: TODO
 */
public class MyApplication {
    private static final String CONFIG_LOCATION = "classpath:application_context.xml";

    private static final String BEAN_NAME = "hello";

    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext(CONFIG_LOCATION);
        Hello hello = (Hello) ac.getBean(BEAN_NAME);
        hello.sayHello();
    }
}

Bean

/**
 * @author jianw.li
 * @date 2020/3/16 11:53 PM
 * @Description: TODO
 */
public class Hello {

	public void sayHello() {
		System.out.println("Hello World");
	}
}

配置文件

​ 在resources下建立名為classpath:application_context.xml的配置文件,並配置好Bean

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

	<bean id="hello" class="com.boyce.bean.Hello"></bean>

</beans>

總體結構

ClassPathXmlApplicationContext繼承體系如下:

KWJf7l

IoC總體結構圖如下:

image-20200322155805716

源碼分析

ClassPathXmlApplicationContext

//構造函數,創建ClassPathXmlApplicationContext,其中configLocation為Bean所在的文件路徑
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
		this(new String[] {configLocation}, true, null);
}

public ClassPathXmlApplicationContext(
			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
			throws BeansException {
    //null
		super(parent);
    //設置配置路徑至ApplicationContext中
		setConfigLocations(configLocations);
		if (refresh) {
      //核心方法,refresh會將舊的ApplicaionContext銷毀
			refresh();
		}
	}

AbstractApplicationContext

refresh

​ 核心方法,refresh銷毀舊ApplicationContext,生成新的ApplicationContext

@Override
	public void refresh() throws BeansException, IllegalStateException {
		//加鎖.沒有明確對象,只是想讓一段代碼同步,可以創建Object startupShutdownMonitor = new Object()
		synchronized (this.startupShutdownMonitor) {
			// 為context刷新準備.設置啟動時間,設置激活狀態等
			prepareRefresh();

			// 告知子類刷新內部bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

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

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				//初始化所有未懶載入單例
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				//廣播初始化完成
				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();
			}
		}
	}
obtainFreshBeanFactory

​ 告知子類刷新內部bean factory.

​ 核心方法,初始化BeanFactory、載入Bean、註冊Bean

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
		//刷新Bean工廠,關閉並銷毀舊BeanFacroty
		refreshBeanFactory();
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		if (logger.isDebugEnabled()) {
			logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
		}
		return beanFactory;
	}
refreshBeanFactory

​ 關閉並銷毀舊BeanFactory,創建與初始化新BeanFactory。為什麼是DefaultListableBeanFactory?

@Override
	protected final void refreshBeanFactory() throws BeansException {
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
			//初始化DefaultListableBeanFactory,為什麼選擇實例化DefaultListableBeanFactory?而不是其他的Bean工廠
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			//Bean工廠序列化設置id
			beanFactory.setSerializationId(getId());
			//定製Bean工廠,設置不允許覆蓋Bean,不允許迴圈依賴等
			customizeBeanFactory(beanFactory);
			//將Bean載入至Bean工廠中
			loadBeanDefinitions(beanFactory);
			//此處synchronized塊與#hasBeanFactory中的synchronized塊存在關聯,此處鎖住之後hasBeanFactory中的synchronized塊將等待
			//避免beanFactory未銷毀或未關閉的情況
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}
customizeBeanFactory

​ 定製BeanFactory。設置Bean覆蓋、迴圈依賴等。什麼是迴圈依賴?

protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
		if (this.allowBeanDefinitionOverriding != null) {
			//預設值為false不允許對Bean進行覆蓋
			beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
		if (this.allowCircularReferences != null) {
			//預設值為false,不允許迴圈依賴
			beanFactory.setAllowCircularReferences(this.allowCircularReferences);
		}
	}

AbstractXmlApplicationContext

loadBeanDefinitions
@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);
		loadBeanDefinitions(beanDefinitionReader);
	}
loadBeanDefinitions
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
		Resource[] configResources = getConfigResources();
		if (configResources != null) {
			//載入資源對象,最終還是會回到這種方式去載入bean.
			reader.loadBeanDefinitions(configResources);
		}
		String[] configLocations = getConfigLocations();
		if (configLocations != null) {
			//載入資源路徑嗎
			reader.loadBeanDefinitions(configLocations);
		}
	}

AbstractBeanDefinitionReader

loadBeanDefinitions
@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;
	}
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
		ResourceLoader resourceLoader = getResourceLoader();
		if (resourceLoader == null) {
			throw new BeanDefinitionStoreException(
					"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
		}

		if (resourceLoader instanceof ResourcePatternResolver) {
			// Resource pattern matching available.
			try {
        // 將xml轉為Resource,所以上面的兩種資源載入方式,最終都會回到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;
	}

XmlBeanDefinitionReader

loadBeanDefinitions

​ 從詳細的XML文件中載入Bean

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

		//用於存儲編譯過的Reource
		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 {
			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();
			}
		}
	}
doLoadBeanDefinitions

​ 從xml文件中載入bean,將xml轉為Document並註冊Bean

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
		try {
			//將xml轉為Document
			Document doc = doLoadDocument(inputSource, resource);
			//註冊Bean
			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);
		}
	}
registerBeanDefinitions

​ 計算從當前配置文件中載入bean的數量

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		int countBefore = getRegistry().getBeanDefinitionCount();
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}

DefaultBeanDefinitionDocumentReader

registerBeanDefinitions

​ 從“spring-beans” xsd中解析bean。什麼是xsd?XML結構定義 ( XML Schemas Definition)

@Override
	public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;
		logger.debug("Loading bean definitions");
		Element root = doc.getDocumentElement();
		doRegisterBeanDefinitions(root);
	}
doRegisterBeanDefinitions

​ 從根節點開始註冊每一個Bean

/**
	 * Register each bean definition within the given root {@code <beans/>} element.
	 * 從根節點開始註冊每一個Bean
	 */
	protected void doRegisterBeanDefinitions(Element root) {
		// Any nested <beans> elements will cause recursion in this method. In
		// order to propagate and preserve <beans> 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.
		//可以通過spring.profiles.active設定當前環境激活的profile,例如配置prod,可以通過@ActiveProfiles配置

		//解析bean定義
		BeanDefinitionParserDelegate parent = this.delegate;
		this.delegate = createDelegate(getReaderContext(), root, parent);

		if (this.delegate.isDefaultNamespace(root)) {
			//獲取元素中profile屬性,可以通過xml或@Profile設置當前bean所屬的profile
			String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
			//不能做profile屬性則直接跳過
			if (StringUtils.hasText(profileSpec)) {
				//配置多profile
				String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
						profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
				//元素中的profile是否是環境指定的profiles之一
				if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
					if (logger.isInfoEnabled()) {
						logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
								"] not matching: " + getReaderContext().getResource());
					}
					//不在當前環境指定的profile中則return
					return;
				}
			}
		}

		preProcessXml(root);
		parseBeanDefinitions(root, this.delegate);
		postProcessXml(root);

		this.delegate = parent;
	}
parseBeanDefinitions

​ 從文檔根節點開始解析元素,import、alias、bean等

/**
	 * Parse the elements at the root level in the document:
	 * "import", "alias", "bean".
	 * @param root the DOM root element of the document
	 */
	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)) {
						//解析default namespace下的元素(例如:<import>、<alias>、<beans>、<bean>)
						parseDefaultElement(ele, delegate);
					}
					else {
						//解析Custom元素(例如:<mvc>、<context>、<aop>)
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			delegate.parseCustomElement(root);
		}
	}
parseDefaultElement

​ 解析default namespace下的元素(例如:

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
		if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
			importBeanDefinitionResource(ele);
		}
		else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
			processAliasRegistration(ele);
		}
		else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
			//瞭解bean解析源碼
			processBeanDefinition(ele, delegate);
		}
		else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
			// recurse
			doRegisterBeanDefinitions(ele);
		}
	}
processBeanDefinition

​ 獲取bean元素解析並註冊

/**
	 * Process the given bean element, parsing the bean definition
	 * and registering it with the registry.
	 * 獲取bean元素解析並註冊
	 */
	protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
		//解析bean元素並封裝至BeanDefinitionHolder
		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));
		}

DefaultListableBeanFactory

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;
		//beanDefinitionMap放置所有註冊的Bean
		oldBeanDefinition = this.beanDefinitionMap.get(beanName);

		//如果bean名已存在
		if (oldBeanDefinition != null) {
			//不允許覆蓋bean
			if (!isAllowBeanDefinitionOverriding()) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
						"': There is already [" + oldBeanDefinition + "] bound.");
			}
			//通過比較BeanRole.判斷誰覆蓋誰
			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 + "]");
				}
			}
			//新bean覆蓋舊bean
			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 + "]");
				}
			}
			//將bean放置beanDefinitionMap中
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}
		//beanName沒有與beanDefinitionMap中的重覆
		else {
			//非正常情況下,其他的Bean已經初始化,如果已經有bean初始化,則使用者已經在進行業務操作,無法保證對beanDefinitionMap、beanDefinitionNames、manualSingletonNames進行操作的一些列動作的線程安全,所以需要加鎖。參考:https://blog.csdn.net/qq_41907991/article/details/97614337
			if (hasBeanCreationStarted()) {
				// Cannot modify startup-time collection elements anymore (for stable iteration)
				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
				//bean放置beanDefinitionMap中
				this.beanDefinitionMap.put(beanName, beanDefinition);
				//記錄bean名稱
				this.beanDefinitionNames.add(beanName);
				//bean不需要手動註冊
				this.manualSingletonNames.remove(beanName);
			}
			this.frozenBeanDefinitionNames = null;
		}

		if (oldBeanDefinition != null || containsSingleton(beanName)) {
			resetBeanDefinition(beanName);
		}
	}

調用關係

qRXagm

總結

​ 至此已經完成了IoC容器的初始化。將xml文件中的bean元素解析為Bean,將Bean註冊至註冊中心,併發送註冊事件。DefaultListableBeanFactory建立Bean配置信息,這些信息都存放在BeanDefinitionMap中,由IoC容器來維護。

​ 最後,懂得不多,做得很少。文章肯定又不少錯誤,如大家發現麻煩及時指出,為了避免誤導更多人,我也會及時學習並修改!

引用參考

[1]《Spring技術內幕》

[2]https://www.javadoop.com/post/spring-ioc

[3]https://github.com/seaswalker/spring-analysis/blob/master/note/Spring.md#classpathxmlapplicationcontext

原創文章,轉發請標註https://www.cnblogs.com/boycelee/p/12595884.html


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 定義: 抽象工廠模式:提供一個創建一系列相關或相互依賴對象的介面,而無需指定他們的具體類。抽象工廠模式主要解決涉及到多個產品系列的問題。 代碼實例: 先回顧上一篇中工廠方法模式的例子,該示例以 BloggsCal 和 MegaCal 兩種格式管理編碼。如果增加更多的編碼格式,這種類結構會橫向增長,但 ...
  • 來源https://juejin.im/post/5cf45fc151882502f9490639 作者:譚朝紅 1、介紹 從電腦誕生開始,就伴隨著電腦應用程式的演變。簡短的回顧歷史,我們可以清楚的看到應用程式發生的巨大變化。上世紀70年代中期,隨著個人PC機的爆炸式增長以及程式員的崛起,讓計算 ...
  • 微服務架構下規範不以規矩,不能成方圓--孟子 應用與組件拆分,一組件一倉庫Maven安全規範分支策略研發協同平臺研發變更流程開放的,任務可插撥pipeline今天先到這兒,希望對雲原生,技術領導力, 企業管理,系統架構設計與評估,團隊管理, 項目管理, 產品管管,團隊建設 有參考作用 , 您可能感興... ...
  • 我的學習方式是通過 網路視頻 和 書籍,把他們總結寫成博客,視頻主要以‘老男孩’的視頻講解。 初識python python是一門編程語言,創始人是吉多·範羅蘇姆(Guido van Rossum) ,1989年聖誕節期間,吉多·範羅蘇姆在阿姆斯特丹打發時間,決心開發一個新的腳本解釋程式,作為ABC ...
  • Robberies 題意:題目的大概意思是說一個人想去強銀行,但他又怕被抓,但他通過觀察計算出了在每個銀行被抓的概率,最後他提出如果他能承受最大被抓的概率,現在他想知道,在他能忍受的情況下,他能搶得的最大金額。 題解:這一題屬於0-1背包的變種題,它與那些常規的題目的不同之處主要體現在如下方面: ( ...
  • 在Java中,允許一個類的定義位於另一個類的內部,前者稱為內部類,後者稱為外部類。 1.內部類的分類 成員內部類(靜態和非靜態)和局部內部類(方法內、代碼塊內、構造器內) 2.成員內部類的理解 一方面,作為外部類的成員: >調用外部類的結構 >可以被static修飾 >可以被4種不同的許可權修飾 另一 ...
  • 我的LeetCode:https://leetcode cn.com/u/ituring/ 我的LeetCode刷題源碼[GitHub]:https://github.com/izhoujie/Algorithmcii LeetCode 1162. 地圖分析 題目 你現在手裡有一份大小為 N x N ...
  • 隊列,同棧一樣是一個非常基礎、常用的數據結構。 隊列的基本操作:後進先出。 隊列有以下類型: 1. 順序隊列 2. 鏈式隊列 3. 迴圈隊列:隊滿條件:(tail + 1) % n == head,隊空條件:head == tail,tail 位置不存儲數據 4. 阻塞隊列 5. 併發隊列 6. 優 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...