关于php:从binswoft开始阅读Swoft框架源码六三BeanProcessor之bean初始化

通过后面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了.

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理