做了几年 Java 开发每天和 Spring系列框架打交道。虽然各种注解、中间件用的飞起但说实话心里挺虚的。框架帮我们屏蔽了太多细节舒服是舒服了可久而久之感觉自己像个被惯坏的孩子——离了脚手架就不会盖房了。为了治好这份“技术焦虑症”也为了满足那点该死的好奇心我决定自己动手从零开始撸一个简易版的 Spring 容器。不求功能强大只求把那些神秘的面纱亲手揭开。Spring可以通过getBean方法获取bean它是怎么做到的呢大致可以分为下面这个过程尝试从三级缓存中直接获取Bean。如果获取到要检查下是否为FactoryBean如果是就用FactoryBean创建Bean然后返回。判断父容器和dependOn如果有就递归调用getBean。本文章不实现这一点直接创建Bean。这三步复杂点在于直接创建BeanSpring通过经典的doCreateBean方法实现了这个过程 它是怎么做的呢大致是下面这样的过程尝试从缓存中获取自己创建通过createBeanInstance方法创建Bean将创建Bean的ObjectFactory放入三级缓存中便于解决循环依赖通过populateBean填充Bean的属性通过initializeBean执行Aware和BeanPostProcessor方法注册Bean的销毁方法本文章会演示构造器注入不会涉及到自动装配Autowire所以只实现前三步。其中难点在于第二步也就是创建Bean。createBeanInstance创建Bean的过程实际上是根据前面解析得到BeanDefiniton然后利用反射进行创建。有两种方式一种有参数的构造器一种是无参构造器。Spring用了大量代码来为Bean选出最合适的构造器autowireConstructor方法。Winter容器只做了简单实现。一、GetBean总逻辑public Object getBean(String beanName){ Object bean null; // 1.尝试从缓存中直接获取 Object cachedInstance getSingleton_FromCache(beanName); // 2.缓存命中则判断是否为factorybean如果是再用它创建bean if(cachedInstance ! null){ bean getObjectForBeanInstance(cachedInstance,beanName); } else{ // 获取BeanDefinition BeanDefinition bd this.beanDefinitionMap.get(beanName); // 判断是否单例当前直接加载单例 if(bd.getIsSingleton()){ bean getSingleton_ByObjectFactory(beanName, () - createBean(beanName, bd)); } } return bean; }二、CreateBeanInstance用构造器创建Beanprivate BeanWrapper createBeanInstance(String beanName, BeanDefinition bd) { BeanWrapper beanWrapper new BeanWrapper(); try { // 解析className为BeanDefinition设置beanClass Class? resolvedBeanClass bd.resolveBeanClass(ClassUtils.getDefaultClassLoader()); bd.setBeanClass(resolvedBeanClass); // 判断用哪种方式 if(CollectionUtils.isNotEmpty(bd.getArgList())){ // 构造器注入 beanWrapper autowireConstructor(beanName,bd); }else{ // 无参构造 Object instantance this.instantiationStrategy.instantiate(beanName,bd,this); beanWrapper.setWrappedObject(instantance); } } catch (ClassNotFoundException e) { throw new RuntimeException(e); } return beanWrapper; }其中有参构造方法autowireConstructor如下private BeanWrapper autowireConstructor(String beanName, BeanDefinition bd) { BeanWrapper bw new BeanWrapper(); // 要用的构造器和参数 Constructor? constructorToUse null; ListObject argToUse new ArrayList(); // 获取所有的构造器 ListConstructor? candidateConstructors Arrays.asList(bd.getBeanClass().getDeclaredConstructors()); // 构造器只有一个参数为空无参构造 if(candidateConstructors.size() 1 CollectionUtils.isEmpty(bd.getArgList())){ constructorToUse candidateConstructors.get(0); // 直接实例化,argToUse是空list Object instance this.instantiationStrategy.instantiate(bd, beanName, this, constructorToUse, argToUse); bw.setWrappedObject(instance); return bw; } //按参数排序选最多的简化逻辑了这是bd中参数也肯定是有的也包括只有一个构造函数的情况) constructorToUse candidateConstructors.stream().max(Comparator.comparingInt(Constructor::getParameterCount)).get(); // 解析arg Parameter[] params constructorToUse.getParameters(); ListConstructorArg argConfigList bd.getArgList(); for(Parameter parameter : params){ // 取出参数名一样的 ConstructorArg configArg argConfigList.stream() .filter(arg - Strings.CS.equals(parameter.getName(), arg.getName())).findFirst().get(); if(configArg.getType() ConstructorArgEnum.VALUE){ // 这里原版还要涉及类型转换这里就省略了只用String argToUse.add(configArg.getValue()); }else{ String refBeanName configArg.getValue(); Object refBean getBean(refBeanName); argToUse.add(refBean); } } // 构造器构造 Object instance this.instantiationStrategy.instantiate(bd, beanName, this, constructorToUse,argToUse); bw.setWrappedObject(instance); return bw; }三、XML配置测试这是我写Winter以来最激动的时刻因为我第一次见到Bean被放进我写的容器中。下面是xml文件的配置配置了4个Bean其中包括依赖其他Bean的OrderService。?xml version1.0 encodingUTF-8? !-- beans是所有其他标签bean、import、alias的根标签 -- beans xmlnshttp://www.springframework.org/schema/beans xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd !-- 构造器注入 -- bean idorderService classcom.hsiao.test.OrderService init-methodinitOrder constructor-arg namestockService refstockService/ constructor-arg namename valuephone-order/ /bean !-- Setter 方法注入 -- bean idstockService classcom.hsiao.test.StockService constructor-arg namename valuestock/ constructor-arg namememberService refmemberService/ /bean bean idmemberService classcom.hsiao.test.MemberService/ !-- 静态方法 -- !-- bean idmallService classcom.hsiao.test.MallServiceFactory factory-methodgetStaticMall/-- !-- 非静态方法需要先实例化Factory再调用方法-- !-- bean idmallServiceFactory classcom.hsiao.test.MallServiceFactory/-- !-- bean idmallService factory-beanmallServiceFactory factory-methodgetMall/-- !-- FactoryBean方式比上面两种方式都好-- bean idmallService classcom.hsiao.test.MallServiceFactoryBean constructor-arg namename valuename/ /bean /beans接下来在测试方法中加载这个配置文件然后通过getBean方法获取OrderService因为OrderService依赖StockService而StockService依赖memberService所以最终这3个Bean都会被注册进容器中。public class App { public static void main( String[] args ) throws DocumentException, FileNotFoundException { ClassPathResource beanConfigXml new ClassPathResource(META-INFO/bean-config.xml); WinterBeanFactory beanFactory new WinterBeanFactory(); BeanDefinitionReader beanDefinitionReader new BeanDefinitionReader(beanFactory); beanDefinitionReader.loadBeanDefinition(beanConfigXml); Object orderService beanFactory.getBean(orderService); System.out.println(haha); } }现在激动人心的时刻到了来看测试结果。这3个Bean真的被放进BeanFactory了成功了