从bin/swoft开始,阅读Swoft框架源码(六--二)--BeanProcessor之bean配置解析

马尔科夫尼可夫

经过上一节的注解解析后,swoft将获取到的注解对象解析成了定义对象,完成了对这些原始注解对象的梳理工作.

本小节将梳理获取到的这些定义对象的解析.

解析定义对象的入口方法:

private function parseDefinitions(): void
{
     // 实例化解析器,将前面解析好的数据传递进去
     $annotationParser = new DefinitionObjParser(
         $this->definitions,
         $this->objectDefinitions,
         $this->classNames,
         $this->aliases
     );
     // 调用解析器的解析方法
     // Collect info
     $definitionData = $annotationParser->parseDefinitions();
     // 将返回的结果保存在容器上
     [$this->definitions, $this->objectDefinitions, $this->classNames, $this->aliases] = $definitionData;
}

解析器的入口方法:

public function parseDefinitions(): array
{
     // 遍历bean配置,得到bean名称和配置(定义)
     foreach ($this->definitions as $beanName => $definition) {
         // 如果已经通过上一节的注解解析,生成了这个定义对象
         if (isset($this->objectDefinitions[$beanName])) {
             // 获取这个定义对象
             $objectDefinition = $this->objectDefinitions[$beanName];
             // 重置这个定义对象
             $this->resetObjectDefinition($beanName, $objectDefinition, $definition);
             // 继续下一个
             continue; 
         }
         // 否则,根据bean配置创建定义对象
         $this->createObjectDefinition($beanName, $definition);
     }
     return [$this->definitions, $this->objectDefinitions, $this->classNames, $this->aliases];
}

重置定义对象:

private function resetObjectDefinition(string $beanName, ObjectDefinition $objDefinition, array $definition): void
{
     // 获取bean配置中定义的类名
     // Parse class name
     $className = $definition['class'] ?? '';
     // 获取定义对象的类名
     $objClassName = $objDefinition->getClassName();
     // 如果配置中定义了类名且与定义对象中的类名不同,抛出异常
     if (!empty($className) && $className !== $objClassName) {
        throw new InvalidArgumentException('Class for annotations and definitions must be the same Or not to define class');
     }
     // 更新定义对象
     $objDefinition = $this->updateObjectDefinitionByDefinition($objDefinition, $definition);
     // 将更新后的定义对象设置回定义对象池
     $this->objectDefinitions[$beanName] = $objDefinition;
}

更新定义对象方法:

private function updateObjectDefinitionByDefinition(ObjectDefinition $objDfn, array $definition): ObjectDefinition
{
     // 将bean配置解析成构造参数注入对象、属性注入对象数组、选项数组
     [$constructInject, $propertyInjects, $option] = $this->parseDefinition($definition);
     // 设置构造注入
     // Set construct inject
     if (!empty($constructInject)) {
        $objDfn->setConstructorInjection($constructInject);
     }
     // 遍历属性注入数组并设置到定义对象中
     // Set property inject
     foreach ($propertyInjects as $propertyName => $propertyInject) {
        // 设置属性注入对象
        // 如果已经有这个属性的设置,则回被覆盖
        $objDfn->setPropertyInjection($propertyName, $propertyInject);
     }
     // 类型数组
     $scopes = [
         Bean::SINGLETON,
         Bean::PROTOTYPE,
         Bean::REQUEST,
     ];
     // 获取选项中的类型定义
     $scope = $option['scope'] ?? '';
     // 获取选项中的别名定义
     $alias = $option['alias'] ?? '';
     // 也就是说不能在这里定义session类型
     if (!empty($scope) && !in_array($scope, $scopes, true)) {
        throw new InvalidArgumentException('Scope for definition is not undefined');
     }
     // 设置类型
     // Update scope
     if (!empty($scope)) {
        $objDfn->setScope($scope);
     }
     // 更新别名
     // 感觉这里的代码是有问题的
     // 获取对象原始别名和删除别名映射的操作是不是应该放在
     // 设置操作的前面?
     // 否则这一步操作就等于没有删除前面定义的别名
     // 且设置了新的别名
     // Update alias
     if (!empty($alias)) {
         // 设置定义对象的别名
         $objDfn->setAlias($alias);
         // 获取定义对象别名
         $objAlias = $objDfn->getAlias();
         unset($this->aliases[$objAlias]);
         // 设置新的别名
         $this->aliases[$alias] = $objDfn->getName();
     }
     // 返回已经更新的定义对象
     return $objDfn;
}

