分類
發燒車訊

福斯推出概念電動車,續航力上看600公里

福斯汽車(Volkswagen)因排氣造假醜聞而加速電動車研發腳步,不僅研發電動車平台MEB,更預計在今年9月底的巴黎車展上推出首輛由MEB平台打造的電動概念車。這款電動概念車的續航力據報導可達400~600公里。

媒體報導,福斯這款即將面世MEB平台電動概念車,外觀較接近既有車款,大小類似Golf,空間則接近Passat。為維持價格合理,並未採用太多昂貴的輕量複合材質。

值得注意的是,在NEDC標準測試下,這款概念車的續航力可達400~600公里之遠。優異的續航性能與合理定價,將有助提高消費者對產品的接受度。此外,這款概念車據稱已十分接近量產階段,預期會在2019年正式投產。

MEB平台也會陸續研發出多款電動汽車,如Phaeton、Budd-e等。福斯計畫在2025年之前推出至少30款電動車款,並在2030年時將電動車銷售量提高到旗下總銷量的三分之一。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司“嚨底家”!

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!

聚甘新

分類
發燒車訊

軟件架構的核心思想

目錄

  • 前言
  • 一、軟件架構的定義
  • 二、軟件架構與設計
  • 三、軟件架構的幾個方面
    • 系統
    • 結構
    • 環境
    • 利益相關者
  • 四、軟件架構的結構特徵
    • 運行時結構
    • 模塊結構
  • 五、軟件架構的質量屬性
    • 可修改性
    • 可測試性
    • 可擴展性
    • 性能
    • 可用性
    • 安全性
    • 可部署性
  • 六、其他常見概念
    • 內聚
    • 耦合
    • 企業架構
    • 系統架構

前言

welcome to chenqionghe’s blog,架構能力其實更像是一種內功,需要我們不斷地去學習,讓我們用一張正能量的圖片開啟美好的學習生活,let’s do it~

一、軟件架構的定義

架構是一個系統的基本組織,涵蓋所包含的組件,組件之間的關係、組件與環境的關係,以及指導架構設計和演進的原則等內容

二、軟件架構與設計

軟件架構和軟件設計中都有“設計”的意思,但與後者相比,前者具有更高的抽象性和更廣的範圍

  • 軟件架構關注如何對系統中的結構和交互進行較高級別的描述,它關注的是那些與系統骨架相關的決策問題,例如:功能、組織 、技術、業務和質量標準等。
  • 軟件設計關注構成系統的部件或組件,以及子系統是如何組織的,關注的問題通常更接近代碼或模塊本身,例如:
    • 將代碼分成哪些模塊?如何組織?
    • 給不同的功能分配哪些類(或模塊)?
    • 對於類“C”應該使用哪種設計模式?
    • 在運行時對象之間是如何交互的?傳遞什麼消息?如何實施交互?

三、軟件架構的幾個方面

系統

系統是以特定方式組織的組件集合,以實現特定的功能。
軟件系統是其軟件組件的集合。一個系統通常可以劃分為若干個子系統。

結構

結構是根據某個指導規則或原則來組成或組織在一起的一組元素的集合,可以是軟件或硬件系統。
軟件架構可以根據觀察者的上下文展示各個層次的結構

環境

軟件系統所在的上下文或環境對其軟件架構有直接的影響。
這樣的上下因素可以是技術、商業、專業、操作等

利益相關者

任何對某個系統及其成功與否感興趣或關心的個體或團體,都是利益相關者。
例如:架構師、開發團隊、客戶、項目經理和營銷團隊等

四、軟件架構的結構特徵

運行時結構

在運行時創建的對象及其之間的交互方式經常決定部署架構。
部署架構與可擴展性、性能、安全性和交互操作性等質量屬性密切相關

模塊結構

為了分解任務,如何拆分代碼並把代碼組織到模塊和包中,這與系統的可維護性和可修改性密切相關,因為

  • 在代碼組織過程中考慮到可擴展性的話,通常會將父類放在單獨定義好的具有恰當文檔和配置的包中,這樣就可以輕易地通過增加外部模塊進行擴展,而不需要處理太多的依賴關係
  • 對於那些依賴於外部或第三方開發者(庫、框架等)的代碼,通過會根據提供的安裝或部署步驟,從外部源手動或自動地獲取並補丁全部各種依賴。此類代碼還提供多種文檔(例如README、INSTALL)等,它們清楚地記錄了這些步驟

五、軟件架構的質量屬性

質量屬性是系統的可度量和可測試的特性,可用於評估系統在其指定環境中的非功能性需求方面的達成情況

可修改性

定義為對系統進行修改的容易程度,以及系統對理髮進行調整的靈活性。
這是討論的修改不光是代碼的修改、部署的修改,而是任何層次上的修改。
一般架構師對可修改性的興趣點如下:

  • 難點:對系統進行修改的難易程度
  • 成本:進行修改需要的時間和資源
  • 風險:任何與系統修改相關的風險

代碼可讀性越強,其可修改性就越強,代碼的可修改性與可讀性成正比

可測試性

指一個軟件系統支持通過測試來檢測故障的程度。
可測試性也可以認為是一個軟件系統向最終用戶和集成測試隱藏了多少bug
一個系統的可測試性越好,它能隱藏的bug就越少。

可擴展性

指系統能夠適應不斷增長的負載需求,但同時要保證可接受的處理性能,一般分為兩大類:

  • 橫向(水平)擴展性。意味着通過向其中添加更多多的計算節點。
  • 縱向(垂直)擴展性。涉及系統單個節點中資源的添加或移除

性能

指系統在給定的計算資源內完成的工作量,完成的工作量和計算資源的比例(work/unit)越高,性能越高。

計算資源的單位有以下幾種

  • 響應時間。一個函數或執行單元運行所需要的時間。
  • 延遲。某個系統被激活並提供響應所需的時間。
  • 吞吐量。系統處理信息的某種比率。

可用性

指系統處於完全可操作狀態的程度,以便在任何時候獲得調用請求時可以執行的能力

  • 可靠性
    系統的可用性和可靠性密切相關,系統越可靠,可用性就越高。
  • 故障恢復能力
    影響可用性的另一個因素是從故障中恢復的能力,包括了故障檢測、故障恢復、故障預防
  • 數據一致性
    CAP定理指出,系統的可用性與其數據一致性有密切聯繫。一致性和可用性一般膛會同時成立,因為可能通信失敗,系統可以在一致性或可用性之間進行選擇

安全性

避免被未經過身份驗證的訪問損害數據和邏輯,同時繼續向通過誰的其他系統和角色提供服務的一種能力。

可部署性

指軟件從開發環境到產品運行環境移交的難易程度。
有以下相關因素:

  • 模塊結構。
    將系統劃分為易於部署的一個個子單元,則部署會容易
  • 產品運行環境與開發環境。與
    開發環境結構非常相似的產品運行環境會使部署
  • 開發生態系統支持。
    為系統提供成熟的工具鏈支持,允許各種依賴關係自動建立和驗證等配置項內容,從而提高可部署性。
  • 標準配置。
    一個好的方式是開發者保持開發環境的配置結構和產品運行環境一致。
  • 標準化基礎設施。
    將部署保持在一個標準化的基礎設施上,提高可部署性
  • 容器使用
    隨着Docker容器技術的普及,可以規範軟件,減少啟動/停止的開銷,從而使部署更容易。

六、其他常見概念

內聚

一個模塊內相關聯程度的度量,描述的是模塊內的功能聯繫
若一個模塊之間各元素聯繫緊密,則內聚性就高(高內聚)
如果能做到將模塊做成一個功能類聚、獨立性強、內部緊密結合才是一個理想的類聚模塊,這對初學都來說,非常不容易,不光是個人技術能力的挑戰,更是對某個領域業務水平的挑戰。

耦合

各模塊間相互聯繫緊密程度的一種度量。
模塊之間聯繫少,耦合性就越低,模塊之間的相對獨立性就越強。

企業架構

企業架構是一個定義企業結構和行為的概念藍圖。它確定了企業結構、流程、人員和信息流動如何與其核心目標相一致,以便有效地實現當前和未來的目標

系統架構

系統架構是系統的基本組織形式,由其結構和行為視圖表示。該結構由兩部分確定:構成系統的組件和組件的行為。組件的行為是指組件之間的交互,以及組件與外部系統之間的交互

以上內容由chenqionghe整理,參考《軟件架構-Python語言實現》,light weight baby~

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

聚甘新

分類
發燒車訊

spring源碼分析——BeanPostProcessor接口

 

  BeanPostProcessor是處理bean的後置接口,beanDefinitionMaps中的BeanDefinition實例化完成后,完成populateBean,屬性設置,完成

初始化后,這個接口支持對bean做自定義的操作。

一:BeanPostProcessor的使用

定義一個測試用的model對象,name屬性默認為hello

public class BeanDemo {

	private String name = "hello";

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		final StringBuffer sb = new StringBuffer("BeanDemo{");
		sb.append("name='").append(name).append('\'');
		sb.append('}');
		return sb.toString();
	}
}

  

自定義一個MyBeanPostProcessor類,實現BeanPostProcessor接口

@Service
public class MyBeanPostProcessor implements BeanPostProcessor {

	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return null;
	}

	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		if(beanName.equals("beanDemo")){
			BeanDemo beanDemo = (BeanDemo)bean;
			beanDemo.setName("kitty");
			return beanDemo;
		}
		return bean;
	}
}

  

 

 

從運行結果看,spring中維護的beanName為beanDemo的對象,name屬性為ketty

 

 

二:看看源碼怎麼實現的

1:實例化並且註冊到beanPostProcessors集合中

 

 

 

 

主要的實例化邏輯在這個接口,這個接口的作用就是把所有實現BeanPostProcessor接口的類實例化,然後註冊到 beanPostProcessors這個緩存中

 

 

	public static void registerBeanPostProcessors(
			ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {

		// 獲取所有實現接口BeanPostProcessor的beanName
		String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);

		// Register BeanPostProcessorChecker that logs an info message when
		// a bean is created during BeanPostProcessor instantiation, i.e. when
		// a bean is not eligible for getting processed by all BeanPostProcessors.
		int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
		beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));

		// Separate between BeanPostProcessors that implement PriorityOrdered,
		// Ordered, and the rest.
		/**
		 * 把實現PriorityOrdered 和 Ordered 和 其他的處理器分開
		 */
		List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
		List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
		List<String> orderedPostProcessorNames = new ArrayList<>();
		List<String> nonOrderedPostProcessorNames = new ArrayList<>();
		/**
		 * 1:遍歷集合postProcessorNames
		 * 2:判斷類型,如果是PriorityOrdered,則實例化對象放入priorityOrderedPostProcessors集合,
		 * Ordered 則放入orderedPostProcessorNames集合,其他的放入nonOrderedPostProcessorNames集合
 		 */
		for (String ppName : postProcessorNames) {
			if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
				BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
				priorityOrderedPostProcessors.add(pp);
				if (pp instanceof MergedBeanDefinitionPostProcessor) {
					internalPostProcessors.add(pp);
				}
			}
			else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
				orderedPostProcessorNames.add(ppName);
			}
			else {
				nonOrderedPostProcessorNames.add(ppName);
			}
		}

		// First, register the BeanPostProcessors that implement PriorityOrdered.
		// 首先對priorityOrderedPostProcessors集合中實例對象排序,然後註冊,放入beanFactory中緩存下來
		sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
		registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);

		// Next, register the BeanPostProcessors that implement Ordered.
		// 然後再實例化實現Ordered接口的對象,完成註冊
		List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>();
		for (String ppName : orderedPostProcessorNames) {
			BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
			orderedPostProcessors.add(pp);
			if (pp instanceof MergedBeanDefinitionPostProcessor) {
				internalPostProcessors.add(pp);
			}
		}
		sortPostProcessors(orderedPostProcessors, beanFactory);
		registerBeanPostProcessors(beanFactory, orderedPostProcessors);

		// Now, register all regular BeanPostProcessors.
		// 最後實例化什麼都沒有實現的,完成實例化並註冊
		List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
		for (String ppName : nonOrderedPostProcessorNames) {
			BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
			nonOrderedPostProcessors.add(pp);
			if (pp instanceof MergedBeanDefinitionPostProcessor) {
				internalPostProcessors.add(pp);
			}
		}
		registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);

		// Finally, re-register all internal BeanPostProcessors.
		// 最後再次註冊內部postProcessor
		sortPostProcessors(internalPostProcessors, beanFactory);
		registerBeanPostProcessors(beanFactory, internalPostProcessors);

		// Re-register post-processor for detecting inner beans as ApplicationListeners,
		// moving it to the end of the processor chain (for picking up proxies etc).
		beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
	}

  

 

 

