Initial cut of feature to create factory beans using the @FactoryBean annotation within a @Component

This commit is contained in:
Mark Pollack
2009-03-07 07:42:25 +00:00
parent 6281948cf9
commit fc9c3009fe
15 changed files with 617 additions and 22 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -211,11 +211,33 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
}
postProcessComponentBeanDefinitions(beanDefinitions);
return beanDefinitions;
}
protected void postProcessComponentBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
//TODO refactor increment index count as part of naming strategy.
int count = 0;
Set<BeanDefinitionHolder> factoryBeanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitions) {
Set<BeanDefinition> candidates = findCandidateFactoryMethods(beanDefinitionHolder);
for (BeanDefinition candidate : candidates ) {
//TODO refactor to introduce naming strategy and some sanity checks.
String beanName = beanDefinitionHolder.getBeanName() + "$" + candidate.getFactoryMethodName() + "#" + count++;
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
factoryBeanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
}
}
beanDefinitions.addAll(factoryBeanDefinitions);
}
/**
* Apply further settings to the given bean definition,
* beyond the contents retrieved from scanning the component class.

View File

@@ -17,9 +17,11 @@
package org.springframework.context.annotation;
import java.io.IOException;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
@@ -27,13 +29,18 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
@@ -67,6 +74,9 @@ public class ClassPathScanningCandidateComponentProvider implements ResourceLoad
protected static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
protected static final String QUALIFIER_CLASS_NAME = "org.springframework.beans.factory.annotation.Qualifier";
protected static final String SCOPE_CLASS_NAME = "org.springframework.context.annotation.Scope";
protected final Log logger = LogFactory.getLog(getClass());
@@ -223,6 +233,94 @@ public class ClassPathScanningCandidateComponentProvider implements ResourceLoad
}
return candidates;
}
public Set<BeanDefinition> findCandidateFactoryMethods(final BeanDefinitionHolder beanDefinitionHolder) {
Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
AbstractBeanDefinition containingBeanDef = (AbstractBeanDefinition)beanDefinitionHolder.getBeanDefinition();
Resource resource = containingBeanDef.getResource();
boolean debugEnabled = logger.isDebugEnabled();
boolean traceEnabled = logger.isTraceEnabled();
try {
if (resource.isReadable()) {
MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
Set<MethodMetadata> factoryMethodMetadataSet = metadataReader.getAnnotationMetadata().getAnnotatedMethods("org.springframework.beans.factory.annotation.FactoryMethod");
for (MethodMetadata methodMetadata : factoryMethodMetadataSet) {
if (isCandidateFactoryMethod(methodMetadata)) {
ScannedGenericBeanDefinition factoryBeanDef = new ScannedGenericBeanDefinition(metadataReader);
if (!methodMetadata.isStatic()) {
factoryBeanDef.setFactoryBeanName(beanDefinitionHolder.getBeanName());
}
factoryBeanDef.setFactoryMethodName(methodMetadata.getMethodName());
addQualifierToFactoryMethodBeanDefinition(methodMetadata, factoryBeanDef);
addScopeToFactoryMethodBeanDefinition(containingBeanDef, methodMetadata, factoryBeanDef);
factoryBeanDef.setResource(containingBeanDef.getResource());
factoryBeanDef.setSource(containingBeanDef.getSource());
if (debugEnabled) {
logger.debug("Identified candidate factory method in class: " + resource);
}
candidates.add(factoryBeanDef);
}
else {
if (traceEnabled) {
logger.trace("Ignored because not matching any filter: " + resource);
}
}
}
}
} catch (IOException ex) {
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
}
return candidates;
}
private void addScopeToFactoryMethodBeanDefinition(
AbstractBeanDefinition containingBeanDefinition,
MethodMetadata factoryMethodMetadata,
ScannedGenericBeanDefinition factoryBeanDefinition) {
if (factoryMethodMetadata.hasAnnotation(SCOPE_CLASS_NAME)) {
Map<String, Object> attributes = factoryMethodMetadata.getAnnotationAttributes(SCOPE_CLASS_NAME);
factoryBeanDefinition.setScope(attributes.get("value").toString());
} else {
factoryBeanDefinition.setScope(containingBeanDefinition.getScope());
}
}
protected void addQualifierToFactoryMethodBeanDefinition(MethodMetadata methodMetadata,
ScannedGenericBeanDefinition beanDef) {
//Add qualifiers to bean definition
if (methodMetadata.hasAnnotation(QUALIFIER_CLASS_NAME))
{
Map<String, Object> attributes = methodMetadata.getAnnotationAttributes(QUALIFIER_CLASS_NAME);
beanDef.addQualifier(new AutowireCandidateQualifier(Qualifier.class, attributes.get("value")));
}
if (methodMetadata.hasMetaAnnotation(QUALIFIER_CLASS_NAME))
{
//Need the attribute that has a qualifier meta-annotation.
Set<String> annotationTypes = methodMetadata.getAnnotationTypesWithMetaAnnotation(QUALIFIER_CLASS_NAME);
if (annotationTypes.size() == 1)
{
String annotationType = annotationTypes.iterator().next();
Map<String, Object> attributes = methodMetadata.getAnnotationAttributes(annotationType);
beanDef.addQualifier(new AutowireCandidateQualifier(annotationType, attributes.get("value")));
}
}
}
protected boolean isCandidateFactoryMethod(MethodMetadata methodMetadata) {
//TODO decide if we can support generic wildcard return types, parameter-less method and put in appropriate checks
return true;
}
/**
* Resolve the specified base package into a pattern specification for