【Spring源碼分析】非懶載入的Bean實例化過程(上篇)

来源:http://www.cnblogs.com/xrq730/archive/2017/02/03/6361578.html
-Advertisement-
Play Games

代碼入口 上文【Spring源碼分析】Bean載入流程概覽,比較詳細地分析了Spring上下文載入的代碼入口,並且在AbstractApplicationContext的refresh方法中,點出了finishBeanFactoryInitialization方法完成了對於所有非懶載入的Bean的初 ...


代碼入口

上文【Spring源碼分析】Bean載入流程概覽,比較詳細地分析了Spring上下文載入的代碼入口,並且在AbstractApplicationContext的refresh方法中,點出了finishBeanFactoryInitialization方法完成了對於所有非懶載入的Bean的初始化。

finishBeanFactoryInitialization方法中調用了DefaultListableBeanFactory的preInstantiateSingletons方法,本文針對preInstantiateSingletons進行分析,解讀一下Spring是如何構造Bean實例對象出來的。

 

DefaultListableBeanFactory的preInstantiateSingletons方法

DefaultListableBeanFactory的preInstantiateSingletons方法,顧名思義,初始化所有的單例Bean,看一下方法的定義:

 1 public void preInstantiateSingletons() throws BeansException {
 2     if (this.logger.isInfoEnabled()) {
 3         this.logger.info("Pre-instantiating singletons in " + this);
 4     }
 5     synchronized (this.beanDefinitionMap) {
 6         // Iterate over a copy to allow for init methods which in turn register new bean definitions.
 7         // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
 8         List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
 9         for (String beanName : beanNames) {
10             RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
11             if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
12                 if (isFactoryBean(beanName)) {
13                     final FactoryBean factory = (FactoryBean) getBean(FACTORY_BEAN_PREFIX + beanName);
14                     boolean isEagerInit;
15                     if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
16                         isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
17                             public Boolean run() {
18                                 return ((SmartFactoryBean) factory).isEagerInit();
19                             }
20                         }, getAccessControlContext());
21                     }
22                     else {
23                         isEagerInit = (factory instanceof SmartFactoryBean &&
24                                 ((SmartFactoryBean) factory).isEagerInit());
25                     }
26                     if (isEagerInit) {
27                         getBean(beanName);
28                     }
29                 }
30                 else {
31                     getBean(beanName);
32                 }
33             }
34         }
35     }
36 }

前面的代碼比較簡單,根據beanName拿到BeanDefinition(即Bean的定義)。由於此方法實例化的是所有非懶載入的單例Bean,因此要實例化Bean,必須滿足11行的三個定義:

(1)不是抽象的

(2)必須是單例的

(3)必須是非懶載入的

接著簡單看一下第12行~第29行的代碼,這段代碼主要做的是一件事情:首先判斷一下Bean是否FactoryBean的實現,接著判斷Bean是否SmartFactoryBean的實現,假如Bean是SmartFactoryBean的實現並且eagerInit(這個單詞字面意思是渴望載入,找不到一個好的詞語去翻譯,意思就是定義了這個Bean需要立即載入的意思)的話,會立即實例化這個Bean。Java開發人員不需要關註這段代碼,因為SmartFactoryBean基本不會用到,我翻譯一下Spring官網對於SmartFactoryBean的定義描述:

  • FactoryBean介面的擴展介面。介面實現並不表示是否總是返回單獨的實例對象,比如FactoryBean.isSingleton()實現返回false的情況並不清晰地表示每次返回的都是單獨的實例對象
  • 不實現這個擴展介面的簡單FactoryBean的實現,FactoryBean.isSingleton()實現返回false總是簡單地告訴我們每次返回的都是單獨的實例對象,暴露出來的對象只能夠通過命令訪問
  • 註意:這個介面是一個有特殊用途的介面,主要用於框架內部使用與Spring相關。通常,應用提供的FactoryBean介面實現應當只需要實現簡單的FactoryBean介面即可,新方法應當加入到擴展介面中去

 

代碼示例

