1. 程式人生 > >手寫實現Spring IOC

手寫實現Spring IOC

Spring中有兩大核心內容:
一.IOC
二.AOP
今天我手寫實現了IOC,來總結一下IOC的原理和實現方式

首先IOC底層所用到的技術
1>xml配置檔案
2>dom4j解析xml
3>工廠設計模式
4>反射
5>內省
首先看一下IOC demo的流程圖
p1
執行環境
1.jdk1.8,IntelliJ IDEA
ps(如果報錯報找不到xml檔案,請在target目錄下的classes中手動複製)
首先我們構建bean類:

package com.example.writeioc.config;

import java.util.ArrayList;
import
java.util.List; public class Bean { private String name; private String className; private List<Property> properties = new ArrayList<Property>(); public String getName() { return name; } public void setName(String name) { this.name = name; } public
String getClassName() { return className; } public void setClassName(String className) { this.className = className; } public List<Property> getProperties() { return properties; } public void setProperties(List<Property> properties) { this
.properties = properties; } @Override public String toString(){ return "Bean[name = ]" + name + " , className = " + className + " , properties" + properties + " ]"; } }

然後例項寫兩個bean類

package com.example.writeioc.bean;

public class A {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

package com.example.writeioc.bean;

public class B {
    private A a;

    public A getA() {
        return a;
    }

    public void setA(A a) {
        this.a = a;
    }
}

然後寫一個屬性類(Property):

package com.example.writeioc.config;

public class Property {
    private String name;
    private String value;
    private String ref;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public String getRef() {
        return ref;
    }

    public void setRef(String ref) {
        this.ref = ref;
    }
}

工廠介面:

package com.example.writeioc.main;

public interface BeanFactory {
    //根據bean的name獲得bean物件
    Object getBean(String beanName);
}

xml檔案

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean name="A" class="com.example.writeioc.bean.A">
        <property name="name" value="Tom"></property>
    </bean>

