【问题标题】:java dynamically choose class to executejava动态选择要执行的类
【发布时间】:2014-12-02 06:49:54
【问题描述】:

我很确定这对于专家来说是非常基本的东西,但对我来说,作为一个新手,这让我很难过。

我有 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。 logger.trace 在 while 循环中返回:

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

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

【问题讨论】:

  • NPE 在哪一行被抛出?
  • 在 if 语句的 while (pc.webParser.iterator().hasNext()) 循环内: if (((IWebParser) pc.webParser.iterator().getClass(). getClassLoader()).canExecute(url)) {

标签: java parsing dynamic reflection classloader


【解决方案1】:

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

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

修复:

  • webParser 变量中删除static
  • getAllParser 中删除泛型,因此构造函数读取List&lt;IWebParser&gt; 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 循环内的下一个变量。


cmets 的柜子

为什么这是单例设计模式?

单例是一种对象类型,应用程序中应该只有一个实例。在 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,也仅供参考,如果你在一个类中有一个类,你使用$ 来指定类,EG:com.me.parsers.ParserClass$InnerClassParser

【讨论】:

  • 哇。非常感谢。我特别喜欢给出的背景信息。正如您可能已经通过查看我的设计所想到的那样,我真的才刚刚开始学习。所以请原谅我,如果以下是愚蠢的,但是,为什么单例设计模式和静态变量不好?
  • 哦,我还有一个问题。现在,我的 getAllParsers 方法是 List getAllParser() - 如何从我的 xml 中的解析器名称的字符串中获取 IWebParser 元素?目前它说 List 类型中的方法 add(IWebParser) 不适用于参数字符串
  • @siliconchris 更新了答案,代码也做得很好,有一些错误,但这都是学习的一部分。
猜你喜欢
  • 1970-01-01
  • 2013-07-10
  • 1970-01-01
  • 1970-01-01
  • 2023-03-05
  • 1970-01-01
  • 1970-01-01
  • 2019-02-19
  • 1970-01-01
相关资源
最近更新 更多