Initial cut of feature to create factory beans using the @FactoryBean annotation within a @Component
This commit is contained in:
@@ -70,5 +70,18 @@ public interface AnnotationMetadata extends ClassMetadata {
|
||||
* annotation is defined.
|
||||
*/
|
||||
Map<String, Object> getAnnotationAttributes(String annotationType);
|
||||
|
||||
|
||||
// TODO return null would be more consistent with other methods if no match is found
|
||||
|
||||
/**
|
||||
* Retrieve the method meta-data for all methods that have the
|
||||
* given annotation type.
|
||||
* @param annotationType the annotation type to look for
|
||||
* @return a Set of (@link MethodMetadata) for methods that
|
||||
* have a matching annotation. The return value will be an
|
||||
* empty set if no methods match the annotation type.
|
||||
*/
|
||||
Set<MethodMetadata> getAnnotatedMethods(String annotationType);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.springframework.core.type;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public interface MethodMetadata {
|
||||
|
||||
int getModifiers();
|
||||
|
||||
boolean isStatic();
|
||||
|
||||
String getMethodName();
|
||||
|
||||
//TODO does the method return type have a generic wildcard or generic type parameters?
|
||||
|
||||
// annotation metadata
|
||||
|
||||
Set<String> getAnnotationTypes();
|
||||
|
||||
boolean hasAnnotation(String annotationType);
|
||||
|
||||
Map<String, Object> getAnnotationAttributes(String annotationType);
|
||||
|
||||
/**
|
||||
* Determine whether the underlying class has an annotation that
|
||||
* is itself annotated with the meta-annotation of the given type.
|
||||
* @param metaAnnotationType the meta-annotation type to look for
|
||||
* @return whether a matching meta-annotation is defined
|
||||
*/
|
||||
boolean hasMetaAnnotation(String metaAnnotationType);
|
||||
|
||||
/**
|
||||
* Return the names of all meta-annotation types defined on the
|
||||
* given annotation type of the underlying class.
|
||||
* @return the meta-annotation type names
|
||||
*/
|
||||
Set<String> getMetaAnnotationTypes(String annotationType);
|
||||
|
||||
Set<String> getAnnotationTypesWithMetaAnnotation(String qualifierClassName);
|
||||
}
|
||||
@@ -17,7 +17,9 @@
|
||||
package org.springframework.core.type;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -96,4 +98,21 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
|
||||
return null;
|
||||
}
|
||||
|
||||
public Set<MethodMetadata> getAnnotatedMethods(String annotationType) {
|
||||
Method[] methods = getIntrospectedClass().getMethods();
|
||||
Set<MethodMetadata> annotatedMethods = new LinkedHashSet<MethodMetadata>();
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
Method method = methods[i];
|
||||
Annotation[] methodAnnotations = method.getAnnotations();
|
||||
for (int j = 0; j < methodAnnotations.length; j++) {
|
||||
Annotation ann = methodAnnotations[j];
|
||||
if (ann.annotationType().getName().equals(annotationType)) {
|
||||
MethodMetadata mm = new StandardMethodMetadata(method);
|
||||
annotatedMethods.add(mm);
|
||||
}
|
||||
}
|
||||
}
|
||||
return annotatedMethods;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
package org.springframework.core.type;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
|
||||
public class StandardMethodMetadata implements MethodMetadata {
|
||||
|
||||
private final Method introspectedMethod;
|
||||
|
||||
public StandardMethodMetadata(Method method) {
|
||||
introspectedMethod = method;
|
||||
}
|
||||
|
||||
public final Method getIntrospectedMethod() {
|
||||
return this.introspectedMethod;
|
||||
}
|
||||
|
||||
|
||||
public Map<String, Object> getAnnotationAttributes(String annotationType) {
|
||||
Annotation[] anns = getIntrospectedMethod().getAnnotations();
|
||||
for (int i = 0; i < anns.length; i++) {
|
||||
Annotation ann = anns[i];
|
||||
if (ann.annotationType().getName().equals(annotationType)) {
|
||||
return AnnotationUtils.getAnnotationAttributes(ann);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Set<String> getAnnotationTypes() {
|
||||
Set<String> types = new HashSet<String>();
|
||||
Annotation[] anns = getIntrospectedMethod().getAnnotations();
|
||||
for (int i = 0; i < anns.length; i++) {
|
||||
types.add(anns[i].annotationType().getName());
|
||||
}
|
||||
return types;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public String getMethodName() {
|
||||
return introspectedMethod.getName();
|
||||
}
|
||||
|
||||
public int getModifiers() {
|
||||
return introspectedMethod.getModifiers();
|
||||
}
|
||||
|
||||
public boolean hasAnnotation(String annotationType) {
|
||||
Annotation[] anns = getIntrospectedMethod().getAnnotations();
|
||||
for (int i = 0; i < anns.length; i++) {
|
||||
if (anns[i].annotationType().getName().equals(annotationType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isStatic() {
|
||||
return Modifier.isStatic(getIntrospectedMethod().getModifiers());
|
||||
}
|
||||
|
||||
public Set<String> getMetaAnnotationTypes(String annotationType) {
|
||||
Annotation[] anns = getIntrospectedMethod().getAnnotations();
|
||||
for (int i = 0; i < anns.length; i++) {
|
||||
if (anns[i].annotationType().getName().equals(annotationType)) {
|
||||
Set<String> types = new HashSet<String>();
|
||||
Annotation[] metaAnns = anns[i].annotationType().getAnnotations();
|
||||
for (Annotation meta : metaAnns) {
|
||||
types.add(meta.annotationType().getName());
|
||||
}
|
||||
return types;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public boolean hasMetaAnnotation(String metaAnnotationType) {
|
||||
|
||||
//TODO can refactor into shared (utility) method with StandardAnnotationMetadata
|
||||
Annotation[] anns = getIntrospectedMethod().getAnnotations();
|
||||
for (int i = 0; i < anns.length; i++) {
|
||||
Annotation[] metaAnns = anns[i].annotationType().getAnnotations();
|
||||
for (Annotation meta : metaAnns) {
|
||||
if (meta.annotationType().getName().equals(metaAnnotationType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Set<String> getAnnotationTypesWithMetaAnnotation(
|
||||
String qualifierClassName) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -22,15 +22,18 @@ import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.EmptyVisitor;
|
||||
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.core.type.MethodMetadata;
|
||||
|
||||
/**
|
||||
* ASM class visitor which looks for the class name and implemented types as
|
||||
@@ -46,8 +49,9 @@ class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor imple
|
||||
private final Map<String, Map<String, Object>> attributesMap = new LinkedHashMap<String, Map<String, Object>>();
|
||||
|
||||
private final Map<String, Set<String>> metaAnnotationMap = new LinkedHashMap<String, Set<String>>();
|
||||
|
||||
|
||||
|
||||
private final Set<MethodMetadata> methodMetadataSet = new LinkedHashSet<MethodMetadata>();
|
||||
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
|
||||
@@ -55,11 +59,23 @@ class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor imple
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String desc,
|
||||
String signature, String[] exceptions) {
|
||||
MethodMetadataReadingVisitor md = new MethodMetadataReadingVisitor(classLoader, name, access);
|
||||
methodMetadataSet.add(md);
|
||||
return md;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(final String desc, boolean visible) {
|
||||
final String className = Type.getType(desc).getClassName();
|
||||
final Map<String, Object> attributes = new LinkedHashMap<String, Object>();
|
||||
final Map<String, Object> metaAttributes = new LinkedHashMap<String, Object>();
|
||||
return new EmptyVisitor() {
|
||||
@Override
|
||||
public void visit(String name, Object value) {
|
||||
@@ -107,7 +123,7 @@ class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor imple
|
||||
Annotation[] metaAnnotations = annotationClass.getAnnotations();
|
||||
Set<String> metaAnnotationTypeNames = new HashSet<String>();
|
||||
for (Annotation metaAnnotation : metaAnnotations) {
|
||||
metaAnnotationTypeNames.add(metaAnnotation.annotationType().getName());
|
||||
metaAnnotationTypeNames.add(metaAnnotation.annotationType().getName());
|
||||
}
|
||||
metaAnnotationMap.put(className, metaAnnotationTypeNames);
|
||||
}
|
||||
@@ -115,6 +131,7 @@ class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor imple
|
||||
// Class not found - can't determine meta-annotations.
|
||||
}
|
||||
attributesMap.put(className, attributes);
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -131,7 +148,7 @@ class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor imple
|
||||
public Set<String> getMetaAnnotationTypes(String annotationType) {
|
||||
return this.metaAnnotationMap.get(annotationType);
|
||||
}
|
||||
|
||||
|
||||
public boolean hasMetaAnnotation(String metaAnnotationType) {
|
||||
Collection<Set<String>> allMetaTypes = this.metaAnnotationMap.values();
|
||||
for (Set<String> metaTypes : allMetaTypes) {
|
||||
@@ -142,8 +159,21 @@ class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor imple
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public Map<String, Object> getAnnotationAttributes(String annotationType) {
|
||||
return this.attributesMap.get(annotationType);
|
||||
}
|
||||
|
||||
|
||||
public Set<MethodMetadata> getAnnotatedMethods(String annotationType) {
|
||||
Set<MethodMetadata> annotatedMethods = new LinkedHashSet<MethodMetadata>();
|
||||
for (MethodMetadata method : methodMetadataSet) {
|
||||
if (method.hasAnnotation(annotationType))
|
||||
{
|
||||
annotatedMethods.add(method);
|
||||
}
|
||||
}
|
||||
return annotatedMethods;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.core.type.classreading;
|
||||
|
||||
import org.objectweb.asm.ClassAdapter;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.commons.EmptyVisitor;
|
||||
|
||||
@@ -33,7 +34,7 @@ import org.springframework.util.ClassUtils;
|
||||
* @author Ramnivas Laddad
|
||||
* @since 2.5
|
||||
*/
|
||||
class ClassMetadataReadingVisitor extends EmptyVisitor implements ClassMetadata {
|
||||
class ClassMetadataReadingVisitor extends ClassAdapter implements ClassMetadata {
|
||||
|
||||
private String className;
|
||||
|
||||
@@ -48,8 +49,12 @@ class ClassMetadataReadingVisitor extends EmptyVisitor implements ClassMetadata
|
||||
private String superClassName;
|
||||
|
||||
private String[] interfaces;
|
||||
|
||||
|
||||
|
||||
public ClassMetadataReadingVisitor()
|
||||
{
|
||||
super(new EmptyVisitor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(int version, int access, String name, String signature, String supername, String[] interfaces) {
|
||||
this.className = ClassUtils.convertResourcePathToClassName(name);
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
package org.springframework.core.type.classreading;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.MethodAdapter;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.EmptyVisitor;
|
||||
import org.springframework.core.type.MethodMetadata;
|
||||
|
||||
public class MethodMetadataReadingVisitor extends MethodAdapter implements MethodMetadata {
|
||||
|
||||
private final Map<String, Map<String, Object>> attributesMap = new LinkedHashMap<String, Map<String, Object>>();
|
||||
|
||||
private final Map<String, Set<String>> metaAnnotationMap = new LinkedHashMap<String, Set<String>>();
|
||||
|
||||
private ClassLoader classLoader;
|
||||
private String name;
|
||||
private int access;
|
||||
private boolean isStatic;
|
||||
|
||||
public MethodMetadataReadingVisitor(ClassLoader classLoader, String name, int access) {
|
||||
super(new EmptyVisitor());
|
||||
this.classLoader = classLoader;
|
||||
this.name = name;
|
||||
this.access = access;
|
||||
this.isStatic = ((access & Opcodes.ACC_STATIC) != 0);
|
||||
}
|
||||
|
||||
public Map<String, Object> getAnnotationAttributes(String annotationType) {
|
||||
return this.attributesMap.get(annotationType);
|
||||
}
|
||||
|
||||
public Set<String> getAnnotationTypes() {
|
||||
return this.attributesMap.keySet();
|
||||
}
|
||||
|
||||
public String getMethodName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int getModifiers() {
|
||||
return access;
|
||||
}
|
||||
|
||||
public boolean hasAnnotation(String annotationType) {
|
||||
return this.attributesMap.containsKey(annotationType);
|
||||
}
|
||||
|
||||
public Set<String> getMetaAnnotationTypes(String annotationType) {
|
||||
return this.metaAnnotationMap.get(annotationType);
|
||||
}
|
||||
|
||||
public boolean hasMetaAnnotation(String metaAnnotationType) {
|
||||
Collection<Set<String>> allMetaTypes = this.metaAnnotationMap.values();
|
||||
for (Set<String> metaTypes : allMetaTypes) {
|
||||
if (metaTypes.contains(metaAnnotationType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isStatic() {
|
||||
return isStatic;
|
||||
}
|
||||
|
||||
|
||||
public Set<String> getAnnotationTypesWithMetaAnnotation(String metaAnnotationType) {
|
||||
|
||||
///metaAnnotationMap.put(className, metaAnnotationTypeNames);
|
||||
Set<String> annotationTypes = new LinkedHashSet<String>();
|
||||
Set< Map.Entry<String, Set<String>> > metaValues = metaAnnotationMap.entrySet();
|
||||
Iterator<Map.Entry<String, Set<String>> > metaIterator = metaValues.iterator();
|
||||
while (metaIterator.hasNext())
|
||||
{
|
||||
Map.Entry<String, Set<String>> entry = metaIterator.next();
|
||||
String attributeType = entry.getKey();
|
||||
Set<String> metaAttributes = entry.getValue();
|
||||
if (metaAttributes.contains(metaAnnotationType))
|
||||
{
|
||||
annotationTypes.add(attributeType);
|
||||
}
|
||||
}
|
||||
return annotationTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(final String desc, boolean visible) {
|
||||
final String className = Type.getType(desc).getClassName();
|
||||
final Map<String, Object> attributes = new LinkedHashMap<String, Object>();
|
||||
return new EmptyVisitor() {
|
||||
@Override
|
||||
public void visit(String name, Object value) {
|
||||
// Explicitly defined annotation attribute value.
|
||||
attributes.put(name, value);
|
||||
}
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
try {
|
||||
Class annotationClass = classLoader.loadClass(className);
|
||||
// Check declared default values of attributes in the annotation type.
|
||||
Method[] annotationAttributes = annotationClass.getMethods();
|
||||
for (int i = 0; i < annotationAttributes.length; i++) {
|
||||
Method annotationAttribute = annotationAttributes[i];
|
||||
String attributeName = annotationAttribute.getName();
|
||||
Object defaultValue = annotationAttribute.getDefaultValue();
|
||||
if (defaultValue != null && !attributes.containsKey(attributeName)) {
|
||||
attributes.put(attributeName, defaultValue);
|
||||
}
|
||||
}
|
||||
// Register annotations that the annotation type is annotated with.
|
||||
Annotation[] metaAnnotations = annotationClass.getAnnotations();
|
||||
Set<String> metaAnnotationTypeNames = new HashSet<String>();
|
||||
for (Annotation metaAnnotation : metaAnnotations) {
|
||||
metaAnnotationTypeNames.add(metaAnnotation.annotationType().getName());
|
||||
}
|
||||
metaAnnotationMap.put(className, metaAnnotationTypeNames);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
// Class not found
|
||||
}
|
||||
attributesMap.put(className, attributes);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -23,7 +23,7 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Scope {
|
||||
|
||||
@@ -23,9 +23,12 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
@@ -37,15 +40,18 @@ import org.springframework.stereotype.Component;
|
||||
*/
|
||||
public class AnnotationMetadataTests extends TestCase {
|
||||
|
||||
|
||||
public void testStandardAnnotationMetadata() throws IOException {
|
||||
StandardAnnotationMetadata annInfo = new StandardAnnotationMetadata(AnnotatedComponent.class);
|
||||
doTestAnnotationInfo(annInfo);
|
||||
doTestMethodAnnotationInfo(annInfo);
|
||||
}
|
||||
|
||||
public void testAsmAnnotationMetadata() throws IOException {
|
||||
MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();
|
||||
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(AnnotatedComponent.class.getName());
|
||||
doTestAnnotationInfo(metadataReader.getAnnotationMetadata());
|
||||
doTestMethodAnnotationInfo(metadataReader.getAnnotationMetadata());
|
||||
}
|
||||
|
||||
private void doTestAnnotationInfo(AnnotationMetadata metadata) {
|
||||
@@ -77,6 +83,16 @@ public class AnnotationMetadataTests extends TestCase {
|
||||
assertEquals(String.class, specialAttrs.get("clazz"));
|
||||
assertEquals(Thread.State.NEW, specialAttrs.get("state"));
|
||||
}
|
||||
|
||||
private void doTestMethodAnnotationInfo(AnnotationMetadata classMetadata) {
|
||||
Set<MethodMetadata> methods = classMetadata.getAnnotatedMethods("org.springframework.beans.factory.annotation.Autowired");
|
||||
assertEquals(1, methods.size());
|
||||
for (MethodMetadata methodMetadata : methods) {
|
||||
Set<String> annotationTypes = methodMetadata.getAnnotationTypes();
|
||||
assertEquals(1, annotationTypes.size());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@@ -93,6 +109,17 @@ public class AnnotationMetadataTests extends TestCase {
|
||||
@Scope("myScope")
|
||||
@SpecialAttr(clazz = String.class, state = Thread.State.NEW)
|
||||
private static class AnnotatedComponent implements Serializable {
|
||||
|
||||
@Autowired
|
||||
public void doWork(@Qualifier("myColor") java.awt.Color color) {
|
||||
|
||||
}
|
||||
@Test
|
||||
public void doSleep()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user