定義四類容器,高優先級有序、有序、無序、內部

 

 分類放入四種容器:

 

 

註冊BeanPostProcessor,將實現BeanPostProcessor接口的對象放入beanPostProcessors緩存中

 

 

 

 

 

 

註冊完PriorityOrdered的實現類后,再處理Ordered的實現類

 

 

註冊什麼都沒有實現的BeanPostProcessor接口實現類,

 

 

最後註冊內部的BeanPostProcessor對象

 

 到這裏BeanPostProcessor的實例化以及註冊工作完成,在beanFactory的beanPostProcessors集合中已經緩存了所有的beanPostProcessor的對象

 

2:BeanPostProcessor的使用

因為這個接口是bean的後置接口,所以需要bean創建並初始化完成,才可以發揮作用,上一步的緩存只是埋好點,以備使用,因為bean的實例化流程我們

還沒有分析,這裏直接看一下怎麼使用的

 

 

我們看一下init方法后的攔截,因為這個時候已經init完成,可以在後置接口中對bean做一下修改的操作

 

 

調用到我們自定義的MyBeanPostProcessor實現類:

 

 

把這個beanDemo對象屬性修改一下,修改完,再返回,將這個對象緩存到spring的一級緩存中。

 

 

總結:

  BeanPostProcessor接口主要是對bean對象做一些自定義的操作,修改bean對象的信息,aop代理也是通過這種方式實現的,

在refresh的registryBeanPostProcessor方法中實例化BeanPostProcessor對象,並且註冊到beanFactory容器的beanPostProcessors的緩存中,

然後在後續的操作中攔截使用。

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司“嚨底家”!

※推薦評價好的iphone維修中心

聚甘新

分類
發燒車訊

ASP.NET Core 對Controller進行單元測試

單元測試對我們的代碼質量非常重要。很多同學都會對業務邏輯或者工具方法寫測試用例,但是往往忽略了對Controller層寫單元測試。我所在的公司沒見過一個對Controller寫過測試的。今天來演示下如果對Controller進行單元測試。以下內容默認您對單元測試有所了解,比如如何mock一個接口。在這裏多叨叨一句,面向接口的好處,除了能夠快速的替換實現類(其實大部分接口不會有多個實現),最大的好處就是可以進行mock,可以進行單元測試。

測試Action

下面的Action非常簡單,非常常見的一種代碼。根據用戶id去獲取用戶信息然後展示出來。下面看看如何對這個Action進行測試。

   public class UserController : Controller
    {
        private readonly IUserService _userService;
        public UserController(IUserService userService)
        {
            _userService = userService;
        }

        public IActionResult UserInfo(string userId)
        {
            if (string.IsNullOrEmpty(userId))
            {
                throw new ArgumentNullException(nameof(userId));
            }

            var user = _userService.Get(userId);
            return View(user);
        }
      
    }

測試代碼:

  [TestMethod()]
        public void UserInfoTest()
        {

            var userService = new Mock<IUserService>();
            userService.Setup(s => s.Get(It.IsAny<string>())).Returns(new User());

            var ctrl = new UserController(userService.Object);
            //對空參數進行assert
            Assert.ThrowsException<ArgumentNullException>(() => {
                var result = ctrl.UserInfo(null);
            });
            //對空參數進行assert
            Assert.ThrowsException<ArgumentNullException>(() => {
                var result = ctrl.UserInfo("");
            });

            var result = ctrl.UserInfo("1");
            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result, typeof(ViewResult));
        }

我們對一個Action進行測試主要的思路就是模擬各種入參,使測試代碼能夠到達所有的分支,並且Assert輸出是否為空,是否為指定的類型等。

對ViewModel進行測試

我們編寫Action的時候還會涉及ViewModel給視圖傳遞數據,這部分也需要進行測試。修改測試用例,加入對ViewModel的測試代碼:

  [TestMethod()]
        public void UserInfoTest()
        {
            var userService = new Mock<IUserService>();
            userService.Setup(s => s.Get(It.IsAny<string>())).Returns(new User()
            {
                Id = "x"
            }) ;

            var ctrl = new UserController(userService.Object);
            Assert.ThrowsException<ArgumentNullException>(() => {
                var result = ctrl.UserInfo(null);
            });
            Assert.ThrowsException<ArgumentNullException>(() => {
                var result = ctrl.UserInfo("");
            });

            var result = ctrl.UserInfo("1");
            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result, typeof(ViewResult));
            //對viewModel進行assert
            var vr = result as ViewResult;
            Assert.IsNotNull(vr.Model);
            Assert.IsInstanceOfType(vr.Model, typeof(User));
            var user = vr.Model as User;
            Assert.AreEqual("x", user.Id);
        }

對ViewData進行測試

我們編寫Action的時候還會涉及ViewData給視圖傳遞數據,這部分同樣需要測試。修改Action代碼,對ViewData進行賦值:

   public IActionResult UserInfo(string userId)
        {
            if (string.IsNullOrEmpty(userId))
            {
                throw new ArgumentNullException(nameof(userId));
            }

            var user = _userService.Get(userId);

            ViewData["title"] = "user_info";

            return View(user);
        }
      

修改測試用例,加入對ViewData的測試代碼:

   [TestMethod()]
        public void UserInfoTest()
        {
            var userService = new Mock<IUserService>();
            userService.Setup(s => s.Get(It.IsAny<string>())).Returns(new User()
            {
                Id = "x"
            }) ;

            var ctrl = new UserController(userService.Object);
            Assert.ThrowsException<ArgumentNullException>(() => {
                var result = ctrl.UserInfo(null);
            });
            Assert.ThrowsException<ArgumentNullException>(() => {
                var result = ctrl.UserInfo("");
            });

            var result = ctrl.UserInfo("1");
            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result, typeof(ViewResult));

            var vr = result as ViewResult;
            Assert.IsNotNull(vr.Model);
            Assert.IsInstanceOfType(vr.Model, typeof(User));
            var user = vr.Model as User;
            Assert.AreEqual("x", user.Id);
            //對viewData進行assert
            Assert.IsTrue(vr.ViewData.ContainsKey("title"));
            var title = vr.ViewData["title"];
            Assert.AreEqual("user_info", title);
        }

對ViewBag進行測試

因為ViewBag事實上是ViewData的dynamic類型的包裝,所以Action代碼不用改,可以直接對ViewBag進行測試:

     [TestMethod()]
        public void UserInfoTest()
        {
            var userService = new Mock<IUserService>();
            userService.Setup(s => s.Get(It.IsAny<string>())).Returns(new User()
            {
                Id = "x"
            }) ;

            var ctrl = new UserController(userService.Object);
            Assert.ThrowsException<ArgumentNullException>(() => {
                var result = ctrl.UserInfo(null);
            });
            Assert.ThrowsException<ArgumentNullException>(() => {
                var result = ctrl.UserInfo("");
            });

            var result = ctrl.UserInfo("1");
            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result, typeof(ViewResult));

            var vr = result as ViewResult;
            Assert.IsNotNull(vr.Model);
            Assert.IsInstanceOfType(vr.Model, typeof(User));
            var user = vr.Model as User;
            Assert.AreEqual("x", user.Id);

            Assert.IsTrue(vr.ViewData.ContainsKey("title"));
            var title = vr.ViewData["title"];
            Assert.AreEqual("user_info", title);
            //對viewBag進行assert
            string title1 = ctrl.ViewBag.title;
            Assert.AreEqual("user_info", title1);
        }

設置HttpContext

我們編寫Action的時候很多時候需要調用基類里的HttpContext,比如獲取Request對象,獲取Path,獲取Headers等等,所以有的時候需要自己實例化HttpContext以進行測試。

    var ctrl = new AccountController();
    ctrl.ControllerContext = new ControllerContext();
    ctrl.ControllerContext.HttpContext = new DefaultHttpContext();

對HttpContext.SignInAsync進行mock

我們使用ASP.NET Core框架進行登錄認證的時候,往往使用HttpContext.SignInAsync進行認證授權,所以單元測試的時候也需要進行mock。下面是一個典型的登錄Action,對密碼進行認證后調用SignInAsync在客戶端生成登錄憑證,否則跳到登錄失敗頁面。

   public async Task<IActionResult> Login(string password)
        {
            if (password == "123")
            {
                var claims = new List<Claim>
                {
                  new Claim("UserName","x")
                };
                var authProperties = new AuthenticationProperties
                {
                };
                var claimsIdentity = new ClaimsIdentity(
                  claims, CookieAuthenticationDefaults.AuthenticationScheme);
                await HttpContext.SignInAsync(
                    CookieAuthenticationDefaults.AuthenticationScheme,
                    new ClaimsPrincipal(claimsIdentity),
                    authProperties);
                return Redirect("login_success");
            }

            return Redirect("login_fail");
        }

HttpContext.SignInAsync其實個時擴展方法,SignInAsync其實最終是調用了IAuthenticationService里的SignInAsync方法。所以我們需要mock的就是IAuthenticationService接口,否者代碼走到HttpContext.SignInAsync會提示找不到IAuthenticationService的service。而IAuthenticationService本身是通過IServiceProvider注入到程序里的,所以同時需要mock接口IServiceProvider。

    [TestMethod()]
        public async Task LoginTest()
        {
            var ctrl = new AccountController();

            var authenticationService = new Mock<IAuthenticationService>();
            var sp = new Mock<IServiceProvider>();
            sp.Setup(s => s.GetService(typeof(IAuthenticationService)))
                .Returns(() => {
                    return authenticationService.Object;
                });
            ctrl.ControllerContext = new ControllerContext();
            ctrl.ControllerContext.HttpContext = new DefaultHttpContext();
            ctrl.ControllerContext.HttpContext.RequestServices = sp.Object;

           var result = await ctrl.Login("123");
            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result, typeof(RedirectResult));
            var rr = result as RedirectResult;
            Assert.AreEqual("login_success", rr.Url);

            result = await ctrl.Login("1");
            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result, typeof(RedirectResult));
            rr = result as RedirectResult;
            Assert.AreEqual("login_fail", rr.Url);
        }

對HttpContext.AuthenticateAsync進行mock

HttpContext.AuthenticateAsync同樣比較常用。這個擴展方法同樣是在IAuthenticationService里,所以測試代碼跟上面的SignInAsync類似,只是需要對AuthenticateAsync繼續mock返回值success or fail。

     public async Task<IActionResult> Login()
        {
            if ((await HttpContext.AuthenticateAsync()).Succeeded)
            {
                return Redirect("/home");
            }

            return Redirect("/login");
        }

