关于php:从binswoft开始阅读Swoft框架源码六一BeanProcessor之注解解析

44次阅读

共计 13475 个字符,预计需要花费 34 分钟才能阅读完成。

Bean 又是一个 Swoft 的外围概念.
BeanProcessor 就是能在 Swoft 中应用 Bean 的根底.

因为 bean 处理器全副由 swoft 原生实现, 所以代码量较大, 逻辑也比较复杂, 所以 bean 处理器这一模块将分为三个子章节 (注解对象解析、定义对象解析、初始化 bean) 来进行剖析.

这里先介绍一个概念 (这是作者本人的了解, 并非官网的解释): 定义对象

首先, 先看 bean 的概念:

1.bean 对象实际上就是一个对象模版, 不同的 bean 类型代表了如何依据这个模版对象返回对应的对象.
2.bean 总共有 4 种类型: singleton、prototype、request 和 session.
    singleton 类型间接返回这个模版对象自身.
    prototype 是将这个模版对象作为新对象的原型, 返回模版对象的克隆对象.
    request 和 session 是依据模版对象克隆出新的对象, 而后在某一种条件下放弃单例, 直至条件不满足新对象被销毁.

而后基于模版这个概念, 得出 定义对象 的概念:

用来定义返回对象类型、构造和内容的模版.

仍旧先从入口办法 handle 动手:

public function handle(): bool
{if (!$this->application->beforeBean()) {return false;}
     
     // 实例化 handler
     $handler = new BeanHandler();
     
     // 获取所有定义的 bean 配置
     $definitions = $this->getDefinitions();
     
     // 获取在注解处理器中 parseOneClassAnnotation 办法注册的 parser 对象
     $parsers = AnnotationRegister::getParsers();
     
     // 获取注解处理器注册的注解对象
     $annotations = AnnotationRegister::getAnnotations();
     // 将获取到的 bean 配置保留到 bean 容器中
     // 这里的 BeanFactory 实际上是将内容又转交给了 Container
     BeanFactory::addDefinitions($definitions);
     // 将获取的注解对象转交给 bean 容器
     BeanFactory::addAnnotations($annotations);
     // 将获取的 parser 对象转交给 bean 容器
     BeanFactory::addParsers($parsers);
     // 将后面实例化的 handler 设置为 bean 容器的 handler
     BeanFactory::setHandler($handler);
     // bean 容器初始化
     // 后附初始化代码
     BeanFactory::init();
     
     $stats = BeanFactory::getStats();
     CLog::info('Bean is initialized(%s)', SwoftHelper::formatStats($stats));
     /* @var Config $config */
     $config = BeanFactory::getBean('config');
     CLog::info('Config path is %s', $config->getPath());
     if ($configEnv = $config->getEnv()) {CLog::info('Config env=%s', $configEnv);
     } else {CLog::info('Config env is not setting');
     }
     return $this->application->afterBean();}

getDefinitions 实现:

