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