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