2012-07-14 396 views
2

在我的库的代码中,我使用JAXB从XML文件加载类名,以便稍后使用Class.forName()对其进行实例化。虚构的例子来说明情况:是否有加载类Class.forName()的类的替代方法?

public void libraryMethod() throws Exception { 
    List<String> classNames = loadClassNamesFromXML(); 

    for (String className : classNames) { 
    Class.forName(className).newInstance().doThings(); 
    } 
} 

现在,有些用户使用OSGi的配置他们的应用程序,以及它们的加载我的图书馆有不同的类加载器比他们班使用配置我的XML结构。这意味着加载可能会失败,因为无法找到该类。

有没有更可靠的方法来加载这样的类?或者还有其他方式来配置这些实例吗?我打开导致此建议:

public void libraryMethod() throws Exception { 
    // Spring does things like this: 
    List<SomeType> instances = loadInstancesFromXML(); 

    for (SomeType instance : instances) { 
    instance.doThings(); 
    } 
} 

一些制约因素:

  • 从图书馆的角度看,这些实例的生命周期是不重要的。如果他们有(用户)状态,我的图书馆不会注意到。
  • 我想在这个库中保持简单的事情,所以我想避免在像spring这样的配置框架上创建外部依赖。所以我只对标准JDK 6+发行版可以实现的解决方案感兴趣。
  • 我真的想保留我简单的XML配置文件(对XML结构的轻微改动都可以)。
+1

您已经在一个特殊的解决方案中相当深入,而不会显示您尝试解决的实际问题。你能解释一下你的xml配置是干什么的吗?为什么你必须从其他包中加载类而不导入包? – 2012-07-14 18:39:17

+0

@ChristianSchneider:我写了一个库([jOOQ](http://www.jooq.org))。我不能导入任意的用户包,但我希望库用户能够将'SomeType'的实现注入到我的库中。有了Spring,注入这样的实现将非常容易,但这意味着我的库对spring本身会产生“沉重的”依赖。 – 2012-07-14 19:27:58

+0

做一些研究之前提出问题,去反思(懒惰initilaztion) – Pawan 2012-09-28 06:20:10

回答

4

如果你希望你的库在OSGi和非OSGi环境下都是灵活的,你应该允许用户提供他们自己的ClassLoader,或者让他们告诉你的库有哪些类名。阅读Neil Bartlett blog post

+0

尼尔是最终的权威,没有理由进一步讨论这个问题:-)当他总结时,停止猜测。 – 2012-07-14 16:51:42

1

JDBC4驱动程序在jar中使用ServiceProvider机制向JVM注册Driver实现(参见java.util.ServiceLoader javadocs),其中包含META-INF/services/java.sql.Driver。让驱动程序在类路径上自动注册驱动程序,不需要使用Class.forName。相反,应用程序代码使用ServiceLoader.load来发现注册的驱动程序。其他配置可以使用相同的机制。也许可以使用类似的东西?另外,当使用服务提供者机制注册自己的实现时,使用像spi这样的注释看起来相当方便。

+0

是的。 ServiceProvider是Service Registry上的另一个拙劣的模仿。但是,如果100%的OSGi不是一个选项,ServiceProvider仍然比没有好。尽管如此,它对ClassLoader没有帮助。 – 2012-07-15 05:50:49

+0

@IvanDubrov:有趣。如果我使用ServiceProvider,你能解释一下“类加载问题”吗? – 2012-07-15 08:06:09

+0

它使用ThreadContext类加载器(在OSGi中已损坏),或者需要显式提供单个类加载器,但在OSGi中可能有多个包提供服务,因此您有几个类加载器。 – 2012-07-15 17:16:40

2

感谢您的解释。 Spring不会让OSGi更容易。你不能简单地从你不导入的包中注入一个实现类。在OSGi中,您通常使用OSGi服务来注入源自您的包之外的实现,并且在编译时不知道您。

因此,您的用户将实现您指定的接口并将其实现作为OSGi服务发布。然后,您可以选择所有这些服务,或者让用户在xml配置中为其服务指定一个ldap筛选器。

这个问题的好处是您不必加载类并关心类加载器。所以这是OSGi推荐的方式。如果你想在OSGi内部和外部使用相同的解决方案,那么指定classloader + classname是另一种选择。

+0

感谢您的解释。我之所以认为spring可能是一种解决方案,是因为那些有问题的特定用户自己构建了[jOOQ](http://www.jooq.org)并可能修补它。但很高兴知道这通常不起作用。 LDAP听起来像是矫枉过正,但服务可能是个好主意。尽管@ MarkoTopolnik的上下文类加载器似乎是一种解决方法,但允许指定自定义类加载器似乎是迄今为止最好的选择。 – 2012-07-15 08:01:56

+0

当您在OSGi中筛选服务impls时,可以在LDAP语法中使用过滤器。这在所有OSGi框架中都不是特别的。发布服务的人可以为其提供属性,而搜索服务的人使用这些过滤器对属性进行搜索。如果你使用服务aproach,你不需要类加载器,但是你依赖于OSGi。 – 2012-07-15 21:13:23

2

一般来说,在OSGi中你应该使用一项服务。 Class.forName/XML配置如此流行的原因是只有一个类受到控制。为了配置其余部分,它需要知道要初始化/调用的类。

在OSGi中,这个问题并不存在,因为每个模块(bundle)(can)都可以通过声明式服务(或以老式的方式通过激活器)获得控制权。所以在OSGi中你有一个对等模型。任何人都可以注册一项服务并依赖其他服务。

因此,不用指定类名并假设它们是全局唯一的(它们不是大型系统中的),使用服务并不会留下Java编译器;这些类名很容易出错。一般而言,这意味着您通常只需注册您的服务并等待被调用,因为不需要初始化客户端。然而,白板模式地址时,你要了解你的客户(与bndtools和BND注解)的情况:

“服务器”

@Component 
public class MyLib { 
    @Reference(type='*') 
    void addSomeType(SomeType st) { 
     st.doThings(); 
    } 
} 

客户

@Component 
public class MyClient implements SomeType { 
    public void doThings() { ... } 
} 

希望这有助于。

+0

这很有趣。然而,我不想在我的图书馆中引入如此多的OSGi,因为不使用OSGi的图书馆用户也应该能够从此功能中获益...... – 2012-07-16 14:22:05

+0

其实,看着jooq,您可能想要让生活更模块化并支持_extender_模式。在OSGi中,跟踪变得活跃的捆绑是微不足道的 – 2013-07-15 07:06:13

相关问题