自己实现Spring IoC容器(三)完成IoC容器

上一章自己实现Spring IoC容器(二)读取配置文件我把读取配置文件这一块功能完成了,现在就可以根据配置文件来创建IoC容器了

#定义接口

先模仿一下Spring假装地定义一个BeanFactory接口,虽然对于我这个项目意义不大

package edu.jyu.core;

public interface BeanFactory {
	/**
	 * 根据name返回bean
	 * @param name
	 * @return
	 */
	Object getBean(String name);
}

#初始化容器
然后定义一个BeanFactory的实现类ClassPathXmlApplicationContext,这就是实现容器的类。然后我在这个类的构造方法中根据配置文件的信息将scope值为singleton的bean对象创建出来并放到容器中,但是不创建scope值为prototype的bean对象,而是在后面每次调用getBean方法时创建一个新的bean对象。

ClassPathXmlApplicationContext类

package edu.jyu.core;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.beanutils.BeanUtils;

import edu.jyu.config.Bean;
import edu.jyu.config.Property;
import edu.jyu.config.parsing.ConfigurationManager;

public class ClassPathXmlApplicationContext implements BeanFactory {
	// 存放配置文件信息
	private Map<String, Bean> config;
	// 存放bean对象的容器
	private Map<String, Object> context = new HashMap<>();

	/**
	 * 将配置文件中设置为单例的bean对象创建好放入容器中
	 * 
	 * @param path
	 */
	public ClassPathXmlApplicationContext(String path) {
		// 读取配置文件中bean的信息
		config = ConfigurationManager.getBeanConfig(path);
		// 遍历初始化bean
		if (config != null) {
			for (Entry<String, Bean> e : config.entrySet()) {
				// 获取bean信息
				String beanName = e.getKey();
				Bean bean = e.getValue();
				// 如果设置成单例的才创建好bean对象放进容器中
				if (bean.getScope().equals(Bean.SINGLETON)) {
					Object beanObj = createBeanByConfig(bean);
					context.put(beanName, beanObj);
				}
			}

		}
	}

	/**
	 * 根据bean的配置信息创建bean对象
	 * 
	 * @param bean
	 * @return
	 */
	private Object createBeanByConfig(Bean bean) {
		// 根据bean信息创建对象
		Class clazz = null;
		Object beanObj = null;
		try {
			clazz = Class.forName(bean.getClassName());
			// 创建bean对象
			beanObj = clazz.newInstance();
			// 获取bean对象中的property配置
			List<Property> properties = bean.getProperties();
			// 遍历bean对象中的property配置,并将对应的value或者ref注入到bean对象中
			for (Property prop : properties) {
				Map<String, Object> params = new HashMap<>();
				if (prop.getValue() != null) {
					params.put(prop.getName(), prop.getValue());
					// 将value值注入到bean对象中
					BeanUtils.populate(beanObj, params);
				} else if (prop.getRef() != null) {
					Object ref = context.get(prop.getRef());
					// 如果依赖对象还未被加载则递归创建依赖的对象
					if (ref == null) {
						ref = createBeanByConfig(config.get(prop.getRef()));
						//下面这句代码写错了,现在修改过来
						//ref = createBeanByConfig(bean);
					}
					params.put(prop.getName(), ref);
					// 将ref对象注入bean对象中
					BeanUtils.populate(beanObj, params);
				}
			}
		} catch (Exception e1) {
			e1.printStackTrace();
			throw new RuntimeException("创建" + bean.getClassName() + "对象失败");
		}
		return beanObj;
	}

	@Override
	public Object getBean(String name) {
		return null;
	}

}

在构造方法中,先是通过ConfigurationManager获取配置文件信息,然后根据配置信息找出scope值为singleton的bean标签并创建相应的bean对象,创建好了并放入存放bean对象的容器context中。需要注意的是createBeanByConfig方法中存在着递归调用,因为可能在创建一个bean对象的时候它所依赖的bean对象还没有被创建,所以要先创建它依赖的bean对象。

#实现getBean方法
初始化容器后,现在来实现一下BeanFactorygetBean方法

public Object getBean(String name) {
	Bean bean = config.get(name);
	Object beanObj = null;
	if (bean.getScope().equals(Bean.SINGLETON)) {
		// 如果将创建bean设置成单例则在容器中找
		beanObj = context.get(name);
	} else if (bean.getScope().equals(Bean.PROTOTYPE)) {
		// 如果是prototype则新创建一个对象
		beanObj = createBeanByConfig(bean);
	}
	return beanObj;
}

这个方法的注释已经写的很明白了,就不再重复说了。

#测试
现在整个项目大致完成了,接下来就来测试一下

测试类TestApplicationContext

package edu.jyu.core;

import org.junit.Test;

import edu.jyu.bean.A;
import edu.jyu.bean.B;

public class TestApplicationContext {

	@Test
	public void test() {
		BeanFactory ac = new ClassPathXmlApplicationContext("/applicationContext.xml");
		A a = (A) ac.getBean("A");
		A a1 = (A) ac.getBean("A");
		B b = (B) ac.getBean("B");
		B b1 = (B) ac.getBean("B");
		System.out.println(b.getAName() + ":" + b.getAge());
		System.out.println("a==a1 : "+(a==a1));
		System.out.println("b==b1 : "+(b==b1));
	}
}

下面这句代码想要测试的是A类对象是否被注入到B中,还有A类对象的name属性是否注入了Jason字符串,B类对象的age是否被注入了13

System.out.println(b.getAName() + ":" + b.getAge());

下面两句代码想要测试的是scope的两个值singletonprototype对应的语义是否实现,比如A中定义的scope值是singleton(没有写默认就是singleton),那么每次获得的A类对象都是同一个对象。B中定义的scope值是prototype,那么每次获得的B类对象都是重新创建的一个对象。

System.out.println("a==a1 : "+(a==a1));
System.out.println("b==b1 : "+(b==b1));

输出结果

Jason:13
a==a1 : true
b==b1 : false

从输出结果来看,功能没错。

#结语
我自己实现的IoC容器无疑是十分简(jian)洁(lou)的,有很多健壮性判断也没有,一切都是按照理想的情况实现,所以不能直接拿来用,但是IoC原理还是体现了。作为一个山寨大王,以后我还会继续山寨一些框架,比如过段时间我准备山寨Spring MVC~


好了,整个IoC容器大功告成,项目已经上传到Github上
https://github.com/HuangFromJYU/JSpring-IoC

如果大家有什么问题或者发现什么错误可以发邮件到jasonwong_hjj@qq.com,共同学习共同进步

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值