為了後面的代碼分析方便,事先我定義一個Bean:

 1 package org.xrq.action;
 2 
 3 import org.springframework.beans.factory.BeanClassLoaderAware;
 4 import org.springframework.beans.factory.BeanNameAware;
 5 import org.springframework.beans.factory.InitializingBean;
 6 
 7 public class MultiFunctionBean implements InitializingBean, BeanNameAware, BeanClassLoaderAware {
 8 
 9     private int    propertyA;
10     
11     private int    propertyB;
12     
13     public int getPropertyA() {
14         return propertyA;
15     }
16 
17     public void setPropertyA(int propertyA) {
18         this.propertyA = propertyA;
19     }
20 
21     public int getPropertyB() {
22         return propertyB;
23     }
24 
25     public void setPropertyB(int propertyB) {
26         this.propertyB = propertyB;
27     }
28     
29     public void initMethod() {
30         System.out.println("Enter MultiFunctionBean.initMethod()");
31     }
32 
33     @Override
34     public void setBeanClassLoader(ClassLoader classLoader) {
35         System.out.println("Enter MultiFunctionBean.setBeanClassLoader(ClassLoader classLoader)");
36     }
37 
38     @Override
39     public void setBeanName(String name) {
40         System.out.println("Enter MultiFunctionBean.setBeanName(String name)");
41     }
42 
43     @Override
44     public void afterPropertiesSet() throws Exception {
45         System.out.println("Enter MultiFunctionBean.afterPropertiesSet()");
46     }
47     
48     @Override
49     public String toString() {
50         return "MultiFunctionBean [propertyA=" + propertyA + ", propertyB=" + propertyB + "]";
51     }
52     
53 }

定義對應的spring.xml:

1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4     xsi:schemaLocation="http://www.springframework.org/schema/beans
5     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
6     
7     <bean id="multiFunctionBean" class="org.xrq.action.MultiFunctionBean" init-method="initMethod" />
8     
9 </beans>

利用這個MultiFunctionBean,我們可以用來探究Spring載入Bean的多種機制。

 

doGetBean方法構造Bean流程

上面把getBean之外的代碼都分析了一下,看代碼就可以知道,獲取Bean對象實例,都是通過getBean方法,getBean方法最終調用的是DefaultListableBeanFactory的父類AbstractBeanFactory類的doGetBean方法,因此這部分重點分析一下doGetBean方法是如何構造出一個單例的Bean的。

看一下doGetBean方法的代碼實現,比較長:

  1 protected <T> T doGetBean(
  2         final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
  3         throws BeansException {
  4 
  5     final String beanName = transformedBeanName(name);
  6     Object bean;
  7 
  8     // Eagerly check singleton cache for manually registered singletons.
  9     Object sharedInstance = getSingleton(beanName);
 10     if (sharedInstance != null && args == null) {
 11         if (logger.isDebugEnabled()) {
 12             if (isSingletonCurrentlyInCreation(beanName)) {
 13                 logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
 14                         "' that is not fully initialized yet - a consequence of a circular reference");
 15             }
 16             else {
 17                 logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
 18             }
 19         }
 20         bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
 21     }
 22 
 23     else {
 24         // Fail if we're already creating this bean instance:
 25         // We're assumably within a circular reference.
 26         if (isPrototypeCurrentlyInCreation(beanName)) {
 27             throw new BeanCurrentlyInCreationException(beanName);
 28         }
 29 
 30         // Check if bean definition exists in this factory.
 31         BeanFactory parentBeanFactory = getParentBeanFactory();
 32         if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
 33             // Not found -> check parent.
 34             String nameToLookup = originalBeanName(name);
 35             if (args != null) {
 36                 // Delegation to parent with explicit args.
 37                 return (T) parentBeanFactory.getBean(nameToLookup, args);
 38             }
 39             else {
 40                 // No args -> delegate to standard getBean method.
 41                 return parentBeanFactory.getBean(nameToLookup, requiredType);
 42             }
 43         }
 44 
 45         if (!typeCheckOnly) {
 46             markBeanAsCreated(beanName);
 47         }
 48 
 49         final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
 50         checkMergedBeanDefinition(mbd, beanName, args);
 51 
 52         // Guarantee initialization of beans that the current bean depends on.
 53         String[] dependsOn = mbd.getDependsOn();
 54         if (dependsOn != null) {
 55             for (String dependsOnBean : dependsOn) {
 56                 getBean(dependsOnBean);
 57                 registerDependentBean(dependsOnBean, beanName);
 58             }
 59         }
 60 
 61         // Create bean instance.
 62         if (mbd.isSingleton()) {
 63             sharedInstance = getSingleton(beanName, new ObjectFactory() {
 64                 public Object getObject() throws BeansException {
 65                     try {
 66                         return createBean(beanName, mbd, args);
 67                     }
 68                     catch (BeansException ex) {
 69                         // Explicitly remove instance from singleton cache: It might have been put there
 70                         // eagerly by the creation process, to allow for circular reference resolution.
 71                         // Also remove any beans that received a temporary reference to the bean.
 72                         destroySingleton(beanName);
 73                         throw ex;
 74                     }
 75                 }
 76             });
 77             bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
 78         }
 79 
 80         else if (mbd.isPrototype()) {
 81             // It's a prototype -> create a new instance.
 82             Object prototypeInstance = null;
 83             try {
 84                 beforePrototypeCreation(beanName);
 85                 prototypeInstance = createBean(beanName, mbd, args);
 86             }
 87             finally {
 88                 afterPrototypeCreation(beanName);
 89             }
 90             bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
 91         }
 92 
 93         else {
 94             String scopeName = mbd.getScope();
 95             final Scope scope = this.scopes.get(scopeName);
 96             if (scope == null) {
 97                 throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
 98             }
 99             try {
100                 Object scopedInstance = scope.get(beanName, new ObjectFactory() {
101                     public Object getObject() throws BeansException {
102                             beforePrototypeCreation(beanName);
103                         try {
104                             return createBean(beanName, mbd, args);
105                         }
106                         finally {
107                             afterPrototypeCreation(beanName);
108                         }
109                     }
110                 });
111                 bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
112             }
113             catch (IllegalStateException ex) {
114                 throw new BeanCreationException(beanName,
115                         "Scope '" + scopeName + "' is not active for the current thread; " +
116                         "consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",
117                         ex);
118             }
119         }
120     }
121 
122     // Check if required type matches the type of the actual bean instance.
123     if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
124         try {
125             return getTypeConverter().convertIfNecessary(bean, requiredType);
126         }
127         catch (TypeMismatchException ex) {
128             if (logger.isDebugEnabled()) {
129                 logger.debug("Failed to convert bean '" + name + "' to required type [" +
130                         ClassUtils.getQualifiedName(requiredType) + "]", ex);
131             }
132             throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
133         }
134     }
135     return (T) bean;
136 }