測試用例:


        [TestMethod()]
        public async Task LoginTest1()
        {
            var authenticationService = new Mock<IAuthenticationService>();
            //設置AuthenticateAsync為success
            authenticationService.Setup(s => s.AuthenticateAsync(It.IsAny<HttpContext>(), It.IsAny<string>()))
                .ReturnsAsync(AuthenticateResult.Success(new AuthenticationTicket(new System.Security.Claims.ClaimsPrincipal(), "")));
            var sp = new Mock<IServiceProvider>();
            sp.Setup(s => s.GetService(typeof(IAuthenticationService)))
                .Returns(() => {
                    return authenticationService.Object;
                });

            var ctrl = new AccountController();
            ctrl.ControllerContext = new ControllerContext();
            ctrl.ControllerContext.HttpContext = new DefaultHttpContext();
            ctrl.ControllerContext.HttpContext.RequestServices = sp.Object;

            var act = await ctrl.Login();
            Assert.IsNotNull(act);
            Assert.IsInstanceOfType(act, typeof(RedirectResult));
            var rd = act as RedirectResult;
            Assert.AreEqual("/home", rd.Url);
            //設置AuthenticateAsync為fail
            authenticationService.Setup(s => s.AuthenticateAsync(It.IsAny<HttpContext>(), It.IsAny<string>()))
               .ReturnsAsync(AuthenticateResult.Fail(""));

            act = await ctrl.Login();
            Assert.IsNotNull(act);
            Assert.IsInstanceOfType(act, typeof(RedirectResult));
            rd = act as RedirectResult;
            Assert.AreEqual("/login", rd.Url);

        }

Filter進行測試

我們寫Controller的時候往往需要配合很多Filter使用,所以Filter的測試也很重要。下面演示下如何對Fitler進行測試。

    public class MyFilter: ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            if (context.HttpContext.Request.Path.Value.Contains("/abc/"))
            {
                context.Result = new ContentResult() {
                    Content = "拒絕訪問"
                };
            }

            base.OnActionExecuting(context);
        }
    }

對Filter的測試最主要的是模擬ActionExecutingContext參數,以及其中的HttpContext等,然後對預期進行Assert。

       [TestMethod()]
        public void OnActionExecutingTest()
        {
            var filter = new MyFilter();
            var actContext = new ActionContext(new DefaultHttpContext(),new RouteData(), new ActionDescriptor());
            actContext.HttpContext.Request.Path = "/abc/123";
            var listFilters = new List<IFilterMetadata>();
            var argDict = new Dictionary<string, object>();
            var actExContext = new ActionExecutingContext(
                actContext ,
                listFilters ,
                argDict ,
                new AccountController()
                );
             filter.OnActionExecuting(actExContext);

            Assert.IsNotNull(actExContext.Result);
            Assert.IsInstanceOfType(actExContext.Result, typeof(ContentResult));
            var cr = actExContext.Result as ContentResult;
            Assert.AreEqual("拒絕訪問", cr.Content);

            actContext = new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor());
            actContext.HttpContext.Request.Path = "/1/123";
            listFilters = new List<IFilterMetadata>();
            argDict = new Dictionary<string, object>();
            actExContext = new ActionExecutingContext(
                actContext,
                listFilters,
                argDict,
                new AccountController()
                );
            filter.OnActionExecuting(actExContext);
            Assert.IsNull(actExContext.Result);
        }

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

聚甘新

分類
發燒車訊

001.OpenShift介紹

一 OpenShift特性

1.1 OpenShift概述


Red Hat OpenShijft Container Platform (OpenShift)是一個容器應用程序平台,它為開發人員和IT組織提供了一個雲應用程序平台,用於在安全的、可伸縮的資源上部署新應用程序,而配置和管理開銷最小。

OpenShift構建於Red Hat Enterprise Linux、Docker和Kubernetes之上,為當今的企業級應用程序提供了一個安全且可伸縮的多租戶操作系統,同時還提供了集成的應用程序運行時和庫。

OpenShift帶來了健壯、靈活和可伸縮的特性。容器平台到客戶數據中心,使組織能夠實現滿足安全性、隱私性、遵從性和治理需求的平台。不願意管理自己的OpenShift集群的客戶可以使用Red Hat提供的公共雲平台OpenShift Online。

1.2 OpenShift特性


OpenShift容器平台和OpenShift Online都是基於OpenShift Origin開源軟件項目的,該項目本身使用了許多其他開源項目,如Docker和Kubernetes。

應用程序作為容器運行,容器是單個操作系統內的隔離分區。容器提供了許多與虛擬機相同的好處,比如安全性、存儲和網絡隔離,同時需要的硬件資源要少得多,啟動和終止也更快。OpenShift使用容器有助於提高平台本身及其承載的應用程序的效率、靈活性和可移植性。

OpenShift的主要特性如下:

  • 自助服務平台:OpenShift允許開發人員使用Source-to-Image(S21)從模板或自己的源代碼管理存儲庫創建應用程序。系統管理員可以為用戶和項目定義資源配額和限制,以控制系統資源的使用。
  • 多語言支持:OpenShift支持Java、Node.js、PHP、Perl以及直接來自Red Hat的Ruby。同時也包括來自合作夥伴和更大的Docker社區的許多其他代碼。MySQL、PostgreSQL和MongoDB數據庫。Red Hat還支持在OpenShift上本地運行的中間件產品,如Apache httpd、Apache Tomcat、JBoss EAP、ActiveMQ和Fuse。
  • 自動化:OpenShift提供應用程序生命周期管理功能,當上游源或容器映像發生更改時,可以自動重新構建和重新部署容器。根據調度和策略擴展或故障轉移應用程序。
  • 用戶界面:OpenShift提供用於部署和監視應用程序的web UI,以及用於遠程管理應用程序和資源的CLi。它支持Eclipse IDE和JBoss Developer Studio插件,以便開發人員可以繼續使用熟悉的工具,並支持REST APl與第三方或內部工具集成。
  • 協作:OpenShift允許在組織內或與更大的社區共享項目。
  • 可伸縮性和高可用性:OpenShift提供了容器多租戶和一個分佈式應用程序平台,其中包括彈性,以處理隨需增加的流量。它提供了高可用性,以便應用程序能夠在物理機器宕機等事件中存活下來。OpenShift提供了對容器健康狀況的自動發現和自動重新部署。
  • 容器可移植性:在OpenShift中,應用程序和服務使用標準容器映像進行打包,組合應用程序使用Kubernetes進行管理。這些映像可以部署到基於這些基礎技術的其他平台上。
  • 開源:沒有廠商鎖定。
  • 安全性:OpenShift使用SELinux提供多層安全性、基於角色的訪問控制以及與外部身份驗證系統(如LDAP和OAuth)集成的能力。
  • 動態存儲管理:OpenShift使用Kubernetes持久卷和持久卷聲明的方式為容器數據提供靜態和動態存儲管理
  • 基於雲(或不基於雲):可以在裸機服務器、活來自多個供應商的hypervisor和大多數IaaS雲提供商上部署OpenShift容器平台。
  • 企業級:Red Hat支持OpenShift、選定的容器映像和應用程序運行時。可信的第三方容器映像、運行時和應用程序由Red Hat認證。可以在OpenShift提供的高可用性的強化安全環境中運行內部或第三方應用程序。
  • 日誌聚合和metrics:可以在中心節點收集、聚合和分析部署在OpenShift上的應用程序的日誌信息。OpenShift能夠實時收集關於應用程序的度量和運行時信息,並幫助不斷優化性能。
  • 其他特性:OpenShift支持微服務體繫結構,OpenShift的本地特性足以支持DevOps流程,很容易與標準和定製的持續集成/持續部署工具集成。

二 OpenShift架構

2.1 OpenShift架構概述


OpenShift容器平台是一組構建在Red Hat Enterprise Linux、Docker和Kubernetes之上的模塊化組件和服務。OpenShift增加了遠程管理、多租戶、增強的安全性、應用程序生命周期管理和面向開發人員的自服務接口。

OpenShift的架構:



  • RHEL:基本操作系統是Red Hat Enterprise Linux;
  • Docker:提供基本的容器管理API和容器image文件格式;
  • Kubernetes:管理運行容器的主機集群(物理或虛擬主機)。它處理描述由多個資源組成的多容器應用程序的資源,以及它們如何互連;
  • Etcd:一個分佈式鍵值存儲,Kubernetes使用它來存儲OpenShift集群中容器和其他資源的配置和狀態信息。


OpenShift在Docker + Kubernetes基礎設施之上添加了提供容器應用程序平台所需的更富豐的功能:

OpenShift-Kubernetes extensions:其它資源類型存儲在Etcd中,由Kubernetes管理。這些額外的資源類型形成OpenShift內部狀態和配置,以及由標準 Kubernetes管理的應用程序資源;

Containerized services:完成許多基礎設施功能,如網絡和授權。其中一些一直運行,另一些則按需啟動。OpenShift使用Docker和Kubernetes來實現大多數內部功能。即大多數OpenShift內部服務作為由Kubernetes管理的容器;

Runtimes and xPaaS:供開發人員使用的 base image,每個image都預配置了特定的runtime或db。xPaaS提供了一組用於JBoss中間件產品(如JBoss EAP和ActiveMQ)的 base image;

DevOps tools and user experience:OpenShift提供了Web UI和CLI管理工具,從而實現配置和監視應用程序、OpenShift服務和資源。Web和CLI工具都是由相同的REST api構建的,可供IDE和CI平台等外部工具使用。OpenShift 還可以訪問外部SCM存儲庫和容器registry,並將它們的構件引入OpenShift Cloud。

OpenShift不會向開發人員和系統管理員屏蔽Docker和Kubernetes的核心基礎設施。相反,它將它們用於內部服務,並允許將Docker和Kubernetes資源導入OpenShift集群,同時原始Docker和資源可以從OpenShift集群導出,並導入到其他基於docker的基礎設施中。

OpenShift添加到Docker + Kubernetes的主要價值是自動化開發工作流,因此應用程序的構建和部署在OpenShift集群中按照標準流程進行。開發者不需要知道底層Docker的細節。OpenShift接受應用程序,打包它,並將其作為容器啟動。

2.2 Master和nodes


OpenShift集群是一組節點服務器,它們運行容器,並由一組主服務器集中管理。服務器可以同時充當master和node,但是為了增加穩定性,這些角色通常是分開的。

OpenShift工作原理和交互視圖:




master節點運行OpenShift核心服務,如身份驗證,並未管理員提供API入口。

nodes節點運行包含應用程序的容器,容器又被分組成pod。

OpenShift master運行Kubernetes master服務和Etcd守護進程;

node運行Kubernetes kubelet和kube-proxy守護進程。

雖然在描述中通常沒有聲明,但實際上master本身也是node。

scheduler和management/replication是Kubernetes主服務,而Data Store是Etcd守護進程。

Kubernetes的調度單元是pod,它是一組共享虛擬網絡設備、內部IP地址、TCP/UDP端口和持久存儲的容器。pod可以是任何東西,從完整的企業應用程序(包括作為不同容器的每一層)到單個容器中的單個微服務。例如,一個pod,一個容器在Apache下運行PHP,另一個容器運行MySQL。

Kubernetes管理replicas來縮放pods。副本是一組共享相同定義的pod。

三 管理OpenShift

3.1 OpenShift項目及應用


除了Kubernetes的資源(如pods和services)之外,OpenShift還管理projects和users。一個projects對Kubernetes資源進行分組,以便用戶可以使用訪問權限。還可以為projects分配配額,從而限制了已定義的pod、volumes、services和其他資源。

OpenShift中沒有application的概念,OpenShift client提供了一個new-app命令。此命令在projects中創建資源,但它們都不是應用程序資源。這個命令是為標準開發人員工作流配置帶有公共資源的proiect的快捷方式。

OpenShift使用lables(標籤)對集群中的資源進行分類。默認情況下,OpenShift使用app標籤將相關資源分組到應用程序中。

3.2 使用Source-to-image構建映像


OpenShift允許開發人員使用標準源代碼管理倉庫(SCM)和集成開發環境(ide)來發布應用。

