step5
看完了前面的几步,到如今我们必定要想到的问题就是,数据要是放在xml中怎么读?
事实上依照正常思维一步一步来,从xml中读数据和之前手工配进去并没有什么大的差别,仅仅要读出来就OK了。
先看測试程序,
public void Step5() throws Exception { // 1.读取配置 XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(); xmlBeanDefinitionReader.loadBeanDefinitions("bin/resources/tinyioc.xml"); // 2.初始化BeanFactory并注冊bean BeanFactory beanFactory = new AbstractBeanFactory(); for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getBeanDefinitionMap().entrySet()) { ((AbstractBeanFactory)beanFactory).registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue()); } // 3.获取bean HelloWorldServiceImpl helloWorldService = (HelloWorldServiceImpl) beanFactory.getBean("helloWorldService"); helloWorldService.helloWorld3(); }
关于路径问题
在java中,获取文件(包含xml,jpg等等)有两种方式
Class类下的getResource(String path)方法与
ClassLoader类下的getResource(String path)方法
先说后面一种,ClassLoader.getResource()參数中不带"/",默认就是根路径(在Eclipse中,跟路径就是project下的bin文件夹,在默认情况下,eclipse会把项目中的src下的内容复制到bin下,因此也能够理解为根文件夹就是src文件夹)
第一种Class.getResource()能够带"/"也能够不带
一旦带了/ 就默认从根路径(就是bin 就是src)下查找了
假设没有/ 就从这个类本身的那个路径下查找
具体资料见
http://www.cnblogs.com/yejg1212/p/3270152.html
在step5这个样例里
InputStream is = new FileInputStream(local);这里的local是从项目文件夹算的,因此还得加上bin
看了測试代码大家就会知道,我们的程序结构了吧,XmlBeanDefinitionReader的主要作用就是读取xml,然后转换成一个个BeanDefinition,再存储进BeanDefinitionMap,再创建一个BeanFactory,将BeanDefinitionMap中的记录一个一个再注冊一边。
public class XmlBeanDefinitionReader { //bean清单 就是前面说的学校里面的学生信息表 private Map<String, BeanDefinition> beanDefinitionMap; public XmlBeanDefinitionReader(){ beanDefinitionMap = new HashMap<String, BeanDefinition>(); } public void loadBeanDefinitions(String local) throws IOException, ParserConfigurationException, SAXException { InputStream is = new FileInputStream(local); parseNode(is); }
看了loadBeanDefinitions,非常easy吧,就是建一个InputStream,连接到文件上,然后从文件里读数据。
这里面的东西不难,可是比較繁杂,牵扯最多的就是对xml的解析
相关知识见
http://blog.csdn.net/dlf123321/article/details/39649089
/** * 通过InputStream 获得每个bean * @param is * @throws ParserConfigurationException * @throws SAXException * @throws IOException */ public void parseNode(InputStream is) throws ParserConfigurationException, SAXException, IOException { DocumentBuilderFactory domfac = DocumentBuilderFactory.newInstance(); DocumentBuilder domBuilder = domfac.newDocumentBuilder(); // 默认是project文件夹 // InputStream is = new FileInputStream("bin/resources/tinyioc.xml"); Document doc = domBuilder.parse(is); Element root = doc.getDocumentElement(); NodeList beans = root.getChildNodes(); for (int i = 0; i < beans.getLength(); i++) if (beans.item(i) instanceof Element) { Element el=(Element)beans.item(i); parseElement(el); } is.close(); } /** * 分析每个bean的id class * @param el */ public void parseElement(Element el){ String id=el.getAttribute("id"); String classPath=el.getAttribute("class"); // System.out.println(id+" "+classPath); BeanDefinition bd=new BeanDefinition(); bd.setBeanClassName(classPath); parseProperties(el,bd); beanDefinitionMap.put(id, bd); } /** * 分析每个bean的參数 并增加到beandefinition的property里面 * @param el * @param bd */ public void parseProperties(Element el,BeanDefinition bd){ NodeList bl=el.getElementsByTagName("property"); for (int i = 0; i < bl.getLength(); i++) if (bl.item(i) instanceof Element) { Element property=(Element)bl.item(i); String name=property.getAttribute("name"); // System.out.print(" "+name+" "); if (property.getAttribute("ref")!="") { BeanReference br=new BeanReference(property.getAttribute("ref")); PropertyValue pV=new PropertyValue(name,br); bd.getPropertyValues().addPropertyValue(pV); // System.out.println(" "+br.getName()+" "); } if (property.getAttribute("value")!="") { String value=property.getAttribute("value"); PropertyValue pV=new PropertyValue(name, value); bd.getPropertyValues().addPropertyValue(pV); // System.out.println(value); } } } public Map<String, BeanDefinition> getBeanDefinitionMap() { return beanDefinitionMap; }
再剩下的代码,參考step4就ok
step6
假设细致,比对XmlBeanDefinitionReader与AbstractBeanFactory,就能发现两个类里面都有beanDefinitionMap,重写两边,不合适。
另外在step5中
// 2.初始化BeanFactory并注冊bean BeanFactory beanFactory = new AbstractBeanFactory(); for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getBeanDefinitionMap().entrySet()) { ((AbstractBeanFactory)beanFactory).registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue()); }
依照使用者与创建者分离的原则,初始化注冊的代码出如今client也不合适;
怎么办?
合起来呗。
还是先写測试代码 例如以下
<pre name="code" class="java">public void Step7() throws Exception { ApplicationContext ac=new ApplicationContext("bin/resources/tinyioc.xml"); HelloWorldServiceImpl helloWorldService = (HelloWorldServiceImpl) ac.getBean("helloWorldService"); helloWorldService.helloWorld3(); }
美丽!关键就是ApplicationContext,上面已经说了,要把XmlBeanDefinitionReader与AbstractBeanFactory合起来,也就是说要把getBean与loadBeanDefinitions装到一个类里面去
package com.myspring.context; import java.util.Map; import com.bjsxt.spring.BeanFactory; import com.myspring.beans.BeanDefinition; import com.myspring.beans.factory.AbstractBeanFactory; import com.myspring.beans.xml.XmlBeanDefinitionReader; public class ApplicationContext implements BeanFactory { private AbstractBeanFactory abf=new AbstractBeanFactory(); public ApplicationContext(String local) throws Exception { // InputStream is = new FileInputStream(local); loadBeanDefinitions(abf,local); } protected void loadBeanDefinitions(AbstractBeanFactory beanFactory,String configLocation) throws Exception { XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(); xmlBeanDefinitionReader.loadBeanDefinitions(configLocation); for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getBeanDefinitionMap().entrySet()) { beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue()); } } @Override public Object getBean(String id) { // TODO Auto-generated method stub Object obj=null; try { obj = abf.getBean(id); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return obj; } }
AbstractBeanFactory与ApplicationContext都继承了BeanFactory,然后我们不直接用BeanFactory,而是让ApplicationContext中装一个AbstractBeanFactory,这不就是最简单的代理模式么?
代码到这里,就算是完毕了黄亿华大神的TinySpring中IoC的大部分代码
之所以说是大部分,也就是说如今大家看到的代码另一些部分与TinySpring不同
主要在这几个方面
1 Resources部分
在我完毕的代码里,读取xml的时候直接就是一个InputStream,TinySpring的方式是有一个Resource类,同一时候另一个LoadResource类用来载入资源,当然实现的内部机理都是inputstream;
2 接口问题
我一直觉得,良好的代码是一次一次重构出来的,依我如今的水平,确实不可以非常清晰地说出,分了那么多层接口,抽象类的实际作用,因此在我的代码里各个部分都非常"薄弱"(仅仅有一层)
3 对于类的载入,有两种方式一种直接载入,一种延迟载入,TinySpring最開始的那几个step还是在getBean的时候才newInstance的,可是到后面
protected void onRefresh() throws Exception{ beanFactory.preInstantiateSingletons(); } public void preInstantiateSingletons() throws Exception { for (Iterator<String> it = this.beanDefinitionNames.iterator(); it.hasNext();) { String beanName = (String) it.next(); getBean(beanName); } }
所以的类都直接载入了;
4 单例模式
TinySpring中一个bean默认仅仅会载入一次,第二次getBean()的时候会取出之前已经creat的那个;
public Object getBean(String name) throws Exception { BeanDefinition beanDefinition = beanDefinitionMap.get(name); if (beanDefinition == null) { throw new IllegalArgumentException("No bean named " + name + " is defined"); } Object bean = beanDefinition.getBean(); if (bean == null) { //******************查找beanDefinition bean = doCreateBean(beanDefinition); bean = initializeBean(bean, name); beanDefinition.setBean(bean); } return bean; } protected Object doCreateBean(BeanDefinition beanDefinition) throws Exception { Object bean = createBeanInstance(beanDefinition); beanDefinition.setBean(bean); //******************写入beanDefinition applyPropertyValues(bean, beanDefinition); return bean; }
我写的代码中,没有上面的步骤,因此即使第二次get一个已经get过得bean,仍然会产生一个新的bena!
我写的代码 下载地址
http://download.csdn.net/detail/dlf123321/7992633
參考资料
http://www.cnblogs.com/yejg1212/p/3270152.html
http://blog.csdn.net/dlf123321/article/details/39649089