国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發(fā)設(shè)計 > 正文

Java利用ByteArrayOutputStream和ByteArrayInputStream避免重復讀取配置文件

2019-11-14 14:53:44
字體:
供稿:網(wǎng)友

最近參與了github上的一個開源項目 Mycat,是一個MySQL的分庫分表的中間件。發(fā)現(xiàn)其中讀取配置文件的代碼,存在頻繁多次重復打開,讀取,關(guān)閉的問題,代碼寫的很初級,稍微看過一些框架源碼的人,是不會犯這樣的錯誤的。于是對其進行了一些優(yōu)化。

優(yōu)化之前的代碼如下所示:

    PRivate static Element loadRoot() {        InputStream dtd = null;        InputStream xml = null;        Element root = null;        try {            dtd = ConfigFactory.class.getResourceAsStream("/mycat.dtd");            xml = ConfigFactory.class.getResourceAsStream("/mycat.xml");            root = ConfigUtil.getDocument(dtd, xml).getDocumentElement();        } catch (ConfigException e) {            throw e;        } catch (Exception e) {            throw new ConfigException(e);        } finally {            if (dtd != null) {                try {                    dtd.close();                } catch (IOException e) { }            }            if (xml != null) {                try {                    xml.close();                } catch (IOException e) { }            }        }        return root;    }

然后其它方法頻繁調(diào)用 loadRoot():

    @Override    public UserConfig getUserConfig(String user) {    	Element root = loadRoot();        loadUsers(root);        return this.users.get(user);    }    @Override    public Map<String, UserConfig> getUserConfigs() {    	Element root = loadRoot();        loadUsers(root);        return users;    }    @Override    public SystemConfig getSystemConfig() {    	Element root = loadRoot();        loadSystem(root);        return system;    }    // ... ...

