扒一扒spring,dom4j實現模擬實現讀取xml

haifeiWu發表於2017-08-23

今天leadr提出需求,原來公司專案中讀取解析xml檔案的程式碼效率太低,考慮切換一種xml為資料封裝格式與讀取方式以提高效率。我這靈機一動spring對bean的依賴注入就是讀取xml檔案,可以嘗試扒一扒spring的原始碼,來實現一個輕量級的方案。

重構xml檔案,向spring的xml檔案格式看齊

重構完成的xml檔案格式如下:

<?xml version="1.0" encoding="UTF-8"?>

<beans>
    <bean name="iaminformation" class="com.whf.readxml.model.IamConfig">
        <property name="iamUrl" value="http:192.168.7.154:8080" />
        <property name="token" value="abcdefg" />
        <property name="iamName" value="中電科" />
        <property name="sourceNumber" value="4" />
        <property name="timeSpan" value="12" />
    </bean>

    <bean name="plugin" class="com.whf.readxml.model.PluginAttributes">
        <property name="name" value="ADTest1" />
        <property name="pluginType" value="AdPlugin" />
        <property name="code" value="100001" />
        <property name="url" value="ldap://192.168.7.241:389" />
        <property name="adminPsw" value="1qaz2wsx2017" />
        <property name="adminName" value="QUARKDATA\administrator" />
        <property name="adDn" value="OU=test,DC=quarkdata,DC=com" />
        <property name="securityAuthentication" value="simple" />

        <property name="staffFieldMatch">
            <bean name="staffFieldMatch" class="com.whf.readxml.model.StaffFieldMatch">
                <property name="idField" value="objectGUID" />
                <property name="userNameField" value="sAMAccountName" />
                <property name="firstNameField" value="givenName" />
                <property name="lastNameField" value="sn" />
                <property name="displayNameField" value="displayName" />
                <property name="phoneNumberField" value="" />
                <property name="telField" value="homePhone" />
                <property name="emailField" value="email" />
            </bean>
        </property>

        <property name="orgFieldMatch">
            <bean name="orgFieldMatch" class="com.whf.readxml.model.OrgFieldMatch">
                <property name="idField" value="objectGUID" />
                <property name="nameField" value="name" />
                <property name="displayNameField" value="ou" />
            </bean>
        </property>

        <property name="groupFieldMatch">
            <bean name="groupFieldMatch" class="com.whf.readxml.model.GroupFieldMatch">
                <property name="idFied" value="objectGUID" />
                <property name="nameField" value="sAMAccountName" />
                <property name="displayNameField" value="displayName" />
                <property name="decriptionField" value="info" />
            </bean>
        </property>
    </bean>

    <bean name="plugin" class="com.whf.readxml.model.PluginAttributes">
        <property name="name" value="LDAPTest1" />
        <property name="pluginType" value="LdapPlugin" />
        <property name="code" value="100002" />
        <property name="url" value="ldap://192.168.7.245/" />
        <property name="adminPsw" value="test123456" />
        <property name="adminName" value="cn=admin,dc=thundersoft,dc=com" />
        <property name="adDn" value="dc=thundersoft,dc=com" />
        <property name="securityAuthentication" value="simple" />
        <property name="staffFieldMatch">
            <bean name="staffFieldMatch" class="com.whf.readxml.model.StaffFieldMatch">
                <property name="idField" value="gidNumber" />
                <property name="userNameField" value="uid" />
                <property name="firstNameField" value="givenName" />
                <property name="lastNameField" value="sn" />
                <property name="displayNameField" value="displayName" />
                <property name="phoneNumberField" value="telephoneNumber" />
                <property name="telField" value="tel" />
                <property name="emailField" value="email" />
            </bean>
        </property>

        <property name="orgFieldMatch">
            <bean name="orgFieldMatch" class="com.whf.readxml.model.OrgFieldMatch">
                <property name="idField" value="dn" />
                <property name="nameField" value="ou" />
                <property name="displayNameField" value="orgDisplayName" />
            </bean>
        </property>

        <property name="groupFieldMatch">
            <bean name="groupFieldMatch" class="com.whf.readxml.model.GroupFieldMatch">
                <property name="idFied" value="dn" />
                <property name="nameField" value="cn" />
                <property name="displayNameField" value="groupDisplayName" />
                <property name="decriptionField" value="description" />
            </bean>
        </property>
    </bean>
</beans>複製程式碼

看起來很眼熟的有沒有,跟spring的配置檔案一樣哦。

扒一扒spring讀取xml檔案的原始碼

手動扒了一下spring讀取xml檔案的程式碼,由於spring過於龐大,讀取spring的xml方法又按照讀取不同的標籤被分拆出n多個方法,樓主能力有限就不在這裡賣弄了,不過spring從配置檔案把bean載入到bean工廠跟樓主下面讀取xml檔案的方式理論上是一樣的。

廢話不多說,上程式碼:

/**
 * 模擬spring依賴注入的方式讀取xml檔案.
 * @author whf
 * @date Aug 23, 2017
 */