private function getDefinitions(): array
{
     // Core beans
     $definitions = [];
     // 此处获取到的 autoLoaders 就是上一章注解处理器注册的 autoLoader
     $autoLoaders = AnnotationRegister::getAutoLoaders();
     
     // 获取生效的 autoLoaders
     // get disabled loaders by application
     $disabledLoaders = $this->application->getDisabledAutoLoaders();
     
     // 遍历 autoLoaders
     // 获取各 autoLoader 中 beans 办法注册的 bean 配置
     // 保留在 definitions 数组中
     foreach ($autoLoaders as $autoLoader) {
         // 如果不是 DefinitionInterface 的实现, 跳过
         if (!$autoLoader instanceof DefinitionInterface) {continue;}
         // 获取 autoLoader 的类名, 蕴含命名空间
         $loaderClass = get_class($autoLoader);
         // If the component is disabled by app.
         // 如果是被 app 定义的有效 autoLoader, 则打印并跳过
         if (isset($disabledLoaders[$loaderClass])) {CLog::info('Auto loader(%s) is <cyan>DISABLED</cyan>, skip handle it', $loaderClass);
             continue; 
         }
         // 如果以后 autoLoader 自身不是 enable, 则打印并跳过
         // If the component is disabled by self.
         if (!$autoLoader->isEnable()) {CLog::info('Auto loader(%s) is <cyan>DISABLED</cyan>, skip handle it', $loaderClass);
             continue; 
         }
         // 获取注册的 bean 配置并与返回值合并
         $definitions = ArrayHelper::merge($definitions, $autoLoader->beans());
     }
     
     // 获取利用的 bean 文件
     // Application bean definitions
     $beanFile = alias($this->application->getBeanFile());
     if (!file_exists($beanFile)) {throw new InvalidArgumentException(sprintf('The bean config file of %s is not exist!', $beanFile));
     }
     // 获取利用 bean 文件中定义的 bean 配置
     /** @noinspection PhpIncludeInspection */
     $beanDefinitions = require $beanFile;
     
     // 合并两种路径中获取的 bean 配置, 而后返回给调用者
     return ArrayHelper::merge($definitions, $beanDefinitions);
}

bean 容器初始化

public function init(): void
{
     // Parse annotations
     // 解析注解对象, 作用是将后面注解处理器获取的注解对象
     // 分门别类的保留到 Container 容器内
     $this->parseAnnotations();
     
     // Parse definitions
     $this->parseDefinitions();
     // Init beans
     $this->initializeBeans();}

本章节次要解说注解对象的解析

注解解析的入口办法 parseAnnotations:

private function parseAnnotations(): void
{
     // 实例化注解对象解析器
     // 实例化只是单纯的将传入的属性进行保留
     // 未做其它操作
     $annotationParser = new AnnotationObjParser(
         $this->definitions,
         $this->objectDefinitions,
         $this->classNames,
         $this->aliases
     );
     
     // 解析注解
     // 后附实现代码
     $annotationData = $annotationParser->parseAnnotations($this->annotations, $this->parsers);
     
     // 将解析后果保留在容器内
     [$this->definitions, $this->objectDefinitions, $this->classNames, $this->aliases] = $annotationData;
}

swoft 中对于 annotations 数组构造的正文:

/**
 * @var array
 * * @example
 * [
 *    'loadNamespace' => [ // 注解的命名空间
 *        'className' => [ // 调用注解的类的类名
 *             'annotation' => [ // 调用的类注解对象汇合
 *                  new ClassAnnotation(), 
 *                  new ClassAnnotation(), 
 *                  new ClassAnnotation(), 
 *             ] 
 *             'reflection' => new ReflectionClass(), // 调用注解类的类反射对象 
 *             'properties' => [ // 调用类的属性汇合
 *                  'propertyName' => [ // 调用类的属性名
 *                      'annotation' => [ // 属性上调用的注解对象汇合
 *                          new PropertyAnnotation(), 
 *                          new PropertyAnnotation(), 
 *                          new PropertyAnnotation(), 
 *                      ] 
 *                     'reflection' => new ReflectionProperty(), // 调用类的属性反射对象
 *                  ] 
 *             ], 
 *            'methods' => [ // 调用类的办法汇合
 *                  'methodName' => [ // 调用类的办法名
 *                      'annotation' => [ // 办法上调用的注解对象汇合
 *                          new MethodAnnotation(), 
 *                          new MethodAnnotation(), 
 *                          new MethodAnnotation(), 
 *                      ] 
 *                     'reflection' => new ReflectionFunctionAbstract(), // 调用类办法的反射对象
 *                  ] 
 *            ] 
 *        ] 
 *    ] 
 * ] 
 */

AnnotationObjParser 的 parseAnnotations 实现:

