Tiny Spring Analysis

Bean factory interface class:

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

 

Bean wrapper class:

/**  
 * The content and metadata of the Bean are stored in the BeanFactory, which wraps the entity of the 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 ();  
        }
    }
    //Omit some get/set methods  
}  

 

Bean factory implementation class:

public class AbstractBeanFactory implements BeanFactory {  
    // Store the details of all beans in the Factory
    private Map<String, BeanDefinition> beanDefinitionMap =
          new ConcurrentHashMap<String, BeanDefinition>();
    // Store the Names of all beans in the Factory
    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;  
    }  
      
    /**
     * Register the newly added BeanDefinition to the BeanDefinitionMap
     */  
    public void registerBeanDefinition(String name, BeanDefinition beanDefinition)
        throws Exception {  
        beanDefinitionMap.put(name, beanDefinition);  
        beanDefinitionNames.add(name);
    }  
}  

So far, a simple IOC container has been built.

 

have a test:

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

first step:

BeanDefinition is instantiated directly from the Bean object.

public void Step1() throws Exception {  
    BeanFactory beanFactory = new AbstractBeanFactory();
    BeanDefinition beanDefinition = new BeanDefinition(new HelloWorldServiceImpl());
    // register
    ((AbstractBeanFactory)beanFactory).registerBeanDefinition("helloworld",beanDefinition);
          
    HelloWorldServiceImpl h = (HelloWorldServiceImpl) beanFactory.getBean("helloworld");
    h.helloWorld2();  
}
Print result: helloworld

 

Step 2:

BeanDefinition is instantiated according to className.

public void Step2() throws Exception {  
    BeanFactory beanFactory = new AbstractBeanFactory();  
    BeanDefinition beanDefinition = new BeanDefinition();
    beanDefinition.setBeanClassName("com.myspring.HelloWorldServiceImpl");
    // register
    ((AbstractBeanFactory)beanFactory).registerBeanDefinition("helloworld",beanDefinition);
          
    HelloWorldServiceImpl h = (HelloWorldServiceImpl) beanFactory.getBean("helloworld");
 
    h.helloWorld2();  
}  
Then add the following code before the bean returned by the AbstractBeanFactory.getBean() method:
if (bean == null) {
     bean = beanDefinition.getBeanClass().newInstance();
}

  

third step:

What if we have simple parameters inside HelloWorldServiceImpl? Such as:
private String text;
private int a;

public void helloWorld() {
    System.out.println(text + a + " ss");
}
Since there are parameters, we design a PropertyValue:
/**
 * Attribute injection for beans
 */
public class PropertyValue {  
    private final String name;
    private final Object value;

    // Omit get, set methods
}  
Then add a List<PropertyValue> pvs = new ArrayList<PropertyValue> in the BeanDefinition. After all, a class cannot be limited to only one property.
If there are duplicate properties in a class, as configured in the spring configuration file:
<bean id="userService" class="com.bjsxt.services.UserService" >  
     <property name="userDao" bean="u" />  
     <property name="userDao" bean="u" />  
</bean>  
So we need to add another class PropertyValues
/**
 * Wraps all PropertyValues ​​of an object. <br/>
 */  
public class PropertyValues {  
    private final List<PropertyValue> propertyValueList = new ArrayList<PropertyValue>();  
    public PropertyValues() {  
    }  
  
    public void addPropertyValue(PropertyValue pv) {  
        // TODO Here you can judge the duplicate propertyName

        this.propertyValueList.add(pv);  
    }
    public List<PropertyValue> getPropertyValues() {  
        return this.propertyValueList;  
    }  
}
So add private PropertyValues ​​propertyValues; in BeanDefinition. The corresponding getBean method should also be changed.
@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 is to get all fields (not just public)
             Field field = bean.getClass().getDeclaredField(fieldName);  
             field.setAccessible(true);
             field.set(bean, value);  
             field.setAccessible(false);
          }  
      }      
}  
 

have a test:

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

Test result: Hello World! 15 ss

 

the fourth step:

The member variables in the above class are still basic variables such as int, double, etc. (String is also considered a basic variable here). What if I add a reference variable to the class?

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

The output method of OutputService is very simple to output the content of text.
Then, as it should be, we will design a reference class:

public class BeanReference {  
    private String name;       
    private Object bean;  
}  
For BeanReference, we can use as follows
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();
}  

Below is the creatBean part

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();
        // Call getBean in a loop
        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 is to get all fields (not just public)
        Field field = bean.getClass().getDeclaredField(fieldName);  
        field.setAccessible(true);
        field.set(bean, value);  
        field.setAccessible(false);  
    }          
}  

Test result: Hello World! with referencebean

 

the fifth step:

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 ();
}  
In java, there are two ways to obtain files:
the getResource(String path) method under the Class class and the getResource(String path) method under the ClassLoader class.

 

ClassLoader.getResource() parameter without "/". The default is the root path (in Eclipse, the root path is the bin folder under the project. By default, Eclipse will copy the contents of the src in the project to the bin, so it can also be understood that the root directory is the src directory) .
Class.getResource() can take "/" or not. Once ' / ' is included, it will be searched from the root path (that is, bin is src) by default; if there is no ' / ', it will be searched from the path of the class itself.

The main function of XmlBeanDefinitionReader is to read xml, then convert it into BeanDefinition, store it in BeanDefinitionMap, create a BeanFactory, and register the records in BeanDefinitionMap one by one.

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

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326392437&siteId=291194637