我很确定这对专家来说是很基本的东西,但是对我来说,作为一个新手,这给我带来了困难。

我有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


谁能告诉我,我在这里做错了吗???

最佳答案

您这里发生了一些奇怪的事情。我看到的问题:


您获得了某种单例设计模式,但仍在使用静态变量。
您使用迭代器错误
您在不需要泛型的地方有泛型
您的IWebParser接口可以返回Boolean,可以为null。它应该能够返回null吗?还是应该是原始类型(boolean)。如果它返回null,并且在if语句中包含它,那么您将获得NPE。 EG:Boolean b=null; if(b) {} // NPE!!


修正:


static变量中删除webParser
摆脱getAllParser的泛型,使构造函数读取List<IWebParser> getAllParser()(也可以看到我将ArrayList替换为List)。
修复您的迭代器用法,现在获取迭代器的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循环中下一个变量的原因。



业主发表评论

为什么是单例设计模式?

单例是一种对象,在该对象中应在应用程序中创建一个且只有一个实例。在Java中,通常是通过将private构造函数与通常称为getInstance()的公共静态方法一起使用来实现的。然后,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,如果在类中有一个类,则也可以使用$来指定该类,例如:

10-08 09:08