天天看点

TinySpring分析二

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中,跟路径就是工程下的bin文件夹,在默认情况下,eclipse会把项目中的src下的内容拷贝到bin下,因此也可以理解为根目录就是src目录)

第一种Class.getResource()可以带"/"也可以不带

一旦带了/ 就默认从根路径(就是bin 就是src)下查找了

如果没有/ 就从这个类本身的那个路径下查找

在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,很简单吧,就是建一个InputStream,连接到文件上,然后从文件中读数据。

这里面的东西不难,但是比较繁杂,牵扯最多的就是对xml的解析

/**
   * 通过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();


    // 默认是工程目录
  //  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());
    }      

按照使用者与创建者分离的原则,初始化注册的代码出现在客户端也不合适;

怎么办?

合起来呗。

还是先写测试代码 如下

<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;
  }      

继续阅读