[Spring系列]-IoC源码分析与手动实现-1

引言

文章针对spring3.x的实现,主要分析xml配置形式下ioc容器相关的内容。同时也会结合我自己的项目minispring做相关的源码/伪代码展示和说明,以辅助理解。


原始ioc

ioc概念

使用spring开发时会经常听到两个术语:ioc容器,bean对象。bean对象就是java中的业务对象,常见的就如xxxservice,xxxcomponent,在java开发中,想要使用这些类,我们需要通过手动new一个该类的实例,再使用这个实例进行具体的操作,如执行业务等等。

对bean的定义,bean一般是无状态的,因此没有并发问题,后面会补充

ioc容器存在的意义:框架代替我们去管理上面说的这些实例,我们不需要去考虑这些实例的生命周期,构建,属性注入等等。ioc本身的含义即:控制反转,让spring框架去控制我们的实例。 没有ioc的情况下,构建并使用一个servicebean过程如下:

 

java

代码解读

复制代码

 Aservice a = new AserviceImpl();  //如果a里有b的引用  Bservice b = new BserviceImpl();  a.setB(b);

简单的引用关系还好,如果引用关系复杂需要我们去手动管理,非常不便。且这个bean生成的时机也需要coder自己把握,总不能每次用都去new一个,coder可能需要自己实现一个repository去在服务启动的某个时机注册这些实例。

有了ioc的情况下,我们构建并使用一个bean的过程如下:

 

xml

代码解读

复制代码

 xml配置  <bean id="aservice" class="com.test.service.Aservice" init-method="init">   <property type="com.test.service.BService" name="bservice" ref="bservice"/>  </bean>  ​  Aserivce aservice = applicationContext.getBean("aservice");

这样,只需要在xml配置好我们的bean以及其依赖关系,后续使用的时候就可以从spring上下文中的ioc容器获取这个bean。后续会对xml配置的方式进行优化,如包扫描以及引入注解自动扫描依赖等等。获取bean的方式也不是每次都需要手动从ioc中拿取,一般来说在服务启动时,bean们已经被放到该放的位置,只要做到刚刚说的自动扫描依赖,就可以即拿即用。也就是常见的Autowired处理。

 ioc容器的存在,让spring框架集中管理实例,方便维护,构建简单,降低耦合度。同时基于此概念可以进行更多有意思的操作。如懒加载等等。


ioc实现

ioc实现的原理就是:jdk提供的反射机制以及工厂模式。

以xml形式引入,首先展示下xml的配置方式

 

xml

代码解读

复制代码

 <bean id="userService" class="com.test.service.UserService"/>  <bean id="reentryHandler" class="com.test.ReentryRejectedExecutionHandler"></bean>  <bean id="taskExecutor" class="com.minis.scheduling.concurrent.ThreadPoolTaskExecutor"            init-method="initializeExecutor">          <property type="int" name="corePoolSize" value="2"/>          <property type="int" name="maxPoolSize" value="4"/>  </bean>  <bean id="asyncExecutionInterceptor" class="com.minis.aop.AsyncExecutionInterceptor">          <property type="com.minis.scheduling.concurrent.ThreadPoolTaskExecutor" name="executor" ref="taskExecutor"/>  </bean>

xml配置只是一个形式,理论上来说随便怎么配都行,只要你的applicationcontext解析方案和配置的方式一样。回到xml配置,可以通过saxreader等工具对xml进行解析,并将其映射到内存中的BeanDefinition(bean定义)中,扫描完xml就得到了一个beanDefinitionList。每个BeanDefinition通过反射创建出一个实例,存入beanRepository,与其名称一一对应就是singleton-bean。这就是ioc创建bean的大致流程,实际的创建不会像上面说的这么简单,需要考虑到其他bean的注册等等因素。

组件

BeanDefinition,内含dependsOn,id,className,beanClass,propertyValues,constructorArgumentValues等

BeanFactory 提供getBean(String name)能力,提供beanDefinitionList的存储能力。

SingletonbeanRegistry 实现BeanFactory以及BeanRegistry

ClassPathXmlApplicationContext 提供对这些组件的组合,流程的运行。

xml处理流程

bean存储

实现了BeanRegistry,提供了bean的存储能力,实际上就是有几个容器,展示一下

 

java

代码解读

复制代码

     protected List<String> beanNames=new ArrayList<>();      protected Map<String, Object> singletonObjects =new ConcurrentHashMap<>(256);      protected Map<String,Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);      protected Map<String,Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);

beanFactory在后续getBean时会有registryBean的操作,往beanRegistry里的容器放我们的bean。

refresh

直接贴代码加注释,核心是onRefresh方法,真正开始进行bean的实例化,其余的几个方法会提前实例化一些工具bean,例如beanpostprocessor系的实现。onRefresh会进入beanFactory的refresh方法,即对beanDefinitions进行遍历,同时getBean()

 

java

代码解读

