簡單學習下springIoc以及利用反射機制是如何實現的

什麼是springIoc

IOC(Inversion of Control)
意爲控制反轉,他是一種設計思想.並非實際的技術.最核心的思想就是將對象實例創建的控制權交給程序(IOC 容器)
IOC 容器:
一個管理所有控制反轉過程中創建的對象的 key-value 容器結構(可以簡單理解爲:hashMap)
Spring 的 IOC(控制反轉)中對象實例構建的方式有哪些?
1.無參構造創建
2.靜態工廠創建:類的靜態方法構造
3.實例工廠創建:對象的實例方法構造
演示代碼:
spring-ioc.xml 配置文件內容:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 創建方式1:空參構造創建 -->
    <bean id="a" class="com.czy.project.demo6.A"/>
    <!--
        創建方式2:靜態工廠創建
        調用A的createBObj方法來創建名爲b的對象放入容器
    -->
    <bean id="b" class="com.czy.project.demo6.A" factory-method="createBObj"/>
    <!--
            創建方式3:實例工廠創建
            調用實例a的createCObj方法來創建名爲c的對象放入容器
     -->
    <bean id="c" factory-bean="a" factory-method="createCObj"/>
</beans>

Class A 代碼:

public class A {
    public A() {
        System.out.println("A 無參構造器被調用了.");
    }
    public static B createBObj() {
        System.out.println("A 的靜態方法 createBObj 被調用了.");
        return new B();
    }
      public C createCObj() {
        System.out.println("A 的實例方法 createCObj 被調用了.");
        return new C();
    }
}

Class B 代碼:

public class B {
 
}

Class C 代碼:

public class C {

}

啓動類的代碼:

public class Main {
    public static void main(String[] args) {
        ApplicationContext context =
                new ClassPathXmlApplicationContext("spring-ioc.xml");
        A a = (A) context.getBean("a");
        B b = (B) context.getBean("b");
        C c = (C) context.getBean("c");
    }
}

輸出結果:

在這裏插入圖片描述
通過上面的案例,我們已經知道了 IOC 的概念及實例構建的方式.接下來我們來探討一下他的核心實現方式.

手寫springIoc實現思路

說明:以下代碼需要的xml配置文件以及實體類還是用上邊的

spring中是要在xml中配置bean的,都知道面向對象的思想,那你肯定能想到肯定是要把xml文件中的內容讀取出來,並用對象接收。那麼
1.創建用於接收xml中bean的配置類

/**
 * 用於接收xml中bean標籤的屬性
 * 沒什麼東西,就是根據bean標籤中的屬性來定義的
 * Author : czy
 */
public class BeanConfig {
    private String id;
    private String clazz;
    private String factoryMethod;
    private String factoryBean;
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getClazz() {
        return clazz;
    }
    public void setClazz(String clazz) {
        this.clazz = clazz;
    }
    public String getFactoryMethod() {
        return factoryMethod;
    }
    public void setFactoryMethod(String factoryMethod) {
        this.factoryMethod = factoryMethod;
    }
    public String getFactoryBean() {
        return factoryBean;
    }
    public void setFactoryBean(String factoryBean) {
        this.factoryBean = factoryBean;
    }
}

spring中入口是不是new ClassPathXmlApplicationContext(“xml文件名”);
那咱們就用XmlApplicationContext(“xml文件名”); //Are You Ok?–>ok
2.創建XmlApplicationContext類

/**
 * 相當於Springl中ClassPathXmlApplicationContext
 * 1、解析xml文件
 * 2、獲取xml文件所有的節點內容
 * 3、利用java反射機制創建實例對象
 * 4、將實例對象添加到ioc容器
 * czy say :SO EASY
 */
public class XmlApplicationContext {
    // xml路徑
    private String xmlPath;
    // 存放bean(也就是ioc的容器)
    private static HashMap map = new HashMap();
    //存放從xml中解析出來的所有bean
    private List<BeanConfig> beanConfigs = new ArrayList<BeanConfig>();

