当前位置: 首页 > 后端技术 > PHP

从bin-swoft入手,阅读Swoft框架源码(6--3)--BeanProcessor之bean初始化

时间:2023-03-30 04:27:04 PHP

通过前面两节的工作,已经在容器中保存了完整的bean定义对象和名称别名映射。本章的工作是梳理swoft是如何使用bean定义对象来生成最终可以供业务使用的bean对象的。初始化bean入口方法:privatefunctioninitializeBeans():void{//遍历保存的定义对象数组,获取bean名称和定义对象/*@varObjectDefinition$objectDefinition*/foreach($this->objectDefinitionsas$beanName=>$objectDefinition){//获取定义对象的类型$scope=$objectDefinition->getScope();//如果是请求类型//排除请求if($scope===Bean::REQUEST){//将定义的对象传递给请求定义对象数组//从全局定义对象数组中删除这个定义对象unset($this->objectDefinitions[$beanName]);//跳过继续;}//如果类型是session//排除sessionif($scope===Bean::SESSION){//将定义对象转储到session定义对象数组$this->sessionDefinitions[$beanName]=$objectDefinition;//从全局定义对象数组中移除这个定义对象取消设置($this->objectDefinitions[$beanName]);//跳过继续;}//创建bean对象//新bean$this->newBean($beanName);}}创建bean对象:privatefunctionnewBean(string$beanName,string$id=''){//首先检查bean是否已经存在,存在直接返回bean//首先检查bean是否已经存在创造。if(isset($this->singletonPool[$beanName])||isset($this->prototypePool[$beanName])){返回$this->get($beanName);}//获取定义对象//获取对象定义$objectDefinition=$this->getNewObjectDefinition($beanName);//获取类型$scope=$objectDefinition->getScope();//获取别名$alias=$objectDefinition->getAlias();//缓存反射类信息//获取定义对象保存的类名$className=$objectDefinition->getClassName();//创建一个新的类反射对象或者从缓存中取出该类的反射对象$reflectClass=Reflections::cache($className);//初始化前调用回调//初始化前bean$this->beforeInit($beanName,$className,$objectDefinition);//构造参数$constructArgs=[];//获取构造注入对象$constructInject=$objectDefinition->getConstructorInjection();//如果存在if($constructInject!==null){//获取构造参数$constructArgs=$this->getConstructParams($constructInject,$id);}//如果有代理类,将原来的类反射对象替换为代理类反射对象的类//就是代理类。例如:AOP、RPC客户端类if($this->handler){$oldCName=$className;$className=$this->handler->classProxy($className);//来自handler->classProxy()的新类名if($oldCName!==$className){$reflectClass=newReflectionClass($className);}}//传入反射类和构造参数,创建反射实例$reflectObject=$this->newInstance($reflectClass,$constructArgs);//获取需要注入的属性$propertyInjects=$objectDefinition->getPropertyInjections();//递归从类的顶层父类开始,依次遍历属性注入数组,找到需要注入的属性属性,然后给属性赋值//注入属性值$this->newProperty($reflectObject,$reflectClass,$propertyInjects,$id);//别名//Fix:$aliasId!==$idfordenyloopget//设置bean的别名if($alias&&$alias!==$beanName){$this->aliases[$alias]=$豆名;}//如果反射类有init方法//如果存在则调用init方法if($reflectClass->hasMethod(self::INIT_METHOD)){//调用反射实例的init方法$reflectObject->{self::INIT_METHOD}();}//将反射实例保存在对应类型的bean名称的对象数组中//并根据类型返回对应的实例return$this->setNewBean($beanName,$scope,$reflectObject,$id);}保存新bean对象的方法,4种类型分别存放在4个数组中:privatefunctionsetNewBean(string$beanName,string$scope,$object,string$id=''){switch($scope){caseBean::SINGLETON://单例$this->singletonPool[$beanName]=$object;休息;案例Bean::PROTOTYPE:$this->prototypePool[$beanName]=$对象;//克隆它//将返回值设置为克隆的对象$object=clone$object;休息;caseBean::REQUEST://这里的ID应该是请求的协程ID$this->requestPool[$id][$beanName]=$object;休息;caseBean::SESSION://这里的ID应该是sessionID$this->sessionPool[$id][$beanName]=$object;休息;}return$object;}属性注入方法实现:privatefunctionnewProperty($reflectObject,ReflectionClass$reflectionClass,array$propertyInjects,string$id=''):void{//获取反射类的父反射类对象//新的父属性$parentClass=$reflectionClass->getParentClass();//如果有父类,继续递归,直到找到最上面的父类if($parentClass!==false){$this->newProperty($reflectObject,$parentClass,$propertyInjects,$id);}//遍历属性注入对象数组/*@varPropertyInjection$propertyInject*/foreach($propertyInjectsas$propertyInject){//get获取属性注入对象标识符的属性名$propertyName=$propertyInject->getPropertyName();//如果当前类中没有这个属性名,则跳过后面的注入逻辑if(!$reflectionClass->hasProperty($propertyName)){continue;}/**@noinspectionPhpUnhandledExceptionInspection*///从反射类中获取对应的反射属性对象$reflectProperty=$reflectionClass->getProperty($propertyName);//如果是静态类型,则抛出异常}//获取属性注入对象中保存的值//解析属性值$propertyValue=$propertyInject->getValue();//如果值是一个字符串并且有一个以该值命名的接口//注入接口}elseif(is_array($propertyValue)){//如果是数组,并且数组中存在引用类型//则将其替换成$propertyValue=$this->newPropertyArray($propertyValue,$id);}//引用配置或beanif($propertyInject->isRef()){$propertyValue=$this->getRefValue($propertyValue,$id);//优化:值不存在,跳过调用setterif($propertyValue===null){continue;}}//解析器属性类型$propertyType=ObjectHelper::getPropertyBaseType($reflectProperty);如果(!empty($propertyType)){$propertyValue=ObjectHelper::parseParamType($propertyType,$propertyValue);}//首先,尝试通过setter方法设置值$setter='set'。ucfirst($属性名称);如果(method_exists($reflectObject,$setter)){$reflectObject->$setter($propertyValue);继续;}如果(!$reflectProperty->isPublic()){$reflectProperty->setAccessible(true);}//通过反射设置值$reflectProperty->setValue($reflectObject,$propertyValue);}}数组参数处理方法:privatefunctionnewPropertyArray(array$propertyValue,string$id=''):array{//遍历属性值数组foreach($propertyValueas$proKey=>&$proValue){//如果属性值是一个注入对象,是一个引用类型if($proValueinstanceofArgsInjection&&$proValue->isRef()){//获取引用值//将引用的值解析为真正的bean对象,并替换原数组中的值$proValue=$this->getRefValue($refValue,$id);}}//返回替换值数组return$propertyValue;}通过引用获取真实bean对象的方法:privatefunctiongetRefValue($value,string$id=''){//如果引用不是字符串,直接返回值if(!is_string($value)){return$value;}//如果值的第一个字符不是.(.这个字符在配置中使用)//调用safeNewBean来获取或创建//真正的bean对象if(strpos($value,'.')!==0){return$this->safeNewBean($value,$id);}//删除第一个.字符//删除`.`$value=substr($value,1);//其他:读取config引用if($this->handler){//调用BeanHandler的getReferenceValue方法获取value//其实就是去Config的bean对象中获取配置信息$value=$this->处理程序->getReferenceValue($value);}返回值;}安全获取bean方法:privatefunctionsafeNewBean(string$beanName,string$id=''){try{//调用newBean获取或创建bean//这里有递归//如果两个bean对象相互引用,这里将导致无限循环return$this->newBean($beanName,$id);}catch(Throwable$e){thrownewInvalidArgumentException($e->getMessage(),500,$e);}}总结:初始化过程:1.遍历定义对象数组。2.如果定义对象是request或者session类型,则将定义对象dump到对应的数组中存储。3、如果是单例或者原型类型,根据定义的对象创建一个真正的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已经可以在业务中使用了。