Spring IOC容器源码剖析

一,IOC容器初始化入口


Spring的容器的启动方式有很多种,上篇博文中有做过分析,Spring容器的初始化根本还是ApplicationContext的实例化过程,表现形式的多样化决定了容器不同的初始化方式,但是方式差异性主要还是体现在Resource对象的构造上。原生Spring框架接触时,多半也是从Xml配置文件读取去初始化的,所以代码分析选择从ClassPathXmlApplicationContext 去入手。

 上面这段代码ClassPathXmlApplicationContext根据相对路径去实例化了一个ApplicationContext对象。

/**
	 * Create a new ClassPathXmlApplicationContext, loading the definitions
	 * from the given XML file and automatically refreshing the context.
	 * @param configLocation resource location
	 * @throws BeansException if context creation failed
	 */
	public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
		this(new String[] {configLocation}, true, null);
	}

 然后通过Debug模式,去执行下面的getBean操作,会发现在线程中,前面的实例化之后会调用一个Refresh()方法,而这个方法则是IOC容器初始化的重要方法,然后继续往下走则会调用该类的无参构造。

 二,refresh方法解析


refresh方法中的执行步骤,基本上也就是IOC容器初始化注入对象的步骤,也可以理解为IOC容器的生命周期。

可以看到refresh方法中的所有步骤都存放在一个同步代码块中,这样子做的目的在于,防止重复的加载容器给服务器增加压力,在准备工作中会对之前如果存在的BeanFactory进行销毁,这个后面剖析会说到,在同步块中执行有效的去防止同步初始化IOC容器导致的线程安全问题。

1,prepareRefresh()方法

该方法为BeanFactory,Bean注册注入前置准备方法,话不多说直接点进去看源码。

可以看到里面主要做了两件事,对容器的状态进行调整,Spring配置文件格式校验。格式校验的代码不做过深的剖析,有兴趣的可以下载源码了点进去看看。

 2,obtainFreshBeanFactory()方法

该方法执行后会返回一个实例化的BeanFactory工厂类,底层将配置注入类注册到实例化的工厂对象中待实例化。点进这个方法进去看源码,根据方法名称顾名思义,对是一个工厂类的刷新方法。继续点进去到refreshBeanFactory方法。

☆ refreshBeanFactory方法

这是一个重要的方法,第一步上面有提到,在实例化BeanFactory的时候,回去查看是否存在旧的容器存在,如果存在则删除,并发情况下,删除了容器上次的操作却还在执行,会导致线程不安全性,所以用到同步代码块。

@Override
	protected final void refreshBeanFactory() throws BeansException {
		//如果存在旧的容器则销毁
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
			//创建返回一个BeanFactory对象
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			//工厂对象ID  < className @ 随机数 >
			beanFactory.setSerializationId(getId());
			//参数设置
			customizeBeanFactory(beanFactory);
			//加载BeanDefintion到工厂中
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}

第二步,根据名字顾名思义是去创建一个BeanFactory,具体怎么创建的,点进去看看默认情况下返回的工厂对象。到这里可以发现我们用的ApplicationContext它本身并不是去实例化的一个BeanFactory对象,而是内部持有了一个实例化的DefaultListableBeanFactory对象。

 回到上面第三步,这里不是重点,主要是对工厂对象序列化,赋序列化ID,格式为< className @ 随机数 >,用于作为工厂对象的唯一标识。

☆ customizeBeanFactory方法 && loadBeanDefinitions

这两个方法个人认为是很重要的方法,先说customizeBeanFactory方法,点进去看看,主要是去加载两个配置参数,为什么说很重要?这两个参数在Spring循环依赖加载里面有起到关键的判断作用。

 loadBeanDefinitions方法,则是将配置类转换成BeanDefinition对象然后加载到BeanFactory实例化对象中去,同样也是直接点进去跟踪,可以看到首先是实例化了一个XmlBeanDefinitionReader对象,然后对对象中的一些属性进行赋值,进行初始化。到这里为止一个XmlBeanDefinitionReader对象则完成了实例化与初始化。下面的loadBeanDefinitions方法则成为了重点。

 这里先去获取Resource,如果获取不到则通过配置的xml路径去加载配置,但是无论是那种方式获取,最后都交由XmlBeanDefinitionReader对象的loadBeanDefinitions方法。

 然后继续往下走,这里对拿到的配置路径记数,这里有点像Mybatis中的加载记数操作,应该是防止重复加载与日志输出打印相关,遍历的没一个location都会再传递给loadBeanDefinitions方法。

 继续往下走,会实例化一个ResourceLoader对象,然后去加载location成Resource对象,继续往下传递到方法loadBeanDefinitions中

 继续往下走,(略过一些方法重载,这里篇幅问题不贴出来了)然后到了doLoadBeanDefinitions方法,这个方法就是真正的去解析XML配置去加载Bean属性成BeanDefinition了。

 点进去,加载XML方法内部我就不展示出来了,用的是SAX解析,讲解析出来的内容向下传递。

 点进去,这里开始实例化了一个BeanDefinition构造器,主要是看registerBean方法。省略掉中间方法重载、接口调用的步骤,最终会跳转到DefaultBeanDefinitionDocumentReader类的doRegisterBeanDefinitions方法中,而方法参数则是XML解析返回的结果。上面第一部分的代码主要是应用Bean与Bean之前父类引用相关的问题处理。着重还是回到parseBeanDefinitions方法中。

 在解析BeanDefintioin的过程中默认调用的解析方法parseDefaultElement,从这里点进去继续剖析到parseDefaultElement方法。

 可以看到这里会通过xml标签的类型去判断,我们这里主要看Bean元素标签的解析,点进去继续看。

 将解析XML标签内容传递解析成BeanDefinitionHodler对象(BeanDefinition持有对象),向下传递,进行注册。而BeanDefinitionHolder对象中则有解析后的BeanDefinition对象和BeanName标识符。点下去继续看。

 向下走之后,从持有者对象中获取了BeanName,BeanDefinition对象,然后通过传递给BeanDefinitionRegistry进行注册。这里会发现一个问题,对于别名的类,发现会再重新注册一次,也就是说同类容器中配置别名的Bean,对其的获取方式有两种方式。

 点进去这个方法,回到了我们的工厂类DefaultListableBeanFactory中,这个方法前半段都是进行判断是否已经存在,重点在于下半部注册部分。到这里位置,所有的单例Definition就都已经注册到了BeanFactory容器中,BeanFactory也算实例化完成。

 3,prepareBeanFactory方法

