模拟实现Spring IoC功能
为了加深理解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]