public function parseAnnotations(array $annotations, array $parsers): array
{
     // 设置解析器对象
     $this->parsers = $parsers;
     // 设置注解对象
     $this->annotations = $annotations;
     // 遍历注解对象, 获取命名空间和命名空间下所有的类
     foreach ($this->annotations as $loadNameSpace => $classes) {
         // 遍历命名空间下的所有类, 获取类名和该类的所有注解对象
         foreach ($classes as $className => $classOneAnnotations) {
            // 解析这个类的注解
            $this->parseOneClassAnnotations($className, $classOneAnnotations);
         }
     }
     return [$this->definitions, $this->objectDefinitions, $this->classNames, $this->aliases];
}

每一个类的注解解析:

private function parseOneClassAnnotations(string $className, array $classOneAnnotations): void
{
     // Check class annotation tag
     // 如果被解析的类没有注解对象, 则抛出异样
     if (!isset($classOneAnnotations['annotation'])) {
         throw new AnnotationException(sprintf('Property or method(%s) with `@xxx` must be define class annotation',
             $className
         ));
     }
     
     // 解析类注解
     // Parse class annotations
     // 获取类对应的所有注解对象
     $classAnnotations = $classOneAnnotations['annotation'];
     // 获取类对应的反射对象
     $reflectionClass = $classOneAnnotations['reflection'];
     // 创立参数数组, 蕴含类名、类反射对象、类所有注解对象
     $classAry = [
         $className,
         $reflectionClass,
         $classAnnotations
     ];
     // 解析类注解(前面还会顺次解析属性和办法注解), 返回定义对象
     $objectDefinition = $this->parseClassAnnotations($classAry);
     
     // 解析属性注解
     // Parse property annotations
     // 保留属性注入对象的数组
     $propertyInjects = [];
     // 获取以后类下的所有属性
     $propertyAllAnnotations = $classOneAnnotations['properties'] ?? [];
     // 遍历属性数组, 失去属性名和对应属性注解汇合
     foreach ($propertyAllAnnotations as $propertyName => $propertyOneAnnotations) {
         // 获取属性的所有注解对象
         $proAnnotations = $propertyOneAnnotations['annotation'] ?? [];
         // 解析属性注解
         $propertyInject = $this->parsePropertyAnnotations($classAry, $propertyName, $proAnnotations);
         // 保留获取的属性注入对象
         if ($propertyInject) {$propertyInjects[$propertyName] = $propertyInject;
         }
     }
     // Parse method annotations
     // 保留办法注入对象的数组
     $methodInjects = [];
     // 获取办法注解对象
     $methodAllAnnotations = $classOneAnnotations['methods'] ?? [];
     // 遍历办法注解对象
     foreach ($methodAllAnnotations as $methodName => $methodOneAnnotations) {
         // 获取注解对象数组
         $methodAnnotations = $methodOneAnnotations['annotation'] ?? [];
         // 解析办法注解对象
         $methodInject = $this->parseMethodAnnotations($classAry, $methodName, $methodAnnotations);
         // 不会进入, 因为此值永远为 null
         if ($methodInject) {$methodInjects[$methodName] = $methodInject;
         }
     }
     // 如果未获取到类的定义对象, 则间接返回
     if (!$objectDefinition) {return;}
     // 如果属性注入对象不为空, 则将属性注入对象
     // 保留到以后的定义模版对象上
     if (!empty($propertyInjects)) {$objectDefinition->setPropertyInjections($propertyInjects);
     }
     // 保留办法注入对象, 这里永远为空
     if (!empty($methodInjects)) {$objectDefinition->setMethodInjections($methodInjects);
     }
     
     // Object definition and class name
     // 获取定义对象的名称
     $name = $objectDefinition->getName();
     // 获取定义对象的别名
     $aliase = $objectDefinition->getAlias();
     // 获取以后对象已保留的类名与定义对象名的映射
     $classNames = $this->classNames[$className] ?? [];
     // 增加类名与定义对象名的映射
     $classNames[] = $name;
     // 去重并设置回以后对象
     $this->classNames[$className]   = array_unique($classNames);
     // 按名称保留定义对象
     $this->objectDefinitions[$name] = $objectDefinition;
     // 如果有别名, 设置别名与定义对象名称的映射
     if (!empty($aliase)) {$this->aliases[$aliase] = $name;
     }
}