OpenShift中的source -to-lmage (S2I)流程從SCM倉庫中提取代碼,自動判斷所需的runtime,基於runtime啟動一個pod,在pod中編譯應用。

當編譯成功時,將在runtime image中添加層並形成新的image,推送進入OpenShift internal registry倉庫,接着基於這個image將創建新的pod,運行應用程序。

S2I可被視為已經內置到OpenShift中的完整的CI/CD管道。

CI/CD有不同的形式,根據具體場景表現不同。例如,可以使用外部CI工具(如Jenkins)啟動構建並運行測試,然後將新構建的映像標記為成功或失敗,將其推送到QA或生產。

3.2 管理OpenShift資源

OpenShift資源定義,如image、container、pod、service、builder、template等,都存儲在Etcd中,可以由OpenShift CLI, web控制台或REST API進行管理。

OpenShift的資源科通過JSON或YAML文件查看,並且在類似Git或版本控制的SCM中共享。OpenShift甚至可以直接從外部SCM檢索這些資源定義。

大多數OpenShift操作不需要實時響應,OpenShift命令和APIs通常創建或修改存儲在Etcd中的資源描述。Etcd然後通知OpenShift控制器,OpenShift控制器會就更改警告這些資源。

這些控制器採取行動,以便使得資源的最終態反應達到更改效果。例如,如果創建了一個新的pod資源,Kubernetes將在node上調度並啟動該pod,使用pod資源確定要使用哪個映像、要公開哪個端口,等等。或者一個模板被更改,從而指定應該有更多的pod來處理負載,OpenShift會安排額外的pod(副本)來滿足更新后的模板定義。

注意:雖然Docker和Kubernetes是OpenShift的底層,但是必須主要使用OpenShift CLi和OpenShift APls來管理應用程序和基礎設施。OpenShift增加了額外的安全和自動化功能,當直接使用Docker或Kubernetes命令和APls時,這些功能必須手動配置,或者根本不可用。因此強烈建議不要使用docker或Kubernetes的命令直接管理應用。

四 OpenShift網絡

4.1 OpenShift網絡概述


Docker網絡相對簡單,Docker創建一個虛擬內核橋接器(docker0網卡),並將每個容器網絡接口連接到它。

Docker本身沒有提供允許一個主機上的pod連接到另一個主機上的pod的方法。Docker也沒有提供嚮應用程序分配公共固定IP地址的方法,以便外部用戶可以訪問它。

但Kubernetes提供service和route資源來管理pods之間的網絡,以及從外部到pods的路由流量。service在不同pods之間提供負載均衡用於接收網絡請求,同時為service的所有客戶機(通常是其他pods)提供一個內部IP地址。

container和pods不需要知道其他pods在哪裡,它們只連接到service。route為service提供一個固定的惟一DNS名稱,使其對OpenShift集群之外的客戶端可見。

Kubernetes service和route資源需要外部(功能)插件支持。service需要軟件定義的網絡(SDN),它將在不同主機上的pod之間提供通信,route需要轉發或重定向來自外部客戶端的包到服務內部IP。

OpenShift提供了一個基於Open vSwitch的SDN,路由由分佈式HAProxy farm提供。

五 OpenShift持久性存儲

5.1 永久存儲


pod可以在一個節點上停止,並隨時在另一個節點上重新啟動。同時pod的默認存儲是臨時存儲,通過對於類似數據庫需要永久保存數據的應用不適合。

Kubernetes為管理容器的外部持久存儲提供了一個框架。Kubernetes提供了PersistentVolume資源,它可以在本地或網絡中定義存儲。pod資源可以使用PersistentVolumeClaim資源來訪問對應的持久存儲卷。

Kubernetes還指定了一個PersistentVolume資源是否可以在pod之間共享,或者每個pod是否需要具有獨佔訪問權的自己PersistentVolume。當pod移動到另一個節點時,它將保持與相同的PersistentVolumeClaim和PersistentVolumne資源的關聯。這意味着pod的持久存儲數據跟隨它,而不管它將在哪個節點上運行。

OpenShift向Kubernetes提供了多種VolumeProvider,如NFS、iSCSI、FC、Gluster或OpenStack Cinder。

OpenShift還通過StorageClass資源為應用程序提供動態存儲。使用動態存儲,可以選擇不同類型的後端存儲。後面存儲根據應用程序的需要劃分為不同的“tiers”。例如,可以定義一個名為“fast”的存儲類和另一個名為“slow”的存儲類,前者使用更高速的後端存儲,後者提供普通的存儲。當請求存儲時,最終用戶可以指定一個Persistentvolumeclaim,並使用一個註釋指定他們所需的StorageClass。

六 OpenShift高可用

6.1 OpenShift高可用概述


OpenShift平台集群的高可用性(HA)有兩個不同的方面:

OpenShift基礎設施本身的HA(即主機);

以及在OpenShift集群中運行的應用程序的HA。

默認情況下,OpenShift為master提供了完全支持的本機HA機制。

對於應用程序或“pods”,如果pod因任何原因丟失,Kubernetes將調度另一個副本,將其連接到服務層和持久存儲。如果整個節點丟失,Kubernetes會為它所有的pod安排替換節點,最終所有的應用程序都會重新可用。pod中的應用程序負責它們自己的狀態,因此它們需要自己維護應用程序狀態(如HTTP會話複製或數據庫複製)。

七 Image Streams

7.1 Image Streams


要在OpenShift中創建一個新的應用程序,除了應用程序源代碼之外,還需要一個base image(S2I builder image)。如果源代碼或image任何一個更新,就會生成一個新的image,並且基於此新image創建新的pod,同時替換舊的pod。

即當應用程序代碼發生更改時,容器映像需要更新,但如果構建器映像發生更改,則部署的pod也需要更新。

Image Streams包括由tag標識的大量的image。應用程序是針對Image Streams構建的。Image Streams可用於在創建新image時自動執行操作。構建和部署可以監視Image Streams,以便在添加新image時接收通知,並分別執行構建或部署。

OpenShift默認情況下提供了幾個Image Streams,包括許多流行的runtime和frameworks。

Image Streams tag是指向Image Streams中的image的別名。通常縮寫為istag。它包含一個image歷史記錄,表示為tag曾經指向的所有images的堆棧。

每當使用特定的istag標記一個新的或現有的image時,它都會被放在歷史堆棧的第一個位置(標記為latest)。之前tag再次指向舊的image。同時允許簡單的回滾,使標籤再次指向舊的image。 本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

聚甘新

分類
發燒車訊

EOS基礎全家桶(十三)智能合約基礎

簡介

智能合約是現在區塊鏈的一大特色,而不同的鏈使用的智能合約的虛擬機各不相同,編碼語言也有很大差異。而今天我們開始學習EOS的智能合約,我也是從EOS初期一直開發合約至今,期間踩過無數坑,也在Stack Overflow上提過問(最後自己解決了),在實際生產中也積累了很多經驗,所以我會連續幾周分多次分享合約開發的經驗,今天先來點基礎的。

一些C++的編程基礎

EOS就是使用C++開發的,這也為它帶來了諸多好處,而合約也沿用C++作為開發語言,雖然合約中無法直接使用Boost等框架(你可以自己引入,但這也意味着合約會很大,會佔用大量賬號的內存),但是我們還是可以使用很多C++的小型庫,並伴隨着eosio.cdt的發展,融入了更多實用的合約功能。

如果你之前沒有使用C系列的開發語言做過開發,比如:C語言、C++或者是C#,那麼你需要先學習下C語言的基本語法和數據結構,這裏我不做展開,在我們的系列文章的開篇就介紹了我推薦的Learn EOS – c/c++ 教程英文版,有一定英語基礎的朋友可以直接看這個,其他朋友也可以在網上找一些C++的入門教程看下。

如果你已經有了一定的C語言基礎,那麼寫合約的話,你會發現需要的基礎也並不多,依葫蘆畫瓢就能寫出各種基礎功能了,所以,你並不需要擔心太多語言上的門檻,畢竟合約只是一個特定環境下運行的程序,你能用到的東西並不會很多。

CDT選擇

EOS的早期版本進行合約開發還沒有CDT工具,那時的合約藉助的是源碼中的工具eosiocpp,所以你看2018年的博客,進行合約編譯都是用它,但你現在是見不到了。隨着官方CDT的迭代,在CDT的1.4版本開始被官方推薦使用,CDT後面也經歷了幾個大的版本更新,逐步改善合約編寫方式,更加趨於簡潔、直觀。

但是不同的CDT版本,也意味着編譯器的不同,所以合約開發也會有所區別,比如一些語法變了,一些庫名稱變了,增加了一些新的標註……

我們的教程側重還是介紹最新的語法,所以推薦使用1.6以上的版本。我也會盡量在後面的介紹中補充說明老的CDT的寫法,方便大家對照網上其他老博客的合約。

來個HelloWorld

學習任何編程,我們都不能少了Mr.HelloWorld,先來給大家打個招呼吧。

#include <eosio/eosio.hpp>

using namespace eosio;

class [[eosio::contract]] hello : public contract
{
public:
    using contract::contract;

    [[eosio::action]] void hi(name user)
    {
        print("Hello, ", user);
    }
};

  

基本合約結構及類型

hello合約就是一個最簡單的合約了,而且還有一個可調用的action為hi。我們首先還是來介紹下一個合約的程序結構吧。

  • 程序頭

包含了引入的頭文件、庫文件等,還有全局的命名空間的引入等。

#include <eosio/eosio.hpp>

using namespace eosio;

  

這裏eosio庫是我們的合約基礎庫,所有和eos相關的類型和方法,都在這個庫裏面,而這個庫裏面eosio.hpp是基礎,包含了contract等的定義,所以所有的合約都要引入。

【CDT老版本】早期cdt版本中庫名稱不是eosio,而是eosiolib

默認的,我們引入了eosio的命名空間,因為eosio的所有內容都是在這個命名空間下的,所以我們全局引入,會方便我們後續的代碼編寫。

  • 合約類定義

其實就是定義了一個class,繼承contract,並通過[[eosio::contract]]標註這個類是一個合約。使用using引入contract也是為了後續代碼可以更簡潔。

class [[eosio::contract]] hello : public contract{
public:
    using contract::contract;
}

  

【CDT老版本】早期cdt版本中直接使用了CONTRACT來定義合約類,比如:CONTRACT hello: public contract {}

  • action定義

寫一個public的方法,參數盡量用簡單或者是eosio內置的類型定義,無返回值(合約調用無法返回任何結果,除非報錯),然後在用[[eosio::action]]標註這個方法是一個合約action就行。

注意:action的名稱要求符合name類型的規則,name規則請看下面的常用類型中的說明。

[[eosio::action]]
void hi( name user ) {
    print( "Hello, ", user);
}

  

因為合約無法調試,所以只能通過print來打印信息,或者直接通過斷言拋出異常來進行調試。

【CDT老版本】早期cdt版本中直接使用ACTION來定義方法,比如:ACTION hi( name user ){}

  • 常用類型
類型 說明 示例
name 名稱類型,賬號名、表名、action名都是該類型,只能使用26個小寫字母和1到5的数字,特殊可以使用小數點,總長不超過13。 name("hi") 或者 "hi"_n
asset 資產類型,Token都是使用該類型,包含了Token符號和小數位,是一個複合類型,字符形式為1.0000 EOS asset(10000, symbol("TADO", 4)就是1.0000 TADO)
uint64_t 無符號64位整型,主要數據類型,表主鍵、name實質都是改類型 uint64_t amount = 10000000;
  • 內置常用對象或方法

在合約中,contract基類提供了一些方便的內置對象。

首先是get_self()或者是_self,這個方法可以獲取到當前合約所在的賬號,比如你把hello合約部署到了helloworld111這個賬號,那麼get_self()就可以獲取到helloworld111。

