Spring IOC原理

容器初始化

容器的初始化首先是在对应的构造器中进行,在applicationContext的实现类构造器中,首先对参数路径中的${}进行了处理,用系统变量替换(setConfigLocations方法)然后调用refresh方法(这个就是最核心的容器初始化方法)。

01

1、Resource定位:

在refresh方法调用obtainFreshbeanfactory方法告诉子类刷新beanfactory(其中是调用refreshbeanfactory刷新后getbeanfactory获取刷新后的factory返回)。在刷新过程refreshbeanfactory中如果factory已经有了要消除再新建factory,其中loadBeanDeFinitions是加载bean定义的方法

在loadBeanDeFinitions方法中创建了BeanDeFinitionReader的实现类调用其loadBeanDeFinitions方法(这个方法是重载方法,参数有为Resource的也有为String路径的,getConfigResources方法(认返回null,子类重写,如ClasspathXmlApplicationContext类)和getConfigLocations方法获得Resource集合和资源路径集合(一般一个为空,一般是将容器的参数path设定为configLocations,ClasspathXmlApplicationContext有一种构造器是不设定configLocations而是直接用参数path生成ClassPathResource集合设定为configResources)分别进行load,实际上以路径为参数的重载方法在定位完Resource也会调用以resource为参数的loadBeanDeFinitions来解析载入BeanDeFinition,这个是第二步在下面介绍)。

在BeanDeFinitionReader的loadBeanDeFinitions(path参数)方法中根据ResourceLoader类型以两种方式加载(如果是ant正则表达式方式的(如PathMatchingResourcePatternResolver)一个路径定位多个resource或者认方式(applicationContext继承的是DefaultResourceLoader实现方式)定位一个resource),分别调用ResourceLoader的getResource(以/开头的构建ClasspathContextResource,以classpath开头的去掉classpath构建ClassPathResource,如果都不是的尝试构建UrlResource,如果构建失败就调用getResourceByPath这个具体applicationContext实现类里重写的方法构建特定Resource,如FileSystemXmlApplicationContext就是FileSystemResource)或getResources(PathMatchingResourcePatternResolver的正则方式这里不详细描述)完成Resource定位。

2、从Resource中解析和载入BeanDeFinition:

同样在BeanDeFinitionReader的loadBeanDeFinitions中调用完resourceLoader的getResource获取Resource后将resource作为参数调用自己(BeanDeFinitionReader)的loadBeanDeFinitions(是一个接口方法给子类实现,因为不同的reader加载resource的方式不同)载入BeanDeFinition。

例如XmlBeanDeFinitionReader是对XML文件的IO操作,(将现在要处理的Resource加入当前线程正在处理(ThreadLocal)的Resource集合中)首先从resource中拿出InputStream封装成InputSource调用自身的doLoadBeanDeFinitions方法

doLoadBeanDeFinitions方法调用doLoadDocument方法封装成Document-----是用validationMode(认是自动校验方式,意思是如果没有显示定义校验的方式就用XSD方式)和DocumentLoader(XmlBeanDeFinitionReader中认的是DefaultDocumentLoader)等参数调用DocumentLoader的loadDocument方法将Resource封装成Document类(具体封装方式不做介绍,有兴趣的可以自己了解一下,用的是builder模式做的)调用registerBeanDeFinitions方法解析载入bean。

registerBeanDeFinitions方法是用BeanDeFinitionDocumentReader的registerBeanDeFinitions具体解析Document(树形结构,从root(就是beans标签)开始往下解析)中每个element各个标签的解析和载入。其中如果是bean标签BeabDeFinitionParserDelegate的parseBeanDeFinitionElement方法对XML元素的信息按照spring的bean的规则进行解析(property的解析,当中value和ref解析方式不同,如果是value构建TypedStringValue,如果ref的话构建RuntimeBeanReference,这个在之后依赖注入的时候用到,还有id,name,等属性的解析)得到的BeanDeFinition的封装BeanDeFinitionHolder(包括BeanDeFinition,beanName(这里是标识符的意思,如果有id,id做标识符,没有id,name属性中第一个别名做标识符)和别名列表(name属性中的内容,如果没有id,name中第一个不作为别名而是标识符))来进行下一步bean的注册(BeanDeFinitionReaderUtils.registerBeanDeFinition)。

其他如import,alias等标签自行看源码理解。

3、BeanDeFinition在IOC容器的注册

