引言
文章针对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的基础上,添加一些便于开发者的特性,简单列举几点我的项目中实现的:
- 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。也是后面循环依赖发生的原因。下面贴一点代码
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处还会进行研究,目前的循环依赖解决并不彻底,只是最基本的解决方案。还会再更新