模拟实现Spring IoC功能

本文通过解析bean.xml文件,模拟实现了Spring的IoC功能。首先使用jdom解析器处理XML,然后分别解析bean和property元素。在解析过程中,利用反射创建对象实例,完成依赖注入。只展示了核心代码,完整源码可下载查看。

为了加深理解Spring 今天自己写了一个模拟的Spring....


步骤:

1.利用jdom解析bean.xml(pull,sax也可以,我这里用了jdom)

2.先解析所有的<bean/>,再解析所有的<property/>.如果边解析<bean/>,边解析<property/>,会导致property的ref找不到对应的bean.

3.利用反射,根据解析到的类路径,new出一个实例,实现Ioc.


目录结构:


这里只给出核心代码,其余的bean,dao,service,并不重要,就不给出了.有兴趣的同志可以点击~这里下载源码.~

ClassPathXmlApplicationContext:

package glut.spring;

import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;

public class ClassPathXMLApplicationContext {
	/**
	 * 用于存放<bean/>
	 */
	private Map<String, Object> beans = new HashMap<>();
	/**
	 * 用于存放<property/>
	 */
	private Map<String, List<Element>> properties = new HashMap<>();

	/**
	 * 将xml文件转为输入流,作为参数传入.
	 * @param is
	 * @throws Exception
	 */
	public ClassPathXMLApplicationContext(InputStream is) throws Exception {
		// TODO Auto-generated constructor stub
		autoWired(is);
	}

	/**
	 * 模拟DI
	 * @param is
	 * @throws Exception
	 */
	private void autoWired(InputStream is) throws Exception {
		SAXBuilder sb = new SAXBuilder();

		Document doc = sb.build(is);

		Element rootElement = doc.getRootElement();

		List<Element> elementOfBeans = rootElement.getChildren("bean");

		//遍历xml中所有的<bean/>
		for (Element e : elementOfBeans) {

			String beanId = e.getAttributeValue("id");
			String beanClz = e.getAttributeValue("class");

			Object beanInstance = Class.forName(beanClz).newInstance();
			//将beanId和bean的实例存入map
			beans.put(beanId, beanInstance);
			//把所有的property先存着,等bean初始化完毕再初始化property,否则可能会导致某些property无法初始化
			properties.put(beanId, e.getChildren("property"));

		}

		//Dependency Injection Simulation
		for (Entry<String, List<Element>> entry : properties.entrySet()) {
			for (Element e : entry.getValue()) {
				String propertyName = e.getAttributeValue("name");
				String propertyRef = e.getAttributeValue("ref");

				//通过set方法注入
				String methodName = "set"
						+ propertyName.substring(0, 1).toUpperCase()
						+ propertyName.substring(1);

				//通过beanId获得对应的bean
				Object beanInstance = beans.get(entry.getKey());

				//通过ref的值去寻找对应的bean,如果没有对应的bean,在下面用到getClass的时候会抛出异常.
				Object refBeanInstance = beans.get(propertyRef);

				Method setterMethod = beanInstance.getClass().getMethod(
						methodName,//呵呵,功能有点简陋,默认只支持refBean实现的第一个接口.
						refBeanInstance.getClass().getInterfaces()[0]);

				//调用对应的setter方法,将ref的bean注入到指定的bean中.
				setterMethod.invoke(beanInstance, refBeanInstance);

			}
		}
	}

	/**
	 * 根据beanName获得bean
	 */
	public Object getBean(String beanName) {
		// TODO Auto-generated method stub
		return beans.get(beanName);
	}

}

测试代码:

package glut.test;

import glut.bean.User;
import glut.service.UserService;
import glut.spring.ClassPathXMLApplicationContext;

import org.junit.Test;

public class SpringTest {
	@Test
	public void test() throws Exception {
		ClassPathXMLApplicationContext ctx = new ClassPathXMLApplicationContext(
				this.getClass().getClassLoader()
						.getResourceAsStream("beans.xml"));

		UserService userService = (UserService) ctx.getBean("userService");

		User user = new User("user", "123");

		userService.add(user);
	}
}
打印的结果为User的toString:

User [uid=user, pwd=123]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值