转载请联系bugstack公众号。目录1.前言2.目标3.设计4.实现1.项目结构2.定义属性3.Bean定义完成4.Bean属性填充5.测试1.事前准备2.测试用例3.测试结果6.总结七、系列推荐1、前言超卖、掉单、幂等,你的程序总是让人无法抗拒!想一想,运营宣传活动已经七八天了,我们兴高采烈地等着页面上线的最后一天,突然出现了一堆异常,资金流失,闪退,用户流量被稍纵即逝,到头来一死了之!就编程开发而言,可能是大部分初级程序员日常开发的真实写照,即使有测试人员去验证,也会有bug上线的现象,只是当时没有发现!因为人写代码,肯定有错误,即使是老coder就程序bug而言,会包括产品PRD过程中的bug,运行配置活动中的bug,研发开发过程中功能实现的bug,遗漏的bug测试和验证过程中的流程,以及上线过程中运维服务相关配置的错误。其实这些都是可以通过工艺规范的建立和一定的研发经验的积累,逐步减少的。另一类是沟通留下的bug。通常是提出业务需求,确定产品方案,实施研发。最终,UI、测试、运营、架构等各个环节的人员都必须参与到一个项目的承接中。从开发到线上运营,这群人其实很难保持统一的信息传播。比如在项目开发的中期,运营对产品说了一个新的需求。产品觉得功能不大,然后找了对应的前端研发,加了逻辑,没想到,还可能影响到后端开发测试的用例。虽然最终功能上线了,但并不在整个产研测试需求的覆盖范围内,无形中埋下了一个坑。所以,如果你想让你的程序非常耐打,能够接住农夫的三拳,那么你要做的可不仅仅是一个简单的实体coder!2.目标首先我们回顾一下这几章都完成了什么,包括:实现一个容器,定义并注册bean,实例化bean,根据是否包含构造函数实现不同的实例化策略,那么我们在创建对象实例化的时候漏掉了什么?其实类中是否存在属性还缺少一个属性问题,如果一个类包含属性,那么在实例化的时候需要填写属性信息,这样才能创建一个完整的对象。属性的填充不只是int、Long、String,还有还没有实例化的对象属性,都需要在创建Bean的时候填充。不过这里我们暂时先不考虑Bean的循环依赖,否则整个功能会被放大,让新人在学习的时候难以掌握。后续核心功能实现后,我们会逐步完善。3.基于属性填充的设计使用newInstance或者Cglib创建Bean后,开始补全属性信息,然后可以在类AbstractAutowireCapableBeanFactory的createBean方法中添加属性补全方法。这部分也可以在实习期间学习Spring源码。这里的实现也是Spring的简化版。后续对比学习会更容易理解。属性填充必须在类实例化之后进行,即在AbstractAutowireCapableBeanFactory的createBean方法中添加applyPropertyValues动作。由于我们在创建bean的时候需要填写属性操作,所以我们需要在bean定义BeanDefinition类中添加PropertyValues信息。另外,填充属性信息还包括Bean的对象类型,即需要定义一个BeanReference,其实就是一个简单的Bean名称,在具体实例化操作时递归创建并填充,即与Spring源码实现相同。Spring源码中的BeanReference是一个接口4.实现1.项目结构small-spring-step-04└──src├──main│└──java│└──cn.bugstack.springframework.beans│├──.java│└└──SimpleInstantiationStrategy.java│└──BeanFactory.java│├──BeansException.java│├──PropertyValue.java│└──PropertyValues.java└──test└──java└────cn.bugstack.springframework.test├──bean│├──UserDao.java│└──UserService.java└──ApiTest.java项目源码:公众号《bugstack虫洞栈》,回复:Spring专栏,获取完整源码SpringBean容器类关系,如图5-2图5-2本章需要新增三个类,BeanReference(类引用),PropertyValue(属性值),PropertyValues(属性集合),分别用于类和其他类型的属性填充操作。另外,变化的类主要是AbstractAutowireCapableBeanFactory,完成createBean中的属性填充部分。2.定义属性cn.bugstack.springframework.beans.PropertyValuepublicclassPropertyValue{privatefinalStringname;privatefinalObjectvalue;publicPropertyValue(Stringname,Objectvalue){this.name=name;this.value=value;}//...get/set}cn。bugstack.springframework.beans.PropertyValuespublicclassPropertyValues{privatefinalListpropertyValueList=newArrayList<>();publicvoidaddPropertyValue(PropertyValuepv){this.propertyValueList.add(pv);}publicPropertyValue[]getPropertyValues(){returnthisList.propertyValue0(new]);}publicPropertyValuegetPropertyValue(StringpropertyName){for(PropertyValuepv:this.propertyValueList){if(pv).getName().equals(propertyName)){returnpv;}}returnnull;}}这两个类的作用是创建一个类中用来传递属性信息的类,因为可能有很多属性,它也是需要定义一个集合包。3.Bean定义完成cn.bugstack.springframework.beans.factory.config.BeanDefinitionpublicclassBeanDefinition{privateClassbeanClass;privatePropertyValuespropertyValues;publicBeanDefinition(ClassbeanClass){this.beanClass=beanClass;this.propertyValues=newPropertyValues();}publicBeanDefinition(ClassbeanDefinitionValues();}publicBeanDefinition(ClassbeanDefinition){this.beanClass=beanClass;this.propertyValues=propertyValues!=null?propertyValues:newPropertyValues();}//...get/set}在Bean注册的过程中,需要传递Bean信息,在前面的几个章节中NewBeanDefinition(UserService.class,propertyValues)在newBeanDefinition(UserService.class,propertyValues)的测试中都有体现;所以为了把属性交给Bean定义,填充了PropertyValues属性这里,并对两个构造函数做了一些简单的优化,避免后面for循环的时候,需要判断属性填充是否为emptyy。4.Bean属性填充cn.bugstack.springframework.beans.factory.support.AbstractAutowireCapableBeanFactorypublicabstractclassAbstractAutowireCapableBeanFactoryextendsAbstractBeanFactory{privateInstantiationStrategyinstantiationStrategy=newCglibSubclassingInstantiationStrategy();@OverrideprotectedObjectcreateBean(StringbeanName,BeanDefinitionbeanDefinition,Object[]args)throwsBeansException{Objectbean=null;try{bean=createBeanInstance(beanDefinition,beanName,args);//给Bean填充属性applyPropertyValues(beanName,bean,beanDefinition);}catch(Exceptione){thrownewBeansException("Instantiationofbeanfailed",e);}addSingleton(beanName,bean);returnbean;}protectedObjectcreateBeanInstance(BeanDefinitionbeanDefinition),StringbeanName,Object[]args){ConstructorconstructorToUse=null;Class>beanClass=beanDefinition.getBeanClass();Constructor>[]declaredConstructors=beanClass.getDeclaredConstructors();for(Constructorctor:declaredConstructors){if(null!=参数&&ctor.getParameterTypes().length==args.length){constructorToUse=ctor;break;}}returngetInstantiationStrategy().instantiate(beanDefinition,beanName,constructorToUse,args);}/***Bean属性填充*/protectedvoidapplyPropertyValues(StringbeanName,Objectbean,BeanDefinitionbeanDefinition){try{PropertyValuespropertyValues=beanDefinition.getPropertyValues();for(PropertyValuepropertyValue:propertyValues.getPropertyValues()){Stringname=propertyValue.getName();Objectvalue=propertyValue.getValue();if(valueinstanceofBeanReference){//A依靠B,获取B的实例化BeanReferencebeanReference=(BeanReference)value;value=getBean(beanReference.getBeanName());}//属性填充BeanUtil.setFieldValue(bean,name,value);}}catch(Exceptione){thrownewBeansException("Errorsettingpropertyvalues:"+beanName);}}publicInstantiationStrategygetInstantiationStrategy(){returninstantiationStrategy;}publicvoidsetInstantiationStrategy(InstantiationStrategyinstantiationStrategy){this.instantiationStrategy=instantiationStrategy;}}这个类的内容有点长,主要包括三个方法:createBean、createBeanInstance、applyPropertyValues,这里我们主要关注createBean方法中调用的applyPropertyValues方法。填充操作,如果遇到BeanReference,那么就需要递归获取Bean实例,调用getBean方法。当依赖的Bean对象被创建时,会递归返回属性填充。这里需要注意的是,我们还没有处理循环依赖的问题。这部分比较大,后面会补充。BeanUtil.setFieldValue(bean,name,value)是hutool-all工具类中的一个方法,你也可以自己实现5.测试1.提前准备cn.bugstack.springframework.test.bean.UserDaopublicclassUserDao{privatestaticMaphashMap=newHashMap<>();static{hashMap.put("10001","小傅");hashMap.put("10002","八杯水");hashMap.put("10003""阿毛");}publicStringqueryUserName(StringuId){returnhashMap.get(uId);}}cn.bugstack.springframework.test.bean.UserServicepublicclassUserService{privateStringuId;privateUserDaouserDao;publicvoidqueryUserInfo(){System.out.println("Queryuserinformation:"+userDao.queryUserName(uId));}//...get/set}Dao和Service是我们平时开发中经常用到的场景。在UserService中注入UserDao,这样Bean属性的依赖就可以体现出来。2.测试用例@Testpublicvoidtest_BeanFactory(){//1。初始化BeanFactoryDe??faultListableBeanFactorybeanFactory=newDefaultListableBeanFactory();//2.UserDao注册beanFactory.registerBeanDefinition("userDao",newBeanDefinition(UserDao.class));//3.UserService设置属性[uId,userDao]PropertyValuespropertyValues=newPropertyValues();propertyValues.addPropertyValue(newPropertyValue("uId","10001"));propertyValues.addPropertyValue(newPropertyValue("userDao",newBeanReference("userDao")));//4.UserService注入beanBeanDefinitionbeanDefinition=newBeanDefinition(UserService.class,propertyValues);beanFactory.registerBeanDefinition("userService",beanDefinition);//5.UserService获取beanUserServiceuserService=(UserService)beanFactory.getBean("userService");userService.queryUserInfo();}与直接获取Bean对象不同的是,这次我们还需要首先将userDao注入到Bean容器中。beanFactory.registerBeanDefinition("userDao",newBeanDefinition(UserDao.class));接下来是属性填充的操作,一个是普通属性newPropertyValue("uId","10001"),一个是对象属性newPropertyValue("userDao",newBeanReference("userDao"))下一个操作很简单,正常获取userService对象,调用方法即可。3、查询用户信息的测试结果:小傅进程结束,退出码为0从测试结果可以看出,我们的属性填充成功了,因为只有属性填充完成后,才能调用Dao方法,如:userDao.queryUserName(uId)然后我们在Debug调试的情况下,看看是否进入了实现的Bean属性填充,如下:要成为BeanReference,需要获取创建的Bean实例。6.小结本章我们扩展了AbstractAutowireCapableBeanFactory类中的对象创建功能。依赖是否有构造函数的实例化策略完成后,我们开始补充Bean的属性信息。当Bean属性为Bean对象时,需要递归处理。最后,属性填充时需要反射操作,也可以使用一些工具类进行处理。我们正在一步步实现每一章的功能点,让新人更好的接受Spring中的设计思想。尤其是在一些已经开发好的类中,如何扩展新功能的设计更为重要。有时候学习编程比做简单的实现更能提升编程思维。本章完成了Bean创建操作的开发。接下来我们要在整个框架的基础上完成资源属性的加载,也就是要把Xml配置搬过来,让我们的小框架越来越像Spring。另外,在框架实现的过程中,所有的类名都会引用Spring源码,相应的设计实现步骤也对应Spring源码,但是会简化一些流程,但是可以使用同一个类name来搜索每个Spring源代码中函数的实现。