通过前面2小节的工作,现在容器内已经保存了完整的bean定义对象和名称、别名映射.
本章的工作是梳理swoft如何利用bean的定义对象,生成最终可供业务使用的bean对象.
初始化bean的入口方法:
private function initializeBeans(): void
{
// 遍历保存的定义对象数组,得到bean名称和定义对象
/* @var ObjectDefinition $objectDefinition */
foreach ($this->objectDefinitions as $beanName => $objectDefinition) {
// 获取定义对象的类型
$scope = $objectDefinition->getScope();
// 如果是request类型
// Exclude request
if ($scope === Bean::REQUEST) {
// 将定义对象转存到request定义对象数组
$this->requestDefinitions[$beanName] = $objectDefinition;
// 从全局定义对象数组中删除这个定义对象
unset($this->objectDefinitions[$beanName]);
// 跳过
continue;
}
// 如果类型是session
// Exclude session
if ($scope === Bean::SESSION) {
// 转存定义对象到session定义对象数组
$this->sessionDefinitions[$beanName] = $objectDefinition;
// 从全局定义对象数组中移除这个定义对象
unset($this->objectDefinitions[$beanName]);
// 跳过
continue;
}
// 创建bean对象
// New bean
$this->newBean($beanName);
}
}
创建bean对象:
private function newBean(string $beanName, string $id = '')
{
// 首先检查这个bean是否已经存在,如果存在则直接返回这个bean
// 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);
// 获取类型
$scope = $objectDefinition->getScope();
// 获取别名
$alias = $objectDefinition->getAlias();
// Cache reflection class info
// 获取定义对象保存的类名
$className = $objectDefinition->getClassName();
// 创建一个新的类反射对象或从缓存中取出这个类的反射对象
$reflectClass = Reflections::cache($className);
// 调用初始化前回调
// Before initialize bean
$this->beforeInit($beanName, $className, $objectDefinition);
// 构造参数
$constructArgs = [];
// 获取构造注入对象
$constructInject = $objectDefinition->getConstructorInjection();
// 如果存在
if ($constructInject !== null) {
// 获取构造参数
$constructArgs = $this->getConstructParams($constructInject, $id);
}
// 如果存在代理类,则将原来的类反射对象替换为代理类的类反射对象
// It's proxy class. eg: AOP, RPC client class
if ($this->handler) {
$oldCName = $className;
$className = $this->handler->classProxy($className);
// New class name from handler->classProxy()
if ($oldCName !== $className) {
$reflectClass = new ReflectionClass($className);
}
}
// 传入反射类和构造参数,创建反射实例
$reflectObject = $this->newInstance($reflectClass, $constructArgs);
// 获取需要注入的属性
$propertyInjects = $objectDefinition->getPropertyInjections();
// 递归从类的最顶层父类开始,依次遍历属性注入数组,查找需要注入的属性,然后给该属性赋值
// Inject properties values
$this->newProperty($reflectObject, $reflectClass, $propertyInjects, $id);
// Alias name
// Fix: $aliasId !== $id for deny loop get
// 设置bean的别名
if ($alias && $alias !== $beanName) {
$this->aliases[$alias] = $beanName;
}
// 如果反射类有init方法
// Call init method if exist
if ($reflectClass->hasMethod(self::INIT_METHOD)) {
// 调用反射实例的init方法
$reflectObject->{self::INIT_METHOD}();
}
// 将反射实例按bean名称保存在对应类型的对象数组中
// 并根据类型返回对应的实例
return $this->setNewBean($beanName, $scope, $reflectObject, $id);
}
保存新bean对象的方法,4中类型分别保存在4个数组中:
private function setNewBean(string $beanName, string $scope, $object, string $id = '')
{
switch ($scope) {
case Bean::SINGLETON: // Singleton
$this->singletonPool[$beanName] = $object;
break;
case Bean::PROTOTYPE:
$this->prototypePool[$beanName] = $object;
// Clone it
// 将返回值设置为克隆对象
$object = clone $object;
break;
case Bean::REQUEST:
// 这里的ID应该是请求的协程ID
$this->requestPool[$id][$beanName] = $object;
break;
case Bean::SESSION:
// 这里的ID应该是sessionID
$this->sessionPool[$id][$beanName] = $object;
break;
}
return $object;
}
属性注入方法实现:
private function newProperty(
$reflectObject,
ReflectionClass $reflectionClass,
array $propertyInjects,
string $id = ''
): void {
// 获取反射类的父反射类对象
// New parent properties
$parentClass = $reflectionClass->getParentClass();
// 如果有父类,则继续递归,直到找到最顶层父类
if ($parentClass !== false) {
$this->newProperty($reflectObject, $parentClass, $propertyInjects, $id);
}
// 遍历属性注入对象数组
/* @var PropertyInjection $propertyInject */
foreach ($propertyInjects as $propertyInject) {
// 获取属性注入对象标识的属性名
$propertyName = $propertyInject->getPropertyName();
// 如果当前类中没有这个属性名,则跳过后续的注入逻辑
if (!$reflectionClass->hasProperty($propertyName)) {
continue;
}
/** @noinspection PhpUnhandledExceptionInspection */
// 从反射类中获取对应的反射属性对象
$reflectProperty = $reflectionClass->getProperty($propertyName);
// 如果是static类型,则抛出异常
if ($reflectProperty->isStatic()) {
throw new InvalidArgumentException(sprintf('Property %s for bean can not be `static` ', $propertyName));
}
// 获取属性注入对象中保存的value
// Parse property value
$propertyValue = $propertyInject->getValue();
// 如果值是字符串且存在以这个值命名的接口
// Inject interface
if (is_string($propertyValue) && interface_exists($propertyValue)) {
$propertyValue = InterfaceRegister::getInterfaceInjectBean($propertyValue);
} elseif (is_array($propertyValue)) {
// 如果是数组,且数组内存在引用类型
// 则将其替换成
$propertyValue = $this->newPropertyArray($propertyValue, $id);
}
// Refer config or bean
if ($propertyInject->isRef()) {
$propertyValue = $this->getRefValue($propertyValue, $id);
// Optimize: Value not exists, skip call setter
if ($propertyValue === null) {
continue;
}
}
// Parser property type $propertyType = ObjectHelper::getPropertyBaseType($reflectProperty);
if (!empty($propertyType)) {
$propertyValue = ObjectHelper::parseParamType($propertyType, $propertyValue);
}
// First, try set value by setter method
$setter = 'set' . ucfirst($propertyName);
if (method_exists($reflectObject, $setter)) {
$reflectObject->$setter($propertyValue);
continue;
}
if (!$reflectProperty->isPublic()) {
$reflectProperty->setAccessible(true);
}
// Set value by reflection
$reflectProperty->setValue($reflectObject, $propertyValue);
}
}
处理数组参数的方法:
private function newPropertyArray(array $propertyValue, string $id = ''): array
{
// 遍历属性值数组
foreach ($propertyValue as $proKey => &$proValue) {
// 如果属性值是注入对象,且是引用类型
if ($proValue instanceof ArgsInjection && $proValue->isRef()) {
// 获取引用的值
$refValue = $proValue->getValue();
// 将引用的值解析为真实的bean对象并替换原数组中的值
$proValue = $this->getRefValue($refValue, $id);
}
}
// 返回被替换后的值数组
return $propertyValue;
}
通过引用获取真实bean对象的方法:
private function getRefValue($value, string $id = '')
{
// 如果引用不是一个字符串,则直接返回该值
if (!is_string($value)) {
return $value;
}
// 如果值的第一个字符不是.(.这个字符是配置的使用方式)
// 则调用safeNewBean去获取或创建
// 真实的bean对象
if (strpos($value, '.') !== 0) {
return $this->safeNewBean($value, $id);
}
// 移除第一个.字符
// Remove `.`
$value = substr($value, 1);
// Other: read config reference
if ($this->handler) {
// 调用BeanHandler的getReferenceValue方法获取值
// 实际上就是去Config的bean对象中获取配置信息
$value = $this->handler->getReferenceValue($value);
}
return $value;
}
安全获取bean方法:
private function safeNewBean(string $beanName, string $id = '')
{
try {
// 调用newBean获取或创建bean
// 这里就出现了递归
// 如果两个bean对象相互引用,此处就会导致死循环
return $this->newBean($beanName, $id);
} catch (Throwable $e) {
throw new InvalidArgumentException($e->getMessage(), 500, $e);
}
}
总结:
初始化流程:
1.遍历定义对象数组.
2.如果定义对象是request或者session类型,则转存定义对象到对应数组中保存.
3.如果是singleton或者prototype类型,则根据定义对象创建真正的bean对象,保存在容器对应的对象池中.
创建bean对象的流程:
1.检查bean是否已经存在于对应的对象池,如果存在则不再创建.
2.获取要创建的bean对象的定义对象.
3.根据定义对象中设置的类名创建反射类对象,并将该反射类对象缓存起来.
4.调用bean生命周期--初始化前回调.
5.从定义对象获取构造注入对象,再从构造注入对象中获取需要注入的构造参数.
6.检查当前要创建的bean对象的类是否有代理类,如果有,则将先前创建的类反射对象替换成代理类的类反射对象.
7.通过类反射对象和构造参数,创建该类的实例对象(这就是我们最终需要的bean对象).
8.通过定义对象获取属性注入对象数组.
9.根据将属性注入对象中的值类型,以不同方式将值赋值给实例对象.
9.1.递归遍历反射类及其所有父类.
9.2.依次遍历属性注入对象数组,找到匹配的属性.
9.3.判断值的类型,如果是普通类型,则直接进入赋值流程.
9.4.如果是引用类型(或数组中包含引用类型),则调用获取引用值的方法,获取(递归,如果这个bean对象不存在则继续调用bean对象生成方法)引用值对应的bean对象.
10.保存别名和bean名称的映射.
11.如果反射类有init方法,则调用反射实例的init方法.
12.将最终的反射实例保存在对应的对象池中,并按类型返回一个对应的对象,此处保存的反射实例就是我们最终生成的bean对象.
当然,还有一些支线细节,此小节中没有讲到的,可能后续回再做补充.比如创建bean对象流程中的:
1.第6步中涉及的代理类.
2.……
至此,bean处理器的工作已经完成.现在,我们已经可以在业务中使用bean了.