对Spring框架的IOC容器进行简单分析(上)

这一系列博客主要是对spring4的ioc容器创建bean并初始化的整个过程,进行一个简单的分析。由于一口气全写上可能会造成该博客过长,所以选择分段。

本篇主要内容:主要搭建环境debug到finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory)方法


由于学艺不精,如果出了一些差错,还望见谅。这里也要特别感谢一下雷神,我其实只是按照他提供的思路走了一遍,这是雷神讲解spring ioc源码的地址

简单描述本篇博客要做的事,分两步

  1. 搭建测试环境,
    就三步,新建一个java项目,写一个java bean,在spring配置文件配置多个bean(方便观察)。
  2. 开始debug调试,观察控制台打印的信息,进入打印信息的方法
    第一个断点:给测试类中的ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml");打上断点,debug向下执行完该方法后,控制台打印所有信息,所以该方法完成了ioc容器的创建和配置bean的初始化。
    第二个断点:当进入ClassPathXmlApplicationContext类,执行带三个参数的构造器中的refresh()方法后,控制台打印所有信息,所以给该方法打上断点。
    第三个断点:依然是ClassPathXmlApplicationContext类,当执行prepareRefresh();方法后,控制台打印部分日志;当执行ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();方法后,控制台打印剩下的日志;当执行finishBeanFactoryInitialization(beanFactory);方法后,控制台打印构造器被调用信息,即配置文件中的bean完成初始化,所以给该方法打上断点。

搭建测试环境


用IDE新建项目,添加必要的jar包

这里用的是eclipse j2e,先new一个java project,在src文件夹下编写测试类(用main方法);在根目录下新建lib文件夹,里面用来放置需要导入的spring jar包。

目录结构
可以去官网下载spring源码,这里用的是 Spring4,版本:4.3.0。如何下载jar包
在这里插入图片描述
开始调试IOC的功能,所以在lib文件夹导入必须的四个核心jar包,和一个依赖日志包:

spring-core-4.3.0.RELEASE.jar
spring-context-4.3.0.RELEASE.jar
spring-expression-4.3.0.RELEASE.jar
spring-beans-4.3.0.RELEASE.jar
commons-logging-1.2.jar (日志包)

导入成功后,选中导入的jar包,右键Build Path,选择Add to Build Path


开始编写测试Bean和spring配置文件

准备一个java bean,我这里选择一个Person类,在空参和有参构造器中写上打印语句,方便提示

package com.qin.bean; //包名

public class Person {
	
	private String name;
	private Integer age;
	private String gender;
	private String email;
	
	public Person() {       
		super();
        System.out.println("person无参构造器调用...");
	}

	public Person(String name, Integer age, String gender, String email) {
		super();
		this.name = name;
		this.age = age;
		this.gender = gender;
		this.email = email;
        System.out.println("person有参构造器调用...");
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	public String getGender() {
		return gender;
	}

	public void setGender(String gender) {
		this.gender = gender;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + ", gender=" + gender + ", email=" + email + "]";
	}
	
}

在根目录下新建conf源码文件夹,在里面新建Spring Bean Configuration File,这里取名叫ioc.xml。如果没装spring插件,点我

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<!-- 给ioc中添加三个person bean并分别赋值 -->
	<bean id="person01" class="com.qin.bean.Person">
		<constructor-arg  name="name" value="张三"></constructor-arg>
		<constructor-arg  name="age" value="18"></constructor-arg>
		<constructor-arg  name="gender" value=""></constructor-arg>
		<constructor-arg  name="email" value="123@456.com"></constructor-arg>
	</bean>
	<bean id="person02" class="com.qin.bean.Person">
		<constructor-arg  name="name" value="李四"></constructor-arg>
		<constructor-arg  name="age" value="15"></constructor-arg>
		<constructor-arg  name="gender" value=""></constructor-arg>
		<constructor-arg  name="email" value="666@999.com"></constructor-arg>
	</bean>
	<bean id="person03" class="com.qin.bean.Person">
		<constructor-arg  name="name" value="王五"></constructor-arg>
		<constructor-arg  name="age" value="36"></constructor-arg>
		<constructor-arg  name="gender" value=""></constructor-arg>
		<constructor-arg  name="email" value="789456123@123.com"></constructor-arg>
	</bean>
</beans>

用main方法,测试能不能从ioc中拿到在xml中配置的属性

package com.qin.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


// 测试类
public class SourceTest {

	public static void main(String[] args) {
        // 拿到ioc容器
		ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml");
		// 从容器中拿到person
		Object person = ioc.getBean("person01");
		// 打印
		System.out.println(person);
	}
}

如果正常看到配置好的Person,则环境搭建结束:
环境搭建成功!


开始debug调试(逐层分析第1-3断点)

ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml");打上端点,右键Debug As,出现debug界面:
debug预备
点击step into,如果进入ClassLoader,点击Step Return退出到原来的测试类即可
classloader
在这里插入图片描述

再点击step into,来到ClassPathXmlApplicationContext类中(如果是新建的项目,需要绑定源码)
第一次进入ClassPathXml
可以观察到这个方法是在调用有三个参数的构造方法,点击step over并仔细观察控制台

// 真正调用的是这个有三个参数的构造方法
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
			throws BeansException {

		super(parent);
		setConfigLocations(configLocations);
		if (refresh) {
            // 可以看到当这个方法执行时,控制台打印日志和构造器信息
            // 所以这个方法一旦执行,所有单实例bean创建完成
			refresh();
		}
	}

控制台打印信息
给该refresh方法打上断点,重新debug。通过Resume执行到该断点后,点击step into进入该方法:

public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
            // 点开beanFactory,发现配置文件中配置的bean属性都在
            // 观察控制台,发现此时并未打印 person构造器调用...
            // 所以这个方法是:spring解析xml配置文件,将要创建的所有bean的信息保存起来
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				// bean工厂的后置处理器
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				// 调用bean工厂后置处理器
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				// 注册bean工厂的后置处理器
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				//支持国际化
				initMessageSource();

				// Initialize event multicaster for this context.
				//处理时间多派发器
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				// 留给子类的方法
				onRefresh();

				// Check for listener beans and register them.
				// 注册监听器
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				// 完成BeanFactory的初始化
				// 初始化所有单实例bean
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}

继续点击step over,并观察控制台,当放行到prepareRefresh();后,发现控制台打印部分日志。
放行到ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();后,控制台打印剩下的日志。从控制台日志,可以判断出这段代码完成了从ioc.xml配置文件中加载xml bean定义,查看beanFactory中的参数,如下图:
在这里插入图片描述
可以看到,beanFactory中包含了我们在配置文件中bean的所有配置信息,所以这个方法其实就从加载的配置文件里解析出所有的bean信息,然后再放在这个beanFactory里。注意!此时并未打印“person构造器调用…”,说明这只是加载bean的配置信息,还没有开始为bean进行初始化。
继续step over
继续step over,并仔细观察控制台。当执行完finishBeanFactoryInitialization(beanFactory);的时候,发现构造器全部打印。即bean真正完成初始化
在这里插入图片描述
给该方法打上断点,重新debug。通过Resume执行到该断点后,点击step into进入该方法。


目前就这些~~

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值