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 DISABLED , skip handle it', $loaderClass);
continue;
}
// 如果当前autoLoader本身不是enable,则打印并跳过
// If the component is disabled by self.
if (!$autoLoader->isEnable()) {
CLog::info('Auto loader(%s) is DISABLED , 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'
* ]
*/