- 作者:老汪软件技巧
- 发表时间: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处理。
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处理流程
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
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。其生命周期如下
循环依赖
简单文字描述一下,ABC三个service互相在内部autowired了对方,这里遇到一个死锁的问题。
此时abc互相阻塞,这里我们需要用到多个容器来中转bean,因为java对象在jvm里是可以先被创建后再赋值的。因此流程如下。以getBean(A)为起点。
网上都说spring的三级缓存,其第三级缓存在我的项目中被替换成了直接newInstance一个bean,这个三级缓存应该和aop的具体处理方式有关,后续在aop处还会进行研究,目前的循环依赖解决并不彻底,只是最基本的解决方案。还会再更新