摘要:源碼解讀系列二啟動階段都干了些啥閱讀框架源碼了解啟動階段的那些事兒小伙伴剛接觸的時候會感覺壓力有點大更直觀的說法是難開發(fā)組是不贊成難這個說法的的代碼都是實現(xiàn)的而又是世界上最好的語言的代碼閱讀起來是很輕松的之后開發(fā)組會用系列源碼解讀文章深
date: 2018-8-01 14:22:17
title: swoft| 源碼解讀系列二: 啟動階段, swoft 都干了些啥?
description: 閱讀 sowft 框架源碼, 了解 sowft 啟動階段的那些事兒
小伙伴剛接觸 swoft 的時候會感覺 壓力有點大, 更直觀的說法是 難. 開發(fā)組是不贊成 難 這個說法的, swoft 的代碼都是 php 實現(xiàn)的, 而 php 又是 世界上最好的語言, swoft 的代碼閱讀起來是很輕松的.
之后開發(fā)組會用 系列源碼 解讀文章, 深入解析 swoft. 我們相信, 這會成為一段輕松之旅.
swoft 源碼解讀系列一: 好難! swoft demo 都跑不起來怎么破? docker 了解一下唄~
swoft 源碼解讀系列二: 啟動階段, swoft 都干了些啥?
附上社區(qū)小伙伴 隨風(fēng) 制作的流程圖:
程序入口看過 官方文檔-服務(wù)啟動與管理 章節(jié), 就知道 swoft 的入口時 php bin/swoft start, 用來啟動 http server. 運行這個命令, 就為我們打開了新世界的大門
root@e38a7e5aff40 /v/w/s/swoft# ps aux PID USER TIME COMMAND 1 root 0:00 php -a 708 root 0:01 php-swoft master process (bin/swoft) 709 root 0:00 php-swoft manager process 711 root 0:01 php-swoft task process 712 root 0:01 php-swoft worker process 713 root 0:49 php-swoft reload process 779 root 0:00 ps aux
熟悉 swoole-wiki 的小伙伴, 就能看到熟悉的:
master 進(jìn)程
manager 進(jìn)程
worker 進(jìn)程
task-worker 進(jìn)程
swoole-wiki 上的 運行流程圖 和 進(jìn)程/線程結(jié)構(gòu)圖 值得細(xì)細(xì)品味, 這是我們之后理解和使用 swoole 進(jìn)行服務(wù)器開發(fā)的基礎(chǔ), 這里按下暫時不表.
而我們?yōu)榱伺?swoft啟動階段都干了些啥, 可以直接運行 php bin/swoft, 慢慢 調(diào)試/輸出 即可. 是的, 沒有什么高級技巧, var_dump() + die() 即可
使用工具閱讀源碼的小技巧沒錯, 這個工具就是 phpstorm, 沒使用 phpstorm 的小伙伴趕緊用起來, 下面以 window 下的快捷鍵為例:
快捷鍵說明: C->ctrl A->alt S->shift
C-b / C-鼠標(biāo)點擊: 跳轉(zhuǎn)到方法/函數(shù)定義的地方
C-A-左右方向鍵: 切換光標(biāo)前后所在的位置
C-e: 查看最近打開的文件
C-q: 查看函數(shù)的注釋說明(知道寫注釋有多重要了吧)
C-p: 查看函數(shù)的參數(shù)(還是注釋的重要性)
還有很多好用的功能, 請查看菜單欄的 navigate 菜單欄, 去發(fā)現(xiàn)驚喜吧~
PS: 注釋! 注釋! 注釋!
入口源碼: bootstrapbin/swoft 文件很簡單:
#!/usr/bin/env php run();
我們先來看 bin/bootstrap.php:
require_once dirname(__DIR__) . "/vendor/autoload.php"; require_once dirname(__DIR__) . "/config/define.php"; // init the factory of bean SwoftBeanBeanFactory::init(); /* @var SwoftBootstrapBootsBootable $bootstrap*/ $bootstrap = SwoftApp::getBean(SwoftBootstrapBootstrap::class); $bootstrap->bootstrap();
第一步加載 composer 的 autoload 文件, 使用 composer 的同學(xué)應(yīng)該都知道吧, 不過你知道 autoload 的原理么?
第二步是 config/define.php 文件, 我們進(jìn)去看看:
// Project base path ! defined("BASE_PATH") && define("BASE_PATH", dirname(__DIR__, 1)); // Register alias $aliases = [ "@root" => BASE_PATH, ]; SwoftApp::setAliases($aliases);
做了 2 件事:
定義 PHP 常量
swoft 的別名機制
swoft 的第一個特性 -- 別名機制 來了. 挺新鮮的詞兒, 本質(zhì)很簡單 -- 字符串替換 而已, 比如上面我們設(shè)置 @root, 我們直接打印看看:
$tmp = SwoftApp::getAlias("@root"); var_dump($tmp);die; root@e38a7e5aff40 /v/w/s/swoft# php bin/swoft string(21) "/var/www/swoole/swoft"
使用看看:
$tmp1 = SwoftApp::getAlias("@root"); $tmp2 = SwoftApp::getAlias("@root/foo/bar"); var_dump($tmp1, $tmp2);die; root@e38a7e5aff40 /v/w/s/swoft# php bin/swoft string(21) "/var/www/swoole/swoft" string(29) "/var/www/swoole/swoft/foo/bar"
目前 swoft 中的別名機制在用在 文件目錄/路徑 上, 熟悉 yii框架 的小伙伴知道, yii中別名機制用的場景更多一些, 還能拼接 url 等地方. 不過不管使用多少場景, 本質(zhì)都是 字符串替換.
那為什么不直接使用 PHP常量 這種常規(guī)方式, 而要使用別名機制呢? 別名機制不是更優(yōu)雅么
框架核心: BeanFactory到了框架的核心部分了, 閱讀這塊的代碼要有耐心一點:
// init the factory of bean SwoftBeanBeanFactory::init();
進(jìn)入 init(), 先看第一個:
$properties = self::getProperties(); // 獲取 property 配置 var_dump($properties);die;
看源碼和調(diào)試驗證輔助: 讀取 config/properties 下的配置(文件), merge 到同一個數(shù)組里了
再看第二步, 核心的核心, 容器Container 來了, 這里不再贅述 依賴注入DI/控制反轉(zhuǎn)IoC 等基礎(chǔ)知識, 不熟悉的小伙伴要去補補哦~
self::$container = new Container(); self::$container->setProperties($properties); self::$container->autoloadServerAnnotation(); /** * Register the annotation of server */ public function autoloadServerAnnotation() { $bootScan = $this->getScanNamespaceFromProperties("bootScan"); // 獲取 property 配置中的 bootScan 配置項 var_dump($bootScan); $resource = new ServerAnnotationResource($this->properties); $resource->addScanNamespace($bootScan); // 關(guān)鍵在這一句, 要掃描哪些命名空間(文件) $definitions = $resource->getDefinitions(); var_dump($definitions);die; $this->definitions = array_merge($definitions, $this->definitions); }
重點來看看 $resource->addScanNamespace($bootScan)
注解的前半生: 要掃描哪些文件$resource->addScanNamespace($bootScan) 繼承了抽象基類繞了一下, 最后其實走到了這里
componentNamespaces[] = $ns; // console component if ($component == $this->consoleName) { // console 組件特殊處理 $this->scanNamespaces[$ns] = $componentCommandDir; continue; } foreach ($this->serverScan as $dir) { // 預(yù)定義的命名空間 $scanDir = $componentCommandDir . DS . $dir; if (!is_dir($scanDir)) { continue; } $scanNs = $ns . "" . $dir; $this->scanNamespaces[$scanNs] = $scanDir; } } } } /** * @var array */ protected $serverScan = [ "Command", "Bootstrap", "Aop", ];
// $this->scanNamespaces 的內(nèi)容示例 ["SwoftWebSocketServerBootstrap"]=> string(65) "/var/www/swoole/swoft/vendor/swoft/websocket-server/src/Bootstrap"
恭喜你, 到這里你已經(jīng)理解了一半的注解功能:
swoft 框架是由一個一個功能組件組成, 詳細(xì)內(nèi)容可以移步 swoft框架組件化改造
默認(rèn)掃描注解包含 2 部分內(nèi)容:
config/properties 下 bootScan 配置的命名空間
swoft所有組件下的 Command Bootstrap Aop 命名空間, 其中 console 組件特殊處理
如果到這里你感覺比較難理解, 你需要補充一下基礎(chǔ)知識:
composer 基礎(chǔ)知識: autoload 機制, 命名空間
swoft 組件相關(guān)知識, 在 composer 基礎(chǔ)知識之上
另外, 上面加的測試代碼 var_dump(App::getAlias("@vendor/swoft"));, 可以思考一下 swoft 的別名機制就是為了解決 路徑問題, 為什么這里又不用呢?
注解的后半生: 掃描出的結(jié)果$definitions = $resource->getDefinitions(); 對應(yīng)的內(nèi)容:
/** * 獲取已解析的配置beans * * @return array ** [ * "beanName" => ObjectDefinition, * ... * ] **/ public function getDefinitions() { // 獲取掃描的PHP文件 $classNames = $this->registerLoaderAndScanBean(); // 掃描上一步注冊進(jìn)來的命名空間 $fileClassNames = $this->scanFilePhpClass(); // 額外配置的掃描文件, 大家可以嘗試一下在哪配置的哦 $classNames = array_merge($classNames, $fileClassNames); // 獲取到所有需要掃面的類 foreach ($classNames as $className) { $this->parseBeanAnnotations($className); // 解析bean注解 } $this->parseAnnotationsData(); // 解析注解數(shù)據(jù), 存放到 $this->definitions 中 return $this->definitions; // 最后, 我們使用這個就可以獲取到注解解析出來的了類啦 }
// 看一看注解解析出來的例子 ["SwoftWebSocketServerBootstrapCoreBean"]=> object(SwoftBeanObjectDefinition)#126 (7) { ["name":"SwoftBeanObjectDefinition":private]=> string(41) "SwoftWebSocketServerBootstrapCoreBean" ["className":"SwoftBeanObjectDefinition":private]=> string(41) "SwoftWebSocketServerBootstrapCoreBean" ["scope":"SwoftBeanObjectDefinition":private]=> int(1) ["ref":"SwoftBeanObjectDefinition":private]=> string(0) "" ["constructorInjection":"SwoftBeanObjectDefinition":private]=> NULL ["propertyInjections":"SwoftBeanObjectDefinition":private]=> array(0) { } ["methodInjections":"SwoftBeanObjectDefinition":private]=> array(0) { } }
這里隱藏了掃描不同類型注解的細(xì)節(jié), 因為我們后面閱讀不同組件源碼時會一一遇到, 這里只要理解大致原理即可
后面的 2 句比較簡單:
$definition = self::getServerDefinition(); self::$container->addDefinitions($definition); /** * @return array * @throws InvalidArgumentException */ private static function getServerDefinition(): array { $file = App::getAlias("@console"); $configDefinition = []; if (is_readable($file)) { $configDefinition = require_once $file; } $coreBeans = self::getCoreBean(BootBeanCollector::TYPE_SERVER); var_dump($coreBeans);die; return ArrayHelper::merge($coreBeans, $configDefinition); }
簡單打印一下就可以知道結(jié)果:
root@e38a7e5aff40 /v/w/s/swoft# php bin/swoft array(1) { ["commandRoute"]=> array(1) { ["class"]=> string(35) "SwoftConsoleRouterHandlerMapping" } }大功告成: 初始化 Bean
self::$container->initBeans(); // 進(jìn)去查看 /** * @throws InvalidArgumentException * @throws ReflectionException */ public function initBeans() { $autoInitBeans = $this->properties["autoInitBean"] ?? false; if (!$autoInitBeans) { return; } // 循環(huán)初始化 foreach ($this->definitions as $beanName => $definition) { $this->get($beanName); } } /** * 獲取一個bean * * @param string $name 名稱 * * @return mixed * @throws ReflectionException * @throws InvalidArgumentException */ public function get(string $name) { // 已經(jīng)創(chuàng)建 if (isset($this->singletonEntries[$name])) { // 單例, 初始化過就直接返回 return $this->singletonEntries[$name]; } // 未定義 if (!isset($this->definitions[$name])) { throw new InvalidArgumentException(sprintf("Bean %s not exist", $name)); } /* @var ObjectDefinition $objectDefinition */ $objectDefinition = $this->definitions[$name]; return $this->set($name, $objectDefinition); // 沒有初始化則進(jìn)行初始化 } /** * 創(chuàng)建bean * * @param string $name 名稱 * @param ObjectDefinition $objectDefinition bean定義 * * @return object * @throws ReflectionException * @throws InvalidArgumentException */ private function set(string $name, ObjectDefinition $objectDefinition) { // bean創(chuàng)建信息 $scope = $objectDefinition->getScope(); $className = $objectDefinition->getClassName(); $propertyInjects = $objectDefinition->getPropertyInjections(); $constructorInject = $objectDefinition->getConstructorInjection(); if ($refBeanName = $objectDefinition->getRef()) { return $this->get($refBeanName); } // 構(gòu)造函數(shù) $constructorParameters = []; if ($constructorInject !== null) { $constructorParameters = $this->injectConstructor($constructorInject); } $reflectionClass = new ReflectionClass($className); $properties = $reflectionClass->getProperties(); // new實例 $isExeMethod = $reflectionClass->hasMethod($this->initMethod); $object = $this->newBeanInstance($reflectionClass, $constructorParameters); // 屬性注入 $this->injectProperties($object, $properties, $propertyInjects); // 執(zhí)行初始化方法 if ($isExeMethod) { $object->{$this->initMethod}(); } if (!$object instanceof AopInterface) { $object = $this->proxyBean($name, $className, $object); } // 單例處理 if ($scope === Scope::SINGLETON) { $this->singletonEntries[$name] = $object; } return $object; }
Bean 初始化的所有細(xì)節(jié)都在這里了:
注解解析后獲取到的類相關(guān)的所有信息
注入構(gòu)造函數(shù)(construct)
初始化類(new), 此時會執(zhí)行構(gòu)造函數(shù)
注入屬性(property)
執(zhí)行初始化方法, 這就是為什么 Bean 里面定義的 init() 也會執(zhí)行的
AOP處理, 找到實際代理的類
單例處理
返回生成好的 Bean 對象
到這里 整個 swoft 核心中的核心 就已經(jīng)呈現(xiàn)在你面前了, 總結(jié)起來也很簡單:
swoft啟動時要去哪里掃描注解
swoft掃描到的注解如何初始化 Bean
有了 SwoftBeanBeanFactory::init(); 以后, 我們需要使用 Bean, 只需要:
SwoftBeanBeanFactory::getBean("xxx"); // 下面的寫法只是一層封裝而已 SwoftApp::getBean("xxx"); /** * get bean * * @param string $name 名稱 * * @return mixed */ public static function getBean(string $name) { return ApplicationContext::getBean($name); }bootstrap階段的最后: 各項配置
通過在合適的地方打印:
/* @var SwoftBootstrapBootsBootable $bootstrap*/ $bootstrap = SwoftApp::getBean(SwoftBootstrapBootstrap::class); var_dump($bootstrap); $bootstrap->bootstrap(); /** * bootstrap */ public function bootstrap() { $bootstraps = BootstrapCollector::getCollector(); // 需要執(zhí)行哪些 bootstrap var_dump($bootstraps);die; $temp = array_column($bootstraps, "order"); array_multisort($temp, SORT_ASC, $bootstraps); foreach ($bootstraps as $bootstrapBeanName => $name){ /* @var Bootable $bootstrap*/ $bootstrap = App::getBean($bootstrapBeanName); $bootstrap->bootstrap(); } }
結(jié)果如下:
root@e38a7e5aff40 /v/w/s/swoft# php bin/swoft object(Bootstrap_5b6dd8716a6dc)#209 (1) { ["__handler_5b6dd8716a6dc":"Bootstrap_5b6dd8716a6dc":private]=> object(SwoftProxyHandlerAopHandler)#188 (1) { # 用到了 aop ["target":"SwoftProxyHandlerAopHandler":private]=> object(SwoftBootstrapBootstrap)#186 (0) { } } } array(3) { # 真正執(zhí)行的 bootstrap ["SwoftBootstrapBootsInitPhpEnv"]=> # init php env array(2) { ["name"]=> string(0) "" ["order"]=> int(2) } ["SwoftBootstrapBootsLoadEnv"]=> # 加載 .env 文件 array(2) { ["name"]=> string(0) "" ["order"]=> int(1) } ["SwoftBootstrapBootsLoadInitConfiguration"]=> # 加載 config 目錄的其他配置 array(2) { ["name"]=> string(0) "" ["order"]=> int(3) } }
至此, bootstrap 階段的所有工作就完成了
swoft 中的 bean 到底是啥咧回答 bean 是啥之前, 先記住: 一切皆對象
我們使用對面對象的方式來對問題進(jìn)行抽象, 并使用抽象出來的類實例化后的對象來解決問題, 而實例化后的對象, 就是 swoft 中一個又一個的 Bean
回顧我們整個 bootstrap 階段, 可以概括為自動化做了 2 件事情:
根據(jù)默認(rèn)的注解掃描機制, 實例化 Bean
根據(jù) config/ .env 等配置中中的 bean/property, 對 swoft 中的 Bean 進(jìn)行配置(實例化 Bean, 或者配置 Bean 的 property)
這樣通過配置來示例化類和配置對象屬性的方式, 在 php 框架中大型其道, 典型的如 yii/laravel.
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/29200.html
摘要:源碼解讀系列一好難都跑不起來怎么破了解一下唄閱讀框架源碼第一步搞定環(huán)境小伙伴剛接觸的時候會感覺壓力有點大更直觀的說法是難開發(fā)組是不贊成難這個說法的的代碼都是實現(xiàn)的而又是世界上最好的語言的代碼閱讀起來是很輕松的開發(fā)組會用源碼解讀系列博客深 date: 2018-8-01 14:22:17title: swoft| 源碼解讀系列一: 好難! swoft demo 都跑不起來怎么破? doc...
摘要:源碼解讀系列一好難都跑不起來怎么破了解一下唄閱讀框架源碼第一步搞定環(huán)境小伙伴剛接觸的時候會感覺壓力有點大更直觀的說法是難開發(fā)組是不贊成難這個說法的的代碼都是實現(xiàn)的而又是世界上最好的語言的代碼閱讀起來是很輕松的開發(fā)組會用源碼解讀系列博客深 date: 2018-8-01 14:22:17title: swoft| 源碼解讀系列一: 好難! swoft demo 都跑不起來怎么破? doc...
摘要:和服務(wù)關(guān)系最密切的進(jìn)程是中的進(jìn)程組,絕大部分業(yè)務(wù)處理都在該進(jìn)程中進(jìn)行。隨后觸發(fā)一個事件各組件通過該事件進(jìn)行配置文件加載路由注冊。事件每個請求到來時僅僅會觸發(fā)事件。服務(wù)器生命周期和服務(wù)基本一致,詳情參考源碼剖析功能實現(xiàn) 作者:bromine鏈接:https://www.jianshu.com/p/4c0...來源:簡書著作權(quán)歸作者所有,本文已獲得作者授權(quán)轉(zhuǎn)載,并對原文進(jìn)行了重新的排版。S...
摘要:前言是一個在之上構(gòu)建的一個高性能協(xié)程全??蚣埽诶锩媸且粋€高級技能,所以在相關(guān)的環(huán)境安裝上也給許多人造成了很大的困擾,更是如此,本文將通過以一種極其簡單的方式解決運行環(huán)境和開發(fā)環(huán)境的部署。 showImg(https://segmentfault.com/img/bVbeknK?w=512&h=197); 本系列文章將從使用層面介紹 Swoft 框架的使用及業(yè)務(wù)開發(fā),面向初中級的 P...
摘要:當(dāng)觸發(fā)異常的字節(jié)碼的索引值在某個異常表條目的監(jiān)控范圍內(nèi),虛擬機會判斷所拋出的異常和該條目想要捕獲的異常是否匹配。 作者:李瑞杰目前就職于阿里巴巴,狂熱JVM愛好者讓我們準(zhǔn)備一個函數(shù):showImg(https://user-gold-cdn.xitu.io/2019/5/19/16acbce35adfefb7);然后,反編譯他的字節(jié)碼:showImg(https://user-gold-cd...
閱讀 635·2023-04-25 18:37
閱讀 2796·2021-10-12 10:12
閱讀 8376·2021-09-22 15:07
閱讀 577·2019-08-30 15:55
閱讀 3183·2019-08-30 15:44
閱讀 2204·2019-08-30 15:44
閱讀 1635·2019-08-30 13:03
閱讀 1570·2019-08-30 12:55