替换硬编码的类实现

时间:2016-02-19 08:30:40

标签: java-ee reflection weblogic

在开展新项目时遇到了以下熟悉的案例。

private static Hashtable<String, Class<? extends MyExample>> MyExampleClassCollection = new Hashtable<String, Class<? extends MyExample>>();

static {
    MyExampleClassCollection.put("example1", MyExampleImplementation1.class);
    MyExampleClassCollection.put("example2", MyExampleImplementation2.class);
    MyExampleClassCollection.put("example3", MyExampleImplementation3.class);
}

public static MyExample getMyExample(String myExampleType){

    Class<? extends MyExample> templateClass = MyExampleClassCollection.get(myExampleType);

    try {
        Constructor ctor = templateClass.getConstructor(Connection.class);
        return (MyExample)ctor.newInstance();
    } catch (Exception e) {
        throw new RuntimeException(e);
    }

在这种情况下,您尝试创建更通用的代码,并最终硬编码通用类的不同实现。不得不为每个新实现在 Hashtable 上创建一个新条目。

我在上一个项目中看到的解决方案是在DB中存储具体类的几个名称。有一个字段,其中包含将要使用的具体类的名称。然后使用反射再次创建它,例如MyExampleImplementation1。获得不使用HashTable。 此解决方案完全相同,唯一的区别是我从数据库中检索myExampleType

有更优雅的方式吗?我更喜欢使用配置文件。 我以为我可以使用依赖注入(我对此还不是很有经验),但这是一个很好的例子吗?

有没有办法将此功能与我的应用服务器集成?

2 个答案:

答案 0 :(得分:2)

摘要:您不需要自己枚举和存储应用程序所需的组件。容器会为你做这件事;您需要做的就是在需要组件时查找它。

应该在这里做。您将要了解的是服务定位器模式的变体。此解决方案假设您不打算重写大量代码或getMyExample的任何客户端(如果您有,有更好的方法可以执行此操作)。

JNDI

这里的JNDI方法很快,没有废话,但也(IMO)不优雅和丑陋。这很简单:

  1. 在web.xml中为每个类声明一个条目(如果您在Web应用程序中)

    <env-entry>
       <env-entry-name>example1</env-entry-name>
       <env-entry-type>java.lang.Class</env-entry-type>
       <env-entry-value>my.example.MyExampleImplementation1</env-entry-value>
    </env-entry>
    
  2. 随时查看

    此处至少有2个选项 - 使用@Resource注释或使用旧式InitialContextSessionContext JNDI查找(SessionContext更快)。由于您的目标是避免硬编码,因此上下文查找是可行的方法:

    @Resource
    private SessionContext sessionContext;
    
    public static MyExample getMyExample(String myExampleType){
    
        Class<? extends MyExample> theClass =  (Class<? extends MyExample>) sessionContext.lookup(myExampleType);
        //instantiate
    }
    
  3. 这是纯粹的配置,但同样,不是很优雅。此外,对于<env-entry>的类的支持始于JavaEE 6。

    CDI

    CDI的众多优点之一是bean ification ,它默认提供给Java EE空间中的许多人工制品。 让我们从这里开始

    • 您不希望硬编码实例化所需的类型
    • 你正在努力寻找更清洁的代码

    以下CDI结构的组合应该排除你:

    • CDI Bean Manager
    • 命名

    在WEB-INF或META-INF文件夹中启动create a beans.xml文件。这是在Web应用程序中启用CDI的先决条件。

    命名:

    在上面的Web应用程序中启用CDI后,您的应用程序中的几乎所有类都可供CDI引擎检查,即它们根据某些资格规则成为bean。您现在希望能够通过您作为类的作者指定的名称来引用您的实现类。在bean上使用CDI @Named

    @Named("example1")
    public class MyExampleImplementation1
    

    在您的应用程序的任何部分,您现在可以通过该名称引用该类。

    <强> BeanManager

    BeanManager是CDI上下文中bean的霸主 - 它是进入CDI引擎的所有bean和其他内部工作的入口。您将使用它来按名称

    检索所需类的实例
    @Inject BeanManager manager; //inject the bean manager into your class
    
    
    public static MyExample getMyExample(String myExampleType){
        Set<Bean<?>> beans = manager.getBeans("example1"); //retrieve the bean by name. This returns a set, but you're ideally interested in only one bean
        Bean bean = beans.iterator().next(); //get the first (and ideally the only bean in the set)
        CreationalContext ctx = manager.createCreationalContext(bean); //CDI stipulation: there has to be a context associated with everything.
        MyExample theExample = bean.create(ctx); //create an instance of the bean and return
    
        return theExample;
    
    }
    

    使用这种方法,每次添加对新MyExample实现的支持时,都无需继续更新配置文件

    提示:缓存创建的对象以减少两种方法中的查找和实例化开销

答案 1 :(得分:0)

只需进行一些小改动即可使其工作。

假设MyExampleImplementation1实现了MyExample接口,则需要将其作为参数传递,而不是在manager.getBeans()中进行硬编码。

此外,您将需要强制转换结果。

最后,BeanManager的注入不是静态的,因此getMyExample方法也不是静态的。

@Inject BeanManager manager; //inject the bean manager into your class


public MyExample getMyExample(String myExampleType){
    Set<Bean<?>> beans = manager.getBeans(myExampleType); //retrieve the bean by name. This returns a set, but you're ideally interested in only one bean
    Bean bean = beans.iterator().next(); //get the first (and ideally the only bean in the set)
    CreationalContext ctx = manager.createCreationalContext(bean); //CDI stipulation: there has to be a context associated with everything.
    MyExample theExample (MyExample) = bean.create(ctx); //create an instance of the bean and return

    return theExample;

}