    <bean name="B" class="com.example.writeioc.bean.B">
        <property name ="a" ref="A"></property>
    </bean>
</beans>

解析xml類(ConfigManager)

package com.example.writeioc.config;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ConfigManager {
    //讀取配置檔案,並返回結果
    public static Map<String,Bean> getConfig(String path){
        Map<String,Bean> map = new HashMap<String,Bean>();
        //1建立解析器
        SAXReader saxReader = new SAXReader();
        //2載入配置檔案
        InputStream is = ConfigManager.class.getResourceAsStream(path);
        Document document = null;
        try{
            document = saxReader.read(is);
        }catch (DocumentException e){
            e.printStackTrace();
            throw new RuntimeException("請檢查xml配置");
        }
        //3定義xpath表示式去除所有bean元素
        //   //從任意節點選擇名稱為bean的節點   /從父節點選擇  不寫則是從當前節點選擇
        String xpath = "//bean";
        //4對bean元素進行遍歷
        List<Element> list = document.selectNodes(xpath);
        if(list != null){
            for(Element beanFile:list){
                Bean bean = new Bean();
                //將class,name等屬性封裝到bean物件中
                String name = beanFile.attributeValue("name");
                String className = beanFile.attributeValue("class");
                bean.setName(name);
                bean.setClassName(className);
                //獲得bean元素下所有property元素,並將其屬性封裝到property子元素中
                List<Element> children = beanFile.elements("property");
                if(children != null){
                    for(Element child : children){
                        Property property = new Property();
                        String pName = child.attributeValue("name");
                        String pValue = child.attributeValue("value");
                        String pRef = child.attributeValue("ref");

                        property.setName(pName);
                        property.setRef(pRef);
                        property.setValue(pValue);
                        //將property物件封裝到bean中
                        bean.getProperties().add(property);
                    }
                }
                //將bean物件封裝到map中用於返回
                map.put(name,bean);
            }
        }
        return map;
    }
}

通過內省注入bean屬性類:

package com.example.writeioc.utils;


import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;

/**
 * @author Jet
 */
public class BeanUtils {
    public static Method getWriteMethod(Object beanObj,String name){
        //使用內省實現(基於java反射專門用於操作bean的屬性的api)
        Method method = null;
        try{
            //1:分析bean物件-->BeanInfo
            BeanInfo beanInfo = Introspector.getBeanInfo(beanObj.getClass());
            //2:根據BeanInfo獲取所有屬性的描述器
            PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
            //3遍歷描述器
            if(pds != null){
                for(PropertyDescriptor pd : pds){
                    //判斷當前屬性是否是我們要找的屬性
                    String pName = pd.getName();
                    if(pName.equals(name)){
                        method = pd.getWriteMethod();
                    }
                }
            }
            //4返回找到的set方法
        }catch (IntrospectionException e){
            e.printStackTrace();
        }
        //如果沒有找到-->丟擲異常,提示使用者檢查是否建立對應的set方法
        if(method == null){
            throw new RuntimeException("請檢查 " + name + "屬性的set方法是否建立");
        }
        return method;
    }
}

裝配初始化bean類(ClassPathXmlApplicationContext)

package com.example.writeioc.main;



import com.example.writeioc.config.Bean;
import com.example.writeioc.config.ConfigManager;
import com.example.writeioc.config.Property;
import com.example.writeioc.utils.BeanUtils;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class ClassPathXmlApplicationContext implements BeanFactory{
    //配置資訊
    private Map<String,Bean> config;
    //用一個Map來做spring的容器,放置spring所管理的物件
    private Map<String,Object> context = new HashMap<String,Object>();

    //在classPathXmlApplicationContext已建立就初始化容器

    @Override
    //根據Bean名稱獲得bean例項
    public Object getBean(String beanName){
        Object bean = context.get(beanName);
        return bean;
    }

    public ClassPathXmlApplicationContext(String path){
        //1讀取配置檔案獲取初始化的bean的資訊
        config  = ConfigManager.getConfig(path);

        //2遍歷配置,初始化bean
        if(config != null){
            for(Map.Entry<String,Bean> en : config.entrySet()){
                  //獲取配置中的bean資訊
                String beanName = en.getKey();
                Bean bean = en.getValue();
                Object exsitBean = context.get(beanName);
                //因為createBean方法也會向context中放置bean,我們在初始化的時候先要檢視是否已經存在bean
                //如果不存在再建立bean
                if(exsitBean == null){
                    //根據bean配置建立bean物件
                    Object beanObj = createBean(bean);
                    //3將初始化的bean放入容器
                    context.put(beanName,beanObj);
                }
            }
        }
    }
    //根據bean配置建立bean物件
    private Object createBean(Bean bean){
        //1獲得要建立的bean的class
        String className = bean.getClassName();
        Class clazz = null;
        try{
            clazz = Class.forName(className);
        }catch (ClassNotFoundException e){
            e.printStackTrace();
            throw new RuntimeException("請檢查bean的class配置 " + className);
        }
        //將class對應的物件創建出來
        Object beanObj = null;
        try{
            beanObj = clazz.newInstance();
        }catch (Exception e){
            e.printStackTrace();
            throw new RuntimeException("bean沒有空參構造" + className);
        }
        //2獲得bean的屬性,將其注入
        if(bean.getProperties() != null){
            for(Property property : bean.getProperties()){
                //1:簡單value注入
                //獲取要注入的屬性名稱
                String name = property.getName();
                //根據屬性名稱獲得注入屬性對應的set方法
                Method setMethod = BeanUtils.getWriteMethod(beanObj,name);
                //建立一個需要注入bean中的屬性值
                Object parm = null;
                if(property.getValue() != null){
                    //獲取要注入的屬性值
                    String value = property.getValue();
                    parm = value;
                }
                //2其他bean的注入
                if(property.getRef() != null){
                    //先從容器中查詢當前要注入的bean是否已經建立並放入容器中
                    Object exsitBean = context.get(property.getRef());
                    if(exsitBean == null){
                        //如果容器中不存在,則要建立
                        exsitBean = createBean(config.get(property.getRef()));
                        //將建立好的bean放入容器
                        context.put(property.getRef(),exsitBean);
                    }
                    parm = exsitBean;
                }
                //呼叫set方法注入
                try {
                    setMethod.invoke(beanObj,parm);
                }catch (Exception e){
                    e.printStackTrace();
                    throw new RuntimeException("bean的屬性 " + parm + " 沒有對應的set方法,或者引數不正確" + className);
                }
            }
        }
        return beanObj;
    }
}