首先第9行~第21行的代碼,第9行的代碼就不進去看了,簡單說一下:首先檢查一下本地的單例緩存是否已經載入過Bean,沒有的話再檢查earlySingleton緩存是否已經載入過Bean(又是early,不好找到詞語翻譯),沒有的話執行後面的邏輯。

接著第26行~第50行,這裡執行的都是一些基本的檢查和簡單的操作,包括bean是否是prototype的(prototype的Bean當前創建會拋出異常)、是否抽象的、將beanName加入alreadyCreated這個Set中等。

接著第53行~第59行,我們經常在bean標簽中看到depends-on這個屬性,就是通過這段保證了depends-on依賴的Bean會優先於當前Bean被載入

接著第62行~第78行、第80行~第91行、第93行~第120行有三個判斷,顯然上面的MultiFunctionBean是一個單例的Bean也是本文探究的重點,因此執行第62行~第78行的邏輯。getSingleton方法不貼了,有一些前置的判斷,很簡單的邏輯,重點就是調用了ObjectFactory的getObject()方法來獲取到單例Bean對象,方法的實現是調用了createBean方法,createBean方法是AbstractBeanFactory的子類AbstractAutowireCapableBeanFactory的一個方法,看一下它的方法實現:

 1 protected Object createBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
 2         throws BeanCreationException {
 3 
 4     if (logger.isDebugEnabled()) {
 5         logger.debug("Creating instance of bean '" + beanName + "'");
 6     }
 7     // Make sure bean class is actually resolved at this point.
 8     resolveBeanClass(mbd, beanName);
 9 
10     // Prepare method overrides.
11     try {
12         mbd.prepareMethodOverrides();
13     }
14     catch (BeanDefinitionValidationException ex) {
15         throw new BeanDefinitionStoreException(mbd.getResourceDescription(),
16                 beanName, "Validation of method overrides failed", ex);
17     }
18 
19     try {
20         // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
21         Object bean = resolveBeforeInstantiation(beanName, mbd);
22         if (bean != null) {
23             return bean;
24         }
25     }
26     catch (Throwable ex) {
27         throw new BeanCreationException(mbd.getResourceDescription(), beanName,
28                 "BeanPostProcessor before instantiation of bean failed", ex);
29     }
30 
31     Object beanInstance = doCreateBean(beanName, mbd, args);
32     if (logger.isDebugEnabled()) {
33         logger.debug("Finished creating instance of bean '" + beanName + "'");
34     }
35     return beanInstance;
36 }