然後是get_code()或者是_code,這個方法可以獲取到當前交易請求的action方法名,這個在進行內聯action調用時可以用於判斷入口action。

最後是get_datastream()或者_ds,這個方法獲取的是數據流,如果你使用的是複雜類型,或者是自定義類型,那麼你無法在方法的參數上直接獲取到反序列化的變量值,你必須自己通過數據流來解析。

常用的還有獲取當前時間current_time_point(),這個需要引入#include <eosio/transaction.hpp>

數據持久化

當然,合約裏面,我們總會有些功能需要把數據存下來,在鏈上持久化存儲。所以我們就需要定義合約表了。

合約的表存在相應的合約賬號中,可以劃分表範圍(scope),每個表都有一個主鍵,uint64_t類型的,還可以有多個其他索引,表的查詢都是基於索引的。

這裏先提一句,表數據所佔用的內存,默認是合約賬號的內存,也可以使用其他賬號的,但需要權限,這個以後我們再介紹。

我們擴展一下hello合約。

#include <eosio/eosio.hpp>
#include <eosio/transaction.hpp>

using namespace eosio;

class [[eosio::contract]] hello : public contract
{
public:
    using contract::contract;

    hello(name receiver, name code, datastream<const char *> ds) : contract(receiver, code, ds), friend_table(get_self(), get_self().value)
    {
    }

    [[eosio::action]] void hi(name user)
    {
        print("Hello, ", user);

        uint32_t now = current_time_point().sec_since_epoch();

        auto friend_itr = friend_table.find(user.value);
        if (friend_itr == friend_table.end())
        {
            friend_table.emplace(get_self(), [&](auto &f) {
                f.friend_name = user;
                f.visit_time = now;
            });
        }
        else
        {
            friend_table.modify(friend_itr, get_self(), [&](auto &f) {
                f.visit_time = now;
            });
        }
    }

    [[eosio::action]] void nevermeet(name user)
    {
        print("Never see you again, ", user);

        auto friend_itr = friend_table.find(user.value);
        check(friend_itr != friend_table.end(), "I don't know who you are.");

        friend_table.erase(friend_itr);
    }

private:
    struct [[eosio::table]] my_friend
    {
        name friend_name;
        uint64_t visit_time;

        uint64_t primary_key() const { return friend_name.value; }
    };

    typedef eosio::multi_index<"friends"_n, my_friend> friends;

    friends friend_table;
};

  

可以看到,我們已經擴充了不少東西了,包括構造函數,表定義,多索引表配置,並完善了原先的hi方法,增加了nevermeet方法。

我們現在模擬的是這樣一個使用場景,我們遇到一個朋友的時候,就會和他打招呼(調用hi),如果這個朋友是一個新朋友,就會插入一條記錄到我們的朋友表中,如果是一個老朋友了,我們就會更新這個朋友的記錄中的訪問時間。當我們決定不再見這個朋友了,就是絕交了(調用nevermeet),我們就會把這個朋友的記錄刪除。

  • 表定義

首先我們需要聲明我們的朋友表。定義一個結構體,然後用[[eosio::table]]標註這個結構體是一個合約表。在結構體里定義一個函數名primary_key,返回uint64_t類型,作為主鍵的定義。

private:
    struct [[eosio::table]] my_friend
    {
        name friend_name;
        uint64_t visit_time;

        uint64_t primary_key() const { return friend_name.value; }
    };

  

我們這裏聲明了一個my_friend的表,合約的表名不在這裏定義,所以結構體的名稱不必滿足name的規則。我們定義了兩個字段,friend_name(朋友的名稱)和visit_time(拜訪時間),主鍵我們直接使用了friend_name,這個字段是name類型的,而name類型的實質就是一個uint64_t的類型(所以name的規則那麼苛刻)。

【CDT老版本】早期cdt版本中直接使用TABLE來定義合約表,比如:TABLE my_friend{}

  • 多索引表配置

合約里的表都是通過多索引來定義的,這是合約表的結構基礎。所以這裏才是定義表名和查詢索引的地方。

typedef eosio::multi_index<"friends"_n, my_friend> friends;

  

我們現在只介紹最簡單的單索引的定義,以後再介紹多索引的定義方式,這裏的"friends"_n就是定義表名,所以使用了name類型,之後my_friend是表的結構類型,typedef實質上就是聲明了一個類型別名,名字是friends的類型。

  • 構造函數

構造函數這裏並不是必須,但是為了我們能在全局直接使用合約表,所以我們要在構造函數進行表對象的實例化。

public:
    hello(name receiver, name code, datastream<const char *> ds) : contract(receiver, code, ds), friend_table(get_self(), get_self().value)
    {
    }

private:
    friends friend_table;

  

這一段是標準合約構造函數,hello(name receiver, name code, datastream<const char *> ds) : contract(receiver, code, ds),合約類型實例化時會傳入receiver也就是我們的合約賬號(一般情況下),code就是我們的action名稱,ds就是數據流。

friend_table(get_self(), get_self().value)這一段就是對我們定義的friend_table變量的實例化,friend_table變量就是我們定義的多索引表的friends類型的實例。在合約里我們就可以直接使用friend_table變量來進行表操作了。實例化時傳遞的兩個參數正是表所在合約的名稱和表範圍(scope),這裏都使用的是當前合約的名稱。

  • 查詢記錄

查詢有多種方式,也就是多索引表提供了多種查詢的方式,默認的,使用findget方法是直接使用主鍵進行查詢,下次我們會介紹使用第二、第三等索引來進行查詢。find返回的是指針,數據是否存在,需要通過判斷指針是否是指到了表末尾,如果等於表末尾,就說明數據不存在,否則,指針的值就是數據對象。get直接返回的就是數據對象,所以在調用get時,就必須傳遞數據不存在時的錯誤信息。

auto friend_itr = friend_table.find(user.value);
if (friend_itr == friend_table.end())
{
    //數據不存在
}else
{
    //數據存在
}

  

我們在hi方法中先查詢了user是否存在。如果不存在,我們就添加數據,如果存在了,就修改數據中的visit_time字段的值為當前時間。

  • 添加記錄

多索引的表對象添加記錄使用emplace方法,第一個參數就是內存使用的對象,第二個參數就是添加表對象時的委託方法。

uint32_t now = current_time_point().sec_since_epoch();

auto friend_itr = friend_table.find(user.value);
if (friend_itr == friend_table.end())
{
    friend_table.emplace(get_self(), [&](auto &f) {
        f.friend_name = user;
        f.visit_time = now;
    });
}
else
{
    //數據存在
}

  

這裏先定義了一個變量now來表示當前時間,正是使用的內置方法current_time_point(),這個還是用了它的sec_since_epoch()方法,是為了直接獲取秒單位的值。

我們查詢后發現這個user的數據不存在,所以就進行插入操作,內存直接使用的合約賬號的,所以使用get_self(),然後對錶數據對象進行賦值。

  • 修改記錄

多索引的表對象修改記錄使用modify方法,第一個參數是傳遞需要修改的數據指針,第二個參數是內存使用的對象,第二個參數就是表對象修改時的委託方法。

friend_table.modify(friend_itr, get_self(), [&](auto &f) {
    f.visit_time = now;
});

  

我們將查詢到的用戶對象的指針friend_itr傳入,然後內存還是使用合約賬號的,委託中,我們只修改visit_time的值(主鍵是不能修改的)。

  • 刪除記錄
  • 多索引的表對象刪除記錄使用erase方法,只有一個參數,就是要刪除的對象指針,有返回值,是刪除數據后的指針偏移,也就是下一條數據的指針。
auto friend_itr = friend_table.find(user.value);
check(friend_itr != friend_table.end(), "I don't know who you are.");

friend_table.erase(friend_itr);

  

我們的示例中,將查詢到的這條數據直接刪除,併為使用變量來接收下一條數據的指針,在連續刪除數據時,你會需要獲取下一條數據的指針,因為已刪除的數據的指針已經失效了。

編譯

編譯我們再之前也有過介紹,安裝了eosio.cdt后,我們就有了eosio-cpp命令,進入到合約文件夾中,直接執行以下命令就會在當前目錄生成wasm和abi文件。

eosio-cpp -abigen hello.cpp -o hello.wasm

注意:替換命令中使用的hello.cpp為實際合約代碼文件名,而hello.wasm為實際合約的wasm文件名。

當然,編譯不通過的時候,你就要看看錯誤是什麼了,這可能會考驗一下你的C++功底。

發布

決定了要發布的賬號后,記得要購買足夠的內存和抵押足夠的資源。合約的內存消耗我們可以大致這樣估算,看下編譯好了的合約wasm文件有多大,然後乘以10,就是你發布到鏈上大概所需的內存大小了。

發布合約我們使用cleos set contract命令,其後跟合約賬號名和合約目錄,為了方便,我建議你把合約的目錄名保持和合約文件名一致。

cleos set contract helloworld111 ./hello -p helloworld111

這裏我們給出的代碼是將hello目錄下的hello合約發布到helloworld111。我這裏的文件夾是hello,裏面的abi和wasm也都是hello,這樣你不用手動指定合約文件了。

總結

至此,我想大家應該對合約的編寫有了一個大致的了解了,至少你可以參照着寫個簡單的合約出來了,這其中還有很多技巧和高級用法,我會在後續的文章中繼續和大家分享。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

※教你寫出一流的銷售文案?

聚甘新

分類
發燒車訊

人臉識別和手勢識別應用(face++)開發

基礎認識

本項目使用的是face++平台,人臉識別+手勢識別雙確認显示。

python編程,代碼簡介,方便擴展。

 

該項目適用於Windows系統和Linux系統,但必須安裝相應的模塊,其中包括

 

l  Python3  python 庫,邏輯編寫

l  Pillow   窗口開發實現

l  opencv-python python的opencv接口

l  Opencv庫   用於人臉檢測

 

本次測試是在win 10電腦上

 

視頻演示:

https://www.bilibili.com/video/BV1Wk4y1z7H7

 

安裝python3

這個網上到處都是資料,找一找就知道啦

官網:

https://www.python.org/

 安裝pillow

該庫用於python做界面開發,詳細參考:https://www.cnblogs.com/dongxiaodong/p/9971974.html

這個庫一般電腦都自帶有了,可以先不安裝,直接運行代碼。

如果出現以下錯誤,則必須手動安裝

ModuleNotFoundError: No module named ‘PIL’

安裝命令:

pip install pillow

安裝opencv-python

Opencv可以實現人臉檢測、人臉對比識別等功能,但在次只是用它來實現了人臉檢測並做人臉框圖,並沒有更多功能的實現,想要獲取更多功能的學習參考,請訪問:https://www.cnblogs.com/dongxiaodong/p/10134904.html

pip install opencv-python

如果出現紅色字體,表示安裝出錯了,必須從新運行安裝命令

 

 Face++

Face++在項目中用於人臉識別和手勢識別

系統流程主要為如下:

 

測試

(一)  獲取人臉標識

工程目錄:

 

l  運行項目,攝像頭將開啟,實時展示所拍攝的畫面

l  按下空格鍵即可獲取人臉標識,輸出人臉標識和存儲到data文件目錄下

l  此時按下ESC鍵則退出程序

l  同一個人的人臉標識很有可能是不一樣的,因為它更多的是基於本次照片計算

 

(二)  創建人臉庫&人臉標識添加到人臉庫

 

 

 

l  創建人臉標識庫,標識名自定義,但同一用戶內不可有相同的人臉標識庫

l  在函數填寫自己賬戶下唯一的人臉庫標識名

 

 

l  將人臉標識添加到人臉庫中

l  在函數中填寫人臉庫標識和我們第一步獲取的人臉標識,將人臉標識添加到人臉庫中

l  人臉庫可以添加多個不同的人臉標識

 

(三)  人臉庫搜索結果比對

 

