Tiny Spring 分析

Bean工厂接口类:

public interface BeanFactory {  
    Object getBean(String name) throws Exception;  
} 

Bean包装类:

/**  
 * Bean的内容及元数据,保存在BeanFactory中,包装Bean的实体  
 */  
public class BeanDefinition {
    private Object bean;
    private Class<?> beanClass;
    private String beanClassName;
    public BeanDefinition() {     
    }
  
    public BeanDefinition(Object object) {  
        this.bean=object;  
    }  

    public void setBeanClassName(String beanClassName) {  
        this.beanClassName = beanClassName;
        try {  
            this.beanClass = Class.forName(beanClassName);
        } catch (ClassNotFoundException e) {  
            e.printStackTrace();  
        }
    }
    //省略部分get/set方法  
}  

Bean工厂实现类:

public class AbstractBeanFactory implements BeanFactory {  
    // 存放Factory里的所有Bean的详细信息
    private Map<String, BeanDefinition> beanDefinitionMap = 
          new ConcurrentHashMap<String, BeanDefinition>();
    // 存放Factory里的所有Bean的Name
    private final List<String> beanDefinitionNames = new ArrayList<String>();
  
    @Override  
    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();   
        return bean;  
    }  
      
    /** 
     * 将新加入的BeanDefinition注册到BeanDefinitionMap中
     */  
    public void registerBeanDefinition(String name, BeanDefinition beanDefinition) 
        throws Exception {  
        beanDefinitionMap.put(name, beanDefinition);  
        beanDefinitionNames.add(name); 
    }  
}  

至此,一个简单的IOC容器搭建完成。

测试一下:

public class HelloWorldServiceImpl {  
    public void helloWorld2() { 
        System.out.println("hello"); 
    }
}
 

第一步:

BeanDefinition直接根据Bean对象实例化。

public void Step1() throws Exception {  
    BeanFactory beanFactory = new AbstractBeanFactory();
    BeanDefinition beanDefinition = new BeanDefinition(new HelloWorldServiceImpl());
    // 注册
    ((AbstractBeanFactory)beanFactory).registerBeanDefinition("helloworld",beanDefinition);
          
    HelloWorldServiceImpl h = (HelloWorldServiceImpl) beanFactory.getBean("helloworld");
    h.helloWorld2();  
} 
打印结果:helloworld
扫描二维码关注公众号,回复: 247357 查看本文章

第二步:

BeanDefinition根据className实例化。

public void Step2() throws Exception {  
    BeanFactory beanFactory = new AbstractBeanFactory();  
    BeanDefinition beanDefinition = new BeanDefinition();
    beanDefinition.setBeanClassName("com.myspring.HelloWorldServiceImpl");
    // 注册
    ((AbstractBeanFactory)beanFactory).registerBeanDefinition("helloworld",beanDefinition);
          
    HelloWorldServiceImpl h = (HelloWorldServiceImpl) beanFactory.getBean("helloworld"); 
 
    h.helloWorld2();  
}  
然后再在AbstractBeanFactory.getBean()方法返还bean之前加上如下代码:
if (bean == null) {
     bean = beanDefinition.getBeanClass().newInstance();
}

  

第三步:

如果我们在HelloWorldServiceImpl里面有简单的参数怎么办?如:
private String text;
private int a;

public void helloWorld() {
    System.out.println(text + a + " ss");
}
既然有参数,那我们就设计一个PropertyValue:
/**
 * 用于Bean的属性注入
 */
public class PropertyValue {  
    private final String name;
    private final Object value;

    // 省略get,set方法
}  
然后在BeanDefinition里面增加一个List<PropertyValue> pvs = new ArrayList<PropertyValue>,毕竟不能限制一个类只有一个属性。
如果一个类中,有重复的属性,如在spring的配置文件中这样配置:
<bean id="userService" class="com.bjsxt.services.UserService" >  
     <property name="userDao" bean="u" />  
     <property name="userDao" bean="u" />  
</bean>  
因此我们需要再加入一个类PropertyValues
/** 
 * 包装一个对象所有的PropertyValue。<br/> 
 */  
public class PropertyValues {  
    private final List<PropertyValue> propertyValueList = new ArrayList<PropertyValue>();  
    public PropertyValues() {  
    }  
  
    public void addPropertyValue(PropertyValue pv) {  
        // TODO 这里可以对重复propertyName进行判断

        this.propertyValueList.add(pv);  
    }
    public List<PropertyValue> getPropertyValues() {  
        return this.propertyValueList;  
    }  
}
因而在BeanDefinition里加入private PropertyValues propertyValues;即可。相应的getBean方法也要变。
@Override  
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) {
        bean = beanDefinition.getBeanClass().newInstance();  
    }
    creatBean(bean, beanDefinition); 

    return bean;  
}  
 
