1. 程式人生 > 其它 >反射、工廠模式、IOC容器淺嘗 一篇入門

反射、工廠模式、IOC容器淺嘗 一篇入門

反射

什麼是反射?

個人目前理解:反射是在程式執行期間獲取class資訊,用這些資訊做一些操作,例如本文後續要用的例項化一個物件以及為屬性賦值。

如何獲取class資訊

  1. 呼叫物件的getClass方法
  2. 呼叫類的class屬性
  3. 使用Class類中的forName()靜態方法(最安全,效能最好)即:Class.forName(“類的全路徑”)

常用方法

//獲取包名、類名
clazz.getPackage().getName()//包名
clazz.getSimpleName()//類名
clazz.getName()//完整類名
 
//獲取成員變數定義資訊
getFields()//獲取所有公開的成員變數,包括繼承變數
getDeclaredFields()//獲取本類定義的成員變數,包括私有,但不包括繼承的變數
getField(變數名)
getDeclaredField(變數名)
 
//獲取構造方法定義資訊
getConstructor(引數型別列表)//獲取公開的構造方法
getConstructors()//獲取所有的公開的構造方法
getDeclaredConstructors()//獲取所有的構造方法,包括私有
getDeclaredConstructor(int.class,String.class)
 
//獲取方法定義資訊
getMethods()//獲取所有可見的方法,包括繼承的方法
getMethod(方法名,引數型別列表)
getDeclaredMethods()//獲取本類定義的的方法,包括私有,不包括繼承的方法
getDeclaredMethod(方法名,int.class,String.class)
 
//反射新建例項
clazz.newInstance();//執行無參構造建立物件
clazz.newInstance(222,"韋小寶");//執行有參構造建立物件
clazz.getConstructor(int.class,String.class)//獲取構造方法
 
//反射呼叫成員變數
clazz.getDeclaredField(變數名);//獲取變數
clazz.setAccessible(true);//使私有成員允許訪問
f.set(例項,值);//為指定例項的變數賦值,靜態變數,第一引數給null
f.get(例項);//訪問指定例項變數的值,靜態變數,第一引數給null
 
//反射呼叫成員方法
Method m = Clazz.getDeclaredMethod(方法名,引數型別列表);
m.setAccessible(true);//使私有方法允許被呼叫
m.invoke(例項,引數資料);//讓指定例項來執行該方法

工廠模式簡要介紹

推薦先觀看最通俗易懂的講解工廠模式

什麼是工廠模式

個人簡單的理解:工廠模式是建造者模式,為使用者遮蔽了例項化物件的一些細節,可以為使用者做一些資料轉換等工作(對使用者遮蔽),防止使用者濫用建構函式,應用十分廣泛,如單例模式也是使用了這個思想。

三種工廠模式簡單介紹(其實就兩種)

  1. 簡單工廠模式:一個工廠類,提供一個靜態方法,為使用者生成物件,多型的時候使用。
  2. 工廠方法模式:提供一個抽象方法,這樣是為了糾正簡單工廠的缺點(簡單工廠在擴充套件的時候會違反開閉原則)。工廠方法模式在擴充套件的時候只需要繼承抽象基工廠即可。
  3. 抽象工廠模式:其實是有多個抽象方法,可以生成不同大類的產品,不過它擴充套件大類產品的時候還是要修改基類,違反了開閉原則。當抽象工廠模式只有一個產品體系的話就會退化成工廠方法模式。

IOC

簡介

IOC:控制反轉,我們不再需要主動的new物件,而是被動的接受物件。將物件交給容器管理,然後容器通過工廠模式為我們使用者提供物件。

將以上三種技術結合起來實現一個簡易的IOC容器

首先寫一個普通類People

public class People {
    private String name;
    private String age;

    @Override
    public String toString() {
        return "name: "+ name + "age: "+ age;
    }
}

寫一個beans.xml,將我們的普通類註冊進去

<?xml version="1.0" encoding="utf-8" ?>
<xml>
    <bean id="people" class="People">
        <property name="name">翔掌門</property>
        <property name="age">18</property>
    </bean>
</xml>

寫一個工廠BeanFactory

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class BeanFactory {
    // 底層是一個hashmap
    private static HashMap<String,Object> beanMap = new HashMap<>();
    public static Object getBean(String id) {
        return beanMap.get(id);
    }
    static {
        try {
            init();
        } catch (ParserConfigurationException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (SAXException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    // 在這裡完善hashmap
    private static void init() throws ParserConfigurationException, IOException, SAXException, ClassNotFoundException, InstantiationException, IllegalAccessException {
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
        Document document = documentBuilder.parse("resource/beans.xml");
        NodeList beans = document.getElementsByTagName("bean");
        for (int i = 0; i < beans.getLength(); i++) {
            // 獲取<bean>
            Element bean = (Element) beans.item(i);
            // 將這個bean的屬性先儲存起來
            Map<String,String> attrs = getAttrs(bean.getChildNodes());
            // 反射獲取類資訊
            Class<?> clazz = Class.forName(bean.getAttribute("class"));
            Object instance = clazz.newInstance();
            Field[] fields = clazz.getDeclaredFields();
            for (int j = 0; j < fields.length; j++) {
                Field field = fields[j];
                // 讓私有變數可以訪問
                field.setAccessible(true);
                // filed的名字就是示例變數的名字
                field.set(instance,attrs.get(field.getName()));
            }
            // 變數設定完畢
            beanMap.put(bean.getAttribute("id"),instance);
        }
    }

    private static Map<String, String> getAttrs(NodeList childNodes) {
        Map<String,String> map = new HashMap<>();
        for (int i = 0; i < childNodes.getLength(); i++) {
            Node node = childNodes.item(i);

            if(node.getNodeType() == Node.ELEMENT_NODE) {
                Element el = (Element) node;
                String key = el.getAttribute("name");
                String value = el.getFirstChild().getNodeValue();
                map.put(key,value);
            }
        }
        return map;
    }
}

核心就是getBean方法和beanMap。

載入工廠類的時候,將beans.xml解析,獲取所有物件以及屬性,通過反射構造物件,放在beanMap中。

執行

public class Demo2 {
    public static void main(String[] args) {
        Object people = BeanFactory.getBean("people");
        System.out.println(people);
    }
}