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


原文地址:https://www.cnblogs.com/zfyouxi/p/5204477.html