public class XMLBeanFactory {

    private String xmlName;
    private SAXReader reader;
    private Document document;

    /**
     * 構造方法.
     * @param xmlName xmlName.
     */
    public XMLBeanFactory(String xmlName) { // 在構造方法中
        try {
            this.xmlName = xmlName;
            reader = new SAXReader();
            document = reader.read(this.getClass().getClassLoader().getResourceAsStream(xmlName));
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }

    /**
     * 獲取相同型別的bean.
     * @param type bean的class type.
     * @return 返回相同型別的bean的list.
     * @throws Exception Exception.
     */
    public <T> List<T> getBeansOfType(Class<T> type) throws Exception {
        List<T> objects = new ArrayList<>();
        try {
            Element root = document.getRootElement();
            List<Element> beans = root.elements();
            if (beans.size() > 0) {
                for (Element bean : beans) {
                    if (bean.attributeValue("class").equals(type.getName())) {
                        T object = null;
                        String clazz = bean.attributeValue("class");
                        // 通過反射來建立物件
                        Class beanClass = Class.forName(clazz);
                        object = (T) beanClass.newInstance();

                        List<Element> propertys = bean.elements();

                        if (propertys.size() > 0) {
                            for (Element property : propertys) {
                                String key = property.attributeValue("name");
                                Field field = beanClass.getDeclaredField(key);
                                field.setAccessible(true);

                                List<Element> childBean = property.elements();

                                // 如果property下內嵌bean
                                if (childBean.size() > 0) {
                                    Object childObject = getBean(key, property);
                                    field.set(object, childObject);
                                } else {
                                    /*
                                     * 此屬性值是一個字串.這裡單獨處理int,float型別變數.如果不處理,
                                     * 會將String型別直接賦值給int型別,發生ClassCastException
                                     */
                                    String value = property.attributeValue("value");
                                    // 需要對型別進行判斷
                                    if (field.getType().getName().equals("int")) {
                                        // 整數
                                        int x = Integer.parseInt(value);
                                        field.set(object, x);
                                        continue;
                                    }
                                    if (field.getType().getName().equals("float")) {
                                        // 浮點數
                                        float y = Float.parseFloat(value);
                                        field.set(object, y); // 注意double可以接受float型別
                                        continue;
                                    }
                                    field.set(object, value);// 處理String型別
                                }
                            }
                        }
                        objects.add(object);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return objects;
    }
    /**
     * 獲取property內嵌的bean.
     * @param name id或者bean的name
     * @param root 根節點.
     * @return 返回封裝完整的bean.
     * @throws Exception Exception.
     */
    public Object getBean(String name, Element root) throws Exception {

        Object object = null;
        List<Element> beans = root.elements();
        if (beans.size() > 0) {
            for (Element bean : beans) {
                if (bean.attributeValue("name").equals(name)) {
                    // 如果bean name相同則開始建立物件
                    String clazz = bean.attributeValue("class");
                    // 通過反射來建立物件
                    Class beanClass = Class.forName(clazz);
                    object = beanClass.newInstance();

                    List<Element> propertys = bean.elements();

                    if (propertys.size() > 0) {
                        for (Element property : propertys) {
                            String key = property.attributeValue("name");
                            Field field = beanClass.getDeclaredField(key);
                            field.setAccessible(true);

                            List<Element> childBean = property.elements();

                            // 如果property下內嵌bean
                            if (childBean.size() > 0) {
                                field.set(object, getBean(key, property));
                            }

                            if (property.attribute("ref") != null) {
                                /*
                                 * 此屬性的值是一個物件.這裡由於直接呼叫getBean方法賦值給物件,返回的物件一定是Bean引數的物件, 因此強制轉換不會出問題
                                 */
                                String refid = property.attributeValue("ref");
                                field.set(object, getBean(refid));
                            } else {
                                /*
                                 * 此屬性值是一個字串.這裡單獨處理int,float型別變數.如果不處理,會將String型別直接賦值給int型別,
                                 * 發生ClassCastException
                                 */
                                String value = property.attributeValue("value");
                                // 需要對型別進行判斷
                                field.set(object, value);// 處理String型別
                            }
                        }
                    }

                }
            }
        }

        return object;
    }

}複製程式碼

樓主的程式碼就是實現讀取xml檔案中相同型別的bean封裝到list中返回。具體如何實現,看程式碼,註釋寫的很清楚了。

檔案讀取的效率提升120多倍

程式碼完成之後,對比之前的讀取xml的程式碼,比之前的效率提升了120。

總結一下

大牛都說要看開源框架的原始碼,收穫會怎樣怎樣,但是對於大部分向我這樣的偽碼農去扒原始碼的時候總是一頭霧水,不知所云。然而,當我們真正有需求的時候,開原始碼的實現便成了一份巨大的寶藏,帶著我們的目的去扒原始碼,有時候會有事半功倍的效果。樓主親測有效,原始碼雖好,可不要貪杯哦。

文章出處: blog.csdn.net/github_3637…

相關文章