full compliance with the JSR-330 TCK

This commit is contained in:
Juergen Hoeller
2009-10-20 18:18:25 +00:00
parent a429e230b6
commit 94533976d0
12 changed files with 434 additions and 164 deletions

View File

@@ -0,0 +1,164 @@
/*
* 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.context.annotation;
import java.lang.annotation.Annotation;
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
/**
* Convenient adapter for programmatic registration of annotated bean classes.
*
*
* @author Juergen Hoeller
* @since 3.0
*/
public class AnnotatedBeanDefinitionReader {
private final BeanDefinitionRegistry registry;
private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();
private boolean includeAnnotationConfig = true;
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
this.registry = registry;
}
/**
* Return the BeanDefinitionRegistry that this scanner operates on.
*/
public final BeanDefinitionRegistry getRegistry() {
return this.registry;
}
/**
* Set the BeanNameGenerator to use for detected bean classes.
* <p>Default is a {@link AnnotationBeanNameGenerator}.
*/
public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
this.beanNameGenerator = (beanNameGenerator != null ? beanNameGenerator : new AnnotationBeanNameGenerator());
}
/**
* Set the ScopeMetadataResolver to use for detected bean classes.
* Note that this will override any custom "scopedProxyMode" setting.
* <p>The default is an {@link AnnotationScopeMetadataResolver}.
* @see #setScopedProxyMode
*/
public void setScopeMetadataResolver(ScopeMetadataResolver scopeMetadataResolver) {
this.scopeMetadataResolver = scopeMetadataResolver;
}
/**
* Specify the proxy behavior for non-singleton scoped beans.
* Note that this will override any custom "scopeMetadataResolver" setting.
* <p>The default is {@link ScopedProxyMode#NO}.
* @see #setScopeMetadataResolver
*/
public void setScopedProxyMode(ScopedProxyMode scopedProxyMode) {
this.scopeMetadataResolver = new AnnotationScopeMetadataResolver(scopedProxyMode);
}
/**
* Specify whether to register annotation config post-processors.
* <p>The default is to register the post-processors. Turn this off
* to be able to ignore the annotations or to process them differently.
*/
public void setIncludeAnnotationConfig(boolean includeAnnotationConfig) {
this.includeAnnotationConfig = includeAnnotationConfig;
}
public void registerBeans(Class<?>... annotatedClasses) {
for (Class<?> annotatedClass : annotatedClasses) {
registerBean(annotatedClass);
}
}
public void registerBean(Class<?> annotatedClass) {
registerBean(annotatedClass, null, (Class<? extends Annotation>[]) null);
}
public void registerBean(Class<?> annotatedClass, Class<? extends Annotation>... qualifiers) {
registerBean(annotatedClass, null, qualifiers);
}
public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) {
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
abd.setScope(scopeMetadata.getScopeName());
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
if (abd.getMetadata().isAnnotated(Primary.class.getName())) {
abd.setPrimary(true);
}
if (abd.getMetadata().isAnnotated(Lazy.class.getName())) {
Boolean value = (Boolean) abd.getMetadata().getAnnotationAttributes(Lazy.class.getName()).get("value");
abd.setLazyInit(value);
}
if (abd.getMetadata().isAnnotated(DependsOn.class.getName())) {
String[] value = (String[]) abd.getMetadata().getAnnotationAttributes(DependsOn.class.getName()).get("value");
abd.setDependsOn(value);
}
if (qualifiers != null) {
for (Class<? extends Annotation> qualifier : qualifiers) {
if (Primary.class.equals(qualifier)) {
abd.setPrimary(true);
}
else if (Lazy.class.equals(qualifier)) {
abd.setLazyInit(true);
}
else {
abd.addQualifier(new AutowireCandidateQualifier(qualifier));
}
}
}
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
definitionHolder = applyScopedProxyMode(definitionHolder, scopeMetadata);
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
// Register annotation config processors, if necessary.
if (this.includeAnnotationConfig) {
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
}
/**
* Apply the specified scope to the given bean definition.
* @param definition the bean definition to configure
* @param metadata the corresponding scope metadata
* @return the final bean definition to use (potentially a proxy)
*/
private BeanDefinitionHolder applyScopedProxyMode(BeanDefinitionHolder definition, ScopeMetadata metadata) {
ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();
if (scopedProxyMode.equals(ScopedProxyMode.NO)) {
return definition;
}
boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);
return ScopedProxyCreator.createScopedProxy(definition, this.registry, proxyTargetClass);
}
}

View File

@@ -315,5 +315,4 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo
return ScopedProxyCreator.createScopedProxy(definition, this.registry, proxyTargetClass);
}
}

View File