前面的代碼都沒什麼意義,代碼執行到第31行:

 1 protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
 2     // Instantiate the bean.
 3     BeanWrapper instanceWrapper = null;
 4     if (mbd.isSingleton()) {
 5         instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
 6     }
 7     if (instanceWrapper == null) {
 8         instanceWrapper = createBeanInstance(beanName, mbd, args);
 9     }
10     final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
11     Class beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
12 
13     // Allow post-processors to modify the merged bean definition.
14     synchronized (mbd.postProcessingLock) {
15         if (!mbd.postProcessed) {
16             applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
17             mbd.postProcessed = true;
18         }
19     }
20 
21     // Eagerly cache singletons to be able to resolve circular references
22     // even when triggered by lifecycle interfaces like BeanFactoryAware.
23     boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
24             isSingletonCurrentlyInCreation(beanName));
25     if (earlySingletonExposure) {
26         if (logger.isDebugEnabled()) {
27             logger.debug("Eagerly caching bean '" + beanName +
28                     "' to allow for resolving potential circular references");
29         }
30         addSingletonFactory(beanName, new ObjectFactory() {
31             public Object getObject() throws BeansException {
32                 return getEarlyBeanReference(beanName, mbd, bean);
33             }
34         });
35     }
36 
37     // Initialize the bean instance.
38     Object exposedObject = bean;
39     try {
40         populateBean(beanName, mbd, instanceWrapper);
41         if (exposedObject != null) {
42             exposedObject = initializeBean(beanName, exposedObject, mbd);
43         }
44     }
45     catch (Throwable ex) {
46         if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
47             throw (BeanCreationException) ex;
48         }
49         else {
50             throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
51         }
52     }
53 
54     if (earlySingletonExposure) {
55         Object earlySingletonReference = getSingleton(beanName, false);
56         if (earlySingletonReference != null) {
57             if (exposedObject == bean) {
58                 exposedObject = earlySingletonReference;
59             }
60             else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
61                 String[] dependentBeans = getDependentBeans(beanName);
62                 Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length);
63                 for (String dependentBean : dependentBeans) {
64                     if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
65                         actualDependentBeans.add(dependentBean);
66                     }
67                 }
68                 if (!actualDependentBeans.isEmpty()) {
69                     throw new BeanCurrentlyInCreationException(beanName,
70                             "Bean with name '" + beanName + "' has been injected into other beans [" +
71                                 StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
72                             "] in its raw version as part of a circular reference, but has eventually been " +
73                             "wrapped. This means that said other beans do not use the final version of the " +
74                             "bean. This is often the result of over-eager type matching - consider using " +
75                             "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
76                 }
77             }
78         }
79     }
80 
81     // Register bean as disposable.
82     try {
83         registerDisposableBeanIfNecessary(beanName, bean, mbd);
84     }
85     catch (BeanDefinitionValidationException ex) {
86         throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
87     }
88 
89     return exposedObject;
90 }

代碼跟蹤到這裡,已經到了主流程,接下來分段分析doCreateBean方法的代碼。

 

創建Bean實例

第8行的createBeanInstance方法,會創建出Bean的實例,並包裝為BeanWrapper,看一下createBeanInstance方法,只貼最後一段比較關鍵的:

 1 // Need to determine the constructor...
 2 Constructor[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
 3 if (ctors != null ||
 4         mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
 5         mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args))  {
 6     return autowireConstructor(beanName, mbd, ctors, args);
 7 }
 8 
 9 // No special handling: simply use no-arg constructor.
10 return instantiateBean(beanName, mbd);

意思是bean標簽使用構造函數註入屬性的話,執行第6行,否則執行第10行。MultiFunctionBean使用預設構造函數,使用setter註入屬性,因此執行第10行代碼:

 1 protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
 2     try {
 3         Object beanInstance;
 4         final BeanFactory parent = this;
 5         if (System.getSecurityManager() != null) {
 6             beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() {
 7                 public Object run() {
 8                     return getInstantiationStrategy().instantiate(mbd, beanName, parent);
 9                 }
10             }, getAccessControlContext());
11         }
12         else {
13             beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
14         }
15         BeanWrapper bw = new BeanWrapperImpl(beanInstance);
16         initBeanWrapper(bw);
17         return bw;
18     }
19     catch (Throwable ex) {
20         throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
21     }
22 }