bean配置解析:

private function parseDefinition(array $definition): array
{
     // Remove class key
     // 移除类名的定义
     unset($definition['class']);
     
     // Parse construct
     // 获取构造参数
     $constructArgs = $definition[0] ?? [];
     if (!is_array($constructArgs)) {
        throw new InvalidArgumentException('Construct args for definition must be array');
     }
     
     // Parse construct args
     $argInjects = [];
     // 遍历构造参数
     foreach ($constructArgs as $arg) {
         // 获取参数的实际值和是否为引用
         [$argValue, $argIsRef] = $this->getValueByRef($arg);
         // 创建注入的构造参数
         $argInjects[] = new ArgsInjection($argValue, $argIsRef);
     }
     
     // Set construct inject
     $constructInject = null;
     如果需要注入的构造参数不为空
     if (!empty($argInjects)) {
        // 创建__construct的注入方法
        $constructInject = new MethodInjection('__construct', $argInjects);
     }
     // 移除构造参数的定义
     // Remove construct definition
     unset($definition[0]);
     // 解析配置中的__option部分
     // Parse definition option
     $option = $definition['__option'] ?? [];
     if (!is_array($option)) {
        throw new InvalidArgumentException('__option for definition must be array');
     }
     // 移除__option的配置
     // Remove `__option`
     unset($definition['__option']);
     // 解析属性配置
     // Parse definition properties
     $propertyInjects = [];
     // 遍历剩余的所有配置
     foreach ($definition as $propertyName => $propertyValue) {
         // 如果属性名不是字符串,则报错
         if (!is_string($propertyName)) {
            throw new InvalidArgumentException('Property key from definition must be string');
         }
         // 获取属性值和类型
         [$proValue, $proIsRef] = $this->getValueByRef($propertyValue);
         // 如果属性是个数组,则再次遍历设置
         // Parse property for array
         if (is_array($proValue)) {
            $proValue = $this->parseArrayProperty($proValue);
         }
         // 创建属性注入对象
         $propertyInject = new PropertyInjection($propertyName, $proValue, $proIsRef);
         // 保存属性注入对象
         $propertyInjects[$propertyName] = $propertyInject;
     }
     return [$constructInject, $propertyInjects, $option];
}

获取参数的实际值和是否引用:

protected function getValueByRef($value): array
{
     if (!is_string($value)) {
        return [$value, false];
     }
     // Reg match
     $isRef = preg_match('/^${(.*)}$/', $value, $match);
     if ($isRef && isset($match[1])) {
        return [$match[1], (bool)$isRef];
     }
     return [$value, false];
}

创建新的定义对象:

private function createObjectDefinition(string $beanName, array $definition): void
{
     // 获取配置中定义的类名,不存在就报错
     $className = $definition['class'] ?? '';
     if (empty($className)) {
        throw new InvalidArgumentException(sprintf('%s key for definition must be defined class', $beanName));
     }
     // 通过定义对象名称(beanName)和类名实例化新的定义对象
     $objDefinition = new ObjectDefinition($beanName, $className);
     // 更新定义对象
     $objDefinition = $this->updateObjectDefinitionByDefinition($objDefinition, $definition);
     // 将新的定义对象名添加到类名映射数组中
     // 别名在updateObjectDefinitionByDefinition方法中已经更新
     $classNames = $this->classNames[$className] ?? [];
     $classNames[] = $beanName;
     $this->classNames[$className]       = array_unique($classNames);
     // 保存定义对象
     $this->objectDefinitions[$beanName] = $objDefinition;
}

总结:

本小节的主要功能:
    根据bean的配置信息,更新前一小节得到的定义对象,创建前一小节没有创建的定义对象.
阅读 228

酷白发,小酒窝,主角标配的帅小伙~

7 声望
2 粉丝
0 条评论
你知道吗?

酷白发,小酒窝,主角标配的帅小伙~

7 声望
2 粉丝
宣传栏