l  修改為我們剛剛所創建的人臉庫,進行接下來的人臉識別查找

l  運行工程后將開啟攝像頭進行照片實時捕獲識別,並在屏幕中显示識別結果

l  識別包括人臉識別和手勢識別

l  只有在人臉識別正確的情況下才會開啟手勢識別

 

人臉識別失敗

人臉識別成功,無手勢

人臉識別成功,手勢為合攏

人臉識別成功,手勢為打開

 

 

視頻演示:

https://www.bilibili.com/video/BV1Wk4y1z7H7

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司“嚨底家”!

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!

聚甘新

分類
發燒車訊

【Spring】循環依賴 Java Vs Spring

菜瓜:水稻,這次我特意去看了java的循環依賴

水稻:喲,有什麼收穫

菜瓜:兩種情況,構造器循環依賴,屬性循環依賴

  • 構造器循環依賴在邏輯層面無法通過。對象通過構造函數創建時如果需要創建另一個對象,就會存在遞歸調用。棧內存直接溢出
  • 屬性循環依賴可以解決。在對象創建完成之後通過屬性賦值操作。
  • package club.interview.base;
    
    /**
     * 構造器循環依賴 - Exception in thread "main" java.lang.StackOverflowError
     * toString()循環打印也會異常 - Exception in thread "main" java.lang.StackOverflowError
     * @author QuCheng on 2020/6/18.
     */
    public class Circular {
    
        class A {
            B b;
    
    //        public A() {
    //            b = new B();
    //        }
    
    //        @Override
    //        public String toString() {
    //            return "A{" +
    //                    "b=" + b +
    //                    '}';
    //        }
        }
    
        class B {
            A a;
    
    //        public B() {
    //            a = new A();
    //        }
    
    //        @Override
    //        public String toString() {
    //            return "B{" +
    //                    "a=" + a +
    //                    '}';
    //        }
        }
    
        private void test() {
            B b = new B();
            A a = new A();
            a.b = b;
            b.a = a;
            System.out.println(a);
            System.out.println(b);
        }
    
        public static void main(String[] args) {
            new Circular().test();
        }
    }

水稻:厲害啊,Spring也不支持構造函數的依賴注入,而且也不支持多例的循環依賴。同樣的,它支持屬性的依賴注入。

  • 看效果 – 如果toString()打印同樣會出現棧內存溢出。
  • package com.vip.qc.circular;
    
    import org.springframework.stereotype.Component;
    
    import javax.annotation.Resource;
    
    /**
     * @author QuCheng on 2020/6/18.
     */
    @Component("a")
    public class CircularA {
    
        @Resource
        private CircularB circularB;
    
    //    @Override
    //    public String toString() {
    //        return "CircularA{" +
    //                "circularB=" + circularB +
    //                '}';
    //    }
    }
    
    
    package com.vip.qc.circular;
    
    import org.springframework.stereotype.Component;
    
    import javax.annotation.Resource;
    
    /**
     * @author QuCheng on 2020/6/18.
     */
    @Component("b")
    public class CircularB {
    
        @Resource
        private CircularA circularA;
    
    //    @Override
    //    public String toString() {
    //        return "CircularB{" +
    //                "circularA=" + circularA +
    //                '}';
    //    }
    }
    
    
        @Test
        public void testCircular() {
            String basePackages = "com.vip.qc.circular";
            new AnnotationConfigApplicationContext(basePackages);
        }

菜瓜:看來spring的實現應該也是通過屬性注入的吧

水稻:你說的對。先給思路和demo,之後帶你掃一遍源碼,follow me !

  • spring的思路是給已經初始化的bean標記狀態,假設A依賴B,B依賴A,先創建A
    • 先從緩存容器(總共三層,一級拿不到就拿二級,二級拿不到就從三級緩存中拿正在創建的)中獲取A,未獲取到就執行創建邏輯
    • 對象A在創建完成還未將屬性渲染完之前標記為正在創建中,放入三級緩存容器。渲染屬性populateBean()會獲取依賴的對象B。
    • 此時B會走一次getBean邏輯,B同樣會先放入三級緩存,然後渲染屬性,再次走getBean邏輯注入A,此時能從三級緩存中拿到A,並將A放入二級容器。B渲染完成放入一級容器
    • 回到A渲染B的方法populateBean(),拿到B之後能順利執行完自己的創建過程。放入一級緩存
  •  為了證實結果,我把源碼給改了一下,看結果

    • package com.vip.qc.circular;
      
      import org.springframework.stereotype.Component;
      
      import javax.annotation.Resource;
      
      /**
       * @author QuCheng on 2020/6/18.
       */
      @Component("a")
      public class CircularA {
      
          @Resource
          private CircularB circularB;
      
          @Override
          public String toString() {
              return "CircularA{" +
                      "circularB=" + circularB +
                      '}';
          }
      }
      
      
      package com.vip.qc.circular;
      
      import org.springframework.stereotype.Component;
      
      import javax.annotation.Resource;
      
      /**
       * @author QuCheng on 2020/6/18.
       */
      @Component("b")
      public class CircularB {
      
          @Resource
          private CircularA circularA;
      
          @Override
          public String toString() {
              return "CircularB{" +
                      "circularA=" + circularA +
                      '}';
          }
      }
      
      
      測試代碼
      @Test
          public void testCircular() {
              String basePackages = "com.vip.qc.circular";
              new AnnotationConfigApplicationContext(basePackages);
      }
      
      測試結果(我改過源碼了)
      ---- 
      將a放入三級緩存
      將b放入三級緩存
      將a放入二級緩存
      將b放入一級緩存
      從二級緩存中拿到了a
      將a放入一級緩存

        

  • 再看源碼
    • 關鍵類處理getSingleton邏輯 – 緩存容器
      • public class DefaultSingletonBeanRegistry 
        
          /** Cache of singleton objects: bean name to bean instance. */
          // 一級緩存
            private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
        
            /** Cache of singleton factories: bean name to ObjectFactory. */
          // 三級緩存
            private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
        
            /** Cache of early singleton objects: bean name to bean instance. */
          // 二級緩存
            private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
    • 主流程 AbstractApplicationContext#refresh() -> DefaultListableBeanFactory#preInstantiateSingletons() -> AbstractBeanFactory#getBean() & #doGetBean()
      • protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
              @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
           /**
            * 處理FactoryBean接口名稱轉換 {@link BeanFactory#FACTORY_BEAN_PREFIX }
            */
           final String beanName = transformedBeanName(name);
                ...
           // ①從緩存中拿對象(如果對象正在創建中且被依賴注入,會放入二級緩存)
           Object sharedInstance = getSingleton(beanName);
           if (sharedInstance != null && args == null) {
              ...
           }else {      
                    ...
                 if (mbd.isSingleton()) {
                   // ② 將創建的對象放入一級緩存
                    sharedInstance = getSingleton(beanName, () -> {
                       try {
                             // ③ 具體創建的過程,每個bean創建完成之後都會放入三級緩存,然後渲染屬性
                          return createBean(beanName, mbd, args);
                       }catch (BeansException ex) {
                         ...
           ...
           return (T) bean;
        } 
    • ①getSingleton(beanName) – 二級緩存操作
      • protected Object getSingleton(String beanName, boolean allowEarlyReference) {
           // 實例化已經完成了的放在singletonObjects
           Object singletonObject = this.singletonObjects.get(beanName);
           // 解決循環依賴
           if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
              synchronized (this.singletonObjects) {
                 singletonObject = this.earlySingletonObjects.get(beanName);
                 if (singletonObject == null && allowEarlyReference) {
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                       singletonObject = singletonFactory.getObject();
                       this.earlySingletonObjects.put(beanName, singletonObject);
                       if(beanName.equals("a")||beanName.equals("b")||beanName.equals("c"))
                          System.out.println("將"+beanName+"放入二級緩存");;
                       this.singletonFactories.remove(beanName);
                    }
                 }else if(singletonObject != null){
                    System.out.println("從二級緩存中拿到了"+beanName);
                 }
              }
           }
           return singletonObject;
        }
    • ② getSingleton(beanName,lamdba) – 一級緩存操作
      • public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
              Assert.notNull(beanName, "Bean name must not be null");
              synchronized (this.singletonObjects) {
                 Object singletonObject = this.singletonObjects.get(beanName);
                 if (singletonObject == null) {
                    if (this.singletonsCurrentlyInDestruction) {
                    ...
                    // 正在創建的bean加入singletonsCurrentlyInCreation - 保證只有一個對象創建,阻斷循環依賴
                    beforeSingletonCreation(beanName);
                              ...
                    try {
                       singletonObject = singletonFactory.getObject();
                    ...
                    finally {
                    ...
                       // 從singletonsCurrentlyInCreation中移除
                       afterSingletonCreation(beanName);
                    }
                    if (newSingleton) {
                       // 對象創建完畢 - 放入一級緩存(從其他緩存移除)
                       addSingleton(beanName, singletonObject);
                    }
                 }
                 return singletonObject;
              }
           }
                
         //  -----  內部調用一級緩存操作
            protected void addSingleton(String beanName, Object singletonObject) {
                synchronized (this.singletonObjects) {
                    if(beanName.equals("a")||beanName.equals("b")||beanName.equals("c"))
                        System.out.println("將"+beanName+"放入一級緩存");;
                    this.singletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                    this.earlySingletonObjects.remove(beanName);
                    this.registeredSingletons.add(beanName);
                }
            }        
           
    • ③createBean(beanName, mbd, args) – 三級緩存操作
      • protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)throws BeanCreationException {
             ...
           if (instanceWrapper == null) {
              // 5* 實例化對象本身
              instanceWrapper = createBeanInstance(beanName, mbd, args);
           }
           ...
           boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                 isSingletonCurrentlyInCreation(beanName));
           if (earlySingletonExposure) {
              ...
              // 將創建好還未渲染屬性的bean 放入三級緩存
              addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
           }
        
           Object exposedObject = bean;
           try {
              // 渲染bean自身和屬性
              populateBean(beanName, mbd, instanceWrapper);
              // 實例化之後的後置處理 - init
              exposedObject = initializeBean(beanName, exposedObject, mbd);
           }
           catch (Throwable ex) {
           ...
           return exposedObject;
        }
          
          
           // ------------- 內部調用三級緩存操作 
           protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
                Assert.notNull(singletonFactory, "Singleton factory must not be null");
                synchronized (this.singletonObjects) {
                    if (!this.singletonObjects.containsKey(beanName)) {
                        if(beanName.equals("a")||beanName.equals("b")||beanName.equals("c"))
                            System.out.println("將"+beanName+"放入三級緩存");;
                        this.singletonFactories.put(beanName, singletonFactory);
                        this.earlySingletonObjects.remove(beanName);
                        this.registeredSingletons.add(beanName);
                    }
                }
            }       

菜瓜:demo比較簡單,流程大致明白,源碼我還需要斟酌一下,整體有了概念。這個流程好像是摻雜在bean的創建過程中,結合bean的生命周期整體理解可能會更深入一點

水稻:是的。每個知識點都不是單一的,拿着bean的生命周期再理解一遍可能會更有收穫。

 

討論

  • 為什麼是三級緩存,兩級不行嗎?
    • 猜測:理論上兩級也可以實現。多一個二級緩存可能是為了加快獲取的速度。假如A依賴B,A依賴C,B依賴A,C依賴A,那麼C在獲取A的時候只需要從二級緩存中就能拿到A了

總結

  • Spring的處理方式和java處理的思想一致,構造器依賴本身是破壞語義和規範的
  • 屬性賦值–> 依賴注入 。 先創建對象,再賦值屬性,賦值的時候發現需要創建便生成依賴對象,被依賴對象需要前一個對象就從緩存容器中拿取即可

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

聚甘新

分類
發燒車訊

菜渣開源一個基於 EMIT 的 AOP 庫(.NET Core)

目錄

  • 1,快速入門
    • 1.1 繼承 ActionAttribute 特性
    • 1.2 標記代理類型
    • 2,如何創建代理類型
      • 2.1 通過API直接創建
  • 2,創建代理類型
    • 通過API
    • 通過 Microsoft.Extensions.DependencyInjection
      • 通過 Autofac
  • 3,深入使用
    • 代理類型
    • 方法、屬性代理
    • 上下文
      • 攔截方法或屬性的參數
    • 非侵入式代理

Nuget 庫地址:https://www.nuget.org/packages/CZGL.AOP/

Github 庫地址:https://github.com/whuanle/CZGL.AOP

CZGL.AOP 是 基於 EMIT 編寫的 一個簡單輕量的AOP框架,支持非侵入式代理,支持.NET Core/ASP.NET Core,以及支持多種依賴注入框架。

1,快速入門

CZGL.AOP 使用比較簡單,你只需要使用 [Interceptor] 特性標記需要代理的類型,然後使用繼承 ActionAttribute 的特性標記要被代理的方法或屬性。

1.1 繼承 ActionAttribute 特性

ActionAttribute 是用於代理方法或屬性的特性標記,不能直接使用,需要繼承后重寫方法。

示例如下:

    public class LogAttribute : ActionAttribute
    {
        public override void Before(AspectContext context)
        {
            Console.WriteLine("執行前");
        }

        public override object After(AspectContext context)
        {
            Console.WriteLine("執行后");
            if (context.IsMethod)
                return context.MethodResult;
            else if (context.IsProperty)
                return context.PropertyValue;
            return null;
        }
    }

Before 會在被代理的方法執行前或被代理的屬性調用時生效,你可以通過 AspectContext 上下文,獲取、修改傳遞的參數。

After 在方法執行后或屬性調用時生效,你可以通過上下文獲取、修改返回值。

1.2 標記代理類型

在被代理的類型中,使用 [Interceptor] 特性來標記,在需要代理的方法中,使用 繼承了 ActionAttribute 的特性來標記。

此方法是侵入式的,需要在編譯前完成。

[Interceptor]
public class Test : ITest
{
    [Log] public virtual string A { get; set; }
    [Log]
    public virtual void MyMethod()
    {
        Console.WriteLine("運行中");
    }
}

注意的是,一個方法或屬性只能設置一個攔截器。

2,如何創建代理類型

CZGL.AOP 有多種生成代理類型的方式,下面介紹簡單的方式。

請預先創建如下代碼:

    public class LogAttribute : ActionAttribute
    {
        public override void Before(AspectContext context)
        {
            Console.WriteLine("執行前");
        }

        public override object After(AspectContext context)
        {
            Console.WriteLine("執行后");
            if (context.IsMethod)
                return context.MethodResult;
            else if (context.IsProperty)
                return context.PropertyValue;
            return null;
        }
    }

    public interface ITest
    {
        void MyMethod();
    }

    [Interceptor]
    public class Test : ITest
    {
        [Log] public virtual string A { get; set; }
        public Test()
        {
            Console.WriteLine("構造函數沒問題");
        }
        [Log]
        public virtual void MyMethod()
        {
            Console.WriteLine("運行中");
        }
    }

2.1 通過API直接創建

通過 CZGL.AOP 中的 AopInterceptor 類,你可以生成代理類型。

示例如下:

            ITest test1 = AopInterceptor.CreateProxyOfInterface<ITest, Test>();
            Test test2 = AopInterceptor.CreateProxyOfClass<Test>();
            test1.MyMethod();
            test2.MyMethod();

CreateProxyOfInterface 通過接口創建代理類型;CreateProxyOfClass 通過類創建代理類型;

默認調用的是無參構造函數。

2,創建代理類型

通過API

你可以參考源碼解決方案

中的 ExampleConsole 項目。

如果要直接使用 AopInterceptor.CreateProxyOfInterfaceAopInterceptor.CreateProxyOfClass 方法,通過接口或類來創建代理類型。

        ITest test1 = AopInterceptor.CreateProxyOfInterface<ITest, Test>();
        Test test2 = AopInterceptor.CreateProxyOfClass<Test>();

如果要指定實例化的構造函數,可以這樣:

            // 指定構造函數
            test2 = AopInterceptor.CreateProxyOfClass<Test>("aaa", "bbb");
            test2.MyMethod();

通過 Microsoft.Extensions.DependencyInjection

Microsoft.Extensions.DependencyInjection 是 .NET Core/ASP.NET Core 默認的依賴注入容器。

如果需要支持 ASP.NET Core 中使用 AOP,你可以在 Nuget 包中安裝 CZGL.AOP.MEDI

如果你在控制台下使用 Microsoft.Extensions.DependencyInjection,你可以使用名為 BuildAopProxyIServiceCollection 拓展方法來為容器中的類型,生成代理類型。

示例如下:

            IServiceCollection _services = new ServiceCollection();
            _services.AddTransient<ITest, Test>();
            var serviceProvider = _services.BuildAopProxy().BuildServiceProvider();
            serviceProvider.GetService<ITest>();
            return serviceProvider;

你可以參考源碼解決方案中的 ExampleMEDI 項目。

如果你要在 ASP.NET Core 中使用,你可以在 Startup 中,ConfigureServices 方法的最後一行代碼使用 services.BuildAopProxy();

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.BuildAopProxy();
        }

