摘要:作者鏈接來(lái)源簡(jiǎn)書(shū)著作權(quán)歸作者所有,本文已獲得作者授權(quán)轉(zhuǎn)載,并對(duì)原文進(jìn)行了重新的排版。前言為應(yīng)用提供一個(gè)完整的容器作為依賴管理方案,是功能,模塊等功能的實(shí)現(xiàn)基礎(chǔ)。的依賴注入管理方案基于服務(wù)定位器。源碼剖析系列目錄
作者:bromine
鏈接:https://www.jianshu.com/p/a23...
來(lái)源:簡(jiǎn)書(shū)
著作權(quán)歸作者所有,本文已獲得作者授權(quán)轉(zhuǎn)載,并對(duì)原文進(jìn)行了重新的排版。
Swoft Github: https://github.com/swoft-clou...
Swoft為應(yīng)用提供一個(gè)完整的IOC容器作為依賴管理方案 ,是Swoft AOP功能,RPC模塊等功能的實(shí)現(xiàn)基礎(chǔ) 。
他主要解決的功能有三個(gè):
1. 避免了麻煩地手工管理對(duì)象間種種嵌套依賴。
2. 對(duì)象的依賴關(guān)系不再在編譯期確定,提供了運(yùn)行期改變行為的更多彈性。
3. 對(duì)象可以不再依賴具體實(shí)現(xiàn),而是依賴抽象的接口或者抽象類
對(duì)依賴管理有興趣的同學(xué)可以查閱馬丁大叔的這篇文章
Bean通過(guò)類級(jí)別注解@Bean定義,Bean定義后程序可以直接通過(guò)App::getBean()獲取到一個(gè)Bean的實(shí)例。
App::getBean()提供 服務(wù)定位器 式的依賴管理方式,用于可以通過(guò)訪問(wèn)服務(wù)定位器獲取特定的實(shí)例,服務(wù)定位器解決了"實(shí)例構(gòu)造,實(shí)例間依賴管理,具體實(shí)現(xiàn)類選擇"的問(wèn)題,并對(duì)用戶屏蔽相關(guān)細(xì)節(jié)。
Container->set()方法是App::getBean()底層實(shí)際創(chuàng)建bean的方法。原理是通過(guò)反射和各種注解(參考注解章節(jié))提供的信息和方法構(gòu)造Bean的一個(gè)代理對(duì)象。
//SwoftBeanContainer.php /** * 創(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(); //ref屬性重定向依賴查找,一般用于在Interface這種需要具體實(shí)現(xiàn)類的Bean上,用于指定實(shí)際使用的實(shí)現(xiàn)類 if (!empty($objectDefinition->getRef())) { $refBeanName = $objectDefinition->getRef(); return $this->get($refBeanName); } // 構(gòu)造函數(shù)參數(shù)注入 $constructorParameters = []; if ($constructorInject !== null) { $constructorParameters = $this->injectConstructor($constructorInject); } $reflectionClass = new ReflectionClass($className); $properties = $reflectionClass->getProperties(); // 通過(guò)反射new實(shí)例 $isExeMethod = $reflectionClass->hasMethod($this->initMethod); $object = $this->newBeanInstance($reflectionClass, $constructorParameters); // 屬性注入 $this->injectProperties($object, $properties, $propertyInjects); // 執(zhí)行Swoft Bean約定的初始化方法`init()` if ($isExeMethod) { $object->{$this->initMethod}(); } //動(dòng)態(tài)代理,具體見(jiàn)AOP章節(jié) if (!$object instanceof AopInterface) { $object = $this->proxyBean($name, $className, $object); } // 單例處理 if ($scope === Scope::SINGLETON) { $this->singletonEntries[$name] = $object; } return $object; }依賴注入
相對(duì)于 服務(wù)定位器,依賴注入是一種更加先進(jìn)的依賴管理實(shí)踐。
在服務(wù)定位器模式中,客戶端需要調(diào)用服務(wù)定位器本身,對(duì)服務(wù)定位器本身存在依賴;
在依賴注入模式中,客戶端和依賴注入管理器之間關(guān)系也是控制反轉(zhuǎn)的,客戶端并不知道依賴管理器的存在,由依賴管理器調(diào)用客戶端并注入具體的依賴對(duì)象。
Swoft的依賴注入管理方案基于服務(wù)定位器。提供的注入方式有三種:
屬性注入/** * @Reference("user") * @var AppLibMdDemoInterface */ private $mdDemoService; /** * @Inject() * @var AppModelsLogicUserLogic */ private $logic; /** * the name of pool * * @Value(name="${config.service.user.name}", env="${USER_POOL_NAME}") * @var string */ protected $name = "";
上面@Reference,@Inject,@value三者是典型的屬性注入用的注解聲明,在一個(gè)Bean類中聲明這三種注解的屬性會(huì)分別被注入特定的Rpc客戶端代理對(duì)象 , 普通的Bean代理對(duì)象 ,和配置文件配置值。
屬性注入元信息的解析Bean的各個(gè)屬性的注入信息是在注解搜集階段完成的,即在Swoft的啟動(dòng)階段就已經(jīng)完成
//SwoftBeanWrapperAbstractWrapper.php /** * 屬性解析 * * @param array $propertyAnnotations * @param string $className * @param string $propertyName * @param mixed $propertyValue * * @return array */ private function parsePropertyAnnotations(array $propertyAnnotations, string $className, string $propertyName, $propertyValue) { $isRef = false; $injectProperty = ""; // 沒(méi)有任何注解 if (empty($propertyAnnotations) || !isset($propertyAnnotations[$propertyName]) || !$this->isParseProperty($propertyAnnotations[$propertyName]) ) { return [null, false]; } // 屬性注解解析 foreach ($propertyAnnotations[$propertyName] as $propertyAnnotation) { $annotationClass = get_class($propertyAnnotation); if (!in_array($annotationClass, $this->getPropertyAnnotations())) { continue; } // 使用具體的解析器(如ValueParser,ReferenceParser等)解析注入元信息 $annotationParser = $this->getAnnotationParser($propertyAnnotation); if ($annotationParser === null) { $injectProperty = null; $isRef = false; continue; } list($injectProperty, $isRef) = $annotationParser->parser($className, $propertyAnnotation, $propertyName, "", $propertyValue); } return [$injectProperty, $isRef]; }
$isRef 決定屬性需要注入一個(gè)Bean還是一個(gè)標(biāo)量值
$injectProperty 指代該屬性要注入的Bean名或者具體標(biāo)量值
這兩者最終會(huì)封裝進(jìn)一個(gè)SwoftBeanObjectDefinition對(duì)象中并保存在AnnotationResource->$definitions中
屬性注入在調(diào)用服務(wù)定位器App::getBean()生成Bean的時(shí)候進(jìn)行,此時(shí)服務(wù)定位器根據(jù)之前解析到的$isRef,$injectProperty信息注入特定的值到屬性中。
// SwoftBeanContainer.php /** * 注入屬性 * * @param mixed $object * @param ReflectionProperty[] $properties $properties * @param mixed $propertyInjects * @throws InvalidArgumentException */ private function injectProperties($object, array $properties, $propertyInjects) { foreach ($properties as $property) { //... // 屬性是數(shù)組 if (is_array($injectProperty)) { $injectProperty = $this->injectArrayArgs($injectProperty); } // 屬性是bean引用 if ($propertyInject->isRef()) { $injectProperty = $this->get($injectProperty); } if ($injectProperty !== null) { $property->setValue($object, $injectProperty); } }
屬性注入依賴于服務(wù)定位器,如果一個(gè)對(duì)象是由用戶手動(dòng)new出來(lái)的,將不會(huì)獲得屬性注入功能。
方法參數(shù)注入Swoft有很多框架按照約定直接調(diào)用Bean的特定方法的地方,如框架會(huì)在收到web請(qǐng)求的時(shí)候調(diào)用Controllert的某個(gè)action方法,如果有合適的AOP連接點(diǎn)會(huì)調(diào)用對(duì)應(yīng)的通知方法.....
在這些框架調(diào)用的種種方法中基本都支持方法參數(shù)注入,Swoft會(huì)根據(jù)參數(shù)類型,參數(shù)名等規(guī)則自動(dòng)給方法的參數(shù)填充合適的值。
方法注入的實(shí)現(xiàn)較為零散,每個(gè)方法注入點(diǎn)都會(huì)有類似的代碼處理注入的數(shù)據(jù),這里看一下action的注入處理。action的參數(shù)注入處理代碼在HandlerAdapter->bindParams()中
//SwoftHttpServerRouteHandlerAdapter.php /** * binding params of action method * * @param ServerRequestInterface $request request object * @param mixed $handler handler * @param array $matches route params info * * @return array * @throws ReflectionException */ private function bindParams(ServerRequestInterface $request, $handler, array $matches) { if (is_array($handler)) { list($controller, $method) = $handler; $reflectMethod = new ReflectionMethod($controller, $method); $reflectParams = $reflectMethod->getParameters(); } else { $reflectMethod = new ReflectionFunction($handler); $reflectParams = $reflectMethod->getParameters(); } $bindParams = []; // $matches = $info["matches"] ?? []; $response = RequestContext::getResponse(); // binding params foreach ($reflectParams as $key => $reflectParam) { $reflectType = $reflectParam->getType(); $name = $reflectParam->getName(); // 未定義參數(shù)類型直接使用$matches對(duì)應(yīng)值 if ($reflectType === null) { if (isset($matches[$name])) { $bindParams[$key] = $matches[$name]; } else { $bindParams[$key] = null; } continue; } /** * @notice ReflectType::getName() is not supported in PHP 7.0, that is why use __toString() */ $type = $reflectType->__toString(); //若類型的特定類型如Request/Response,直接注入對(duì)應(yīng)對(duì)象,否則注入類型轉(zhuǎn)換后的$matches對(duì)應(yīng)值 if ($type === Request::class) { $bindParams[$key] = $request; } elseif ($type === Response::class) { $bindParams[$key] = $response; } elseif (isset($matches[$name])) { $bindParams[$key] = $this->parserParamType($type, $matches[$name]);//類型強(qiáng)轉(zhuǎn)處理 } else { $bindParams[$key] = $this->getDefaultValue($type);//提供一個(gè)指定類型的默認(rèn)值(等價(jià)于0) } } return $bindParams; }$matches對(duì)應(yīng)的是REST模板型路由特定字段的具體值,舉個(gè)例子。若實(shí)際訪問(wèn)/user/100,其匹配的路由為/user/{uid},則$matches會(huì)存儲(chǔ)["uid"=>"100"]信息。
構(gòu)造器注入
其他 方法參數(shù)注入點(diǎn) 的實(shí)現(xiàn)大同小異Swoft當(dāng)前的構(gòu)造器注入實(shí)現(xiàn)尚不完整,可能還有變動(dòng),這里就先不說(shuō)了。
Swoft源碼剖析系列目錄:https://segmentfault.com/a/11...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/30702.html
摘要:作者鏈接來(lái)源簡(jiǎn)書(shū)著作權(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...來(lái)源:簡(jiǎn)書(shū)著作權(quán)歸作者所有,本文已獲得作者授權(quán)轉(zhuǎn)載,并對(duì)原文進(jìn)行了重新的排版。Swoft...
摘要:中的注解注解是里面很多重要功能特別是,容器的基礎(chǔ)。主流的框架中使用的注解都是借用型注釋塊型注釋中的定義自己的注解機(jī)制。在中是注解信息的最終裝載容器。使用的信息構(gòu)造實(shí)例或獲取現(xiàn)有實(shí)例以上就是注解機(jī)制的整體實(shí)現(xiàn)了。源碼剖析系列目錄 作者:bromine鏈接:https://www.jianshu.com/p/ef7...來(lái)源:簡(jiǎn)書(shū)著作權(quán)歸作者所有,本文已獲得作者授權(quán)轉(zhuǎn)載,并對(duì)原文進(jìn)行了重新...
摘要:基于擴(kuò)展實(shí)現(xiàn)真正的數(shù)據(jù)庫(kù)連接池這種方案中,項(xiàng)目占用的連接數(shù)僅僅為。一種是連接暫時(shí)不再使用,其占用狀態(tài)解除,可以從使用者手中交回到空閑隊(duì)列中這種我們稱為連接的歸隊(duì)。源碼剖析系列目錄 作者:bromine鏈接:https://www.jianshu.com/p/1a7...來(lái)源:簡(jiǎn)書(shū)著作權(quán)歸作者所有,本文已獲得作者授權(quán)轉(zhuǎn)載,并對(duì)原文進(jìn)行了重新的排版。Swoft Github: https:...
摘要:和服務(wù)關(guān)系最密切的進(jìn)程是中的進(jìn)程組,絕大部分業(yè)務(wù)處理都在該進(jìn)程中進(jìn)行。隨后觸發(fā)一個(gè)事件各組件通過(guò)該事件進(jìn)行配置文件加載路由注冊(cè)。事件每個(gè)請(qǐng)求到來(lái)時(shí)僅僅會(huì)觸發(fā)事件。服務(wù)器生命周期和服務(wù)基本一致,詳情參考源碼剖析功能實(shí)現(xiàn) 作者:bromine鏈接:https://www.jianshu.com/p/4c0...來(lái)源:簡(jiǎn)書(shū)著作權(quán)歸作者所有,本文已獲得作者授權(quán)轉(zhuǎn)載,并對(duì)原文進(jìn)行了重新的排版。S...
摘要:官方在文檔沒(méi)有提供完整的但我們還是可以在單元測(cè)試中找得到的用法。解決的問(wèn)題是分散在引用各處的橫切關(guān)注點(diǎn)。橫切關(guān)注點(diǎn)指的是分布于應(yīng)用中多處的功能,譬如日志,事務(wù)和安全。通過(guò)將真正執(zhí)行操作的對(duì)象委托給實(shí)現(xiàn)了能提供許多功能。源碼剖析系列目錄 作者:bromine鏈接:https://www.jianshu.com/p/e13...來(lái)源:簡(jiǎn)書(shū)著作權(quán)歸作者所有,本文已獲得作者授權(quán)轉(zhuǎn)載,并對(duì)原文進(jìn)...
閱讀 1961·2021-11-23 09:51
閱讀 873·2021-11-19 09:40
閱讀 829·2021-10-27 14:20
閱讀 5004·2021-10-09 09:52
閱讀 3297·2021-10-09 09:44
閱讀 1729·2021-10-08 10:05
閱讀 5053·2021-09-09 11:47
閱讀 3481·2019-08-30 12:47