在开展新项目时遇到了以下熟悉的案例。
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
。
有更优雅的方式吗?我更喜欢使用配置文件。 我以为我可以使用依赖注入(我对此还不是很有经验),但这是一个很好的例子吗?
有没有办法将此功能与我的应用服务器集成?
答案 0 :(得分:2)
摘要:您不需要自己枚举和存储应用程序所需的组件。容器会为你做这件事;您需要做的就是在需要组件时查找它。
cdi或jndi应该在这里做。您将要了解的是服务定位器模式的变体。此解决方案假设您不打算重写大量代码或getMyExample
的任何客户端(如果您有,有更好的方法可以执行此操作)。
这里的JNDI方法很快,没有废话,但也(IMO)不优雅和丑陋。这很简单:
在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个选项 - 使用@Resource
注释或使用旧式InitialContext
或SessionContext
JNDI查找(SessionContext
更快)。由于您的目标是避免硬编码,因此上下文查找是可行的方法:
@Resource
private SessionContext sessionContext;
public static MyExample getMyExample(String myExampleType){
Class<? extends MyExample> theClass = (Class<? extends MyExample>) sessionContext.lookup(myExampleType);
//instantiate
}
这是纯粹的配置,但同样,不是很优雅。此外,对于<env-entry>
的类的支持始于JavaEE 6。
CDI的众多优点之一是bean ification ,它默认提供给Java EE空间中的许多人工制品。 让我们从这里开始
以下CDI结构的组合应该排除你:
在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;
}