共计 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'
* ]
*/
正文完