自己动手写spring容器(1)
毕业刚刚一年多一点,毕业了后也顺利的进入了一家著名的互联网公司,做的是后台系统,用的呢也是SSI(struts2,spring)框架,平时做做项目,也已足够了,但是感觉越来越没动力了,越来越没有激情了,就像我们的老大说的,"天天接Task,有意思?,有时间不知道把框架的源码看看!",最近加班相对较少,闲下来就来摸索一下spring。
写这篇文章只是想让大家了解一下Spring到底是怎么运行的,并不是想重造噢,希望大家看完这篇文章后能对Spring有更深入的了解,对初学者有所帮助喔!好,言归正传,让我们来一起探索吧!
我们先开看看spring是怎么运行的。。
1 //读取配置文件实例化一个IoC容器 2 ApplicationContext ctx=new ClassPathXmlApplicationContext("resources/beans.xml"); 3 //从容器中获取Bean,注意此处完全“面向接口编程,而不是面向实现” 4 PersonService personService=(PersonService) ctx.getBean("personService",PersonService.class); 5 personService.sayHello();
我们来分析一下,首先是加载spring的配置文件,此处是beans.xml
1 <bean id="personService" class="com.yangyang.service.impl.PersonServiceImpl"> 2 </bean>
然后是通过调用getBean方法来获取并实例化personService对象,最后是调用sayHello方法
那么spring到底是如何做到的呢?很明显,第一步肯定是要解析bean.xml文件,
为此我们写一个自己的ClassPathXmlApplicationContext类来模拟spring的行为,,此处加入一个参数为string类型的构造函数,用来读取配置文件及模拟spring以后的行为,
1 package com.juit; 2 3 public class YhdClassPathXmlApplicationContext { 4 /** 5 * 构造方法,用来模拟spring的行为 6 * @param fileName 7 */ 8 public YhdClassPathXmlApplicationContext(String fileName){ 9 this.readXml(fileName); 10 } 11 /** 12 * 根据文件名读取xml的配置文件 13 * @param fileName 14 * Administer 15 * 2013-8-26 下午11:09:16 16 */ 17 private void readXml(String fileName) { 18 // TODO Auto-generated method stub 19 20 } 21 }
此处readxml啥都没做,现在我们来完成这个代码,根据http://www.cnblogs.com/shunyang/p/3265100.html中提到的方式来解析xml文件,并将解析到的bean存到一个bean定义的类中,为此我们需要准备一个类BeanDefinition 用来存储解析后xml文件。经过分析xml文件,可知比较简单的配置一般有id,class(当然这里为了简单只用了两个)等属性,如下:
当然我们也需要加上一个全局的List的bean,用来存储所有的beans,代码见后面
1 package com.juit; 2 /** 3 * Bean对象 4 * @author Administer 5 * 6 */ 7 public class BeanDefinition { 8 private String id;//bean的id 9 private String className;//bean的类 10 public String getId() { 11 return id; 12 } 13 public void setId(String id) { 14 this.id = id; 15 } 16 public String getClassName() { 17 return className; 18 } 19 public void setClassName(String className) { 20 this.className = className; 21 } 22 public BeanDefinition(String id, String className) { 23 this.id = id; 24 this.className = className; 25 } 26 }
下面是解析xml文件的readXml方法:
1 private void readXml(String fileName) { 2 //创建一个读取器 3 SAXReader saxReader=new SAXReader(); 4 Document document=null; 5 try { 6 //获取要读取的配置文件的路径 7 URL xmlPath=this.getClass().getClassLoader().getResource(fileName); 8 //读取文件内容 9 document=saxReader.read(xmlPath); 10 //获取xml中的根元素 11 Element rootElement=document.getRootElement(); 12 for (Iterator iterator = rootElement.elementIterator(); iterator.hasNext();) { 13 Element element = (Element) iterator.next(); 14 String id=element.attributeValue("id");//获取bean的id属性值 15 String clazz=element.attributeValue("class");//获取bean的class属性值 16 BeanDefinition beanDefinition=new BeanDefinition(id,clazz);
17 beanDefines.add(beanDefinition); 18 } 19 } catch (Exception e) { 20 e.printStackTrace(); 21 } 22 }
解析完xml后,接下来就是bean的实例化,我们在写一个实例化bean的方法。
spring中是使用getBean的方式来获取bean的,类似的可以用Map的get取值来模拟,因此定义一个Map,用来存储bean的id和bean的对应,完整的见下面
1 public class YhdClassPathXmlApplicationContext{ 2 private List<BeanDefinition> beanDefines=new ArrayList<BeanDefinition>();//用来存储所有的beans 3 private Map<String, Object> sigletons =new HashMap<String, Object>();//用来存储实例化后的bean 4 /** 5 * 构造方法,用来模拟spring的行为 6 * @param fileName 7 */ 8 public YhdClassPathXmlApplicationContext1(String fileName){ 9 //1.读取spring的配置文件 10 this.readXml(fileName); 11 //2.实例化bean 12 this.instanceBeans(); 13 } 14 /** 15 * 完成实例化beans 16 * 17 * Administer 18 * 2013-8-26 下午11:24:37 19 */ 20 private void instanceBeans() { 21 // TODO Auto-generated method stub 22 23 }
然后我们来完成instanceBeans方法
1 /** 2 * 完成实例化beans 3 * 4 * Administer 5 * 2013-8-18 上午1:07:51 6 */ 7 private void instanceBeans() { 8 if (beanDefines != null && beanDefines.size() >0) { 9 //对每个bean进行实例化 10 for (BeanDefinition beanDefinition : beanDefines) { 11 try { 12 //bean的class属性存在的时候才进行实例化,否则不进行实例化 13 if (beanDefinition.getClassName() != null && !beanDefinition.getClassName().equals("")) { 14 //实例化的关键操作 15 sigletons.put(beanDefinition.getId(),Class.forName(beanDefinition.getClassName()).newInstance()); 16 System.out.println("id为:"+beanDefinition.getId()+"的bean实例化成功"); 17 } 18 } catch (Exception e) { 19 System.out.println("bean实例化失败"); 20 e.printStackTrace(); 21 } 22 } 23 } 24 }
实例化后我们来写一个getBean方法,用来在外部获取实例化后的bean,这个搞个最简单的根据bean的id来获取
1 /** 2 * 通过bean名称来获取bean对象 3 * @param beanName 4 * @return 5 * Administer 6 * 2013-8-18 上午1:17:02 7 */ 8 public Object getBean(String beanName){ 9 return sigletons.get(beanName); 10 }
这样整个bean的实例化我们已经做完了,是不是也不是很困难,当然我们还缺少一步,我们需要测试我们这个自己写的这个spring是不是OK的,
1 package com.juit; 2 3 import org.junit.BeforeClass; 4 import org.junit.Test; 5 6 import com.juit.YhdClassPathXmlApplicationContext; 7 import com.yangyang.service.PersonService; 8 9 public class SpringTest { 10 11 @BeforeClass 12 public static void setUpBeforeClass() throws Exception { 13 } 14 15 @Test 16 public void testInstanceSping() { 17 YhdClassPathXmlApplicationContext ctx=new YhdClassPathXmlApplicationContext("resources/beans.xml"); 18 PersonService personService=(PersonService)ctx.getBean("personService"); 19 20 } 21 22 }
可以看到控制台打印着"
id为:personService的bean实例化成功
终于大功告成了,当然这些只是我这个菜鸟的理解,欢迎各位大神的指导,接下来下篇将会实现spring的依赖注入。
如果您觉得阅读本文对您有帮助,请微信扫码关注作者,与我进行交流!欢迎各位转载,转载文章之后须在文章页面明显位置给出作者和原文连接,谢谢。