Hi everyone,
Since I couldn't get an answer on the forums I started digging in the source code and I figured out how to to this. I was a bit more complicated than I thought but once I found the proper extension points it was no really that hard.
First I created a new stereotype annotation to mark my interfaces. I called it Fabrication (I've liked it because I believe that it communicates both, the idea that this is not a real bean and that it must be produced by another bean):
Code:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Fabrication {
String value() default "";
String factoryBeanName();
String factoryMethodName();
}
Then I created a sub-class of "ClassPathBeanDefinitionScanner" to handle the new stereotype:
Code:
public class ClassPathFabricationDefinitionScanner extends ClassPathBeanDefinitionScanner {
// constructors ommited
@Override
protected void registerDefaultFilters() {
addIncludeFilter(new AnnotationTypeFilter(Fabrication.class));
}
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isInterface()
&& !Arrays.asList(beanDefinition.getMetadata().getInterfaceNames()).contains(Annotation.class.getName());
}
@Override
protected void postProcessBeanDefinition(AbstractBeanDefinition beanDefinition, String beanName) {
try {
super.postProcessBeanDefinition(beanDefinition, beanName);
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(beanDefinition.getResource());
AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
if(metadata.isAnnotated(Fabrication.class.getName())) {
AnnotationAttributes attributes =
AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(Fabrication.class.getName(), false));
String factoryBeanName = attributes.getString("factoryBeanName");
Assert.hasText(factoryBeanName, "'factoryBeanName' must not be blank");
String factoryMethodName = attributes.getString("factoryMethodName");
Assert.hasText(factoryBeanName, "'factoryMethodName' must not be blank");
beanDefinition.setFactoryBeanName(factoryBeanName);
beanDefinition.setFactoryMethodName(factoryMethodName);
}
} catch(IOException exception) {
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", exception);
}
}
}
Basically I had to override:
- "registerDefaultFilters" to register @Fabrication instead of @Component;
- "isCandidateComponent" to detect non-annotation interfaces instead of concrete classes;
- "postProcessBeanDefinition" to set the factory bean name and method
Then I created a custom XML configuration "fabrication-scan" based on a bean definition parser extending "ComponentScanBeanDefinitionParser":
Code:
public class FabricationScanBeanDefinitionParser extends ComponentScanBeanDefinitionParser {
@Override
protected ClassPathBeanDefinitionScanner createScanner(XmlReaderContext readerContext, boolean useDefaultFilters) {
return new ClassPathFabricationDefinitionScanner(readerContext.getRegistry(), useDefaultFilters);
}
}
Overriding "createScanner" to create my new "ClassPathFabricationDefinitionScanner".
Creating the schema for "fabrication-scan" was more complicated than it had to be because the complex-type within "component-scan" in "spring-context.xsd" was unnamed and therefore I could not reuse/extend it, so I copied and pasted it into the "fabrication-scan" definition (I would be glad to submit a patch for this if someone could point me out where to send it to).
That was basically it. Now all I have to do is add "<fabrication-scan base-package="a.b.c"/>" to my application context and annotate my fabrications with @Fabrication:
Code:
@Fabrication("MyFactory", "createBean")
public interface MyInterface1 {...}
And that is all folks 
Hope it helps someone else. Again, I would be glad to contribute this code if someone could point me out how to do it.
Best Regards
SVPace