@@ -31,6 +31,7 @@ import java.net.URL;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -62,7 +63,6 @@ import org.springframework.core.Ordered;
import org.springframework.jndi.support.SimpleJndiBeanFactory;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
@@ -283,13 +283,6 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
}
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
InjectionMetadata metadata = findResourceMetadata(bean.getClass());
try {
metadata.injectFields(bean, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of resource fields failed", ex);
}
return true;
}
@@ -298,10 +291,10 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
InjectionMetadata metadata = findResourceMetadata(bean.getClass());
try {
metadata.injectMethods(bean, beanName, pvs);
metadata.inject(bean, beanName, pvs);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of resource methods failed", ex);
throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);
}
return pvs;
}
@@ -314,33 +307,34 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(clazz);
if (metadata == null) {
final InjectionMetadata newMetadata = new InjectionMetadata(clazz);
ReflectionUtils.doWithFields(clazz, new ReflectionUtils.FieldCallback() {
public void doWith(Field field) {
LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<InjectionMetadata.InjectedElement>();
Class<?> targetClass = clazz;
do {
LinkedList<InjectionMetadata.InjectedElement> currElements = new LinkedList<InjectionMetadata.InjectedElement>();
for (Field field : targetClass.getDeclaredFields()) {
if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) {
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@WebServiceRef annotation is not supported on static fields");
}
newMetadata.addInjectedField(new WebServiceRefElement(field, null));
currElements.add(new WebServiceRefElement(field, null));
}
else if (ejbRefClass != null && field.isAnnotationPresent(ejbRefClass)) {
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@EJB annotation is not supported on static fields");
}
newMetadata.addInjectedField(new EjbRefElement(field, null));
currElements.add(new EjbRefElement(field, null));
}
else if (field.isAnnotationPresent(Resource.class)) {
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@Resource annotation is not supported on static fields");
}
if (!ignoredResourceTypes.contains(field.getType().getName())) {
newMetadata.addInjectedField(new ResourceElement(field, null));
currElements.add(new ResourceElement(field, null));
}
}
}
});
ReflectionUtils.doWithMethods(clazz, new ReflectionUtils.MethodCallback() {
public void doWith(Method method) {
for (Method method : targetClass.getDeclaredMethods()) {
if (webServiceRefClass != null && method.isAnnotationPresent(webServiceRefClass) &&
method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (Modifier.isStatic(method.getModifiers())) {
@@ -350,7 +344,7 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
throw new IllegalStateException("@WebServiceRef annotation requires a single-arg method: " + method);
}
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method);
newMetadata.addInjectedMethod(new WebServiceRefElement(method, pd));
currElements.add(new WebServiceRefElement(method, pd));
}
else if (ejbRefClass != null && method.isAnnotationPresent(ejbRefClass) &&
method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
@@ -361,7 +355,7 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method);
}
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method);
newMetadata.addInjectedMethod(new EjbRefElement(method, pd));
currElements.add(new EjbRefElement(method, pd));
}
else if (method.isAnnotationPresent(Resource.class) &&
method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
@@ -374,12 +368,16 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
}
if (!ignoredResourceTypes.contains(paramTypes[0].getName())) {
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method);
newMetadata.addInjectedMethod(new ResourceElement(method, pd));
currElements.add(new ResourceElement(method, pd));
}
}
}
});
metadata = newMetadata;
elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
metadata = new InjectionMetadata(clazz, elements);
this.injectionMetadataCache.put(clazz, metadata);
}
}

View File

@@ -16,19 +16,16 @@
package org.springframework.context.annotation;
import java.io.IOException;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultBeanNameGenerator;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.support.AbstractRefreshableApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
@@ -108,8 +105,7 @@ public class ConfigurationClassApplicationContext extends AbstractRefreshableApp
* @see Configuration#value()
*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
throws IOException, BeansException {
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) {
this.delegate.loadBeanDefinitions(beanFactory);
}
@@ -162,34 +158,27 @@ public class ConfigurationClassApplicationContext extends AbstractRefreshableApp
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
for (Class<?> configClass : this.configClasses) {
AbstractBeanDefinition def = BeanDefinitionBuilder.rootBeanDefinition(configClass).getBeanDefinition();
RootBeanDefinition def = new RootBeanDefinition(configClass);
String name = AnnotationUtils.findAnnotation(configClass, Configuration.class).value();
if (!StringUtils.hasLength(name)) {
name = new DefaultBeanNameGenerator().generateBeanName(def, beanFactory);
}
beanFactory.registerBeanDefinition(name, def);
}
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
}
/**
* @see ConfigurationClassApplicationContext#getBean(Class)
*/
@SuppressWarnings("unchecked")
public <T> T getBean(Class<T> requiredType, AbstractApplicationContext context) {
public <T> T getBean(Class<T> requiredType, ListableBeanFactory context) {
Assert.notNull(requiredType, "requiredType may not be null");
Assert.notNull(context, "context may not be null");
Map<String, ?> beansOfType = context.getBeansOfType(requiredType);
Map<String, T> beansOfType = context.getBeansOfType(requiredType);
switch (beansOfType.size()) {
case 0:
throw new NoSuchBeanDefinitionException(requiredType);
case 1:
return (T) beansOfType.values().iterator().next();
return beansOfType.values().iterator().next();
default:
throw new NoSuchBeanDefinitionException(requiredType,
beansOfType.size() + " matching bean definitions found " +
@@ -199,4 +188,5 @@ public class ConfigurationClassApplicationContext extends AbstractRefreshableApp
}
}
}
}