Binding based on custom annotation
I'm new to both stackoverflow and guice. I'm not sure what I'm trying to do is possible with guice, but I sure hope it is.
My problem domain is that I'm trying to write a document reading system. I have defined an interface for a DocumentReader.
public interface DocumentReader {
MetaData readDocument() throws Exception
}
I have also defined a enumeration defining supported file types (i.e. extensions).
public enum FileType {
doc,
png,
txt,
}
I have an implementation for each FileType, providing details of how to parse that file type.
public class MSWordReaderImpl implements DocumentReader {
// ....
}
public class PlainTextReaderImpl implements DocumentReader {
// ....
}
At this point, I have successfully written & tested a module that uses MapBinder & FactoryModuleProvider to inject each of these objects. Each DocumentReader implementation that exists is individually added to the MapBinder. What I'm hoping is that as additional implementations of DocumentReader are writt开发者_JAVA技巧en, I could simply annotate them with which FileType(s) they support, and GUICE would be able to read the annotation and add them to the MapBinder appropriately (without me having to update the module per addition). What I've envisioned is something like this:
@SupportedFileTypes( filetypes={ doc, docx } )
public class MSWordReaderImpl implements DocumentReader {
// ....
}
@SupportedFileTypes( filetypes={txt} )
public class PlainTextReaderImpl implements DocumentReader {
// ....
}
I've read through the GUICE documentation several times, but I'm just not seeing a way to accomplish this (if it is even possible!). Any help would be appreciated.
Thanks!
You could have your module scan the package(s) the classes will go in using reflection, looking for classes that implement DocumentReader
and are annotated with @SupportedFileTypes
. When it finds one, add it to the MapBinder
for each file type.
If you don't want to scan the classpath, you could still simplify this for yourself by writing a method like this:
private void bindDocumentReaders(Class<? extends DocumentReader>... types) {
for (Class<? extends DocumentReader> type : types) {
FileType[] supportedFileTypes = type.getAnnotation(SupportedFileTypes.class)
.filetypes();
// add to MapBinder for each file type
}
}
The easiest way to see how this works is to look at the source for the Named and Names class, and how it is used.
First create a RequiredFileType annotation,
@Retention(RUNTIME)
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@BindingAnnotation
public @interface RequiredFileType {
FileType value();
}
Then annotate the class which depends on Document reader like,
@Inject
ServiceRequiringWordReader(@RequiredFileType(doc) DocumentReader reader)
Then create a RequiredFileTypes utility which is equivalent to Names (code ommitted), and in your module write code like,
bind(DocumentReader.class)
.annotatedWith(RequiredFileTypes.for(doc))
.to(MSWordReaderImpl.class);
bind(DocumentReader.class)
.annotatedWith(RequiredFileTypes.for(docx))
.to(MSWordReaderImpl.class);
bind(DocumentReader.class)
.annotatedWith(RequiredFileTypes.for(txt))
.to(PlainTextReaderImpl.class);
精彩评论