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 或类似的)。所以这是我的问题:
- 在 Java EE 中是否有更简单的方法来支持单例(或其他 EJB),其中该单例的运行时 class 在编译时未知?
- 如果不是:是否可以只注入单例实例而无需额外的 @DynamicProjectManager 注释?
- 是否可以将 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 通过该参数的值。
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 或类似的)。所以这是我的问题:
- 在 Java EE 中是否有更简单的方法来支持单例(或其他 EJB),其中该单例的运行时 class 在编译时未知?
- 如果不是:是否可以只注入单例实例而无需额外的 @DynamicProjectManager 注释?
- 是否可以将 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 通过该参数的值。