2014-10-07 48 views
1

我敢肯定,這對於專家來說是非常基本的東西,但對於我來說,作爲一個新手,這給我一個很難。java動態選擇類執行

我有3個解析器,每個解析器都有自己的功能,將來會有更多的解析器。現在我想要做的是:我希望我的應用程序在運行時根據頁面來選擇正確的解析器。

要做到這一點,我做了以下內容:我有一個接口(IWebParser):

public interface IWebParser { 
    public abstract Object execute(String page, URL url); 
    public abstract List<SimpleWebPosting> parse(String page, URL url, List<String> tokens); 

    public abstract Boolean canExecute(URL url); 
} 

的每一個我的解析器實現了這個接口。我有另一個叫ParserControl的類,其中有一個方法submit(String page,URL url) - 這是我的程序總是調用的一個方法,只要有一個頁面需要解析。該類ParserControl從xml文件中獲得可用的解析器,並嘗試(在while語句中)任何解析器是否可以解析所討論的頁面。這是通過canExecute(URL url)方法完成的。現在,在canExecute上接收到true時,我想執行該特定的解析器。

我班ParserControl看起來是這樣的:

public class ParserControl { 
    private static final Logger logger = Logger.getLogger("de.comlineag.snc.parser.ParserControl"); 
// the list of operational web parser as taken from the properties file is stored within this structure 
private static List<IWebParser> webParser; 
// the ParserControl instance - used during instantiation of the class and later to retrieve the list 
private static ParserControl pc = null; 

// ParserControl is not to be directly instantiated by other classes 
private ParserControl() { 
    try { 
     webParser = getAllParser(); 
    } catch (XPathExpressionException | IOException 
      | ParserConfigurationException | SAXException e) { 
     logger.error("EXCEPTION :: error during parser execution " + e.getMessage()); 
     e.printStackTrace(); 
    } 
}; 

// Static 'instance' method - this method is called every time 
// the submit method is called but can also be called implicitely to get 
// an instance of ParserControl 
public static ParserControl getInstance() throws XPathExpressionException, ParserConfigurationException, SAXException, IOException { 
    if (pc == null) {pc = new ParserControl();} 
    return pc; 
} 


public static List<SimpleWebPosting> submit(String page, URL url, ArrayList<String> tTerms) throws XPathExpressionException, ParserConfigurationException, SAXException, IOException{ 
    logger.trace("ParserControl called"); 
    pc = getInstance(); 

    while (pc.webParser.iterator().hasNext()) { 
     logger.trace("trying parser " + pc.webParser.iterator().getClass().getSimpleName().toString()); 
     if (((IWebParser) pc.webParser.iterator().getClass().getClassLoader()).canExecute(url)) { 
      return ((IWebParser) pc.webParser.iterator().getClass().getClassLoader()).parse(page, url, tTerms); 
     } else { 
      logger.trace("parser " + pc.webParser.iterator().getClass().getSimpleName().toString() + " returned false to canExecute()"); 
     } 
    } 

    return null; 
} 


// retrieves all configured parser from the properties file and creates the parser list 
@SuppressWarnings("unchecked") 
private <T> ArrayList<T> getAllParser() throws IOException, ParserConfigurationException, SAXException, XPathExpressionException { 
    String fileName = "webapp/WEB-INF/properties/webparser.xml"; 
    ArrayList<T> ar = new ArrayList<T>(); 

    File file = new File(fileName); 
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 
    DocumentBuilder db = dbf.newDocumentBuilder(); 
    Document doc = db.parse(file); 

    XPathFactory xPathfactory = XPathFactory.newInstance(); 
    XPath xpath = xPathfactory.newXPath(); 

    String expression = "//parser[@type='webparser']/value"; 
    NodeList nodeList= (NodeList) xpath.compile(expression).evaluate(doc, XPathConstants.NODESET); 
    for (int i = 0 ; i < nodeList.getLength() ; i++) { 
     ar.add((T) nodeList.item(i).getTextContent()); 
     logger.trace("found parser " + nodeList.item(i).getTextContent().toString() + " in configuration file " + fileName); 
    } 
    return ar; 
} 
} 

現在,這個漫長的介紹後,我的問題:當執行這一點,我不能實例解析類,而是得到一個NullPointerException異常。在while循環內的logger.trace返回:

TRACE ParserControl - trying parser Itr <--- I would expect the class name here!!! 
    ERROR SimpleWebCrawler - WEBCRAWLER-Crawler Exception java.lang.NullPointerException 

誰能告訴我,我在做什麼錯在這裏?

+0

