• 欢迎访问搞代码网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站!
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏搞代码吧

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

php 搞代码 3年前 (2022-03-01) 37次浏览 已收录 0个评论

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' 
 * ] 
 */

搞代码网(gaodaima.com)提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发送到邮箱[email protected],我们会在看到邮件的第一时间内为您处理,或直接联系QQ:872152909。本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:从binswoft开始阅读Swoft框架源码六一BeanProcessor之注解解析

喜欢 (0)
[搞代码]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址