• 作者:老汪软件技巧
  • 发表时间:2024-12-29 17:04
  • 浏览量:

引言

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

原始iocioc概念

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

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

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

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

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

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

 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处理。

图片.png

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

ioc实现

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

以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处理流程

图片.png

bean存储

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

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

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

refresh

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

     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都与此方法有关联,简单展示一下核心流程。

 //获取bd
 BeanDefinition bd = beanDefinitionMap.get(beanName);
 //创建实例,涉及到createBean和doCreateBean

spring源码分析_spring5源码分析_

 singleton = createBean(bd);  //注册一下,主要是在map里记录beanname与beanobject的关系  this.registerBean(beanName, singleton);  //bean前置处理器,后续会用到  singleton = applyBeanPostProcessorsBeforeInitialization(singleton, beanName);  //init  invokeInitMethod(bd, singleton);  //后置处理器  applyBeanPostProcessorsAfterInitialization(singleton, beanName);

createBean&doCreateBean

 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

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

ioc增强

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

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

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

autowired

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

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

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

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

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

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

 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。其生命周期如下

图片.png

循环依赖

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

图片.png

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

图片.png

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