什麼行是NPE被拋出? (if(IWebParser)pc.webParser.iterator()。getClass()。getClassLoader())。canExecute()方法在while循環while(pc.webParser.iterator()。hasNext())內部的 – 2014-10-07 18:37:39

+0

url)){ – siliconchris 2014-10-07 18:41:38

回答

4

你在這裏有一些奇怪的東西。我看到的問題:

  • 你有某種單身設計模式,但仍然使用靜態變量。
  • 你使用迭代器錯誤
  • 你有仿製藥的地方仿製藥不需要是
  • IWebParser接口可以返回Boolean,它可以爲null。它應該能夠返回null嗎?或者應該是原始類型(boolean)。如果它返回null並且你在if語句中有它,那麼你將得到一個NPE。 EG:Boolean b=null; if(b) {} // NPE!!

修正:

  • 請從webParser變量
  • 得到getAllParser擺脫仿製藥的static這樣構造讀List<IWebParser> getAllParser()(也因爲你可以看到我換成ArrayListList)。
  • 修復你的迭代器使用,現在你得到迭代器的ClassLoader,並試圖將其轉換爲IWebParser,顯然不會工作。這是你的循環的工作版本,你可以看到我在外面聲明迭代器並使用.next()來獲得循環中的下一個IWebParser

Iterator<IWebParser> it = pc.webParser.iterator(); 
while (it.hasNext()) { 
    IWebParser parser = it.next(); 
    logger.trace("trying parser " + parser.getClass().getSimpleName().toString()); 
    if (parser.canExecute(url)) { 
     return parser.parse(page, url, tTerms); 
    } else { 
     logger.trace("parser " + parser.getClass().getSimpleName().toString() + " returned false to canExecute()"); 
    } 
} 

想象的Iterator是具有在各種各樣的列表中的位置的指針的對象。當您撥打webParser.iterator()時,它會構建一個新的Iterator,指向列表的開頭。現在,如果你試圖循環遍歷這些內容,並且你一直在調用webParser.iterator(),你總是會得到一個指向第一個元素的迭代器。這就是爲什麼在你的循環之外聲明你的Iterator並重新使用它的重要原因。它還值得注意的是,當您要將指針移動到下一個索引時,您只需在迭代器上調用.next(),這就是我聲明parser變量並將其設置爲while循環內的下一個變量的原因。


Awnsers來評論

爲什麼這是一個Singleton設計模式?

單例是一種對象類型,其中應該有一個且只有一個實例在應用程序中創建。在Java中,通常通過使用帶有公共靜態方法(通常名爲getInstance())的private構造函數來獲得。然後,getInstance()方法將創建自身的實例,如果尚未創建並存儲它或返回存儲的實例,則通常使用靜態變量來存儲該類的唯一實例。

當您使用面向對象的編程時,重要的是要充分利用類和類的實例。當你結合靜態變量和方法時,你應該總是考慮爲什麼它們應該是靜態的。我認爲它的安全性總是要開始使用非靜態變量,並且只在需要時才使其變爲靜態變量。在這種情況下,List webParser確實屬於類實例,而不是所有人,它在類的構造函數中初始化,然後僅在類的非靜態實例中使用......因此爲什麼它是靜態的?還有你使用的單例模式,這意味着只有1個實例!

錯誤getAllParsers()

我假設你傳遞一些解析器的類名添加到這個ParserControl類。在這種情況下,您可以使用Class.forName(className).newInstance()

更換線r.add((T) nodeList.item(i).getTextContent());
與線r.add((IWebParser)Class.forName(nodeList.item(i).getTextContent()).newInstance());

您需要的完整路徑傳遞給類。 EG:com.me.parsers.IFrameParser,也FYI如果你有一個班級裏面你使用$指定類,EG:com.me.parsers.ParserClass$InnerClassParser

+0

哇。非常感謝。我特別喜歡給出的背景資料。正如你可能已經看過我的設計所想的那樣,我真的剛剛開始學習。所以請原諒我,如果以下是愚蠢的,但是,爲什麼單身設計模式和靜態變量不好? – siliconchris 2014-10-07 19:31:16

+0

哦,我仍然有問題。現在,我的getAllParsers方法是List getAllParser() - 我如何從解析器名稱的字符串從我的xml中獲取到IWebParser元素?目前它說類型列表中的方法add(IWebParser)不適用於參數字符串 – siliconchris 2014-10-07 19:35:29

+0

@siliconchris更新的答案,在代碼上也不錯,有些錯誤,但這些都是學習的一部分。 – 2014-10-07 19:54:33