BeanDeFinitionReaderUtils.registerBeanDeFinition用BeanDeFinitionRegistry(DefaultListablebeanfactory)的registerBeanDeFinition方法注册beanName和BeanDeFinition(就是把beanName加入到一个已经注册的bean的beanName的Set中,然后put到beanName对应BeanDeFinition的map中,其中如果不允许覆盖并且有同名beanName要报错)。再用BeanDeFinitionRegistry的registeralias方法注册beanName和别名列表(put到一个beanName对应alias的map中,其中如果有alias跟beanName相同的要移除)。

IOC容器依赖注入

02

1、getBean

第一次调用lazy-init的bean是以beanfactory的getBean方法为入口触发的(实现在Abstractbeanfactory实现类中)。如果是单例会缓存起来只加载一次,如果是factorybean这种特殊的bean会把这个bean的实例传入getobjectForBeanInstance方法获得factorybean产生的bean(调用factorybean的getobject方法,这就是自定义factorybean要重写的方法,AOP也是这个原理,详情见下方AOP分析)。在第一次载入时要先判断这个BeanDeFinition在当前beanfactory有没有,没有就从双亲beanfactory中找,一直递归。

找到后要验证是否存在递归依赖,有则报错无则设置当前bean依赖bean的依赖关系到两个map中(一个是被依赖map,一个是依赖map),其中:

  1. 如果是单例第一次载入就调用getSingleton方法(方法中回调了参数中ObjectFactory的getobject方法,这里重写了这个方法调用createBean)获得实例用getobjectForBeanInstance获得factorybean产生的bean(如果它是factorybean的话)。
  2. 如果是prototype加载调用createBean后调用getobjectForBeanInstance。
  3. 如果是其他scope类型:request、session和global session,这三种就用scope.get获取实例(和getSingleton类似回调重写的getobject也就是调用createBean)后调用getobjectForBeanInstance。
  4. 最后如果getBean指定了requiredType要检验获取的bean能不能转化成指定的类型不能的话就报错。

createBean方法就是生成bean的方法并对一些比如init-method属性、后置处理器等一些初始化进行了处理。方法中在实例化之前判断是否有post-processor,如果有这样的processor则短路指定bean的创建,直接返回一个proxy而不是指定的bean(这种processor可以指定生成一个其他类型的对象)没有的话用doCreateBean创建bean返回。

doCreateBean是用createBeanInstance生成BeanWrapper(包装bean)之后用populateBean向其中的bean完成依赖bean的注入(autowire等)。

createBeanInstance创建beanWrapper时分三类进行处理:

  1. 如果有工厂方法调用instantiateUsingFactoryMethod创建。
  2. 如果是构造器注入的方式调用autowireConstructor。
  3. 简单方式调用instantiateBean。调用的是策略类(认SimpleInstantiationStrategy)的instantiate而其中又是通过bean方法是否有跟IOC容器同名的(会被覆盖)来分两类处理(没同名方法的从BeanDeFinition中拿出class直接用jdk的反射拿构造器来newinstance一个实例,如果有同名的则是用cglib的方式来new一个实例)。

populateBean为生成的bean依赖注入,先对非简单类型属性有autowire的进行处理,判断这个属性在之前解析载入beanDeFinition时property里有没有,有的话进行getBean初始化后放入PropertyValue集合中(这个就是propertyname和value的封装),然后更新依赖map,再对非autowire的或一般属性进行注入,有要转化的要经过valueResolver的转化(如果是RuntimeBeanReference之前载入时XML中配置是ref的就getBean(如果在双亲beanfactory中就从双亲中取)获得后也放到PropertyValue集合中,也要更新依赖map)。最后再注入到bean中,这里说的注入其实真实发生在最后的BeanWraper的setPropertyValue(propertyValue集合)方法,具体实现就是通过反射的方式获得setter方法赋值。

2、lazy-init==false初始化(只对singleton,也是认方式)

在refresh方法中的finishbeanfactoryInitialization方法中进行初始化(实际也是调用getBean方法)。

相关文章

HashMap是Java中最常用的集合类框架,也是Java语言中非常典型...
在EffectiveJava中的第 36条中建议 用 EnumSet 替代位字段,...
介绍 注解是JDK1.5版本开始引入的一个特性,用于对代码进行说...
介绍 LinkedList同时实现了List接口和Deque接口,也就是说它...
介绍 TreeSet和TreeMap在Java里有着相同的实现,前者仅仅是对...
HashMap为什么线程不安全 put的不安全 由于多线程对HashMap进...