通过后面2大节的工作,当初容器内曾经保留了残缺的bean定义对象和名称、别名映射.
本章的工作是梳理swoft如何利用bean的定义对象,生成最终可供业务应用的bean对象.
初始化bean的入口办法:
private function initializeBeans(): void{ // 遍历保留的定义对象数组,失去bean名称和定义对象 /* @var ObjectDefinition $objectDefinition */ foreach ($this->objectDefinitions as $beanName => $objectDefinition) { // 获取定义对象的类型 $scope = $objectDefinition->getScope(); // 如果是request类型 // Exclude request if ($scope === Bean::REQUEST) { // 将定义对象转存到request定义对象数组 $this->requestDefinitions[$beanName] = $objectDefinition; // 从全局定义对象数组中删除这个定义对象 unset($this->objectDefinitions[$beanName]); // 跳过 continue; } // 如果类型是session // Exclude session if ($scope === Bean::SESSION) { // 转存定义对象到session定义对象数组 $this->sessionDefinitions[$beanName] = $objectDefinition; // 从全局定义对象数组中移除这个定义对象 unset($this->objectDefinitions[$beanName]); // 跳过 continue; } // 创立bean对象 // New bean $this->newBean($beanName); }}
创立bean对象:
private function newBean(string $beanName, string $id = ''){ // 首先查看这个bean是否曾经存在,如果存在则间接返回这个bean // First, check bean whether has been create. if (isset($this->singletonPool[$beanName]) || isset($this->prototypePool[$beanName])) { return $this->get($beanName); } // 获取定义对象 // Get object definition $objectDefinition = $this->getNewObjectDefinition($beanName); // 获取类型 $scope = $objectDefinition->getScope(); // 获取别名 $alias = $objectDefinition->getAlias(); // Cache reflection class info // 获取定义对象保留的类名 $className = $objectDefinition->getClassName(); // 创立一个新的类反射对象或从缓存中取出这个类的反射对象 $reflectClass = Reflections::cache($className); // 调用初始化前回调 // Before initialize bean $this->beforeInit($beanName, $className, $objectDefinition); // 结构参数 $constructArgs = []; // 获取结构注入对象 $constructInject = $objectDefinition->getConstructorInjection(); // 如果存在 if ($constructInject !== null) { // 获取结构参数 $constructArgs = $this->getConstructParams($constructInject, $id); } // 如果存在代理类,则将原来的类反射对象替换为代理类的类反射对象 // It's proxy class. eg: AOP, RPC client class if ($this->handler) { $oldCName = $className; $className = $this->handler->classProxy($className); // New class name from handler->classProxy() if ($oldCName !== $className) { $reflectClass = new ReflectionClass($className); } } // 传入反射类和结构参数,创立反射实例 $reflectObject = $this->newInstance($reflectClass, $constructArgs); // 获取须要注入的属性 $propertyInjects = $objectDefinition->getPropertyInjections(); // 递归从类的最顶层父类开始,顺次遍历属性注入数组,查找须要注入的属性,而后给该属性赋值 // Inject properties values $this->newProperty($reflectObject, $reflectClass, $propertyInjects, $id); // Alias name // Fix: $aliasId !== $id for deny loop get // 设置bean的别名 if ($alias && $alias !== $beanName) { $this->aliases[$alias] = $beanName; } // 如果反射类有init办法 // Call init method if exist if ($reflectClass->hasMethod(self::INIT_METHOD)) { // 调用反射实例的init办法 $reflectObject->{self::INIT_METHOD}(); } // 将反射实例按bean名称保留在对应类型的对象数组中 // 并依据类型返回对应的实例 return $this->setNewBean($beanName, $scope, $reflectObject, $id);}
保留新bean对象的办法,4中类型别离保留在4个数组中:
private function setNewBean(string $beanName, string $scope, $object, string $id = ''){ switch ($scope) { case Bean::SINGLETON: // Singleton $this->singletonPool[$beanName] = $object; break; case Bean::PROTOTYPE: $this->prototypePool[$beanName] = $object; // Clone it // 将返回值设置为克隆对象 $object = clone $object; break; case Bean::REQUEST: // 这里的ID应该是申请的协程ID $this->requestPool[$id][$beanName] = $object; break; case Bean::SESSION: // 这里的ID应该是sessionID $this->sessionPool[$id][$beanName] = $object; break; } return $object;}
属性注入办法实现:
private function newProperty( $reflectObject, ReflectionClass $reflectionClass, array $propertyInjects, string $id = '' ): void { // 获取反射类的父反射类对象 // New parent properties $parentClass = $reflectionClass->getParentClass(); // 如果有父类,则持续递归,直到找到最顶层父类 if ($parentClass !== false) { $this->newProperty($reflectObject, $parentClass, $propertyInjects, $id); } // 遍历属性注入对象数组 /* @var PropertyInjection $propertyInject */ foreach ($propertyInjects as $propertyInject) { // 获取属性注入对象标识的属性名 $propertyName = $propertyInject->getPropertyName(); // 如果以后类中没有这个属性名,则跳过后续的注入逻辑 if (!$reflectionClass->hasProperty($propertyName)) { continue; } /** @noinspection PhpUnhandledExceptionInspection */ // 从反射类中获取对应的反射属性对象 $reflectProperty = $reflectionClass->getProperty($propertyName); // 如果是static类型,则抛出异样 if ($reflectProperty->isStatic()) { throw new InvalidArgumentException(sprintf('Property %s for bean can not be `static` ', $propertyName)); } // 获取属性注入对象中保留的value // Parse property value $propertyValue = $propertyInject->getValue(); // 如果值是字符串且存在以这个值命名的接口 // Inject interface if (is_string($propertyValue) && interface_exists($propertyValue)) { $propertyValue = InterfaceRegister::getInterfaceInjectBean($propertyValue); } elseif (is_array($propertyValue)) { // 如果是数组,且数组内存在援用类型 // 则将其替换成 $propertyValue = $this->newPropertyArray($propertyValue, $id); } // Refer config or bean if ($propertyInject->isRef()) { $propertyValue = $this->getRefValue($propertyValue, $id); // Optimize: Value not exists, skip call setter if ($propertyValue === null) { continue; } } // Parser property type $propertyType = ObjectHelper::getPropertyBaseType($reflectProperty); if (!empty($propertyType)) { $propertyValue = ObjectHelper::parseParamType($propertyType, $propertyValue); } // First, try set value by setter method $setter = 'set' . ucfirst($propertyName); if (method_exists($reflectObject, $setter)) { $reflectObject->$setter($propertyValue); continue; } if (!$reflectProperty->isPublic()) { $reflectProperty->setAccessible(true); } // Set value by reflection $reflectProperty->setValue($reflectObject, $propertyValue); } }
解决数组参数的办法:
private function newPropertyArray(array $propertyValue, string $id = ''): array{ // 遍历属性值数组 foreach ($propertyValue as $proKey => &$proValue) { // 如果属性值是注入对象,且是援用类型 if ($proValue instanceof ArgsInjection && $proValue->isRef()) { // 获取援用的值 $refValue = $proValue->getValue(); // 将援用的值解析为实在的bean对象并替换原数组中的值 $proValue = $this->getRefValue($refValue, $id); } } // 返回被替换后的值数组 return $propertyValue;}
通过援用获取实在bean对象的办法:
private function getRefValue($value, string $id = '') { // 如果援用不是一个字符串,则间接返回该值 if (!is_string($value)) { return $value; } // 如果值的第一个字符不是.(.这个字符是配置的应用形式) // 则调用safeNewBean去获取或创立 // 实在的bean对象 if (strpos($value, '.') !== 0) { return $this->safeNewBean($value, $id); } // 移除第一个.字符 // Remove `.` $value = substr($value, 1); // Other: read config reference if ($this->handler) { // 调用BeanHandler的getReferenceValue办法获取值 // 实际上就是去Config的bean对象中获取配置信息 $value = $this->handler->getReferenceValue($value); } return $value; }
平安获取bean办法:
private function safeNewBean(string $beanName, string $id = ''){ try { // 调用newBean获取或创立bean // 这里就呈现了递归 // 如果两个bean对象互相援用,此处就会导致死循环 return $this->newBean($beanName, $id); } catch (Throwable $e) { throw new InvalidArgumentException($e->getMessage(), 500, $e); }}
总结:
初始化流程: 1.遍历定义对象数组. 2.如果定义对象是request或者session类型,则转存定义对象到对应数组中保留. 3.如果是singleton或者prototype类型,则依据定义对象创立真正的bean对象,保留在容器对应的对象池中.创立bean对象的流程: 1.查看bean是否曾经存在于对应的对象池,如果存在则不再创立. 2.获取要创立的bean对象的定义对象. 3.依据定义对象中设置的类名创立反射类对象,并将该反射类对象缓存起来. 4.调用bean生命周期--初始化前回调. 5.从定义对象获取结构注入对象,再从结构注入对象中获取须要注入的结构参数. 6.查看以后要创立的bean对象的类是否有代理类,如果有,则将先前创立的类反射对象替换成代理类的类反射对象. 7.通过类反射对象和结构参数,创立该类的实例对象(这就是咱们最终须要的bean对象). 8.通过定义对象获取属性注入对象数组. 9.依据将属性注入对象中的值类型,以不同形式将值赋值给实例对象. 9.1.递归遍历反射类及其所有父类. 9.2.顺次遍历属性注入对象数组,找到匹配的属性. 9.3.判断值的类型,如果是一般类型,则间接进入赋值流程. 9.4.如果是援用类型(或数组中蕴含援用类型),则调用获取援用值的办法,获取(递归,如果这个bean对象不存在则持续调用bean对象生成办法)援用值对应的bean对象. 10.保留别名和bean名称的映射. 11.如果反射类有init办法,则调用反射实例的init办法. 12.将最终的反射实例保留在对应的对象池中,并按类型返回一个对应的对象,此处保留的反射实例就是咱们最终生成的bean对象.当然,还有一些干线细节,此大节中没有讲到的,可能后续回再做补充.比方创立bean对象流程中的: 1.第6步中波及的代理类. 2.……
至此,bean处理器的工作曾经实现.当初,咱们曾经能够在业务中应用bean了.