解析类注解 parseClassAnnotations 的实现:

private function parseClassAnnotations(array $classAry): ?ObjectDefinition
{
     // 获取传递进来的类注解对象汇合
     [, , $classAnnotations] = $classAry;
     // 返回值
     $objectDefinition = null;
     // 遍历类的注解对象汇合, 找到每一个注解对象
     foreach ($classAnnotations as $annotation) {
         // 获取注解对象的类名
         $annotationClass = get_class($annotation);
         // 如果没有找到这个类的解析器类名, 则跳过
         if (!isset($this->parsers[$annotationClass])) {continue;}
         // 获取这个类的解析器类名
         $parserClassName = $this->parsers[$annotationClass];
         // 通过解析器类名获取注解解析器
         // 办法内是 new 传入的解析器类名.
         // 将参数中的类名、类反射对象、类注解汇合作为结构参数传递给被 new 的解析器
         // 而后返回 new 进去的解析器实例
         $annotationParser = $this->getAnnotationParser($classAry, $parserClassName);
         // 解析类注解
         // ParserInterface 中对于 parse 办法的形容如下:
         // @param int    $type             Class or Method or Property
         // @param object $annotationObject Annotation object
         // @return array
         // Return empty array is nothing to do!
         // When class type return [$beanName, $className, $scope, $alias] is to inject bean
         // When property type return [$propertyValue, $isRef] is to reference value
         // 返回值局部翻译过去就是:
         // 如果返回空数组, 零碎就不会做更多的操作
         // 如果返回数组为[$beanName, $className, $scope, $alias]
         // 则示意注入 bean
         // 如果返回 [$propertyValue, $isRef] 则示意对 value 的援用
         $data = $annotationParser->parse(Parser::TYPE_CLASS, $annotation);
         // 所以, 如果返回为空, 则跳过
         if (empty($data)) {continue;}
         // 类注解长度不为 4, 就是有返回值, 然而不是注入 bean 的状况, 则抛出异样
         if (count($data) !== 4) {throw new InvalidArgumentException(sprintf('%s annotation parse must be 4 size', $annotationClass));
         }
         
         // bean 就相当于是模版对象, 不同的 bean 类型代表了如何依据这个模版对象返回对应的对象
         // singleton 类型间接返回这个模版对象自身
         // prototype 是将这个模版对象作为新对象的原型, 返回模版对象的克隆对象
         // request 和 session 是依据模版对象克隆出新的对象, 而后在某一种条件下放弃单例, 直至条件不满足新对象被销毁
         // 这个办法前面的代码就是创立这个模版对象
         // swoft 外面给这个模版对象取名翻译过去叫做: 定义对象
         
         // 将返回值拆分为对应的变量
         // $name 示意构建的定义对象的名称
         // $className 示意构建的定义对象的类名
         // $scope 示意构建的定义对象的类型
         // $alias 示意构建的定义对象的别名
         [$name, $className, $scope, $alias] = $data;
         
         // 如果没设置名称, 则将类名作为默认名称
         $name = empty($name) ? $className : $name;
         
         // 如果连类名都是空的, 则抛出异样
         if (empty($className)) {throw new InvalidArgumentException(sprintf('%s with class name can not be empty', $annotationClass));
         }
         
         // 实例化定义对象
         // Multiple coverage
         // 动静笼罩, 后遍历到的实例会笼罩后面结构的实例
         // 至于 swoft 有没有对这个遍历程序做过操作, 以及为何会这样动静笼罩, 前面再去钻研.
         $objectDefinition = new ObjectDefinition($name, $className, $scope, $alias);
     }
     // 返回定义对象
     return $objectDefinition;
}

