zoukankan      html  css  js  c++  java
  • Tiny Spring 分析一

    近期一直想看spring的源代码,可是奈何水平太低,庞杂的源代码令我一阵阵的头晕。
    非常有幸,在网上看到了黄亿华大神的<<1000行代码读懂Spring(一)- 实现一个主要的IoC容器>>
    认为相当不错,就以他的代码为基础,自己又写了一个IoC容器(基本上都是黄的代码,我仅仅改了一部分)
    原网页例如以下
    http://my.oschina.net/flashsword/blog/192551


    特此声明,本文不能算严格意义上的原创,仅仅能算是黄文章的再次解读吧。
    开工
    例如以下的代码不须要解释了吧。

    step1

    package com.myspring.factory;
    
    
    public interface BeanFactory {
    	Object getBean(String name) throws Exception;
    }


    package com.myspring.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方法
    }


    package com.myspring.factory;
    
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    
    import com.myspring.bean.BeanDefinition;
    
    public class AbstractBeanFactory implements BeanFactory {
            
    	//存放Factory里的全部bean的具体信息
    	//能够理解为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里
    	*这里仅仅是将beanDefinition的定义放入"注冊表"(beanDefinitionMap) 至于beanDefinition是否有错误 以后再说 这里无论
    	**/
    	public void registerBeanDefinition(String name, BeanDefinition beanDefinition) throws Exception {
    		beanDefinitionMap.put(name, beanDefinition);
    		beanDefinitionNames.add(name); //为什么如今不检測beanDefinition的细节 比如有没有bean? layzload
    	}
    }


    来測试一下,以HelloWorldServiceImpl为例
    package com.myspring;
    
    
    public class HelloWorldServiceImpl {
    
            public void helloWorld2() {
        		System.out.println("hello");
    	}
    }

    測试代码例如以下
    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();
    	}

    非常easy,打印出了hello;
    ok 至此 我们最简单的IoC就搭建完毕了。
    如今我们就一步一步地完好我们的容器


    step2

    第一步的时候,beanDefinition里面我们直接放入了bean,向以下这个,我们放入classname会怎样?
    @Test
    	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();
    	}

    解决的方法非常easy,在AbstractBeanFactory/getBean()方法返还bean之前加上例如以下代码就可以。
    if (bean==null) {
         bean=beanDefinition.getBeanClass().newInstance();

    }

    step3

    继续走,假设我们在HelloWorldServiceImpl里面有简单的參数怎么办,示意代码例如以下
    private String text;
    private int    a;

    public void helloWorld(){
    System.out.println(text+a+" ss");
    }
    既然有參数,那我们就设计一个PropertyValue
    package com.myspring.bean;
    
    
    /**
     * 用于bean的属性注入
     */
    public class PropertyValue {
        private final String name;
        private final Object value;  //	省略get/set  后文对简单的get/set方法将直接省略 不再说明
    }

    下来就是在BeanDefinition里面添加一个List<PropertyValue> pvs=new ArrayList<PropertyValue>,毕竟不能限制一个类仅仅有一个属性吧。
    一个类中不会仅仅有一个參数,那必定就是List了。

    好像说的有道理,眼下我们是人为地给pvs里面加数据,用add()方法,假设一个类中,有反复的属性呢?
    开玩笑,java里能出现两个变量同名吗?
    当然java类里是不存在的,可我们得知道成型的spring但是从xml里面读取数据的
    假设我写成这样 怎么办?

      <bean id="userService" class="com.bjsxt.services.UserService" >
           <property name="userDao" bean="u" />
           <property name="userDao" bean="u" />
      </bean>

    因此我们得再增加一个类PropertyValues

    package com.myspring.bean;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * 包装一个对象全部的PropertyValue。<br/>
     * 为什么封装而不是直接用List?由于能够封装一些操作。

    */ public class PropertyValues { private final List<PropertyValue> propertyValueList = new ArrayList<PropertyValue>(); public PropertyValues() { } public void addPropertyValue(PropertyValue pv) { //TODO:这里能够对于反复propertyName进行推断,直接用list没法做到 // System.out.println(pv.getName()+pv.getValue()); 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); // 可不能够仅仅传一个beandefinition
    											// 在方法里在beanDefinition.getBean();
    											// 这样还能够少传递一个对象呢?
    		return bean;
    	}


    大家看到了关键问题在creatBean方法上
        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();
                    //    System.out.println("par "+list.get(i).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();
                        Field field = bean.getClass().getDeclaredField(fieldName); // getDeclaredField是获得全部的字段(不不过public)
                        field.setAccessible(true); // 这一步必须有
                        field.set(bean, value);
                        field.setAccessible(false); // 这一步必须有
                    }
                }
            
        }
    
    
    
    


    ok,看看測试代码
    public void Step3() throws Exception {
    		// 1.初始化beanfactory
    		BeanFactory beanFactory = new AbstractBeanFactory();
    
    
    		// 2.bean定义
    		BeanDefinition beanDefinition = new BeanDefinition();
    		beanDefinition.setBeanClassName("com.myspring.HelloWorldServiceImpl");
    
    
    		// 3.设置属性
    		PropertyValues propertyValues = new PropertyValues();
    		propertyValues.addPropertyValue(new PropertyValue("text","Hello World!"));
    		propertyValues.addPropertyValue(new PropertyValue("a",new Integer(15)));
    		beanDefinition.setPropertyValues(propertyValues);
    
    
    		// 4.注冊bean
    		((AbstractBeanFactory)beanFactory).registerBeanDefinition("helloworld", beanDefinition);
    
    
    		HelloWorldServiceImpl h = (HelloWorldServiceImpl) beanFactory
    				.getBean("helloworld");
    		h.helloWorld();
    	}
    
    


    測试结果
    Hello World!15 ss

    step4

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

    例如以下
    private OutputService out;
    public void helloWorld3(){
        out.output(text);
        }
    OutputService的output方法非常easy就是输出text的内容。
    那么下来,理所应当的我们会设计出一个參考类
    package com.myspring;
    
    public class BeanReference {
        private String name;     
        private Object bean;
    }

    对于BeanReference,我们能够按以下的方式使用
    	public void Step4() throws Exception {
    		// 1.初始化beanfactory
    		BeanFactory beanFactory = new AbstractBeanFactory();
    
    
    		// 2.bean定义
    		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);
    
    
    		// 3.设置属性
    		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);
    
    
    		// 4.注冊bean
    		((AbstractBeanFactory)beanFactory).registerBeanDefinition("helloworld", beanDefinition);
    		((AbstractBeanFactory)beanFactory).registerBeanDefinition("out", beanDefinition2);
    
    
    		HelloWorldServiceImpl h = (HelloWorldServiceImpl) beanFactory
    				.getBean("helloworld");
    		h.helloWorld3();
    	}


    看到第四步注冊bean的时候,大家应该想到假设有n个bean,我就得调用registerBeanDefinition方法n次吗?


    眼下就仅仅能是这种方法了,技术用for循环,beanDefinition的名字也没办法,如今毕竟是模拟,各个变量的名字都是由人输入的,以后会从xml中读,就简单多了。


    以下的麻烦的代码大家应该也能猜处理,就是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();
    					Object referenceBean=getBean(beanName);           //循环调用getBean
    					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();
    					Field field = bean.getClass().getDeclaredField(fieldName); // getDeclaredField是获得全部的字段(不不过public)
    					field.setAccessible(true); // 这一步必须有
    					field.set(bean, value);
    					field.setAccessible(false); // 这一步必须有
    				}
    			
    			}


    还是上面的问题,假设不区分是引用类型还是基本类型能够不?


    property里面的value是object类型的,假设我们给里面放的是int,直接set到bean里面,但是这个object要是BeanReference呢,还得取出BeanReference里面的value,然后在循环getbean()。你们说不区分能行吗?
    測试结果
    Hello World! with referencebean


  • 相关阅读:
    P1144 最短路计数
    P2966 [USACO09DEC]牛收费路径Cow Toll Paths
    P2419 [USACO08JAN]牛大赛Cow Contest
    P1462 通往奥格瑞玛的道路
    P1346 电车
    P1339 [USACO09OCT]热浪Heat Wave
    P1418 选点问题
    P1330 封锁阳光大学
    P1182 数列分段Section II
    P2661 信息传递
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/5204477.html
Copyright © 2011-2022 走看看