摘要:中的注解注解是里面很多重要功能特別是,容器的基礎(chǔ)。主流的框架中使用的注解都是借用型注釋塊型注釋中的定義自己的注解機(jī)制。在中是注解信息的最終裝載容器。使用的信息構(gòu)造實(shí)例或獲取現(xiàn)有實(shí)例以上就是注解機(jī)制的整體實(shí)現(xiàn)了。源碼剖析系列目錄
作者:bromine
鏈接:https://www.jianshu.com/p/ef7...
來源:簡(jiǎn)書
著作權(quán)歸作者所有,本文已獲得作者授權(quán)轉(zhuǎn)載,并對(duì)原文進(jìn)行了重新的排版。
Swoft Github: https://github.com/swoft-clou...
注解(Annotations)是Swoft里面很多重要功能特別是AOP,IoC容器的基礎(chǔ)。
注解的定義是:“附加在數(shù)據(jù)/代碼上的元數(shù)據(jù)(metadata)?!笨蚣芸梢曰谶@些元信息為代碼提供各種額外功能。
以另一個(gè)框架PHPUnit為例,注解@dataProvider聲明一個(gè)方法作為測(cè)試用例方法的數(shù)據(jù)提供器。當(dāng)PHPUnit框架執(zhí)行到某一個(gè)測(cè)試用例方法時(shí),會(huì)迭代該數(shù)據(jù)提供器,并將其返回的數(shù)據(jù)作為參數(shù)傳入測(cè)試用例方法,為測(cè)試用例方法提供一套用例所需的測(cè)試數(shù)據(jù)。
//摘自phpseclib庫的單元測(cè)試 public function formatLogDataProvider() { return array( array( //該參數(shù)會(huì)作為$message_log參數(shù)傳到testFormatLog()測(cè)試用例方法中 array("hello world"), array("<--"), //$message_number_log "<-- 00000000 68:65:6c:6c:6f:20:77:6f:72:6c:64 hello world "http://$expected ), array( array("hello", "world"), array("<--", "<--"), "<-- 00000000 68:65:6c:6c:6f hello " . "<-- 00000000 77:6f:72:6c:64 world " ), ); } /** * @dataProvider formatLogDataProvider */ public function testFormatLog(array $message_log, array $message_number_log, $expected) { $ssh = $this->createSSHMock(); $result = $ssh->_format_log($message_log, $message_number_log); $this->assertEquals($expected, $result); }
一般而言,在編程屆中注解是一種和注釋平行的概念。
注釋提供對(duì)可執(zhí)行代碼的說明,單純用于開發(fā)人員閱讀,不影響代碼的執(zhí)行;而注解往往充當(dāng)著對(duì)代碼的聲明和配置的作用,為可執(zhí)行代碼提供機(jī)器可用的額外信息,在特定的環(huán)境下會(huì)影響程序的執(zhí)行。
但是由于官方對(duì)PHP的Annotation方案遲遲沒有達(dá)成一致(最新進(jìn)展可以在 PHP: rfc 看到),目前PHP沒有對(duì)注解的官方實(shí)現(xiàn)。主流的PHP框架中使用的注解都是借用T_DOC_COMMENT型注釋塊(/*型注釋/)中的@Tag,定義自己的注解機(jī)制。
想對(duì)PHP注解的發(fā)展史要有更多了解的朋友可以參考Rafael Dohms的這個(gè)PPT:https://www.slideshare.net/rd...
Doctrine注解引擎Swoft沒有重新造輪子,搞一個(gè)新的的注解方案,而是選擇使用 Doctrine的注解引擎
Doctrine的注解方案也是基于T_DOC_COMMENT型注釋的,Doctrine使用反射獲取代碼的T_DOC_COMMENT型注釋,并將注釋中的特定類型@Tag映射到對(duì)應(yīng)注解類。為此,Swoft首先要為每一個(gè)框架自定義的注解定義注解類。
注解定義@Breaker注解的注解類定義如下。
name = $values["value"]; } if (isset($values["name"])) { $this->name = $values["name"]; } } //按需寫的getter setter code.... }
簡(jiǎn)單幾行,一個(gè)@Breaker的注解類的定義工作就完成了。
注解類加載器的注冊(cè)在框架的bootstap階段,swoft會(huì)掃描所有的PHP源碼文件獲取并解析注解信息。
使用Doctrine首先需要提供一個(gè)類的自動(dòng)加載方法,這里直接使用了swoft當(dāng)前的類加載器。Swoft的類加載器由Composer自動(dòng)生成,這意味著注解類只要符合PSR-4規(guī)范即可自動(dòng)加載。
//SwoftBeanResourceAnnotationResource.php /** * 注冊(cè)加載器和掃描PHP文件 * * @return array */ protected function registerLoaderAndScanBean() { // code code.... AnnotationRegistry::registerLoader(function ($class) { if (class_exists($class) || interface_exists($class)) { return true; } return false; }); // code code.... return array_unique($phpClass); }使用Doctrine獲取注解對(duì)象
掃描各源碼目錄獲取PHP類后,Sworft會(huì)遍歷類列表加載類,獲取類級(jí)別,方法級(jí)別,屬性級(jí)別的所有注解對(duì)象。結(jié)果存放在AnnotationResource的$annotations成員中。
//SwoftBeanResourceAnnotationResource.php /** * 解析bean注解 * * @param string $className * @return null */ public function parseBeanAnnotations(string $className) { if (!class_exists($className) && !interface_exists($className)) { return null; } // 注解解析器 $reader = new AnnotationReader(); $reader = $this->addIgnoredNames($reader);//跳過Swoft內(nèi)部注解 $reflectionClass = new ReflectionClass($className); $classAnnotations = $reader->getClassAnnotations($reflectionClass); // 沒有類注解不解析其它注解 if (empty($classAnnotations)) { return; } foreach ($classAnnotations as $classAnnotation) { $this->annotations[$className]["class"][get_class($classAnnotation)] = $classAnnotation; } // 解析屬性 $properties = $reflectionClass->getProperties(); foreach ($properties as $property) { if ($property->isStatic()) { continue; } $propertyName = $property->getName(); $propertyAnnotations = $reader->getPropertyAnnotations($property); foreach ($propertyAnnotations as $propertyAnnotation) { $this->annotations[$className]["property"][$propertyName][get_class($propertyAnnotation)] = $propertyAnnotation; } } // 解析方法 $publicMethods = $reflectionClass->getMethods(ReflectionMethod::IS_PUBLIC); foreach ($publicMethods as $method) { if ($method->isStatic()) { continue; } $methodName = $method->getName(); // 解析方法注解 $methodAnnotations = $reader->getMethodAnnotations($method); foreach ($methodAnnotations as $methodAnnotation) { $this->annotations[$className]["method"][$methodName][get_class($methodAnnotation)][] = $methodAnnotation; } } }注解的解析
doctrine完成的功能僅僅是將注解映射到將用@Annotation聲明的注解類。swoft需要自行處理注解對(duì)象獲取注解中的信息。這一步有兩個(gè)重要功能:
掃描搜集Bean的所有信息包括Bean名,類名以及該Bean各個(gè)需要注入的屬性信息等,存放到ObjectDefinition數(shù)組中。
//SwoftBeanWrapperAbstractWrapper.php /** * 封裝注解 * * @param string $className * @param array $annotations 注解3劍客,包含了類級(jí)別,方法級(jí)別,屬性級(jí)別的注解對(duì)象,注解解析流程你會(huì)一直看到他 * * @return array|null */ public function doWrapper(string $className, array $annotations) { $reflectionClass = new ReflectionClass($className); // 解析類級(jí)別的注解 $beanDefinition = $this->parseClassAnnotations($className, $annotations["class"]); //code... // parser bean annotation list($beanName, $scope, $ref) = $beanDefinition; // 初始化Bean結(jié)構(gòu),并填充該Bean的相關(guān)信息 $objectDefinition = new ObjectDefinition(); $objectDefinition->setName($beanName); $objectDefinition->setClassName($className); $objectDefinition->setScope($scope); $objectDefinition->setRef($ref); if (!$reflectionClass->isInterface()) { // 解析屬性,并獲取屬性相關(guān)依賴注入的信息 $properties = $reflectionClass->getProperties(); $propertyAnnotations = $annotations["property"]??[]; $propertyInjections = $this->parseProperties($propertyAnnotations, $properties, $className); $objectDefinition->setPropertyInjections($propertyInjections);//PropertyInjection對(duì)象 } // 解析方法 $publicMethods = $reflectionClass->getMethods(ReflectionMethod::IS_PUBLIC); $methodAnnotations = $annotations["method"] ??[]; $this->parseMethods($methodAnnotations, $className, $publicMethods); return [$beanName, $objectDefinition]; }
在注解解析時(shí)Parser會(huì)調(diào)用相關(guān)的Collector搜集功能所需的信息,譬如進(jìn)行事件注冊(cè)。
舉個(gè)例子,BootstrapParser的解析僅僅就是搜集注解。Collector在Swoft中是注解信息的最終裝載容器。一般而言@XXXX注解對(duì)應(yīng)的Parser和Collect就是XXXXParser和XXXXCollect,知道這個(gè)慣例會(huì)大大方便你對(duì)Swoft源碼的閱讀。
//SwoftBeanParserBootstrapParser.php class BootstrapParser extends AbstractParser { /** * @param string $className * @param Bootstrap $objectAnnotation * @param string $propertyName * @param string $methodName * @param mixed $propertyValue * * @return array */ public function parser(string $className, $objectAnnotation = null, string $propertyName = "", string $methodName = "", $propertyValue = null) { $beanName = $className; $scope = Scope::SINGLETON; BootstrapCollector::collect($className, $objectAnnotation, $propertyName, $methodName, $propertyValue); return [$beanName, $scope, ""]; } }
由于框架執(zhí)行前必須完整的獲取各種注解到Collertor和生成Bean定義集合,所以Swoft是不進(jìn)行l(wèi)azyload的。
注解的使用現(xiàn)在我們終于可以用一個(gè)的例子來講解注解是如何運(yùn)行。InitMbFunsEncoding是一個(gè)實(shí)現(xiàn)了Bootable的類,他的作用是在應(yīng)用啟動(dòng)時(shí)候設(shè)定系統(tǒng)的編碼。但是僅僅實(shí)現(xiàn)了Bootable接口并不會(huì)讓框架在啟動(dòng)時(shí)自動(dòng)調(diào)用他。
因此我們需要InitMbFunsEncoding為添加一個(gè)@Bootstrap(order=1)類注解,讓他成為一個(gè)Bootstrap型的Bean。
//SwoftBootstrapBoots.InitMbFunsEncoding.php我們?cè)谏衔囊呀?jīng)提過框架啟動(dòng)時(shí)會(huì)掃描PHP源碼
將Bean的定義信息存放到ObjectDefinition數(shù)組中
將注解信息存放到各個(gè)Collector中
因此在框架的Bootstrap階段,可以從BootstrapCollector中直接獲取所有@Bootstrap型的Bean,實(shí)例化并Bean執(zhí)行。
$name){ //使用Bean的ObjectDefinition信息構(gòu)造實(shí)例或獲取現(xiàn)有實(shí)例 /* @var Bootable $bootstrap*/ $bootstrap = App::getBean($bootstrapBeanName); $bootstrap->bootstrap(); } } //code ...以上就是Swoft注解機(jī)制的整體實(shí)現(xiàn)了。
Swoft源碼剖析系列目錄:https://segmentfault.com/a/11...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/30704.html
摘要:作者鏈接來源簡(jiǎn)書著作權(quán)歸作者所有,本文已獲得作者授權(quán)轉(zhuǎn)載,并對(duì)原文進(jìn)行了重新的排版。同時(shí)順手整理個(gè)人對(duì)源碼的相關(guān)理解,希望能夠稍微填補(bǔ)學(xué)習(xí)領(lǐng)域的空白。系列文章只會(huì)節(jié)選關(guān)鍵代碼輔以思路講解,請(qǐng)自行配合源碼閱讀。 作者:bromine鏈接:https://www.jianshu.com/p/2f6...來源:簡(jiǎn)書著作權(quán)歸作者所有,本文已獲得作者授權(quán)轉(zhuǎn)載,并對(duì)原文進(jìn)行了重新的排版。Swoft...
摘要:和服務(wù)關(guān)系最密切的進(jìn)程是中的進(jìn)程組,絕大部分業(yè)務(wù)處理都在該進(jìn)程中進(jìn)行。隨后觸發(fā)一個(gè)事件各組件通過該事件進(jìn)行配置文件加載路由注冊(cè)。事件每個(gè)請(qǐng)求到來時(shí)僅僅會(huì)觸發(fā)事件。服務(wù)器生命周期和服務(wù)基本一致,詳情參考源碼剖析功能實(shí)現(xiàn) 作者:bromine鏈接:https://www.jianshu.com/p/4c0...來源:簡(jiǎn)書著作權(quán)歸作者所有,本文已獲得作者授權(quán)轉(zhuǎn)載,并對(duì)原文進(jìn)行了重新的排版。S...
摘要:作者鏈接來源簡(jiǎn)書著作權(quán)歸作者所有,本文已獲得作者授權(quán)轉(zhuǎn)載,并對(duì)原文進(jìn)行了重新的排版。文件重載管理進(jìn)程注冊(cè)了一個(gè)名為的該進(jìn)程會(huì)在系統(tǒng)引導(dǎo)的最后一個(gè)階段,即啟動(dòng)前啟動(dòng)。 作者:bromine鏈接:https://www.jianshu.com/p/e63...來源:簡(jiǎn)書著作權(quán)歸作者所有,本文已獲得作者授權(quán)轉(zhuǎn)載,并對(duì)原文進(jìn)行了重新的排版。Swoft Github: https://githu...
摘要:作為定時(shí)任務(wù)的執(zhí)行者,通過每喚醒自身一次,然后把執(zhí)行表遍歷一次,挑選當(dāng)下需要執(zhí)行的任務(wù),通過投遞出去并更新該任務(wù)執(zhí)行表中的狀態(tài)。 作者:bromine鏈接:https://www.jianshu.com/p/b44...來源:簡(jiǎn)書著作權(quán)歸作者所有,本文已獲得作者授權(quán)轉(zhuǎn)載,并對(duì)原文進(jìn)行了重新的排版。Swoft Github: https://github.com/swoft-clou.....
摘要:基于擴(kuò)展實(shí)現(xiàn)真正的數(shù)據(jù)庫連接池這種方案中,項(xiàng)目占用的連接數(shù)僅僅為。一種是連接暫時(shí)不再使用,其占用狀態(tài)解除,可以從使用者手中交回到空閑隊(duì)列中這種我們稱為連接的歸隊(duì)。源碼剖析系列目錄 作者:bromine鏈接:https://www.jianshu.com/p/1a7...來源:簡(jiǎn)書著作權(quán)歸作者所有,本文已獲得作者授權(quán)轉(zhuǎn)載,并對(duì)原文進(jìn)行了重新的排版。Swoft Github: https:...
閱讀 1901·2021-11-22 09:34
閱讀 3039·2021-09-28 09:35
閱讀 13474·2021-09-09 11:34
閱讀 3603·2019-08-29 16:25
閱讀 2834·2019-08-29 15:23
閱讀 2048·2019-08-28 17:55
閱讀 2438·2019-08-26 17:04
閱讀 3053·2019-08-26 12:21