Initial cut of feature to create factory beans using the @FactoryBean annotation within a @Component
This commit is contained in:
parent
6281948cf9
commit
fc9c3009fe
|
|
@ -54,6 +54,7 @@ import org.springframework.core.GenericTypeResolver;
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.core.PriorityOrdered;
|
import org.springframework.core.PriorityOrdered;
|
||||||
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
@ -316,6 +317,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
||||||
});
|
});
|
||||||
ReflectionUtils.doWithMethods(clazz, new ReflectionUtils.MethodCallback() {
|
ReflectionUtils.doWithMethods(clazz, new ReflectionUtils.MethodCallback() {
|
||||||
public void doWith(Method method) {
|
public void doWith(Method method) {
|
||||||
|
if (!isFactoryMethod(method)) {
|
||||||
Annotation annotation = findAutowiredAnnotation(method);
|
Annotation annotation = findAutowiredAnnotation(method);
|
||||||
if (annotation != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
|
if (annotation != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
|
||||||
if (Modifier.isStatic(method.getModifiers())) {
|
if (Modifier.isStatic(method.getModifiers())) {
|
||||||
|
|
@ -329,6 +331,15 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
||||||
newMetadata.addInjectedMethod(new AutowiredMethodElement(method, required, pd));
|
newMetadata.addInjectedMethod(new AutowiredMethodElement(method, required, pd));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isFactoryMethod(Method method) {
|
||||||
|
if (AnnotationUtils.findAnnotation(method, FactoryMethod.class)!= null) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
metadata = newMetadata;
|
metadata = newMetadata;
|
||||||
this.injectionMetadataCache.put(clazz, metadata);
|
this.injectionMetadataCache.put(clazz, metadata);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.beans.factory.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks a method as being a factory-method of the class. Use during component scanning
|
||||||
|
* to create a bean definition that has factory-bean and factory-method metadata
|
||||||
|
*
|
||||||
|
* @author Mark Pollack
|
||||||
|
* @since 3.0
|
||||||
|
* @see RequiredAnnotationBeanPostProcessor
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target({ElementType.METHOD})
|
||||||
|
public @interface FactoryMethod {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -33,7 +33,7 @@ import java.lang.annotation.Target;
|
||||||
* @since 2.5
|
* @since 2.5
|
||||||
*/
|
*/
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
|
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
|
||||||
@Inherited
|
@Inherited
|
||||||
@Documented
|
@Documented
|
||||||
public @interface Qualifier {
|
public @interface Qualifier {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2008 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.springframework.beans.factory.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marker annotation identical in functionality with <aop:scoped-proxy/> tag. Provides a smart
|
||||||
|
* proxy backed by a scoped bean, which can be injected into object instances (usually singletons)
|
||||||
|
* allowing the same reference to be held while delegating method invocations to the backing, scoped
|
||||||
|
* beans.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
public @interface ScopedProxy {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use CGLib-based class proxies (true) or JDK interface-based (false).
|
||||||
|
*
|
||||||
|
* Default is CGLib (true).
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
boolean proxyTargetClass() default true;
|
||||||
|
}
|
||||||
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -213,9 +213,31 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
postProcessComponentBeanDefinitions(beanDefinitions);
|
||||||
return 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,
|
* Apply further settings to the given bean definition,
|
||||||
* beyond the contents retrieved from scanning the component class.
|
* beyond the contents retrieved from scanning the component class.
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,11 @@
|
||||||
package org.springframework.context.annotation;
|
package org.springframework.context.annotation;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
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.BeanDefinitionStoreException;
|
||||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
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.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.context.ResourceLoaderAware;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.core.io.ResourceLoader;
|
import org.springframework.core.io.ResourceLoader;
|
||||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||||
import org.springframework.core.io.support.ResourcePatternUtils;
|
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.CachingMetadataReaderFactory;
|
||||||
import org.springframework.core.type.classreading.MetadataReader;
|
import org.springframework.core.type.classreading.MetadataReader;
|
||||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
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 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());
|
protected final Log logger = LogFactory.getLog(getClass());
|
||||||
|
|
||||||
|
|
@ -224,6 +234,94 @@ public class ClassPathScanningCandidateComponentProvider implements ResourceLoad
|
||||||
return candidates;
|
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
|
* Resolve the specified base package into a pattern specification for
|
||||||
* the package search path.
|
* the package search path.
|
||||||
|
|
|
||||||
|
|
@ -71,4 +71,17 @@ public interface AnnotationMetadata extends ClassMetadata {
|
||||||
*/
|
*/
|
||||||
Map<String, Object> getAnnotationAttributes(String annotationType);
|
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;
|
package org.springframework.core.type;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
|
@ -96,4 +98,21 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
|
||||||
return null;
|
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.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.objectweb.asm.AnnotationVisitor;
|
import org.objectweb.asm.AnnotationVisitor;
|
||||||
|
import org.objectweb.asm.MethodVisitor;
|
||||||
import org.objectweb.asm.Type;
|
import org.objectweb.asm.Type;
|
||||||
import org.objectweb.asm.commons.EmptyVisitor;
|
import org.objectweb.asm.commons.EmptyVisitor;
|
||||||
|
|
||||||
import org.springframework.core.type.AnnotationMetadata;
|
import org.springframework.core.type.AnnotationMetadata;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
import org.springframework.core.type.MethodMetadata;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ASM class visitor which looks for the class name and implemented types as
|
* ASM class visitor which looks for the class name and implemented types as
|
||||||
|
|
@ -47,6 +50,7 @@ class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor imple
|
||||||
|
|
||||||
private final Map<String, Set<String>> metaAnnotationMap = new LinkedHashMap<String, Set<String>>();
|
private final Map<String, Set<String>> metaAnnotationMap = new LinkedHashMap<String, Set<String>>();
|
||||||
|
|
||||||
|
private final Set<MethodMetadata> methodMetadataSet = new LinkedHashSet<MethodMetadata>();
|
||||||
|
|
||||||
private final ClassLoader classLoader;
|
private final ClassLoader classLoader;
|
||||||
|
|
||||||
|
|
@ -56,10 +60,22 @@ class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor imple
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@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
|
@Override
|
||||||
public AnnotationVisitor visitAnnotation(final String desc, boolean visible) {
|
public AnnotationVisitor visitAnnotation(final String desc, boolean visible) {
|
||||||
final String className = Type.getType(desc).getClassName();
|
final String className = Type.getType(desc).getClassName();
|
||||||
final Map<String, Object> attributes = new LinkedHashMap<String, Object>();
|
final Map<String, Object> attributes = new LinkedHashMap<String, Object>();
|
||||||
|
final Map<String, Object> metaAttributes = new LinkedHashMap<String, Object>();
|
||||||
return new EmptyVisitor() {
|
return new EmptyVisitor() {
|
||||||
@Override
|
@Override
|
||||||
public void visit(String name, Object value) {
|
public void visit(String name, Object value) {
|
||||||
|
|
@ -115,6 +131,7 @@ class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor imple
|
||||||
// Class not found - can't determine meta-annotations.
|
// Class not found - can't determine meta-annotations.
|
||||||
}
|
}
|
||||||
attributesMap.put(className, attributes);
|
attributesMap.put(className, attributes);
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -142,8 +159,21 @@ class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor imple
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Map<String, Object> getAnnotationAttributes(String annotationType) {
|
public Map<String, Object> getAnnotationAttributes(String annotationType) {
|
||||||
return this.attributesMap.get(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;
|
package org.springframework.core.type.classreading;
|
||||||
|
|
||||||
|
import org.objectweb.asm.ClassAdapter;
|
||||||
import org.objectweb.asm.Opcodes;
|
import org.objectweb.asm.Opcodes;
|
||||||
import org.objectweb.asm.commons.EmptyVisitor;
|
import org.objectweb.asm.commons.EmptyVisitor;
|
||||||
|
|
||||||
|
|
@ -33,7 +34,7 @@ import org.springframework.util.ClassUtils;
|
||||||
* @author Ramnivas Laddad
|
* @author Ramnivas Laddad
|
||||||
* @since 2.5
|
* @since 2.5
|
||||||
*/
|
*/
|
||||||
class ClassMetadataReadingVisitor extends EmptyVisitor implements ClassMetadata {
|
class ClassMetadataReadingVisitor extends ClassAdapter implements ClassMetadata {
|
||||||
|
|
||||||
private String className;
|
private String className;
|
||||||
|
|
||||||
|
|
@ -49,6 +50,10 @@ class ClassMetadataReadingVisitor extends EmptyVisitor implements ClassMetadata
|
||||||
|
|
||||||
private String[] interfaces;
|
private String[] interfaces;
|
||||||
|
|
||||||
|
public ClassMetadataReadingVisitor()
|
||||||
|
{
|
||||||
|
super(new EmptyVisitor());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(int version, int access, String name, String signature, String supername, String[] interfaces) {
|
public void visit(int version, int access, String name, String signature, String supername, String[] interfaces) {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
|
||||||
@Target(ElementType.TYPE)
|
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Documented
|
@Documented
|
||||||
public @interface Scope {
|
public @interface Scope {
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,12 @@ import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
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.context.annotation.Scope;
|
||||||
import org.springframework.core.type.classreading.MetadataReader;
|
import org.springframework.core.type.classreading.MetadataReader;
|
||||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||||
|
|
@ -37,15 +40,18 @@ import org.springframework.stereotype.Component;
|
||||||
*/
|
*/
|
||||||
public class AnnotationMetadataTests extends TestCase {
|
public class AnnotationMetadataTests extends TestCase {
|
||||||
|
|
||||||
|
|
||||||
public void testStandardAnnotationMetadata() throws IOException {
|
public void testStandardAnnotationMetadata() throws IOException {
|
||||||
StandardAnnotationMetadata annInfo = new StandardAnnotationMetadata(AnnotatedComponent.class);
|
StandardAnnotationMetadata annInfo = new StandardAnnotationMetadata(AnnotatedComponent.class);
|
||||||
doTestAnnotationInfo(annInfo);
|
doTestAnnotationInfo(annInfo);
|
||||||
|
doTestMethodAnnotationInfo(annInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAsmAnnotationMetadata() throws IOException {
|
public void testAsmAnnotationMetadata() throws IOException {
|
||||||
MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();
|
MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();
|
||||||
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(AnnotatedComponent.class.getName());
|
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(AnnotatedComponent.class.getName());
|
||||||
doTestAnnotationInfo(metadataReader.getAnnotationMetadata());
|
doTestAnnotationInfo(metadataReader.getAnnotationMetadata());
|
||||||
|
doTestMethodAnnotationInfo(metadataReader.getAnnotationMetadata());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doTestAnnotationInfo(AnnotationMetadata metadata) {
|
private void doTestAnnotationInfo(AnnotationMetadata metadata) {
|
||||||
|
|
@ -78,6 +84,16 @@ public class AnnotationMetadataTests extends TestCase {
|
||||||
assertEquals(Thread.State.NEW, specialAttrs.get("state"));
|
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)
|
@Target(ElementType.TYPE)
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
|
@ -93,6 +109,17 @@ public class AnnotationMetadataTests extends TestCase {
|
||||||
@Scope("myScope")
|
@Scope("myScope")
|
||||||
@SpecialAttr(clazz = String.class, state = Thread.State.NEW)
|
@SpecialAttr(clazz = String.class, state = Thread.State.NEW)
|
||||||
private static class AnnotatedComponent implements Serializable {
|
private static class AnnotatedComponent implements Serializable {
|
||||||
}
|
|
||||||
|
@Autowired
|
||||||
|
public void doWork(@Qualifier("myColor") java.awt.Color color) {
|
||||||
|
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
public void doSleep()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue