While working on a new project a came across with the following familiar case.
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);
}
It's the case where you are trying to create a more generic code and you end up hardcode the different implementations of your generic class. Having the down side that you have to create a new entry on the Hashtable for every new implementation.
A solution I saw on a previous project I worked on, was to store the several names of the concrete classes in the DB. Having a field with the name of the concrete class that is going to be used. And create it again with reflection, for example MyExampleImplementation1
. Gain that you don't use a HashTable.
This solution is identical, the only difference is I retrieve the myExampleType
from the DB.
Is there a more elegant way? I would prefer to use a configuration file. I thought I could use dependency injection (I'm not very experienced with this yet), but is this a good case for that?
Is there a way to integrate this functionality with my application server ?
Summary : You don't need to enumerate and store components that are required across an application, by yourself. The container will do that for you; all you need to do is look it up whenever you need the components.
cdi or jndi ought to do here. What you're going to wind up with is a variation of the Service Locator pattern. This solution presumes you're not looking to rewrite a lot of code or any of the clients of
getMyExample
(if you are, there are better ways to do it).JNDI
A JNDI approach here is quick, no-nonsense, but also (IMO) inelegant and ugly. It's simple:
Declare an entry per class in your web.xml (if you're in a web application)
Look it up whenever you need it
You have at least 2 options here - using the
@Resource
annotation or using old-fashionedInitialContext
orSessionContext
JNDI lookups (SessionContext
is faster). Since your objective here is to avoid hardcoding, the context lookup is the way to go:It's pure configuration, but again, not very elegant. Also, support for classes as
<env-entry>
started with JavaEE 6.CDI
One of the many benefits of CDI is the beanification that it provides by default to many artefacts in the Java EE space. Let's start here
A combination of the following CDI constructs should sort you out:
To start, create a beans.xml file in your WEB-INF or META-INF folders. This is a prerequisite to enabling CDI in your web application.
Naming:
After enabling CDI in your web application above, almost all your classes in your application are available to the CDI engine to inspect, i.e. they become beans, based on some eligibility rules. You now want to be able to refer to your implementation classes by names that you as the author of the class specify. Use the CDI
@Named
on the beans.In any part of your application, you can now refer to that class, by that name.
BeanManager
The
BeanManager
is the overlord of the beans in the CDI context - it's your gateway into the all the beans and other inner workings of the CDI engine. You'll use this to retrieve an instance of the desired class by nameWith this approach, there's no need to continue updating a configuration file everytime you add support for a new
MyExample
implementationTip : Cache the objects that are created to reduce lookup and instantiation overhead in both approaches
Just a few small changes to make it work.
Assuming that the MyExampleImplementation1 implements an interface MyExample, you need to pass this as parameter rather than hard-code it in the manager.getBeans().
Also you will need to cast the result of the instatiation.
Last thing, the injection of the BeanManager isn't static, so the getMyExample method is not allowed to be static, either.