Java EE单例反射注入

Java EE Singleton Injection by Reflection

Java EE 让我(又一次)头疼(Wildfly 8+):

首先,想象一些 Singleton POJO,其中使用的实例由一些 属性:

Java SE 单例

public class Singleton
{
    private Singleton instance
    public Singleton getInstance()
    {
        if (instance == null)
        {
            try
            {
                String className = System.getProperty("singletonClass");
                instance = Class.forName(className).getInstance();
            }
            catch(Exception e)
            {
            ...
            }
        }
    return instance;
    }
}

您可能会认为这种方法是糟糕的设计。但这是我们的单例实例的方式 "configured".

现在我在 Java EE 世界中遇到了这种方法。我让它工作了,但它只是臃肿丑陋的注释地狱代码——除此之外我不太熟悉 Java EE 可能有更简单的方法:

ProjectManager(应该是抽象的)

@Singleton
@LocalBean
public class ProjectManager
{
...
}

ProjectManager 实施

@Singleton
@LocalBean
public class DefaultProjectManager extends ProjectManager
{
}

制作者注解

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE, ElementType.METHOD
})
public @interface DynamicProjectManager
{

}

制作人

@ApplicationScoped
public class ProjectManagerProducer implements Serializable
{
    @Inject
    @Any
    private Instance<ProjectManager> projectManagers;

    @Produces
    @DynamicProjectManager
    private ProjectManager createProjectManager()
    {
        String clazz = System.getProperty("projectManagerType","DefaultProjectManager");
        try
        {
            return projectManagers.select((Class<ProjectManager>) Class.forName(clazz)).get();
        }
        catch(Exception e)
        {
            return projectManagers.select(DefaultProjectManager.class).get();
        }
    }
}

用法在另一个Bean中:

@Inject
@DynamicProjectManager
private ProjectManager projectManager;

这有效!但我看到很多事情让我晚上睡不好觉。目前我没有使用其他框架(Spring 或类似的)。所以这是我的问题:

  1. 在 Java EE 中是否有更简单的方法来支持单例(或其他 EJB),其中该单例的运行时 class 在编译时未知?
  2. 如果不是:是否可以只注入单例实例而无需额外的 @DynamicProjectManager 注释?
  3. 是否可以将 ProjectManager 抽象化?

保持简单,除非绝对需要,否则不要将 EJB 与 CDI 混合使用。应执行以下操作:

public interface ProjectManager {
    // add some method signatures
}


@ApplicationScoped
public class DefaultProjectManager implements ProjectManager {
    // add implementations
}

public class ProjectManagerClient {

    @Inject
    private ProjectManager projectManager;

    // work with injected bean
}

如果周围有多个 ProjectManager 实现,您可以使用 @Alternative 或者,如果您确实需要 select 在运行时动态 实现,然后对给定的限定符使用 Instance<ProjectManager>select()

按 class 名称选择是一种不好的做法,违反了松散耦合。最好在每个实现中使用不同的限定符,或者如果您更喜欢共享限定符,则向您的限定符添加一个 @NonBinding 参数,这样您就可以 select 通过该参数的值。