分享非常漂亮的WPF介面框架原始碼及外掛化實現原理
本文將按照以下四點來介紹:
(1)ModernUI簡介;
(2)構建通用介面框架的思路;
(3)基於ModernUI和OSGi.NET的外掛化介面框架實現原理及原始碼分析;
(4)其它更有趣的東西~~。
1 ModernUI簡介
ModernUI(http://mui.codeplex.com/)是一個開源的WPF介面庫,利用該介面庫,我們可以建立很酷的應用程式。下面是ModernUI官方示例,你可以從官方網站直接下載原始碼執行,如果是.NET 4.0的話,記得要宣告“NET4”預編譯變數,否則無法編譯通過。
要編寫這樣的WPF介面,我們需要在一個Window上宣告選單和Tab頁面,下圖是定義選單的宣告。
此外,每一個Tab風格頁面,你也需要手動的為選單建立這樣的介面元素。
直接用這樣的方式來使用ModernUI,顯然不太適合團隊協作性的並行開發,因為在一個團隊的協作中,不同的人需要完成不同的功能,實現不同頁面,每個人都需要來更改主介面。
我非常希望模組化的開發方法,因為這可以儘可能的複用現有資產,使程式設計師可以聚焦在自己關注的業務邏輯上,不需要關心UI的使用。下面,我將來描述基於ModernUI實現的一個通用介面框架,這個介面框架允許程式設計師在自己的業務模組中配置需要顯示的介面元素。
2 通用介面框架實現思路
我希望能夠實現這樣的通用介面框架:
(1)程式設計師可以直接實現需要展現業務邏輯的介面,不需要關注如何使用ModernUI;
(2)程式設計師可以通過簡單的配置就可以將自己實現的業務邏輯頁面顯示在主介面中;
(3)這個介面框架可以完全複用。
當我看到ModernUI這個介面庫時,我希望將應用程式做成模組化,每一個模組能夠:
(1)通過以下配置能夠直接顯示二級選單。
(2)通過以下配置能夠直接顯示三級選單。
這樣做的好處是,開發外掛的時候可以不需要關心介面框架外掛;團隊在協作開發應用的時候,可以獨立開發並不需要修改主介面;團隊成員的外掛可以隨時整合到這個主介面;當主介面無法滿足我們的佈局時或者使用者需求無法滿足時,可以直接替換主介面框架而不需要修改任何外掛程式碼。
最終的效果如下,以下介面的幾個選單及點選選單顯示的內容由DemoPlugin外掛、DemoPlugin2外掛來提供。當外掛框架載入更多外掛時,介面上會出現更多的選單;反之,當外掛被解除安裝或者被停止時,則相應的選單將消失掉。
下面我來介紹如何實現。
3 基於ModernUI和OSGi.NET的外掛化介面框架實現原理及原始碼分析
3.1 OSGi.NET外掛框架原理簡介
OSGi.NET框架是一個完全通用的.NET外掛框架,它支援WPF、WinForm、ASP.NET、ASP.NET MVC 3.0/4.0、控制檯等任意.NET應用程式,也就是說,你可以基於該外掛框架來快速構架外掛化的應用程式。OSGi.NET外掛框架提供了外掛化支援、外掛擴充套件和麵向服務支援三大功能。
OSGi.NET外掛框架啟動時,從外掛目錄中搜索外掛,安裝並啟動這些外掛,將這些外掛組裝在外掛框架中;一個外掛可以暴露擴充套件點,允許其它外掛在不更改其程式碼情況下,擴充套件該外掛的功能;外掛間可以通過服務來進行通訊。
在一個外掛應用程式中,它首先要獲取一個入口點,這個入口點由一個外掛來提供,然後進入這個外掛的入口並執行起來。一個提供入口的外掛通常是一個主介面外掛,比如上面介紹的這個WPF介面框架。也就是說,外掛應用程式啟動起來後,會先執行這個介面框架的主介面。而主介面一般都提供了關於介面元素的擴充套件,允許其它外掛將選單、導航和內容頁面註冊到主介面,因此,當主介面執行時,它會將其它外掛註冊的介面元素顯示出來。當用戶點選介面元素時,外掛框架就會載入這個外掛的頁面,某個外掛的頁面在呈現時,則有可能會從資料庫中提取資料展示,這時候,該外掛則可能會呼叫資料訪問服務提供的通用資料訪問介面。OSGi.NET提供的三大功能,剛好能夠非常的吻合這樣的系統的啟動形式。當然,OSGi.NET除了提供外掛三大支撐功能之外,它還支援外掛動態性與隔離性。動態性,意味著我們可以在執行時來動態安裝、啟動、停止、解除安裝和更新外掛,而隔離性則意味著每一個外掛都擁有自己獨立的目錄,有自己獨立的型別載入器和型別空間。
基於OSGi.NET外掛框架,我們很容易實現外掛的動態安裝、遠端管理、自動化部署、自動升級和應用商店。下面,我來描述如何使用OSGi.NET來構建一個WPF外掛應用。
3.2 基於OSGi.NET來實現WPF外掛應用
利用OSGi.NET來建立一個WPF外掛應用非常的簡單。只需要實現:(1)建立一個外掛主程式,定義外掛目錄;(2)在主程式中利用BootStrapper實現OSGi.NET核心升級檢測與自動升級;(3)啟動外掛框架;(4)利用PageFlowService獲取主介面,然後執行主介面。下面我們看一下外掛主程式。(注:如果你安裝了OSGi.NET框架,可以直接使用專案模板來建立WPF主程式專案。)
在這個主程式,我們在專案的屬性將輸出路徑改為bin,並在bin目錄下建立一個Plugins目錄,然後將OSGi.NET四個標準外掛拷貝到Plugins目錄,它們分別用於:(1)外掛遠端管理,即RemotingManagement和WebServiceWrapperService,支援遠端管理控制檯除錯用;(2)外掛管理服務,即UIShell.BundleManagementService,支援對本地外掛管理和外掛倉庫訪問與下載;(3)頁面流服務,即UIShell.PageFlowService,用於獲取主介面。
下面我們來看一下App.xaml.cs原始碼,在這裡實現了外掛載入、啟動和進入主介面的功能。
namespace UIShell.iOpenWorks.WPF { /// <summary> /// WPF startup class. /// </summary> public partial class App : Application { // Use object type to avoid load UIShell.OSGi.dll before update. private object _bundleRuntime; public App() { UpdateCore(); StartBundleRuntime(); } void UpdateCore() // Update Core Files, including BundleRepositoryOpenAPI, PageFlowService and OSGi Core assemblies. { if (AutoUpdateCoreFiles) { new CoreFileUpdater().UpdateCoreFiles(CoreFileUpdateCheckType.Daily); } } void StartBundleRuntime() // Start OSGi Core. { var bundleRuntime = new BundleRuntime(); bundleRuntime.AddService<Application>(this); bundleRuntime.Start(); Startup += App_Startup; Exit += App_Exit; _bundleRuntime = bundleRuntime; } void App_Startup(object sender, StartupEventArgs e) { Application app = Application.Current; var bundleRuntime = _bundleRuntime as BundleRuntime; app.ShutdownMode = ShutdownMode.OnLastWindowClose; #region Get the main window var pageFlowService = bundleRuntime.GetFirstOrDefaultService<IPageFlowService>(); if (pageFlowService == null) { throw new Exception("The page flow service is not installed."); } if (pageFlowService.FirstPageNode == null || string.IsNullOrEmpty(pageFlowService.FirstPageNode.Value)) { throw new Exception("There is not first page node defined."); } var windowType = pageFlowService.FirstPageNodeOwner.LoadClass(pageFlowService.FirstPageNode.Value); if (windowType == null) { throw new Exception(string.Format("Can not load Window type '{0}' from Bundle '{1}'.", pageFlowService.FirstPageNode.Value, pageFlowService.FirstPageNodeOwner.SymbolicName)); } app.MainWindow = System.Activator.CreateInstance(windowType) as Window; #endregion app.MainWindow.Show(); } void App_Exit(object sender, ExitEventArgs e) { if (_bundleRuntime != null) { var bundleRuntime = _bundleRuntime as BundleRuntime; bundleRuntime.Stop(); _bundleRuntime = null; } } // Other codes } }
上述程式碼非常簡單,我將介紹一下每一個函式的功能。
(1)建構函式:呼叫UpdateCore和StartBundleRuntime;
(2)UpdateCore:呼叫BootStrapper程式集的CoreFileUpdater來實現核心檔案升級;
(3)StartBundleRuntime:建立一個BundleRuntime,即外掛框架,BundleRuntime預設建構函式指定的外掛目錄為Plugins;啟動BundleRuntime,即啟動外掛框架;掛載Startup和Exit事件;
(4)在App_Startup事件處理函式中,從外掛框架獲取PageFlowService服務,利用該服務獲取主介面,然後建立該介面例項,並執行;
(5)在App_Exit事件處理函式中,終止外掛框架,釋放資源。
3.3 基於ModernUI實現通用介面外掛框架
我在第2節描述了通用介面框架的思路。這個介面框架將基於OSGi.NET外掛框架三大功能之一——外掛擴充套件來實現。我將按照以下順序來描述實現。
3.3.1 OSGi.NET外掛擴充套件原理
下圖是OSGi.NET外掛擴充套件原理,在這裡,需要暴露擴充套件點的外掛暴露一個ExtensionPoint,提供擴充套件的外掛則宣告一個Extension(XML格式),如下所示。暴露擴充套件點的外掛通過OSGi.NET框架獲取所有Extension,然後對其進行處理。
依據第2節描述,通用介面框架外掛需要暴露擴充套件點和處理擴充套件。暴露擴充套件點意味著它需要定義介面擴充套件的格式。下面我來介紹擴充套件格式的XML定義。
3.3.2 介面擴充套件XML定義
根據介面框架要實現的功能,我們定義的擴充套件格式,如下所示。擴充套件點的名稱為UIShell.WpfShellPlugin.LinkGroups。通過LinkGroup來定義一級選單,通過Link來定義葉子節點選單,通過TabLink來定義三級選單的Tab佈局方式。
<Extension Point="UIShell.WpfShellPlugin.LinkGroups"> <LinkGroup DisplayName="一級選單" DefaultContentSource="預設顯示頁面"> <Link DisplayName="二級選單" Source="二級選單頁面" /> <TabLink DisplayName="三級選單Tab佈局" DefaultContentSource="預設頁面" Layout="List/Tab"> <Link DisplayName="三級選單" Source="三級選單頁面" /> </TabLink> </LinkGroup> </Extension>
介面框架外掛需要做的就是獲取這樣的XML定義,並且自動在介面上將元素創建出來並自動載入外掛提供的頁面。下面我來介紹介面框架如何實現。
3.3.3 介面框架的實現
介面框架基於ModernUI來實現,它需要完成:(1)為Extension建立擴充套件模型;(2)獲取所有擴充套件模型物件,並在主介面建立介面元素;(3)監聽擴充套件變更事件,動態變更介面元素。
首先,我們來看看擴充套件模型的構建。在這裡,定義了LinkGroupData、TabLinkData、LinkData分別對應於擴充套件的XML的元素。
這裡的ShellExtensionPointHandler物件則用於同OSGi.NET框架擴充套件擴充套件資訊,並將其轉換成擴充套件物件模型,然後儲存在LinkGroups屬性中。LinkGroups為ObservableCollection,當新增或者刪除LinkGroup時會丟擲Add/Remov事件。下面來看一下這個類的程式碼。
using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text; using System.Xml; using UIShell.OSGi; namespace UIShell.WpfShellPlugin.ExtensionModel { public class ShellExtensionPointHandler { public const string ExtensionPointName = "UIShell.WpfShellPlugin.LinkGroups"; public IBundle Bundle { get; private set; } public ObservableCollection<LinkGroupData> LinkGroups { get; private set; } public ShellExtensionPointHandler(IBundle bundle) { Bundle = bundle; InitExtensions(); if (Bundle.Context != null) { Bundle.Context.ExtensionChanged += Context_ExtensionChanged; } } void InitExtensions() // Init { if (Bundle.Context == null) { return; } // Get all extensions. var extensions = Bundle.Context.GetExtensions(ExtensionPointName); LinkGroups = new ObservableCollection<LinkGroupData>(); // Convert extensions to LinkGroupData collection. foreach (var extension in extensions) { AddExtension(extension); } } // Handle ExtensionChanged event. void Context_ExtensionChanged(object sender, ExtensionEventArgs e) { if (e.ExtensionPoint.Equals(ExtensionPointName)) { // Create LinkGroupData objects for new Extension. if (e.Action == CollectionChangedAction.Add) { AddExtension(e.Extension); } else // Remove LinkGroupData objects respond to the Extension. { RemoveExtension(e.Extension); } } } // Convert Extension to LinkGroupData instances. void AddExtension(Extension extension) { LinkGroupData linkGroup; foreach (XmlNode node in extension.Data) { if (node is XmlComment) { continue; } linkGroup = new LinkGroupData(extension); linkGroup.FromXml(node); LinkGroups.Add(linkGroup); } } // Remove LinkGroupData instances of the Extension. void RemoveExtension(Extension extension) { var toBeRemoved = new List<LinkGroupData>(); foreach (var linkGroup in LinkGroups) { if (linkGroup.Extension.Equals(extension)) { toBeRemoved.Add(linkGroup); } } foreach (var linkGroup in toBeRemoved) { LinkGroups.Remove(linkGroup); } } } }
這個類有以下幾個方法:
(1)InitExtensions:即從OSGi.NET框架獲取已經註冊的擴充套件資訊,將其轉換成LinkGroupData例項,並儲存;
(2)Context_ExtensionChanged事件處理函式:即當Extension被新增或者刪除時的處理函式,這在外掛安裝和解除安裝時發生,我們需要將新建的Extension轉換成LinkGroupData例項儲存起來,需要已刪除的Extension對應的LinkGroupData例項移除掉。
那接下來我們來看一下主介面如何根據擴扎模型來建立或者刪除介面元素。首先,你可以看到,這個主介面是空的沒有預先定義任何的介面元素。
那你一定猜到了,這個介面肯定是通過程式碼來動態建立介面元素,我們來看看程式碼先。
namespace UIShell.WpfShellPlugin { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : ModernWindow { public static ShellExtensionPointHandler ShellExtensionPointHandler { get; set; } private List<Tuple<LinkGroupData, LinkGroup>> LinkGroupTuples { get; set; } public MainWindow() { InitializeComponent(); LinkGroupTuples = new List<Tuple<LinkGroupData, LinkGroup>>(); ShellExtensionPointHandler = new ShellExtensionPointHandler(BundleActivator.Bundle); ShellExtensionPointHandler.LinkGroups.CollectionChanged += LinkGroups_CollectionChanged; InitializeLinkGroupsForExtensions(); } void InitializeLinkGroupsForExtensions() { foreach (var linkGroupData in ShellExtensionPointHandler.LinkGroups) { CreateLinkGroupForData(linkGroupData); } // 設定第一個頁面 if (ShellExtensionPointHandler.LinkGroups.Count > 0) { var first = ShellExtensionPointHandler.LinkGroups[0]; ContentSource = new Uri(first.FormatSource(first.DefaultContentSource), UriKind.RelativeOrAbsolute); } } void LinkGroups_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { Action action = () => { if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add) { // 新加了LinkGroupData foreach (LinkGroupData item in e.NewItems) { CreateLinkGroupForData(item); } } else if(e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove) { // 刪除了LinkGroupData foreach (LinkGroupData item in e.OldItems) { RemoveLinkGroupForData(item); } } }; Dispatcher.Invoke(action); } void CreateLinkGroupForData(LinkGroupData linkGroupData) { var linkGroup = new LinkGroup { DisplayName = linkGroupData.DisplayName, GroupName = linkGroupData.GroupName }; foreach (var linkData in linkGroupData.Links) { if (linkData is LinkData) { linkGroup.Links.Add(new Link { DisplayName = linkData.DisplayName, Source = new Uri(linkData.FormatSource((linkData as LinkData).Source), UriKind.RelativeOrAbsolute) }); } else if (linkData is TabLinkData) { linkGroup.Links.Add(new Link { DisplayName = linkData.DisplayName, Source = new Uri("[email protected]Holder?LinkId=" + linkData.LinkId.ToString(), UriKind.RelativeOrAbsolute) }); } } if (linkGroupData.IsTitleLink) { TitleLinks.Add(new Link { DisplayName = linkGroupData.DisplayName, Source = new Uri(linkGroupData.FormatSource(linkGroupData.DefaultContentSource), UriKind.RelativeOrAbsolute) }); } MenuLinkGroups.Add(linkGroup); LinkGroupTuples.Add(new Tuple<LinkGroupData, LinkGroup>(linkGroupData, linkGroup)); } void RemoveLinkGroupForData(LinkGroupData linkGroupData) { var tuple = LinkGroupTuples.Find(t => t.Item1.Equals(linkGroupData)); if (tuple != null) { MenuLinkGroups.Remove(tuple.Item2); LinkGroupTuples.Remove(tuple); } } } }
上面的程式碼也很簡單,邏輯很清晰,我來說明一下各個方法的用處:
(1)InitializeLinkGroupsForExtensions:獲取擴充套件模型物件,並將物件轉換成介面元素LinkGroup,然後監聽擴充套件模型變更事件;
(2)LinkGroups_CollectionChanged:擴充套件模型變更事件,當有擴充套件物件新增時,需要新增新的介面元素;反之,則需要移除介面元素;
(3)CreateLinkGroupForData:為擴充套件模型建立介面元素LinkGroup;
(4)RemoveLinkGroupForData:當擴充套件模型被刪除時,需要將對應的介面元素刪除掉。
為了支援外掛化,還需要為ModernUI做一個變更,下面我將來介紹。
3.4 ModernUI外掛化支撐所做的變更
為了支援外掛化,我需要對ModernUI的ContentLoader進行擴充套件,使其支援直接從外掛載入內容頁面。詳細檢視以下程式碼。
/// <summary> /// Loads the content from specified uri. /// </summary> /// <param name="uri">The content uri</param> /// <returns>The loaded content.</returns> protected virtual object LoadContent(Uri uri) { // don't do anything in design mode if (ModernUIHelper.IsInDesignMode) { return null; } string uriString = string.Empty; string paraString = string.Empty; Dictionary<string, string> parameters = new Dictionary<string, string>(); if (uri.OriginalString.Contains('?')) { var uriPara = uri.OriginalString.Split('?'); uriString = uriPara[0]; paraString = uriPara[1]; var parameterStrs = paraString.Split('&'); string[] parameterStrSplitted; foreach (var parameterStr in parameterStrs) { parameterStrSplitted = parameterStr.Split('='); parameters.Add(parameterStrSplitted[0], parameterStrSplitted[1]); } } else { uriString = uri.OriginalString; } object result = null; // 1st Format: [BundleSymbolicName]@[Class Full Name] if (uriString.Contains('@')) { var bundleSymbolicNameAndClass = uriString.Split('@'); if (bundleSymbolicNameAndClass.Length != 2 || string.IsNullOrEmpty(bundleSymbolicNameAndClass[0]) || string.IsNullOrEmpty(bundleSymbolicNameAndClass[1])) { throw new Exception("The uri must be in format of '[BundleSymbolicName]@[Class Full Name]'"相關推薦
[.Net碼農]分享非常漂亮的WPF介面框架原始碼及外掛化實現原理
在上文《分享一個非常漂亮的WPF介面框架》中我簡單的介紹了一個介面框架,有朋友已經指出了,這個介面框架是基於ModernUI來實現的,在該文我將分享所有的原始碼,並詳細描述如何基於ModernUI來構造一個非常通用的、外掛化的WPF開發框架。下載原始碼的同志,希望點選一下推薦。 本文將按照以下四點來介紹:
分享非常漂亮的WPF介面框架原始碼及外掛化實現原理
本文將按照以下四點來介紹: (1)ModernUI簡介; (2)構建通用介面框架的思路; (3)基於ModernUI和OSGi.NET的外掛化介面框架實現原理及原始碼分析; (4)其它更有趣的東西~~。 1 ModernUI簡介 ModernUI(http:
關於WPF介面框架合集
#關於WPF介面框架MahApps.Metro學習筆記《一》 公司因為業務發展,需要使用到WPF這種聽說是非常陳舊的東西,但對於一個新人來說,只要是沒接觸的知識,在我看來,都是一個學習的過程。因為目前階段已經脫離了使用最原始的微軟控制元件構建介面的過程,不是我的
MultipartFile的原始碼及小案例實現
MultipartFile是springframe封裝好的一個專門用於檔案上傳的介面,MultipartFile有一下幾個抽象方法(直接copy的原始碼): public interface MultipartFile { /** * Return the n
php簡訊介面開發經驗及具體開發實現
一、群發簡訊mt 引數名稱 說明 是否必須 備註 Sn 軟體序列號 是 格式XXX-XXX-XXX-XXXXX Pwd 密碼 是 md5(sn+password) 32位大寫密文 Mobile
DELPHI簡訊介面開發經驗及具體開發實現
一、群發簡訊mt 引數名稱 說明 是否必須 備註 Sn 軟體序列號 是 格式XXX-XXX-XXX-XXXXX Pwd 密碼 是 md5(sn+password) 32位大寫密文 Mobile
Spring框架原始碼解析 IOC容器實現BeanDefinition(三)
我們找女朋友,首先必須保證是個女的,這是最低要求,生活不易,我們先從最低的要求出發吧。女朋友是一個抽象的概念,我們必須定義一些屬性,年齡,身高,名字,是否漂亮等等來描述她。不過目前這些統統都沒有,有的也就是說我們的最低要求,女的。 public interface Bean
Java日誌框架:slf4j作用及其實現原理
sof cat 打開 系統 aging .get matching ade you 簡單回顧門面模式 slf4j是門面模式的典型應用,因此在講slf4j前,我們先簡單回顧一下門面模式, 門面模式,其核心為外部與一個子系統的通信必須通過一個統一的外觀對象進行,使得子系統更易於
STL原始碼剖析——stack的實現原理和使用方法詳解
Stack 簡介 stack 是堆疊容器,是一種“先進後出”的容器。 stack 是簡單地裝飾 deque 容器而成為另外一種容器。 使用 stack 時需要加上標頭檔案 #include<s
STL原始碼剖析——deque的實現原理和使用方法詳解
Deque 簡介 deque是“double—ended queue”的縮寫,和vector一樣都是STL的容器,deque 是雙端陣列,而 vector 是單端的。 deque 在介面上和 vector 非常相似,在許多操作的地方
【Android】原始碼分析 - LRUCache快取實現原理
一、Android中的快取策略 一般來說,快取策略主要包含快取的新增、獲取和刪除這三類操作。如何新增和獲取快取這個比較好理解,那麼為什麼還要刪除快取呢?這是因為不管是記憶體快取還是硬碟快取,它們的快取大小都是有限的。當快取滿了之後,再想其新增快取,這個時候就需要刪除一些舊的快取
Http Cookie機制及Cookie的實現原理
轉自:https://itbilu.com/other/relate/4J4n8fIPe.html Cookie是進行網站使用者身份,實現服務端Session會話持久化的一種非常好方式。Cookie最早由Netscape公司開發,現在由 IETF 的RFC 6265標準備對其規範,已被所有主流瀏
Java容器框架(二)--ArrayList實現原理
1. 簡介 在Java容器框架(一)--概述篇 中,對ArrayList做了一些簡單的介紹,它在List家族中具有很重要的角色,它的類繼承關係如下: public class ArrayList<E> extends AbstractList<E>
Java容器框架(三)--LinkedList實現原理
1. 簡介 如果對Java容器家族成員不太熟悉,可以先閱讀Java容器框架(一)--概述篇這邊文章,LinkedList類在List家族中具有重要的位置,基本上可以和ArrayList平起平坐,在功能上甚至比ArrayList還要強大。下面我們先來看看LinkedList繼
Java的Executor框架和執行緒池實現原理
一,Java的Executor框架 1,Executor介面 public interface Executor { void execute(Runnable command); } Executor介面是Executor框架中最基礎的部分,定義了一個用於
SSM框架整合PageHelper外掛,實現分頁功能
本文簡單的介紹瞭如何運用PageHelper外掛實現分頁功能。 一、在 pom.xml 中新增如下依賴: 從下面的地址中檢視最新版本的 jar 包 <!-- 新增分佈外掛的包pageh
[Vue原始碼分析] v-model實現原理
最近小組有個關於vue原始碼分析的分享會,提前準備一下… 前言: 我們都知道使用v-model可以實現資料的雙向繫結,及實現資料的變化驅動dom的更新,dom的更新影響資料的變化。那麼v-model是怎麼實現這一原理的呢?接下來探索一下這部分的原始碼。 前期準備 ①:vue2
Java併發機制及鎖的實現原理
Java併發程式設計概述 併發程式設計的目的是為了讓程式執行得更快,但是,並不是啟動更多的執行緒就能讓程式最大限度地併發執行。在進行併發程式設計時,如果希望通過多執行緒執行任務讓程式執行得更快,會面臨非常多的挑戰,比如上下文切換的問題、死鎖的問題,以及受限於硬體和軟體的資源
Spring5原始碼之IOC容器實現原理
1、 2、 3、定位、載入、註冊 定位:資源配置import,classpath,url 載入:解析配置檔案,把bean包裝成BeanDefinition物件 註冊:把已經初始化的BeanDefinition物件放到IOC容器中
Spring原始碼學習之IOC實現原理(二)-ApplicationContext
一.Spring核心元件結構 總的來說Spring共有三個核心元件,分別為Core,Context,Bean.三大核心元件的協同工作主要表現在 :Bean是包裝我們應用程式自定義物件Object的,Object中存有資料,而Context就是為了這些資料存放提供一個生存環境,儲存各個 bean之間的