還可以在 ProgramIHostBuilder 中使用 .UseServiceProviderFactory(new AOPServiceProxviderFactory()) 來配置使用 CZGL.AOP。

示例:

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
            .UseServiceProviderFactory(new AOPServiceProxviderFactory())
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });

可以參考解決方案中的 ExampleConsoleExampleWebMEDI 兩個項目。

你不必擔心引入 CZGL.AOP 后,使用依賴注入會使程序變慢或者破壞容器中的原有屬性。CZGL.AOP 只會在創建容器時處理需要被代理的類型,不會影響容器中的服務,也不會幹擾到依賴注入的執行。

通過 Autofac

如果需要在 Autofac 中使用 AOP,則需要引用 CZGL.AOP.Autofac 包。

如果你在控制台程序中使用 Autofac,則可以在 Build() 後面使用 BuildAopProxy()

            ContainerBuilder builder = new ContainerBuilder();
            builder.RegisterType<Test>().As<ITest>();
            var container = builder.Build().BuildAopProxy();

            using (ILifetimeScope scope = container.BeginLifetimeScope())
            {
                // 獲取實例
                ITest myService = scope.Resolve<ITest>();
                myService.MyMethod();
            }

            Console.ReadKey();
        }

要注意的是,在已經完成的組件註冊創建一個新的容器后,才能調用 BuildAopProxy() 方法,

這樣針對一個新的容器你可以考慮是否需要對容器中的組件進行代理。

如果在 ASP.NET Core 中使用 Autofac,你需要在 Program 類的 IHostBuilder 中使用:

.UseServiceProviderFactory(new AutofacServiceProviderFactory())

如果需要代理已經註冊的組件,則將其替換為:

 .UseServiceProviderFactory(new CZGL.AOP.Autofac.AOPServiceProxviderFactory())

請參考 源碼解決方案中的 ExampleAutofacExampleWebAutofac 兩個項目。

3,深入使用

代理類型

要被代理的類型,需要使用 [Interceptor]來標記,例如:

    [Interceptor]
    public class Test : ITest
    {
    }

支持泛型類型。

被代理的類型必須是可被繼承的。

類型的構造函數沒有限制,你可以隨意編寫。

在使用 API 創建代理類型並且實例化時,你可以指定使用哪個構造函數。

例如:

			string a="",b="",c="";
			ITest test1 = AopInterceptor.CreateProxyOfInterface<ITest, Test>(a,b,c);

API 會根據參數的多少以及參數的類型自動尋找合適的構造函數。

方法、屬性代理

為了代理方法或屬性,你需要繼承 ActionAttribute 特性,然後為方法或屬性標記此特性,並且將方法或屬性設置為 virtual

一個類型中的不同方法,可以使用不同的攔截器。

        [Log1]
        public virtual void MyMethod1(){}
        
        [Log2]
        public virtual void MyMethod2(){}

對於屬性,可以在屬性上直接使用特性,或者只在 get 或 set 構造器使用。

        [Log] public virtual string A { get; set; }
        
        // 或
        public virtual string A { [Log] get; set; }
        
        // 或
        public virtual string A { get; [Log] set; }

如果在屬性上使用特性,相當於 [Log] get; [Log] set;

上下文

一個簡單的方法或屬性攔截標記是這樣的:

    public class LogAttribute : ActionAttribute
    {
        public override void Before(AspectContext context)
        {
            Console.WriteLine("執行前");
        }

        public override object After(AspectContext context)
        {
            Console.WriteLine("執行后");
            if (context.IsMethod)
                return context.MethodResult;
            else if (context.IsProperty)
                return context.PropertyValue;
            return null;
        }
    }

AspectContext 的屬性說明如下:

字段 說明
Type 當前被代理類型生成的代理類型
ConstructorParamters 類型被實例化時使用的構造函數的參數,如果構造函數沒有參數,則 MethodValues.Length = 0,而不是 MethodValues 為 null。
IsProperty 當前攔截的是屬性
PropertyInfo 當前被執行的屬性的信息,可為 null。
PropertyValue 但調用的是屬性時,返回 get 的結果或 set 的 value 值。
IsMethod 當前攔截的是方法
MethodInfo 當前方法的信息
MethodValues 方法被調用時傳遞的參數,如果此方法沒有參數,則 MethodValues.Length = 0,而不是 MethodValues 為 null
MethodResult 方法執行返回的結果(如果有)

攔截方法或屬性的參數

通過上下文,你可以修改方法或屬性的參數以及攔截返回結果:

    public class LogAttribute : ActionAttribute
    {
        public override void Before(AspectContext context)
        {
            // 攔截並修改方法的參數
            for (int i = 0; i < context.MethodValues.Length; i++)
            {
                context.MethodValues[i] = (int)context.MethodValues[i] + 1;
            }
            Console.WriteLine("執行前");
        }

        public override object After(AspectContext context)
        {
            Console.WriteLine("執行后");

            // 攔截方法的執行結果
            context.MethodResult = (int)context.MethodResult + 664;

            if (context.IsMethod)
                return context.MethodResult;
            else if (context.IsProperty)
                return context.PropertyValue;
            return null;
        }
    }

    [Interceptor]
    public class Test
    {
        [Log]
        public virtual int Sum(int a, int b)
        {
            Console.WriteLine("運行中");
            return a + b;
        }
    }
            Test test = AopInterceptor.CreateProxyOfClass<Test>();

            Console.WriteLine(test.Sum(1, 1));

方法的參數支持 inrefout;支持泛型方法泛型屬性;支持異步方法;

非侵入式代理

此種方式不需要改動被代理的類型,你也可以代理程序集中的類型。

    public class LogAttribute : ActionAttribute
    {
        public override void Before(AspectContext context)
        {
            Console.WriteLine("執行前");
        }

        public override object After(AspectContext context)
        {
            Console.WriteLine("執行后");
            if (context.IsMethod)
                return context.MethodResult;
            else if (context.IsProperty)
                return context.PropertyValue;
            return null;
        }
    }
    public class TestNo
    {
        public virtual string A { get; set; }
        public virtual void MyMethod()
        {
            Console.WriteLine("運行中");
        }
    }
            TestNo test3 = AopInterceptor.CreateProxyOfType<TestNo>(new ProxyTypeBuilder()
                .AddProxyMethod(typeof(LogAttribute), typeof(TestNo).GetMethod(nameof(TestNo.MyMethod)))
                .AddProxyMethod(typeof(LogAttribute), typeof(TestNo).GetProperty(nameof(TestNo.A)).GetSetMethod()));