定义对象的构造大抵如下:

object(Swoft\Bean\Definition\ObjectDefinition)#4377 (7) {["name":"Swoft\Bean\Definition\ObjectDefinition":private]=>
  string(24) "App\WebSocket\TestModule"
  ["className":"Swoft\Bean\Definition\ObjectDefinition":private]=>
  string(24) "App\WebSocket\TestModule"
  ["scope":"Swoft\Bean\Definition\ObjectDefinition":private]=>
  string(9) "singleton"
  ["alias":"Swoft\Bean\Definition\ObjectDefinition":private]=>
  string(0) ""["constructorInjection":"Swoft\Bean\Definition\ObjectDefinition":private]=>
  NULL
  ["propertyInjections":"Swoft\Bean\Definition\ObjectDefinition":private]=>
  array(0) { }
  ["methodInjections":"Swoft\Bean\Definition\ObjectDefinition":private]=>
  array(0) {}}

解析属性注解的实现 parsePropertyAnnotations:

private function parsePropertyAnnotations(
 array $classAry,
 string $propertyName,
 array $propertyAnnotations
): ?PropertyInjection {
     // 属性注入对象
     $propertyInjection = null;
     // 遍历属性注解对象
     foreach ($propertyAnnotations as $propertyAnnotation) {
         // 获取属性注解对象的类名
         $annotationClass = get_class($propertyAnnotation);
         // 如果没有对应的解析器类名, 则跳过
         if (!isset($this->parsers[$annotationClass])) {continue;}
         // 获取解析器类名
         $parserClassName = $this->parsers[$annotationClass];
         // 和类注解解析器获取一样, 都是调用同一个办法
         // 返回的是解析器实例
         $annotationParser = $this->getAnnotationParser($classAry, $parserClassName);
         // 给解析器设置属性名称
         $annotationParser->setPropertyName($propertyName);
         // 调用解析器的解析办法
         $data = $annotationParser->parse(Parser::TYPE_PROPERTY, $propertyAnnotation);
         // 如果返回值是空数组, 则无需解决, 跳过
         if (empty($data)) {continue;}
         // 如果返回值不是 [$propertyValue, $isRef] 这种构造
         // 则抛出异样
         if (count($data) !== 2) {throw new InvalidArgumentException('Return array with property annotation parse must be 2 size');
         }
         // 获取以后属性解析器中保留的模版定义对象
         $definitions = $annotationParser->getDefinitions();
         // 如果有值
         if ($definitions) {
            // 则合并到以后对象的定义对象数组中, 解决实现后整个数组会被返回
            $this->definitions = $this->mergeDefinitions($this->definitions, $definitions);
         }
         // Multiple coverage
         // 又是动静笼罩
         [$propertyValue, $isRef] = $data;
         // 这里创立的是属性注入对象, 与类注解解析办法创立的
         // 定义对象是不同的
         $propertyInjection = new PropertyInjection($propertyName, $propertyValue, $isRef);
     }
     // 返回属性注入对象
     return $propertyInjection;
}

属性注入对象的构造大抵如下:

object(Swoft\Bean\Definition\PropertyInjection)#4398 (3) {["propertyName":"Swoft\Bean\Definition\PropertyInjection":private]=>
  string(5) "logic"
  ["value":"Swoft\Bean\Definition\PropertyInjection":private]=>
  string(28) "App\Model\Logic\MonitorLogic"
  ["isRef":"Swoft\Bean\Definition\PropertyInjection":private]=>
  bool(true)
}

解析办法注解 parseMethodAnnotations:

private function parseMethodAnnotations(
 array $classAry,
 string $methodName,
 array $methodAnnotations
): ?MethodInjection {
     // 须要返回的办法注入对象 此办法未对这个变量再次赋值
     // 所以返回的这个值永远是 null
     $methodInject = null;
     // 遍历办法注解对象
     foreach ($methodAnnotations as $methodAnnotation) {
         // 获取办法注解对象的类名
         $annotationClass = get_class($methodAnnotation);
         
         // 如果不存在对应解析器类名, 则跳过
         if (!isset($this->parsers[$annotationClass])) {continue;}
         // 获取解析器类名
         $parserClassName = $this->parsers[$annotationClass];
         // 获取解析器实例
         $annotationParser = $this->getAnnotationParser($classAry, $parserClassName);
         // 设置被解析的办法名
         $annotationParser->setMethodName($methodName);
         // 调用解析器的解析办法
         $data = $annotationParser->parse(Parser::TYPE_METHOD, $methodAnnotation);
         // 如果返回空数组则跳过
         if (empty($data)) {continue;}
         // 如果解析器有定义对象, 则与以后对象的定义对象数组合并
         $definitions = $annotationParser->getDefinitions();
         if ($definitions) {$this->definitions = $this->mergeDefinitions($this->definitions, $definitions);
         }
     }
     // 此处返回的是 null
     return $methodInject;
}

总结:

1. 注解对象解析办法流程梳理:
    (1): 遍历所有注解对象, 失去命名空间下的所有类名与蕴含类注解对象 (annotation)、调用注解的类的反射对象(reflection)、属性注解对象(properties 如果有的话) 和办法注解对象 (metheds 如果有的话) 构造的数组.
    (2): 遍历第一步失去的每个命名空间下的数组, 失去每个类的类名与对应的数据结构.
    (3): 拿到数据结构后, 先失去对应的解析器, 先调用解析器 parse 办法, 而后解析出 (new 出) 类的定义对象.
    (4): 接着遍历构造中的属性注解对象, 获取到每个属性注解的解析器, 调用 parse 办法、合并解析器中的定义对象到全局定义对象数组, 解析出 (new 出) 属性的注入对象.
    (5): 而后遍历构造中的办法注解对象, 获取到每个办法注解对象的解析器, 调用 parse 办法、合并解析器中的定义对象到全局定义对象数组.
    (6): 保留属性注入对象到定义对象.
    (7): 保留办法注入对象到定义对象(因为没有返回办法注入对象, 此处不会执行).
    (8): 全局保留定义对象名称、定义对象以及别名和定义对象名称的映射关系.
2. 注解对象解析办法性能:
    创立注解类的定义对象, 给定义对象增加属性和办法注入对象, 按名称保留注解对象, 保留调用类的类名与定义对象名的映射关系, 保留别名与定义对象名的映射关系.

附:
bean 配置的数据结构:

/**
 * All definitions * * @var array
 * * @example
 * [
 *     'name' => [ 
 *         'class' => 'className', 
 *         [ 
 *             'construnctArg', 
 *             '${ref.name}', // config params 
 *             '${beanName}', // object 
 *         ], 
 *         'propertyValue', 
 *         '${ref.name}', 
 *         '${beanName}', 
 *         '__option' => [ 
 *              'scope' => '...', 
 *              'alias' => '...', 
 *         ] 
 *     ] 
 * ] 
 */

保留定义对象的数组构造:

/**
 * Bean definitions * * @var ObjectDefinition[]
 * * @example
 * [
 *     'beanName' => new ObjectDefinition, 
 *     'beanName' => new ObjectDefinition, 
 *     'beanName' => new ObjectDefinition 
 * ] 
 */

保留调用类与被调用的定义对象名称的数据结构:

/**
 * Class all bean names (many instances) * * @var array
 * 
 * @example
 * [
 *     'className' => [ // 调用类名
 *         'beanName', // 定义对象名
 *         'beanName', 
 *         'beanName', 
 *     ] 
 * ] 
 */

保留定义对象别名的数据结构:

/**
 * All alias * * @var array
 * 
 * @example
 * [
 *     'alias' => 'beanName', 
 *     'alias' => 'beanName', 
 *     'alias' => 'beanName' 
 * ] 
 */

正文完
 0