Swoft 框架源码阅读(三)

上一篇解读完了关于 AnnotationProcess 这个对象初始化时所做的处理,大致为收集所有的需要加载,并含有注解的类.并解析,分类,映射成一个map然后存入AnnotationRegister中的静态变量中.

那么这次讲解的 BeanProcess 内容有点儿长,分为几个部分讲解完毕

BeanProcess 概述

BeanProcess 文件地址在 swoft/framework/src/Processor/BeanProcessor.php.主要运行内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public function handle(): bool
{
// ....

// 相关处理的类
$handler = new BeanHandler();

// 获取所有配置
$definitions = $this->getDefinitions();

// 获取所有定义过的注释解析类. 也就是每个组件里边都有的 Annotation 文件夹
// 里的 parsers, 他们同来定义自己的注解
$parsers = AnnotationRegister::getParsers();

// 获取所有含有注解的类的映射
$annotations = AnnotationRegister::getAnnotations();

BeanFactory::addDefinitions($definitions);
BeanFactory::addAnnotations($annotations);
BeanFactory::addParsers($parsers);
BeanFactory::setHandler($handler);
BeanFactory::init();

// ....
}

从上面我们可以看出,程序从 AnnotationRegister 获取之前已收集好的类的相关描述,并注入可一个处理类,然后开始了程序的初始化.

根据IDE的跳转提示我们可以知道 init 实际上是调用了一个单例模式的 Contrainer, 并做了初始化.

1
2
3
4
5
6
7
8
9
10
11
public function init(): void
{
// Parse annotations
$this->parseAnnotations();

// Parse definitions
$this->parseDefinitions();

// Init beans
$this->initializeBeans();
}

初始化分为三步

parseAnnotations

1
2
3
4
5
6
7
8
9
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;
}

它调用了 AnnotationObjParser的方法,并重新对传入参数做了赋值

但此时只有 definitions 有值,打印之后则会发现 definitions 的值是config 中的所有配置.其余都为空数组.

那么 parseAnnotations() 方法对各个类,方法,属性上的注解完成解析,并映射成了一个数组的形式。根据追踪,我们可以看到函数实际执行的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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);

// 解析 property 注解
$propertyInjects = [];
$propertyAllAnnotations = $classOneAnnotations['properties'] ?? [];
foreach ($propertyAllAnnotations as $propertyName => $propertyOneAnnotations) {
$proAnnotations = $propertyOneAnnotations['annotation'] ?? [];
$propertyInject = $this->parsePropertyAnnotations($classAry, $propertyName, $proAnnotations);
if ($propertyInject) {
$propertyInjects[$propertyName] = $propertyInject;
}
}

// 解析 method 注解
$methodInjects = [];
$methodAllAnnotations = $classOneAnnotations['methods'] ?? [];
foreach ($methodAllAnnotations as $methodName => $methodOneAnnotations) {
$methodAnnotations = $methodOneAnnotations['annotation'] ?? [];

$methodInject = $this->parseMethodAnnotations($classAry, $methodName, $methodAnnotations);
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;
}
}

实际相关代码太长,这里就不一一贴出了

在了解这个方法作用的时候我们先要知道,在swoft中自定义注解需要有两种类文件.在此我们以Aop 组件来讲解.

组件中 Annotation 文件夹结构如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
.
├── Mapping
│ ├── After.php
│ ├── AfterReturning.php
│ ├── AfterThrowing.php
│ ├── Around.php
│ ├── Aspect.php
│ ├── Before.php
│ ├── PointAnnotation.php
│ ├── PointBean.php
│ └── PointExecution.php
└── Parser
├── AfterParser.php
├── AfterReturningParser.php
├── AfterThrowingParser.php
├── AroundParser.php
├── AspectParser.php
├── BeforeParser.php
├── PointAnnotationParser.php
├── PointBeanParser.php
└── PointExecutionParser.php

Mapping 文件夹内的类则是对注解定义,定义其名字,注解类型,所含参数.总的来说是对注解的一个描述

Parser 文件夹内的文件则是对 Mapping 文件夹里注解的一个解析.

parseDefinitions

而 parseDefinitions 主要职责则是更新配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public function parseDefinitions(): array
{
foreach ($this->definitions as $beanName => $definition) {
if (isset($this->objectDefinitions[$beanName])) {
$objectDefinition = $this->objectDefinitions[$beanName];
$this->resetObjectDefinition($beanName, $objectDefinition, $definition);
continue;
}

$this->createObjectDefinition($beanName, $definition);
}

return [$this->definitions, $this->objectDefinitions, $this->classNames, $this->aliases];
}

objectDefinitions 代表所有已实例化完毕,且完成诸如操作了的对象. 这里的操作则是对该对象的默认配置做更新,或创建操作.

initializeBeans

initializeBeans 操作则是最核心的一环,它初始化了所有 bean, 并在此完成了所有注解的注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private function initializeBeans(): void
{
/* @var ObjectDefinition $objectDefinition */
foreach ($this->objectDefinitions as $beanName => $objectDefinition) {
$scope = $objectDefinition->getScope();
// Exclude request
if ($scope === Bean::REQUEST) {
$this->requestDefinitions[$beanName] = $objectDefinition;
unset($this->objectDefinitions[$beanName]);
continue;
}

// Exclude session
if ($scope === Bean::SESSION) {
$this->sessionDefinitions[$beanName] = $objectDefinition;
unset($this->objectDefinitions[$beanName]);
continue;
}

// New bean
$this->newBean($beanName);
}
}

其中REQUESTSESSION是bean的类型,这个会在后续解释。主要操作集中在newBean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
private function newBean(string $beanName, string $id = '')
{
// First, check bean whether has been create.
if (isset($this->singletonPool[$beanName]) || isset($this->prototypePool[$beanName])) {
return $this->get($beanName);
}

// Get object definition
$objectDefinition = $this->getNewObjectDefinition($beanName);

// ...

// Before initialize bean
$this->beforeInit($beanName, $className, $objectDefinition);

$constructArgs = [];
$constructInject = $objectDefinition->getConstructorInjection();
if ($constructInject !== null) {
$constructArgs = $this->getConstructParams($constructInject, $id);
}

$propertyInjects = $objectDefinition->getPropertyInjections();

// Proxy class
if ($this->handler) {
$className = $this->handler->classProxy($className);
}

$reflectionClass = new ReflectionClass($className);
$reflectObject = $this->newInstance($reflectionClass, $constructArgs);

// Inject properties values
$this->newProperty($reflectObject, $reflectionClass, $propertyInjects, $id);

// Alias
if (!empty($alias)) {
$this->aliases[$alias] = $beanName;
}

// Call init method if exist
if ($reflectionClass->hasMethod(self::INIT_METHOD)) {
$reflectObject->{self::INIT_METHOD}();
}

return $this->setNewBean($beanName, $scope, $reflectObject, $id);
}

此处beforeInit()对每一个bean命名注册,完成了 aop(面向切面) 的初始化。这个点会在后续详细解释。

接下来就可以看到,程序通过Reflection对每个bean实例化,并对含有Property注解的成员进行注入。接着调用每个bean中的init函数。最后在setNewBean()方法中,根据bean的类型,放入不同的pool中。方便调用。至此完成了所有工作

总结

BeanProcesseor其实算得上是Swoft的核心要点了。本次解析的代码设计的地方有点多,有些东西没有详细讲解。这些会在后续慢慢讲解。