通過 ProxyTypeBuilder 來構建代理類型。

代理方法或屬性都是使用 AddProxyMethod,第一個參數是要使用的攔截器,第二個參數是要攔截的方法。

如果要攔截屬性,請分開設置屬性的 getset 構造。

如果多個方法或屬性使用同一個攔截器,則可以這樣:

            TestNo test3 = AopInterceptor.CreateProxyOfType<TestNo>(
                new ProxyTypeBuilder(new Type[] { typeof(LogAttribute) })
                .AddProxyMethod("LogAttribute", typeof(TestNo).GetMethod(nameof(TestNo.MyMethod)))
                .AddProxyMethod("LogAttribute", typeof(TestNo).GetProperty(nameof(TestNo.A)).GetSetMethod()));
            TestNo test3 = AopInterceptor.CreateProxyOfType<TestNo>(
                new ProxyTypeBuilder(new Type[] { typeof(LogAttribute) })
                .AddProxyMethod("LogAttribute", typeof(TestNo).GetMethod(nameof(TestNo.MyMethod)))
                .AddProxyMethod(typeof(LogAttribute2), typeof(TestNo).GetProperty(nameof(TestNo.A)).GetSetMethod()));

在構造函數中傳遞過去所需要的攔截器,然後在攔截時使用。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司“嚨底家”!

※推薦評價好的iphone維修中心

聚甘新

分類
發燒車訊

文本挖掘之情感分析(一)

一、文本挖掘  

     文本挖掘則是對文本進行處理,從中挖掘出來文本中有用的信息和關鍵的規則,在文本挖掘領域應用最往廣泛的是對文本進行分類和聚類,其挖掘的方法分為無監督學習和監督學習。文本挖掘還可以劃分為7大類:關鍵詞提取、文本摘要、文本主題模型、文本聚類、文本分類、觀點提取、情感分析。

   關鍵詞提取:對長文本的內容進行分析,輸出能夠反映文本關鍵信息的關鍵詞。

   文本摘要:許多文本挖掘應用程序需要總結文本文檔,以便對大型文檔或某一主題的文檔集合做出簡要概述。

   文本聚類:主要是對未標註的文本進行標註,常見的有 K均值聚類和層次聚類。

   文本分類:文本分類使用監督學習的方法,以對未知數據的分類進行預測的機器學習方法。

   文本主題模型 LDA:LDA(Latent Dirichlet Allocation)是一種文檔主題生成模型,也稱為一個三層貝恭弘=叶 恭弘斯概率模型,包含詞、主題和文檔三層結構,該模型可以用於獲取語料的主題提取和對不同類別的文檔進行分類。

   觀點抽取:對文本(主要針對評論)進行分析,抽取出核心觀點,並判斷極性(正負面),主要用於電商、美食、酒店、汽車等評論進行分析。

   情感分析:對文本進行情感傾向判斷,將文本情感分為正向、負向、中性。用於口碑分析、話題監控、輿情分析。

   因為自己的論文寫的是關於情感分析方面的內容,因此打算接下來主要寫情感分析系列的內容,今天主要寫關於情感分析的介紹以及發展史。

二、情感分析

1. 含義

     情感分析主要是通過分析人們對於服務、產品、事件、話題來挖掘出說話人/作者觀點、情感、情緒等的研究。情感分析按照研究內容的不同,可以分為:意見挖掘 / 意見提取 / 主觀性分析 / 情感傾向分析、情感情緒分析、情感打分等。情感傾向問題,即是指挖掘出一段語料中說話人/作者對於某一話題/事件所持有的態度,如褒義、貶義、中性、兩者兼有。情感情緒,則是將情感傾向進行更進一步的細化,依據“大連理工大學的情感詞彙本體庫”可知,可以將情感傾向可以細化為:“喜歡”、“憤怒”、“討厭”等具體的7個大類——21個小類別。情感打分,即根據情感態度對於某一事物進行評分,如淘寶系統的1-5分。 文本中的情感分析還可以分為:顯式情感、隱式情感,顯式情感是指包含明顯的情感詞語(如:高興、漂亮、討厭等),隱式情感則是指不包含情感詞語的情感文本,如:“這個杯子上面有一層灰”。由於隱式情感分析難度較大,因此目前的工作多集中在顯式情感分析領域。

     情感分析按照不同的分析對象,可以分為:文章級別的情感分析、句子級別的情感分析、詞彙級別的情感分析。按照不同的研究內容以及研究的粒度的不同,其研究情感分析的方法也有很大的變化。

     目前的情感分析研究可歸納為:情感資源構建、情感元素抽取、情感分類及情感分析應用系統;

     情感資源構建:情感資源一般來說有:情感詞典、情感語料庫。情感詞典的構建即是將現有的、整理好的情感詞典資源進行整合,比如中文情感詞典有:大連理工大學的情感詞彙本體庫、知網Hownet情感詞典、台灣大學的NTUSD簡體中文情感詞典等,根據不同的需求,應用這些情感詞典。情感語料庫,則是我們要分析的文本,如關於新聞的文本、微博評論文本、商品評論文本、電影評論文本等,這些語料的獲取可以是尋找已經整理好的數據,或者自己爬蟲獲取。推薦一個比較全的中文語料庫網站:中文NLP語料庫。

    情感元素抽取:情感元素抽取則是從語料中抽取出來能夠代表說話人/作者情感態度問題的詞彙,也稱為細粒度情感分析。語料中的評價對象和表達抽取是情感元素抽取的核心內容。評價對象是指語料中被討論的主題,比如對於商品評論來說,用戶常提起的“外觀”、“快遞”、“包裝”等方面;表達抽取主要針對顯式情感表達的文本,是指文本抽取出來能夠代表說話人/作者情感、情緒、意見等的詞彙,比如“漂亮”、“贊同”、“不贊同”等。一般來說,評價對象和表達抽取也可以作為相互獨立的兩個任務。一般來說,分析這兩者的方法有:基於規則、基於機器學習。對於評價對象來說,現如今使用最多的方法是利用主題模型中的LDA(Latent Dirichlet Allocation)模型進行分析;對於表達抽取則有:深度學習的方法、基於JST (Joint Sentiment/Topic )模型的方法等。

     情感分類:情感分類則是將文本分為一個具體的類別,比如情感傾向分析,則是將文檔分為:褒義、貶義、中性等。一般來說,進行情感分類的方法有,基於情感詞典、基於機器學習。基於情感詞典,最典型的方法則是基於知網Hownet情感詞典的So-Hownet指標進行情感分類,基於機器學習的方法則有監督學習方法、半監督學習方法等。

    針對於情感分析,現已經存在一些專有平台,如:基於Boson 數據的情感分析平台,基於產品評論的平台Google Shopping。情感分析除了在電商平台應用廣泛之外,情感分析技術還被引入到對話機器人領域。例如,微軟的“小冰”機器人 可以通過分析用戶的文本輸入和表情貼圖,理解用戶當前的情緒狀況,並據此回復文本或者語音等情感回應。部分研究機構還將情感分析技術融入實體機器人中。

 2. 發展史

     V. H. 和 K. R. McKeown 於 1997 年發表的論文 [1],該論文使用對數線性回歸模型從大量語料庫中識別形容詞的正面或負面語義,同時藉助該模型對語料中出現的形容詞進行分類預測。

     Peter Turney在 2002年在論文 [2] 提出了一種無監督學習的算法,其可以很好的將語料中的詞語分類成正面情感詞和負面情感詞。

    2002 年 Bo Pang 等人在論文 [3] 中使用了傳統的機器學習方法對電影評論數據進行分類,同時也驗證了機器學習的方法的確要比之前基於規則的方法要優。

    2003 年 Blei 等人在論文 [4] 中提出了 LDA(Latent Dirichlet Allocation)模型,在之後的情感分析領域的工作中,很多學者/研究人員都使用主題模型來進行情感分析,當然也不只是基於主題模型來進行情感分析研究,還有很多利用深度學習方法來進行情感分析的研究。

   Lin 和 He在 2009 年的論文 [5] 提出了一種基於主題模型的模型 —JST(Joint Sentiment/Topic),其有效的將情感加入到了經典的主題模型當中,因此利用該模型可以獲取到不同情感極性標籤下不同主題的分佈情況。傳統的主題模型獲取文檔的主題以及詞的分佈情況,但並沒有關注到情感的存在,因此基於該模型可以對文檔的情感傾向進行分析。利用 JST 模型可以有效的直接將語料的主題、情感信息挖掘出來,同時 JST 模型還考慮到了主題、文檔、情感、詞之間的聯繫。

    基於對於語義和句法的考慮,Jo 和 H.OH 在 2011 年提出了 ASUM(Aspect and Sentiment Unification Model)模型,該模型和 JST 模型很相似都是四層的貝恭弘=叶 恭弘斯網絡結構 [6] 。

    基於神經網絡的語義組合算法被驗證是一種非常有效的特徵學習手段,2013年,Richard Socher和Christopher Potts等人提出多個基於樹結構的Recursive Neural Network,該方法通過迭代運算的方式學習變量長度的句子或短語的語義表示,在斯坦福情感分析樹庫(Stanford Sentiment Treebank)上驗證了該方法的有效性 [7]。Nal Kalchbrenner等人描述了一個卷積體繫結構,稱為動態卷積神經網絡(DCNN),他們採用它來進行句子的語義建模。 該網絡使用動態k-Max池,這是一種線性序列的全局池操作。 該網絡處理不同長度的輸入句子,並在句子上引入能夠明確捕獲短程和長程關係的特徵圖。 網絡不依賴於解析樹,並且很容易適用於任何語言。該模型在句子級情感分類任務上取得了非常出色的效果[8]。2015年,Kai Sheng Tai,Richard Socher, Christopher D. Manning在序列化的LSTM (Long Short-Term Memory)模型的基礎上加入了句法結構的因素,該方法在句法分析的結果上進行語義組合,在句子級情感分類和文本蘊含任務(text entailment)上都取得了很好的效果[9]。

   2016年,Qiao Qian, Xiaoyan Zhu等人在LSTM和Bi-LSTM模型的基礎上加入四種規則約束,這四種規則分別是: Non-Sentiment Regularizer,Sentiment Regularizer, Negation Regularizer, Intensity Regularizer,利用語言資源和神經網絡相結合來提升情感分類問題的精度。

  除了上面的一些研究,關於情感分析領域的應用仍然有很多,比如:2015 年鄭祥雲等人通過主題模型提取出來圖書館用戶的主題信息,最後利用這些信息來進行個性化圖書的有效推薦。將 JST 模型中直接引入了情感詞典作為外部先驗知識來對新聞文本進行分析,獲取其中的主旨句,並對主旨句進行情感打分,同時利用情感主旨句來代替全文,這樣能夠使得用戶更有效、更快速的閱讀文章。

  總的來說,情感分析在很多的領域被應用,當然情感分析也有很多的局限性,就是過多的依賴於語料庫信息,同時還需要使用自然語言、人工智能的方法來能夠最大化的挖掘出來其中的信息。

 

 參考文獻:

 [1] :Predicting the semantic orientation of adjectives

 [2]:  Thumbs up or thumbs down? Semantic orientation applied to unsupervised classification of reviews 

 [3] : Thumbs up? Sentiment Classification using Machine Learning Techniques

 [4] :   Latent Dirichlet Allocation

 [5] : Joint sentiment/topic model for sentiment analysis

 [6] : Aspect and sentiment unification model for online review analysis

 [7] : Recursive Deep Models for Semantic Compositionality Over a Sentiment Treebank

 [8]:  A Convolutional Neural Network for Modelling Sentences

 [9]: Improved Semantic Representations From Tree-Structured Long Short-Term Memory Networks

 [10] : Linguistically Regularized LSTMs for Sentiment Classification

 

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

聚甘新