public void creatBean(Object bean, BeanDefinition beanDefinition) throws Exception {  
    if (beanDefinition.getPropertyValues() != null)  
        creatBeanWithProperty(bean, beanDefinition);  
    }  
}
public void creatBeanWithProperty(Object bean, BeanDefinition beanDefinition) throws Exception{  
     int size = beanDefinition.getPropertyValues().getPropertyValues().size();
     List<PropertyValue> list = beanDefinition.getPropertyValues().getPropertyValues();
 
     for (int i = 0; i <size ; i++) {  
         if(list.get(i).getValue() instanceof BeanReference){  
             String beanName=((BeanReference)list.get(i).getValue()).getName();  

             Object referenceBean = getBean(beanName);
  
             String ms="set" + Character.toUpperCase(list.get(i).getName().charAt(0)) + list.get(i).getName().substring(1);  
                  
             Method m = bean.getClass().getDeclaredMethod(ms, referenceBean.getClass());
             m.invoke(bean, referenceBean);  
          } else {  
             String fieldName = list.get(i).getName();  
             Object value = list.get(i).getValue();
             // getDeclaredField是获得所有的字段(不仅仅是public)
             Field field = bean.getClass().getDeclaredField(fieldName);  
             field.setAccessible(true);
             field.set(bean, value);  
             field.setAccessible(false);
          }  
      }      
}  
 

测试一下:

public void Step3() throws Exception {  
    BeanFactory beanFactory = new AbstractBeanFactory();  

    BeanDefinition beanDefinition = new BeanDefinition();  
    beanDefinition.setBeanClassName("com.myspring.HelloWorldServiceImpl");  
  
    PropertyValues propertyValues = new PropertyValues();
    propertyValues.addPropertyValue(new PropertyValue("text","Hello World!"));  
    propertyValues.addPropertyValue(new PropertyValue("a",new Integer(15)));  
    beanDefinition.setPropertyValues(propertyValues);  
  
    ((AbstractBeanFactory)beanFactory).registerBeanDefinition("helloworld", beanDefinition);  

     HelloWorldServiceImpl h = (HelloWorldServiceImpl) beanFactory.getBean("helloworld");
     h.helloWorld();  
}  

测试结果:Hello World!15 ss

第四步:

上面的类里面的成员变量依然是int,double等的基本变量(String 在这里也算基本变量),如果我在类里面加一个引用变量呢?

private OutputService out;
public void helloWorld3(){
    out.output(text);
}

OutputService的output方法很简单就是输出text的内容。
那么下来,理所应当的我们会设计出一个参考类:

public class BeanReference {  
    private String name;       
    private Object bean;  
}  
对于BeanReference,我们可以按下面的方式使用
public void Step4() throws Exception {  
    BeanFactory beanFactory = new AbstractBeanFactory();  
  
    BeanDefinition beanDefinition = new BeanDefinition();  
    beanDefinition.setBeanClassName("com.myspring.HelloWorldServiceImpl");  
      
    BeanDefinition beanDefinition2 = new BeanDefinition();  
    beanDefinition2.setBeanClassName("com.myspring.OutputService");  
      
    BeanReference beanReference = new BeanReference("outPutService");  
    beanReference.setBean(beanDefinition2);  
  
    PropertyValues propertyValues = new PropertyValues();  
    propertyValues.addPropertyValue(new PropertyValue("text","Hello World! with referencebean"));  
    propertyValues.addPropertyValue(new PropertyValue("a",new Integer(15)));  
    propertyValues.addPropertyValue(new PropertyValue("out",beanReference));  
    beanDefinition.setPropertyValues(propertyValues);  
  
    ((AbstractBeanFactory)beanFactory).registerBeanDefinition("helloworld", beanDefinition);  
    ((AbstractBeanFactory)beanFactory).registerBeanDefinition("out", beanDefinition2);  
  
    HelloWorldServiceImpl h = (HelloWorldServiceImpl) beanFactory.getBean("helloworld");
 
    h.helloWorld3();
}  

下面的是creatBean部分

int size = beanDefinition.getPropertyValues().getPropertyValues().size();  
for (int i = 0; i <size ; i++) {  
    List<PropertyValue> list = beanDefinition.getPropertyValues().getPropertyValues();  
    if (list.get(i).getValue() instanceof BeanReference) {  
        String beanName = list.get(i).getName();
        // 循环调用getBean
        Object referenceBean = getBean(beanName);
        String ms = "set" + Character.toUpperCase(beanName.charAt(0)) + beanName.substring(1);  
        Method m = bean.getClass().getDeclaredMethod(ms, referenceBean.getClass());  
        m.invoke(bean, referenceBean);        
    } else {  
        String fieldName = list.get(i).getName();  
        Object value = list.get(i).getValue();
        // getDeclaredField是获得所有的字段(不仅仅是public)
        Field field = bean.getClass().getDeclaredField(fieldName);  
        field.setAccessible(true);
        field.set(bean, value);  
        field.setAccessible(false);  
    }          
}  

