您的位置:首页 > 编程语言 > Java开发

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

2017-04-08 10:18 453 查看
上一章自己实现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方法

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

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
的两个值
singleton
prototype
对应的语义是否实现,比如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,共同学习共同进步
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  spring ioc ioc容器