代碼執行到13行:

 1 public Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) {
 2     // Don't override the class with CGLIB if no overrides.
 3     if (beanDefinition.getMethodOverrides().isEmpty()) {
 4         Constructor<?> constructorToUse;
 5         synchronized (beanDefinition.constructorArgumentLock) {
 6             constructorToUse = (Constructor<?>) beanDefinition.resolvedConstructorOrFactoryMethod;
 7             if (constructorToUse == null) {
 8                 final Class clazz = beanDefinition.getBeanClass();
 9                 if (clazz.isInterface()) {
10                     throw new BeanInstantiationException(clazz, "Specified class is an interface");
11                 }
12                 try {
13                     if (System.getSecurityManager() != null) {
14                         constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor>() {
15                             public Constructor run() throws Exception {
16                                 return clazz.getDeclaredConstructor((Class[]) null);
17                             }
18                         });
19                     }
20                     else {
21                         constructorToUse = clazz.getDeclaredConstructor((Class[]) null);
22                     }
23                     beanDefinition.resolvedConstructorOrFactoryMethod = constructorToUse;
24                 }
25                 catch (Exception ex) {
26                     throw new BeanInstantiationException(clazz, "No default constructor found", ex);
27                 }
28             }
29         }
30         return BeanUtils.instantiateClass(constructorToUse);
31     }
32     	   

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

-Advertisement-
Play Games
更多相關文章
  • 四、填色 color 函數有三個參數。第一個參數指定有多少紅色,第二個指定有多少綠色,第三個指定有多少藍色。比如,要得到車子的亮紅色,我們用 color(1,0,0),也就是讓海龜用百分之百的紅色畫筆。 這種紅色、綠色、藍色的混搭叫做RGB(Red,Green,Blue)。因為紅綠藍是色光上的三原色 ...
  • 開發Python的環境有很多,原來已經在vs2013上面搭建好python的開發環境了,但是vs2013每次啟動都占太多記憶體(太強大了吧),這下出了vs code,既輕量又酷炫,正好拿來試一試開發python,點擊visual studio code1.9安裝教程 下麵直接上搭建Python環境步驟 ...
  • 1. 什麼是緩存? 資料庫的緩存指的是應用程式和物理數據源之間的數據。即把物理數據源的數據複製到緩存。有了緩存,可以降低應用程式對物理數據源的訪問頻率,從而提高效率。緩存的介質一般是記憶體,也可以是硬碟。 Hibernate的緩存有三種類型:一級緩存、二級緩存和查詢緩存。 2. 一級緩存 一級緩存即S ...
  • 在Python里,海龜不僅可以畫簡單的黑線,還可以用它畫更複雜的幾何圖形,用不同的顏色,甚至還可以給形狀填色。 一、從基本的正方形開始 引入turtle模塊並創建Pen對象: 前面我們用來創建正方形的代碼如下: 此段代碼太長,我們可以用for迴圈進行優化: 效果如下: 二、畫星星 我們只需把for循 ...
  • 要想讀懂本文,你需要對C語言有基本的瞭解,本文將介紹如何使用gcc編譯器。 首先,我們介紹如何在命令行的方式下使用編譯器編譯簡單的C源代碼。 然後,我們簡要介紹一下編譯器究竟作了哪些工作,以及如何控制編譯的過程。 我們也簡要介紹了調試器的使用方法。 gcc介紹 你能想象使用封閉源代碼的私有編譯器編譯 ...
  • 先說一下需求: 在頁面上顯示資料庫中的所有圖書,顯示圖書的同時,顯示出該圖書所屬的類別(這裡一本書可能同時屬於多個類別) 創建表: 筆者這裡使用 中間表 連接 圖書表 和 圖書類別表,圖書表中 沒有使用外鍵關聯 圖書類別表 而是在中間表中引用了 圖書主鍵 和 類別主鍵 通過中間表來 表示 圖書 和 ...
  • 使用Python的turtle(海龜)模塊畫圖 第一步:讓Python引入turtle模塊,引入模塊就是告訴Python你想要用它。 第二步:創建畫布。調用turtle中的Pen函數。 第三步:移動海龜。 forward的中文意思是“向前地;促進”。所以這行代碼的意思是海龜向前移動50個像素: 讓海 ...
  • 有時候,我們也會有在C++中調用.NET的需求,C++/CLI就是這樣一種技術,它能夠與本地代碼混合編程,從而提供強大的功能,本文將介紹如何使用反射的一些實踐。 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...