I recommend to read this blog entry (snapshot on archive.org) up to the point where the author remembers (s)he has access to Spring's component scan feature.
The initial issue is to scan the class path to find classes with the custom annotation. Once this is done, you have the objects in your standalone application through which using object.getClass().getAnnotations()
, you can then inject the listeners or custom behavior you need to add to the objects holding the custom annotations.
Let's say you have the following custom annotation:
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface MyMessageDriven {}
And you use it some class in you application:
@MyMessageDriven
public class MyObject {}
Now, in the appropriate location in your application, you should have a method to give out all classes carrying MyMessageDriven
:
Set<Class<?>> findAllMessageDrivenClasses() {
final StopWatch sw = new StopWatch();
sw.start();
final Reflections reflections = new Reflections("org.projectx", new TypeAnnotationsScanner());
Set<Class<?>> allMessageDrivens = reflections.getTypesAnnotatedWith(MyMessageDriven.class); // NOTE HERE
sw.stop();
return allMessageDrivens;
}
Having this, I assume that there is a point in your application that either (1) you have access to the objects in your application, or (2) there is a visitor or iterator pattern on all the objects in the application. So, in some point, I assume that we have all targeted objects as objects
:
Set<Class<?>> msgDrivenClasses = findAllMessageDrivenClasses();
for (Object o : objects) {
if (msgDrivenClasses.contains(o.getClass()) {
invokeTheMessageListener(o);
}
}
On the other hand, there should be some implementation of MyMessageListener
that is available when the objects having MyMessageDriven
are found:
void invokeTheMessageListener(Object o) {
theMessageListener.onMessage(o);
}
This answer is tailored from the blog entry so please refer to the blog for configuration of libraries. And, last but not least, this is a sample code for the problem and it can be refactored to more pattern-compatible and elegant style.
Update: There is a requirement that the targeted objects should be aware of their own listeners. So, I'd suggest the following approach. Let's have an interface MyMessageListenerAware
:
interface MyMessageListenerAware {
MyMessageListener getMyMessageListener();
}
// and this is the original MyMessageListener
interface MyMessageListener {
void onMessage(Object o);
}
Now, the target objects should implement the above interface:
class MySampleObject implements MyMessageListenerAware {
public MyMesssageListener getMyMessageLisener() {
return mySampleObjectImplementationOfMyMessageListener;
}
}
Having this, the method invokeTheMessageListener
becomes like:
void invokeMessageListener(Object o) {
if (o instance MyMessageListenerAware) {
MyMessageListener l = ((MyMessageListenerAware) o).getMyMessageListener();
l.onMessage(o);
}
}
Although, I strongly recommend reading about Visitor or Strategy pattern. What you aim to do seems to me like you need certain objects react/act/process to a common object/event in the application but each with their own interpretation/algorithm/implementation.