摘要:作者鏈接來源簡書著作權(quán)歸作者所有,本文已獲得作者授權(quán)轉(zhuǎn)載,并對原文進行了重新的排版。前言為應(yīng)用提供一個完整的容器作為依賴管理方案,是功能,模塊等功能的實現(xiàn)基礎(chǔ)。的依賴注入管理方案基于服務(wù)定位器。源碼剖析系列目錄
作者:bromine
鏈接:https://www.jianshu.com/p/a23...
來源:簡書
著作權(quán)歸作者所有,本文已獲得作者授權(quán)轉(zhuǎn)載,并對原文進行了重新的排版。
Swoft Github: https://github.com/swoft-clou...
Swoft為應(yīng)用提供一個完整的IOC容器作為依賴管理方案 ,是Swoft AOP功能,RPC模塊等功能的實現(xiàn)基礎(chǔ) 。
他主要解決的功能有三個:
1. 避免了麻煩地手工管理對象間種種嵌套依賴。
2. 對象的依賴關(guān)系不再在編譯期確定,提供了運行期改變行為的更多彈性。
3. 對象可以不再依賴具體實現(xiàn),而是依賴抽象的接口或者抽象類
對依賴管理有興趣的同學(xué)可以查閱馬丁大叔的這篇文章
Bean通過類級別注解@Bean定義,Bean定義后程序可以直接通過App::getBean()獲取到一個Bean的實例。
App::getBean()提供 服務(wù)定位器 式的依賴管理方式,用于可以通過訪問服務(wù)定位器獲取特定的實例,服務(wù)定位器解決了"實例構(gòu)造,實例間依賴管理,具體實現(xiàn)類選擇"的問題,并對用戶屏蔽相關(guān)細節(jié)。
Container->set()方法是App::getBean()底層實際創(chuàng)建bean的方法。原理是通過反射和各種注解(參考注解章節(jié))提供的信息和方法構(gòu)造Bean的一個代理對象。
//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這種需要具體實現(xiàn)類的Bean上,用于指定實際使用的實現(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(); // 通過反射new實例 $isExeMethod = $reflectionClass->hasMethod($this->initMethod); $object = $this->newBeanInstance($reflectionClass, $constructorParameters); // 屬性注入 $this->injectProperties($object, $properties, $propertyInjects); // 執(zhí)行Swoft Bean約定的初始化方法`init()` if ($isExeMethod) { $object->{$this->initMethod}(); } //動態(tài)代理,具體見AOP章節(jié) if (!$object instanceof AopInterface) { $object = $this->proxyBean($name, $className, $object); } // 單例處理 if ($scope === Scope::SINGLETON) { $this->singletonEntries[$name] = $object; } return $object; }依賴注入
相對于 服務(wù)定位器,依賴注入是一種更加先進的依賴管理實踐。
在服務(wù)定位器模式中,客戶端需要調(diào)用服務(wù)定位器本身,對服務(wù)定位器本身存在依賴;
在依賴注入模式中,客戶端和依賴注入管理器之間關(guān)系也是控制反轉(zhuǎn)的,客戶端并不知道依賴管理器的存在,由依賴管理器調(diào)用客戶端并注入具體的依賴對象。
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三者是典型的屬性注入用的注解聲明,在一個Bean類中聲明這三種注解的屬性會分別被注入特定的Rpc客戶端代理對象 , 普通的Bean代理對象 ,和配置文件配置值。
屬性注入元信息的解析Bean的各個屬性的注入信息是在注解搜集階段完成的,即在Swoft的啟動階段就已經(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 = ""; // 沒有任何注解 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 決定屬性需要注入一個Bean還是一個標(biāo)量值
$injectProperty 指代該屬性要注入的Bean名或者具體標(biāo)量值
這兩者最終會封裝進一個SwoftBeanObjectDefinition對象中并保存在AnnotationResource->$definitions中
屬性注入在調(diào)用服務(wù)定位器App::getBean()生成Bean的時候進行,此時服務(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ù)定位器,如果一個對象是由用戶手動new出來的,將不會獲得屬性注入功能。
方法參數(shù)注入Swoft有很多框架按照約定直接調(diào)用Bean的特定方法的地方,如框架會在收到web請求的時候調(diào)用Controllert的某個action方法,如果有合適的AOP連接點會調(diào)用對應(yīng)的通知方法.....
在這些框架調(diào)用的種種方法中基本都支持方法參數(shù)注入,Swoft會根據(jù)參數(shù)類型,參數(shù)名等規(guī)則自動給方法的參數(shù)填充合適的值。
方法注入的實現(xiàn)較為零散,每個方法注入點都會有類似的代碼處理注入的數(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對應(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,直接注入對應(yīng)對象,否則注入類型轉(zhuǎn)換后的$matches對應(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]);//類型強轉(zhuǎn)處理 } else { $bindParams[$key] = $this->getDefaultValue($type);//提供一個指定類型的默認值(等價于0) } } return $bindParams; }$matches對應(yīng)的是REST模板型路由特定字段的具體值,舉個例子。若實際訪問/user/100,其匹配的路由為/user/{uid},則$matches會存儲["uid"=>"100"]信息。
構(gòu)造器注入
其他 方法參數(shù)注入點 的實現(xiàn)大同小異Swoft當(dāng)前的構(gòu)造器注入實現(xiàn)尚不完整,可能還有變動,這里就先不說了。
Swoft源碼剖析系列目錄:https://segmentfault.com/a/11...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/30702.html
摘要:作者鏈接來源簡書著作權(quán)歸作者所有,本文已獲得作者授權(quán)轉(zhuǎn)載,并對原文進行了重新的排版。同時順手整理個人對源碼的相關(guān)理解,希望能夠稍微填補學(xué)習(xí)領(lǐng)域的空白。系列文章只會節(jié)選關(guān)鍵代碼輔以思路講解,請自行配合源碼閱讀。 作者:bromine鏈接:https://www.jianshu.com/p/2f6...來源:簡書著作權(quán)歸作者所有,本文已獲得作者授權(quán)轉(zhuǎn)載,并對原文進行了重新的排版。Swoft...
摘要:中的注解注解是里面很多重要功能特別是,容器的基礎(chǔ)。主流的框架中使用的注解都是借用型注釋塊型注釋中的定義自己的注解機制。在中是注解信息的最終裝載容器。使用的信息構(gòu)造實例或獲取現(xiàn)有實例以上就是注解機制的整體實現(xiàn)了。源碼剖析系列目錄 作者:bromine鏈接:https://www.jianshu.com/p/ef7...來源:簡書著作權(quán)歸作者所有,本文已獲得作者授權(quán)轉(zhuǎn)載,并對原文進行了重新...
摘要:基于擴展實現(xiàn)真正的數(shù)據(jù)庫連接池這種方案中,項目占用的連接數(shù)僅僅為。一種是連接暫時不再使用,其占用狀態(tài)解除,可以從使用者手中交回到空閑隊列中這種我們稱為連接的歸隊。源碼剖析系列目錄 作者:bromine鏈接:https://www.jianshu.com/p/1a7...來源:簡書著作權(quán)歸作者所有,本文已獲得作者授權(quán)轉(zhuǎn)載,并對原文進行了重新的排版。Swoft Github: https:...
摘要:和服務(wù)關(guān)系最密切的進程是中的進程組,絕大部分業(yè)務(wù)處理都在該進程中進行。隨后觸發(fā)一個事件各組件通過該事件進行配置文件加載路由注冊。事件每個請求到來時僅僅會觸發(fā)事件。服務(wù)器生命周期和服務(wù)基本一致,詳情參考源碼剖析功能實現(xiàn) 作者:bromine鏈接:https://www.jianshu.com/p/4c0...來源:簡書著作權(quán)歸作者所有,本文已獲得作者授權(quán)轉(zhuǎn)載,并對原文進行了重新的排版。S...
摘要:官方在文檔沒有提供完整的但我們還是可以在單元測試中找得到的用法。解決的問題是分散在引用各處的橫切關(guān)注點。橫切關(guān)注點指的是分布于應(yīng)用中多處的功能,譬如日志,事務(wù)和安全。通過將真正執(zhí)行操作的對象委托給實現(xiàn)了能提供許多功能。源碼剖析系列目錄 作者:bromine鏈接:https://www.jianshu.com/p/e13...來源:簡書著作權(quán)歸作者所有,本文已獲得作者授權(quán)轉(zhuǎn)載,并對原文進...
閱讀 1978·2021-11-23 09:51
閱讀 889·2021-11-19 09:40
閱讀 838·2021-10-27 14:20
閱讀 5033·2021-10-09 09:52
閱讀 3310·2021-10-09 09:44
閱讀 1739·2021-10-08 10:05
閱讀 5109·2021-09-09 11:47
閱讀 3488·2019-08-30 12:47