该方法主要对BeanFactory进行一些特征的设置操作,不做细讲,可以直接对照注释理解

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		//设置当前ApplicationContext的类加载器
		beanFactory.setBeanClassLoader(getClassLoader());
		beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
		//注册属性编辑器
		beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

		// 添加一个ApplicationContextAwareProcessor,主要针对实现了Aware接口的类
		beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

		//忽略的依赖注入信息,如果某个类实现了以下的接口,那么再依赖注入的时候可以忽略掉它们
		beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
		beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
		beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
		beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
		beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
		beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

		// 对于一些特殊的Bean注册赋值
		beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
		beanFactory.registerResolvableDependency(ResourceLoader.class, this);
		beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
		beanFactory.registerResolvableDependency(ApplicationContext.class, this);

		// 在类 实例化后,如果是 ApplicationListener 的子类,
		// 这个postProcessor的作用就是将其添加到 listener 列表中
		// 注册 事件监听器
		beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

		// Detect a LoadTimeWeaver and prepare for weaving, if found.
		// 切面编程的相关相关内容 AspectJ
		if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
			beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
			// Set a temporary ClassLoader for type matching.
			beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
		}

		// Register default environment beans.
		// 注册一些默认使用的Bean evironment、systemProperties、systemEnvironment
		if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
			beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
		}
		if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
			beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
		}
		if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
			beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
		}
	}

☆4,finishBeanFactoryInitialization方法

中间的方法对于prepareBeanFactory方法中装备的那些类或接口,对于实现与继承了这些接口和类的Bean进行注册到BeanFactory中,这里就不多做解析。主要将篇幅留在核心方法的剖析上,该方法则是对单例的非延迟的对象进行实例化。点进去后着重看工厂类对象的preInstantiateSingletons方法,也是开始实例化的入口方法。

 点进去后,首先是获取多有的BeanNames,上面的注册的时候是提到过对于注册到工厂对象中的BeanDefinition,BeanName会按照注册的顺序存放到一个List集合当中,而实例化的过程也是按照这个顺序去执行的。然后就是判断了,确定该类配置不是抽象的,是单例的,不是懒加载的则开始实例化,如果这个类是FactoryBean对象,这会在BeanName前加&,至于区别,可以去查看BeanFactory与FactoryBean的区别,然后处理完了之后会调用getBean方法完成实例化。

 点进去之后会跳转到doGetBean方法。这个方法比较长不一次性贴出,分开贴出来剖析。

首先会对传来的BeanName再进行一次解析,然后根据解析后的BeanName去缓存中取实例化对象,防止重复加载,这里直接跳过If判断中缓存实例化对象的处理,到else中去。

 是否开启循环依赖的配置,上面有提到过配置开启。然后继续往下走,前面有说过我们使用的工厂类并不是直接去实例化的BeanFactory,而是ApplicationContext中包含了实例化的DefaultListableBeanFactory对象,它的底层也是去实现了BeanFactory,这里的作用主要是去获取所有的实例化的实现了BeanFactory的工厂类对象,去查这个Bean是否已经在别的工厂类实例化过了!

 到这里将父类子类属性合并加载,类加载按照依赖顺序实例化。这里不是重点直接过。继续往下走。

 到了重点方法了,这里再次判断了一次是否是单例,如果是则开始实例化了,走createBen方法。

 点进去继续剖析,到这里可以得到一个思路,工厂类中的Bean实例化是通过反射去实例化的,这里通过反射获取了类信息,然后存入到RootBeanDefinition中去,向下传递到doCreateBean方法。

 进到doCreateBean方法,通过createBeanInstance去完成类的初始化。但是到这里需要注意一个问题,在xml配置中是需要去申明属性映射的,这里并没有对属性设置,只是单纯的对类进行了初始化的操作,话不多说继续往下看!

 上面再次进行了一次判断,if中的有关循环依赖对象注入的问题,后面会通过别的篇章去详细说明,这里不做说明,然后通过下面的populateBean设置属性。然后通过Debug模式跟踪到applyPropertyValues方法的调用,继续跟进。

 到这里为止,才真正的完成了对属性的引用的注入。然后再回到属性填充最初的方法的位置,有这样一个方法:initializeBean。

 

 点进去之后可以发现,这个方法会对实现了Aware接口的Bean去单独的初始化并且调用实现的方法。

结语

到此 就是关于Spring IoC 容器初始化源码剖析内容了。

Spring IoC 的设计中,Bean定义的解析和Bean的依赖注入,是两个独立的过程,前半部分是解析成BeanDefinition注册到容器,后半部分则是对容器中的Bean实例化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值