    //對外的構造函數
    public XmlApplicationContext(String xmlPath) {
        this.xmlPath = xmlPath;
        try {
            //解析xml到beanConfigs
            getAllElements();
            //初始化ioc容器
            init();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 讀取xml文件所有的節點內容,並放入beanConfigs集合
     */
    private void getAllElements() throws Exception {
        //1.指定路徑加載xml到jvm,形成輸入流,xml中的所有內容都在這個流中
        InputStream is = XmlApplicationContext.class.getClassLoader().getResourceAsStream(xmlPath);
        //2.創建解析對象
        SAXReader reader = new SAXReader();
        //3.獲得文檔對象(整個xml文件->將輸入流轉換成文檔對象)
        Document document = reader.read(is);
        //4.獲取根節點,也就是對應spring-ioc.xml中的樹幹beans
        Element rootElement = document.getRootElement();
        //5.獲取元素迭代器,用於獲取所有節點內容
        Iterator iterator = rootElement.elementIterator();
        while (iterator.hasNext()) {
            //用於接收xml中bean的屬性值
            BeanConfig beanConfig = new BeanConfig();
            //獲取beans的子節點bean
            Element bean = (Element) iterator.next();
            // 獲取bean的屬性迭代器
            Iterator beanAttributes = bean.attributeIterator();
            while (beanAttributes.hasNext()) {
                Attribute attribute = (Attribute) beanAttributes.next();
                String attributeName = attribute.getName();//屬性名
                String attributeData = (String) attribute.getData();//屬性值
                if ("id".equals(attributeName)) {
                    beanConfig.setId(attributeData);
                } else if ("class".equals(attributeName)) {
                    beanConfig.setClazz(attributeData);
                } else if ("factory-method".equals(attributeName)) {
                    beanConfig.setFactoryMethod(attributeData);
                } else {
                    beanConfig.setFactoryBean(attributeData);
                }
            }
            //將這條bean的屬性值放入beanConfigs集合
            beanConfigs.add(beanConfig);
        }
    }
    
    /**
     * 這裏初始化ioc容器是關鍵
     * 這裏就要利用java的反射機制來創建bean實例
     *
     * @throws Exception
     */
    private void init() throws Exception {
        /**
         * 這裏說一點:
         * 看spring-ioc.xml中:bean標籤有class屬性時就沒有factory-bean屬性,反之
         * 有class屬性時,就要判斷是空參構造創建還是靜態工廠創建,也就是判斷有沒有factory-method屬性
         * 有factory-bean時,必然是實例工廠創建-必須有factory-method
         * 這點要捋清楚
         */
        for (BeanConfig bean : beanConfigs) {
            if (null != bean.getClazz()) {
                //通過全限定類名拿到Class實例clazz
                Class clazz = Class.forName(bean.getClazz());
                if (null != bean.getFactoryMethod()) {//靜態工廠創建bean實例
                    //方法名獲取method對象
                    Method method = clazz.getDeclaredMethod(bean.getFactoryMethod());
                    putBean(bean.getId(), method.invoke(null));
                } else {//構造器創建bean實例
                    putBean(bean.getId(), clazz.newInstance());
                }
            } else if (null != bean.getFactoryBean()) {//實例工廠創建bean實例
                //從容器中拿到實體bean
                Object obj = getBean(bean.getFactoryBean());
                Method method = obj.getClass().getDeclaredMethod(bean.getFactoryMethod());
                putBean(bean.getId(), method.invoke(obj));
            } else {
                System.out.println("czy不知道在搞什麼鬼");
            }
        }
    }

    /**
     * 添加實例對象到ioc容器
     *
     * @param id:就是bean中自定義的id
     * @param object :就是反射機制創建的實例對象
     */
    public void putBean(String id, Object object) {
        map.put(id, object);
    }

    /**
     * 通過id獲取實例對象
     *
     * @param id:就是bean中自定義的id
     * @return
     */
    public Object getBean(String id) {
        return map.get(id);
    }
}

3.創建啓動類Main
是不是和上邊開局演示的入口代碼很像。

public class Main {
    public static void main(String[] args) {
        XmlApplicationContext context = new XmlApplicationContext("spring-ioc.xml");
        A a = (A)context.getBean("a");
        B b = (B) context.getBean("b");
        C c = (C) context.getBean("c");
    }
}

那就要檢驗結果是不是盡人意了。
4.測試
哈哈,測試交給讀者來測試吧!相信你也是很感興趣的。
5.總結
springIoc的思路大致就是這樣子的:

1、解析xml文件
2、獲取xml文件所有的節點內容
3、利用java反射機制創建實例對象
4、將實例對象添加到ioc容器

但是要知道spring的內部實現可不單單是這幾百行啊。
溫馨提示:自己建個工程,代碼直接拷下來就能用,並且讀起來更有感覺。代碼中的註釋純手寫,自我感覺還是比較詳細的,一行一行的讀。相信初學者看完後肯定會知道spring是如何實現ioc的思路的。並且也能掌握如何把xml中的內容讀取出來並用對象接收。

最後爲了方便大家創建工程:

<dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.1.8.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>dom4j</groupId>
      <artifactId>dom4j</artifactId>
      <version>1.6.1</version>
    </dependency>   
</dependencies> 
發佈了21 篇原創文章 · 獲贊 34 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章