ConfigUtil.getDocument(dtd, xml) 方法如下:

    public static Document getDocument(final InputStream dtd, InputStream xml) throws ParserConfigurationException,            SAXException, IOException {        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();//factory.setValidating(false);        factory.setNamespaceAware(false);        DocumentBuilder builder = factory.newDocumentBuilder();        builder.setEntityResolver(new EntityResolver() {            @Override            public InputSource resolveEntity(String publicId, String systemId) {                return new InputSource(dtd);            }        });        builder.setErrorHandler(new ErrorHandler() {            @Override            public void warning(SAXParseException e) {            }            @Override            public void error(SAXParseException e) throws SAXException {                throw e;            }            @Override            public void fatalError(SAXParseException e) throws SAXException {                throw e;            }        });        return builder.parse(xml);    }

顯然這不是很好的處理方式。因為會多次重復讀取配置文件。

1. 第一次優(yōu)化:

為什么不讀取一次,然后緩存起來呢?然后其它方法在調(diào)用 loadRoot() 時,就直接使用緩存中的就行了。但是遇到一個問題,InputStream 是不能被緩存,然后重復讀取的,因為 InputStream 一旦被讀取之后,其 pos 指針,等等都會發(fā)生變化,無法進行重復讀取。所以只能將配置文件的內(nèi)容讀取處理,放入  byte[] 中緩存起來,然后配合 ByteArrayOutputStream,就可以重復讀取 byte[] 緩存中的內(nèi)容了。然后利用 ByteArrayOutputStream 來構(gòu)造 InputStream 就達到了讀取配置文件一次,然后重復構(gòu)造 InputStream 進行重復讀取,相關(guān)代碼如下:

    // 為了避免原代碼中頻繁調(diào)用 loadRoot 去頻繁讀取 /mycat.dtd 和 /mycat.xml,所以將兩個文件進行緩存,    // 注意這里并不會一直緩存在內(nèi)存中,隨著 LocalLoader 對象的回收,緩存占用的內(nèi)存自然也會被回收。    private static byte[] xmlBuffer = null;    private static byte[] dtdBuffer = null;    private static  ByteArrayOutputStream xmlBaos = null;    private static  ByteArrayOutputStream dtdBaos = null;        static {    	InputStream input = ConfigFactory.class.getResourceAsStream("/mycat.dtd");    	if(input != null){    		dtdBuffer = new byte[1024 * 512];    		dtdBaos = new ByteArrayOutputStream();    		bufferFileStream(input, dtdBuffer, dtdBaos);    	}    	        input = ConfigFactory.class.getResourceAsStream("/mycat.xml");    	if(input != null){    	    xmlBuffer = new byte[1024 * 512];            xmlBaos = new ByteArrayOutputStream();            bufferFileStream(input, xmlBuffer, xmlBaos);    	}    }

bufferFileStream 方法:

    private static void bufferFileStream(InputStream input, byte[] buffer, ByteArrayOutputStream baos){        int len = -1;        try {    		while ((len = input.read(buffer)) > -1 ) {    			baos.write(buffer, 0, len);			}    		baos.flush();		} catch (IOException e) {			e.printStackTrace();			logger.error(" bufferFileStream error: " + e.getMessage());		}    }

 loadRoat 優(yōu)化之后如下:

    private static Element loadRoot() {        Element root = null;        InputStream mycatXml = null;        InputStream mycatDtd = null;                if(xmlBaos != null)        	mycatXml = new ByteArrayInputStream(xmlBaos.toByteArray());        if(dtdBaos != null)        	mycatDtd = new ByteArrayInputStream(dtdBaos.toByteArray());                try {		root = ConfigUtil.getDocument(mycatDtd, mycatXml).getDocumentElement();		} catch (ParserConfigurationException | SAXException | IOException e1) {			e1.printStackTrace();			logger.error("loadRoot error: " + e1.getMessage());		}finally{			if(mycatXml != null){				try { mycatXml.close(); } catch (IOException e) {}			}			if(mycatDtd != null){				try { mycatDtd.close(); } catch (IOException e) {}			}		}                return root;    }

這樣優(yōu)化之后,即使有很多方法頻繁調(diào)用 loadRoot() 方法,也不會重復讀取配置文件了,而是使用 byte[] 內(nèi)容,重復構(gòu)造 InputStream 而已。

其實其原理,就是利用 byte[] 作為一個中間容器,對byte進行緩存,ByteArrayOutputStream 將 InputStream 讀取的 byte 存放如 byte[]容器,然后利用 ByteArrayInputStream 從 byte[]容器中讀取內(nèi)容,構(gòu)造 InputStream,只要 byte[] 這個緩存容器存在,就可以多次重復構(gòu)造出 InputStream。 于是達到了讀取一次配置文件,而重復構(gòu)造出InputStream,避免了每構(gòu)造一次InputStream,就讀取一次配置文件的問題

2. 第二次優(yōu)化:

可能你會想到更好的方法,比如:

為什么我們不將 private static Element root = null; 作為類屬性,緩存起來,這樣就不需要重復打開和關(guān)閉配置文件了,修改如下:

public class LocalLoader implements ConfigLoader {    private static final Logger logger = LoggerFactory.getLogger("LocalLoader");    // ... ..        private static Element root = null;    // 然后 loadRoot 方法改為:    private static Element loadRoot() {        InputStream dtd = null;        InputStream xml = null;//        Element root = null;        if(root == null){            try {                dtd = ConfigFactory.class.getResourceAsStream("/mycat.dtd");                xml = ConfigFactory.class.getResourceAsStream("/mycat.xml");                root = ConfigUtil.getDocument(dtd, xml).getDocumentElement();            } catch (ConfigException e) {                throw e;            } catch (Exception e) {                throw new ConfigException(e);            } finally {                if (dtd != null) {                    try {                        dtd.close();                    } catch (IOException e) { }                }                if (xml != null) {                    try {                        xml.close();                    } catch (IOException e) { }                }            }        }                return root;    }

這樣就不需要也不會重復 打開和關(guān)閉配置文件了。只要 root 屬性沒有被回收,那么 root 引入的 Document 對象也會在緩存中。這樣顯然比第一次優(yōu)化要好很多,因為第一次優(yōu)化,還是要從 byte[] 重復構(gòu)造 InputStream, 然后重復 build 出 Document 對象。

3. 第三次優(yōu)化

上面是將 private static Element root = null; 作為一個屬性進行緩存,避免重復讀取。那么我們干嘛不直接將 Document 對象作為一個屬性,進行緩存呢。而且具有更好的語義,代碼更好理解。代碼如下:

public class LocalLoader implements ConfigLoader {	private static final Logger logger = LoggerFactory.getLogger("LocalLoader");    // ... ...    // 為了避免原代碼中頻繁調(diào)用 loadRoot 去頻繁讀取 /mycat.dtd 和 /mycat.xml,所以將 Document 進行緩存,    private static Document document = null;    private static Element loadRoot() {        InputStream dtd = null;        InputStream xml = null;                if(document == null){           try {                dtd = ConfigFactory.class.getResourceAsStream("/mycat.dtd");                xml = ConfigFactory.class.getResourceAsStream("/mycat.xml");                document = ConfigUtil.getDocument(dtd, xml);                return document.getDocumentElement();            } catch (Exception e) {            	logger.error(" loadRoot error: " + e.getMessage());                throw new ConfigException(e);            } finally {                if (dtd != null) {                    try { dtd.close(); } catch (IOException e) { }                }                if (xml != null) {                    try { xml.close(); } catch (IOException e) { }                }            }        }                return document.getDocumentElement();    }

這樣才是比較合格的實現(xiàn)。anyway, 第一種優(yōu)化,學習到了 ByteArrayOutputStream 和 ByteArrayInputStream 同 byte[] 配合使用的方法。

---------------------分割線------------------------------------

參考文章:http://blog.csdn.net/it_magician/article/details/9240727 原文如下:

有時候我們需要對同一個InputStream對象使用多次。比如,客戶端從服務(wù)器獲取數(shù)據(jù) ,利用HttpURLConnection的getInputStream()方法獲得Stream對象,這時既要把數(shù)據(jù)顯示到前臺(第一次讀取),又想把數(shù)據(jù)寫進文件緩存到本地(第二次讀取)。

但第一次讀取InputStream對象后,第二次再讀取時可能已經(jīng)到Stream的結(jié)尾了(EOFException)或者Stream已經(jīng)close掉了。

而InputStream對象本身不能復制,因為它沒有實現(xiàn)Cloneable接口。此時,可以先把InputStream轉(zhuǎn)化成ByteArrayOutputStream,后面要使用InputStream對象時,再從ByteArrayOutputStream轉(zhuǎn)化回來就好了。代碼實現(xiàn)如下:

InputStream input =  httpconn.getInputStream();	            ByteArrayOutputStream baos = new ByteArrayOutputStream();byte[] buffer = new byte[1024];int len;while ((len = input.read(buffer)) > -1 ) {	baos.write(buffer, 0, len);}baos.flush();	           InputStream stream1 = new ByteArrayInputStream(baos.toByteArray());//TODO:顯示到前臺InputStream stream2 = new ByteArrayInputStream(baos.toByteArray());//TODO:本地緩存

 


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 吉木萨尔县| 鸡西市| 漳州市| 简阳市| 永登县| 大埔县| 竹山县| 吉安市| 四川省| 泸州市| 肇源县| 永城市| 大港区| 扎鲁特旗| 阳曲县| 唐河县| 宁津县| 永丰县| 武邑县| 通城县| 青浦区| 柘荣县| 蓝田县| 荣昌县| 卓资县| 安阳县| 安吉县| 金塔县| 衡东县| 宁陵县| 永州市| 绥阳县| 翼城县| 呼和浩特市| 余庆县| 石楼县| 旌德县| 类乌齐县| 桂阳县| 阿拉尔市| 芮城县|