测试结果:Hello World! with referencebean

第五步:

public void Step5() throws Exception {  
    XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader();  
    xmlBeanDefinitionReader.loadBeanDefinitions("bin/resources/tinyioc.xml");  
       
    BeanFactory beanFactory = new AbstractBeanFactory();  
    for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getBeanDefinitionMap().entrySet()) {  
            ((AbstractBeanFactory)beanFactory).registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());  
    }

    HelloWorldServiceImpl helloWorldService = (HelloWorldServiceImpl) beanFactory.getBean("helloWorldService");
    helloWorldService.helloWorld3();
}  
在java中,获取文件有两种方式:
Class类下的getResource(String path)方法与ClassLoader类下的getResource(String path)方法。

ClassLoader.getResource()参数中不带"/"。默认就是根路径(在Eclipse中,根路径就是工程下的bin文件夹,在默认情况下,Eclipse会把项目中的src下的内容拷贝到bin下,因此也可以理解为根目录就是src目录)。
Class.getResource()可以带"/"也可以不带。一旦带了/就默认从根路径(就是bin 就是src)下查找了;如果没有‘/’就从这个类本身的那个路径下查找。

XmlBeanDefinitionReader的主要作用就是读取xml,然后转换成一个个BeanDefinition,再存储进BeanDefinitionMap,再创建一个BeanFactory,将BeanDefinitionMap中的记录一个一个再注册一遍。

public class XmlBeanDefinitionReader {  
    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);
    }
}
/** 
 * 通过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();  
  
    // 默认是工程目录
    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");  
        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");  
                if (property.getAttribute("ref")!="") {  
                    BeanReference br=new BeanReference(property.getAttribute("ref"));     
                    PropertyValue pV=new PropertyValue(name,br);  
                    bd.getPropertyValues().addPropertyValue(pV);  
                }  
                if (property.getAttribute("value")!="") {  
                    String value=property.getAttribute("value");  
                    PropertyValue pV=new PropertyValue(name, value);  
                    bd.getPropertyValues().addPropertyValue(pV);  
                }  
            }  
          
    }  
  
    public Map<String, BeanDefinition> getBeanDefinitionMap() {  
        return beanDefinitionMap;  
    }  
 

第六步:

XmlBeanDefinitionReader与AbstractBeanFactory,就能发现两个类里面都有beanDefinitionMap。

另外,在第五步中

BeanFactory beanFactory = new AbstractBeanFactory();  
for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getBeanDefinitionMap().entrySet()) {  
        ((AbstractBeanFactory)beanFactory).registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());  
}  

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

public void Step7() throws Exception {  
    ApplicationContext ac = new ApplicationContext("bin/resources/tinyioc.xml");  
    HelloWorldServiceImpl helloWorldService = (HelloWorldServiceImpl) ac.getBean("helloWorldService");  
    helloWorldService.helloWorld3();  
}

把XmlBeanDefinitionReader与AbstractBeanFactory合起来,也就是说要把getBean与loadBeanDefinitions装到一个类里面去。

public class ApplicationContext implements BeanFactory {  
    private AbstractBeanFactory abf = new AbstractBeanFactory();  

    public  ApplicationContext(String local) throws Exception {  
        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) {  
        Object obj=null;  
        try {  
            obj = abf.getBean(id);  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
        return obj;   
    }  
}

AbstractBeanFactory与ApplicationContext都继承了BeanFactory,然后我们不直接用BeanFactory,而是让ApplicationContext中装一个AbstractBeanFactory,这不就是最简单的代理模式么?
这里还有一些部分与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();
    // 查找beanDefinition  
    if (bean == null) {
        bean = doCreateBean(beanDefinition);  
        bean = initializeBean(bean, name);  
        beanDefinition.setBean(bean);  
    }  
    return bean;  
}
protected Object doCreateBean(BeanDefinition beanDefinition) throws Exception {  
    Object bean = createBeanInstance(beanDefinition);
    // 写入beanDefinition  
    beanDefinition.setBean(bean);
    applyPropertyValues(bean, beanDefinition);  
    return bean;  
}  

我写的代码中,没有上面的步骤,因此即使第二次get一个已经get过得bean,仍然会产生一个新的bena!

转:http://blog.csdn.net/dlf123321/article/details/39994071,http://blog.csdn.net/dlf123321/article/details/40017593

猜你喜欢

转载自z724130632.iteye.com/blog/2357151
今日推荐