复制代码

  public void refresh() throws BeansException, IllegalStateException {   //bean factory处理器   postProcessBeanFactory(getBeanFactory());   //注册bean处理器,有初始化前和初始化后两个节点插入   registerBeanPostProcessors(getBeanFactory());   //初始化事件发布者   initApplicationEventPublisher();   //开始refresh   onRefresh();   //注册listener   registerListeners();   //bean处理完后通知listener   finishRefresh();   }

getBean

真正实例化bean的方法,autowired、循环依赖处理、aop都与此方法有关联,简单展示一下核心流程。

 

java

代码解读

复制代码

 //获取bd  BeanDefinition bd = beanDefinitionMap.get(beanName);  //创建实例,涉及到createBean和doCreateBean  singleton = createBean(bd);  //注册一下,主要是在map里记录beanname与beanobject的关系  this.registerBean(beanName, singleton);  //bean前置处理器,后续会用到  singleton = applyBeanPostProcessorsBeforeInitialization(singleton, beanName);  //init  invokeInitMethod(bd, singleton);  //后置处理器  applyBeanPostProcessorsAfterInitialization(singleton, beanName);

createBean&doCreateBean
 

java

代码解读

复制代码

 Object obj = doCreateBean(bd);  earlySingletonObjects.put(bd.getId(), obj);  clz = Class.forName(bd.getClassName());  //将属性值注入到bean的初始object中,实际调用handlerProperties方法  populateBean(bd, clz, obj);  ​  doCreateBean()  Class<?>[] paramTypes = new Class<?>[argumentValues.getArgumentCount()];  Object[] paramValues = new Object[argumentValues.getArgumentCount()];  con = clz.getConstructor(paramTypes);  obj = con.newInstance(paramValues);  return obj;

至此,便得到了一个singleton,完成了ioc容器的初始化。中间涉及到autowired的实现,循环依赖的处理后面会继续讲,aop这边简单带过

aop
 

java

代码解读

复制代码

 //此处会把我们的singleton从beanpostprocessor里走一圈,aop可能会对其做一层factorybean的封装,让其有代理能力  singleton = applyBeanPostProcessorsBeforeInitialization(singleton, beanName);  ​  //此处会进入aop的获取代理bean方法,得到的结果就是被封装上切面的代理bean。  if (singleton instanceof FactoryBean)      return this.getObjectForBeanInstance(singleton, beanName);


ioc增强

ioc增强是在其存储,管理bean的基础上,添加一些便于开发者的特性,简单列举几点我的项目中实现的:

  1. bean之间互相引用(简单来说就是aservice里autowired了bservice)
  2. beanPostProcessor(autowired机制,以及后续的mvc和aop都会利用到)
  3. 解决循环依赖问题

在实现这些特性的同时,spring构建了一套结构完善的,抽象的Factory体系。

autowired

autowired注解是实际使用spring中用到最多的一个功能,autowired的使用场景就是我们在一个component中去autowried另一个component,这样服务启动之后,service之间能够正常互相调用。

之前不了解spring原理时有个误区,autowired就是为了spring去做ioc而创造的注解,实际上不是。autowired主要目的跟字面意思差不多,去自动的为某个bean注入另一个bean,避免我们在xml中进行繁琐的配置。

理解autowired需要重点关注的几个点:

  1. autowired的介入时机
  2. autowired具体处理的方式

autowired的介入时机是在createBean之后,此时singleton里已经有了基本的属性值等,这时我们会进入applyBeanPostProcessorsBeforeInitialization的流程,里面会遍历到AutowiredAnnotationBeanPostProcessor 这个processor,这个就是autowired介入的时机,同时里面也包含autowired的处理流程。

autowired处理原理很简单,就是通过反射拉取这个obj的所有field,判断是否有autowired注解,有的话就对其进行getbean,然后注入属性。注意这里的getBean会导致我们递归的寻找引用的其他bean。也是后面循环依赖发生的原因。下面贴一点代码

 

java

代码解读

复制代码

 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {   Object result = bean;   Class<?> clazz = bean.getClass();   Field[] fields = clazz.getDeclaredFields();   if(fields!=null){   for(Field field : fields){   //判断是否被autowired注解修饰的类属性   boolean isAutowired = field.isAnnotationPresent(Autowired.class);   if(isAutowired){   String fieldName = field.getName();                  //出发其他bean的获取   Object autowiredObj = this.getBeanFactory().getBean(fieldName);   field.setAccessible(true);   field.set(bean, autowiredObj);             }         }     }   return result;  }

BeanPostProcessor

上面autowired用到的一个机制,以及ioc容器的refresh里的一个流程提到的机制,即beanPostProcessor。beanPostProcessor用于协助我们在不同的时间点去对bean做一些自定义操作。

BeanPostProcessor主要提供了两种使用方法:postProcessBeforeInitialization以及postProcessAfterInitialization。其生命周期如下

循环依赖

简单文字描述一下,ABC三个service互相在内部autowired了对方,这里遇到一个死锁的问题。

 此时abc互相阻塞,这里我们需要用到多个容器来中转bean,因为java对象在jvm里是可以先被创建后再赋值的。因此流程如下。以getBean(A)为起点。

 网上都说spring的三级缓存,其第三级缓存在我的项目中被替换成了直接newInstance一个bean,这个三级缓存应该和aop的具体处理方式有关,后续在aop处还会进行研究,目前的循环依赖解决并不彻底,只是最基本的解决方案。还会再更新

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值