it = this.injectedElements.iterator(); it.hasNext();) {
Member member = it.next().getMember();
if (!beanDefinition.isExternallyManagedConfigMember(member)) {
beanDefinition.registerExternallyManagedConfigMember(member);
@@ -95,22 +74,10 @@ public class InjectionMetadata {
}
}
- public void injectFields(Object target, String beanName) throws Throwable {
- if (!this.injectedFields.isEmpty()) {
+ public void inject(Object target, String beanName, PropertyValues pvs) throws Throwable {
+ if (!this.injectedElements.isEmpty()) {
boolean debug = logger.isDebugEnabled();
- for (InjectedElement element : this.injectedFields) {
- if (debug) {
- logger.debug("Processing injected field of bean '" + beanName + "': " + element);
- }
- element.inject(target, beanName, null);
- }
- }
- }
-
- public void injectMethods(Object target, String beanName, PropertyValues pvs) throws Throwable {
- if (!this.injectedMethods.isEmpty()) {
- boolean debug = logger.isDebugEnabled();
- for (InjectedElement element : this.injectedMethods) {
+ for (InjectedElement element : this.injectedElements) {
if (debug) {
logger.debug("Processing injected method of bean '" + beanName + "': " + element);
}
diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java
index 6d232c5642a..7728c939f4a 100644
--- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java
+++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java
@@ -780,12 +780,18 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class beanType, String beanName)
throws BeansException {
- for (BeanPostProcessor bp : getBeanPostProcessors()) {
- if (bp instanceof MergedBeanDefinitionPostProcessor) {
- MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
- bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
+ try {
+ for (BeanPostProcessor bp : getBeanPostProcessors()) {
+ if (bp instanceof MergedBeanDefinitionPostProcessor) {
+ MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
+ bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
+ }
}
}
+ catch (Exception ex) {
+ throw new BeanCreationException(mbd.getResourceDescription(), beanName,
+ "Post-processing failed of bean type [" + beanType + "] failed", ex);
+ }
}
/**
diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java
index da266c1526b..1d80a290cfc 100644
--- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java
+++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java
@@ -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.
@@ -16,12 +16,10 @@
package org.springframework.beans.factory.support;
-import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
-import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
@@ -152,8 +150,8 @@ public class BeanDefinitionReaderUtils {
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
- for (int i = 0; i < aliases.length; i++) {
- registry.registerAlias(beanName, aliases[i]);
+ for (String aliase : aliases) {
+ registry.registerAlias(beanName, aliase);
}
}
}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java
new file mode 100644
index 00000000000..6cbc488bdc4
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java
@@ -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.
+ * 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.
+ *
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.
+ *
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.
+ *
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);
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java
index 2428e935890..28b38c633ba 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java
@@ -315,5 +315,4 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo
return ScopedProxyCreator.createScopedProxy(definition, this.registry, proxyTargetClass);
}
-
}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java
index 5f2bde0ab26..1e4495d1aff 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java
@@ -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 elements = new LinkedList();
+ Class> targetClass = clazz;
+
+ do {
+ LinkedList currElements = new LinkedList();
+ 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);
}
}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassApplicationContext.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassApplicationContext.java
index ab1cd69334c..7e6b7e2714a 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassApplicationContext.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassApplicationContext.java
@@ -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 getBean(Class requiredType, AbstractApplicationContext context) {
+ public T getBean(Class requiredType, ListableBeanFactory context) {
Assert.notNull(requiredType, "requiredType may not be null");
Assert.notNull(context, "context may not be null");
-
- Map beansOfType = context.getBeansOfType(requiredType);
-
+ Map 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
}
}
}
+
}
diff --git a/org.springframework.context/src/test/java/org/springframework/context/annotation/jsr330/SpringAtInjectTck.java b/org.springframework.context/src/test/java/org/springframework/context/annotation/jsr330/SpringAtInjectTck.java
new file mode 100644
index 00000000000..bfad584d1c6
--- /dev/null
+++ b/org.springframework.context/src/test/java/org/springframework/context/annotation/jsr330/SpringAtInjectTck.java
@@ -0,0 +1,129 @@
+/*
+ * 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.jsr330;
+
+import junit.framework.Test;
+import org.atinject.tck.Tck;
+import org.atinject.tck.auto.Car;
+import org.atinject.tck.auto.Convertible;
+import org.atinject.tck.auto.Drivers;
+import org.atinject.tck.auto.DriversSeat;
+import org.atinject.tck.auto.FuelTank;
+import org.atinject.tck.auto.Seat;
+import org.atinject.tck.auto.Tire;
+import org.atinject.tck.auto.V8Engine;
+import org.atinject.tck.auto.accessories.Cupholder;
+import org.atinject.tck.auto.accessories.SpareTire;
+
+import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.support.AutowireCandidateQualifier;
+import org.springframework.beans.factory.support.GenericBeanDefinition;
+import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
+import org.springframework.context.annotation.AnnotationConfigUtils;
+import org.springframework.context.annotation.Primary;
+import org.springframework.context.annotation.ScopeMetadata;
+import org.springframework.context.annotation.ScopeMetadataResolver;
+import org.springframework.context.support.GenericApplicationContext;
+
+/**
+ * @author Juergen Hoeller
+ * @since 3.0
+ */
+public class SpringAtInjectTck {
+
+ public static Test suite() {
+ GenericApplicationContext ac = new GenericApplicationContext();
+ AnnotatedBeanDefinitionReader bdr = new AnnotatedBeanDefinitionReader(ac);
+ bdr.setScopeMetadataResolver(new ScopeMetadataResolver() {
+ public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
+ ScopeMetadata metadata = new ScopeMetadata();
+ if (definition instanceof AnnotatedBeanDefinition) {
+ AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
+ metadata.setScopeName(annDef.getMetadata().hasAnnotation(javax.inject.Singleton.class.getName()) ?
+ BeanDefinition.SCOPE_SINGLETON : BeanDefinition.SCOPE_PROTOTYPE);
+ }
+ return metadata;
+ }
+ });
+
+ bdr.registerBean(Convertible.class);
+ bdr.registerBean(DriversSeat.class, Drivers.class);
+ bdr.registerBean(Seat.class, Primary.class);
+ bdr.registerBean(V8Engine.class);
+ bdr.registerBean(SpareTire.class, "spare");
+ bdr.registerBean(Cupholder.class);
+ bdr.registerBean(Tire.class, Primary.class);
+ bdr.registerBean(FuelTank.class);
+
+ ac.refresh();
+ Car car = ac.getBean("convertible", Car.class);
+
+ return Tck.testsFor(car, false, true);
+ }
+
+ public static Test suiteX() {
+ GenericApplicationContext ac = new GenericApplicationContext();
+
+ GenericBeanDefinition carDef = new GenericBeanDefinition();
+ carDef.setScope(GenericBeanDefinition.SCOPE_PROTOTYPE);
+ carDef.setBeanClass(Convertible.class);
+ ac.registerBeanDefinition("car", carDef);
+
+ GenericBeanDefinition driversSeatDef = new GenericBeanDefinition();
+ driversSeatDef.setScope(GenericBeanDefinition.SCOPE_PROTOTYPE);
+ driversSeatDef.setBeanClass(DriversSeat.class);
+ driversSeatDef.addQualifier(new AutowireCandidateQualifier(Drivers.class));
+ ac.registerBeanDefinition("driversSeat", driversSeatDef);
+
+ GenericBeanDefinition seatDef = new GenericBeanDefinition();
+ seatDef.setBeanClass(Seat.class);
+ seatDef.setPrimary(true);
+ ac.registerBeanDefinition("seat", seatDef);
+
+ GenericBeanDefinition engineDef = new GenericBeanDefinition();
+ engineDef.setScope(GenericBeanDefinition.SCOPE_PROTOTYPE);
+ engineDef.setBeanClass(V8Engine.class);
+ ac.registerBeanDefinition("engine", engineDef);
+
+ GenericBeanDefinition spareDef = new GenericBeanDefinition();
+ spareDef.setScope(GenericBeanDefinition.SCOPE_PROTOTYPE);
+ spareDef.setBeanClass(SpareTire.class);
+ spareDef.addQualifier(new AutowireCandidateQualifier(Drivers.class));
+ ac.registerBeanDefinition("spare", spareDef);
+
+ GenericBeanDefinition cupholderDef = new GenericBeanDefinition();
+ cupholderDef.setBeanClass(Cupholder.class);
+ ac.registerBeanDefinition("cupholder", cupholderDef);
+
+ GenericBeanDefinition tireDef = new GenericBeanDefinition();
+ tireDef.setScope(GenericBeanDefinition.SCOPE_PROTOTYPE);
+ tireDef.setBeanClass(Tire.class);
+ tireDef.setPrimary(true);
+ ac.registerBeanDefinition("tire", tireDef);
+
+ GenericBeanDefinition fuelTankDef = new GenericBeanDefinition();
+ fuelTankDef.setBeanClass(FuelTank.class);
+ ac.registerBeanDefinition("fuelTank", fuelTankDef);
+
+ AnnotationConfigUtils.registerAnnotationConfigProcessors(ac);
+ ac.refresh();
+ Car car = ac.getBean("car", Car.class);
+ return Tck.testsFor(car, false, true);
+ }
+
+}
diff --git a/org.springframework.core/src/main/java/org/springframework/util/ClassUtils.java b/org.springframework.core/src/main/java/org/springframework/util/ClassUtils.java
index a5af7ae8d19..b0525af65ff 100644
--- a/org.springframework.core/src/main/java/org/springframework/util/ClassUtils.java
+++ b/org.springframework.core/src/main/java/org/springframework/util/ClassUtils.java
@@ -694,13 +694,28 @@ public abstract class ClassUtils {
*/
public static Method getMostSpecificMethod(Method method, Class> targetClass) {
Method specificMethod = null;
- if (method != null && !Modifier.isPrivate(method.getModifiers()) &&
+ if (method != null && isOverridable(method, targetClass) &&
targetClass != null && !targetClass.equals(method.getDeclaringClass())) {
specificMethod = ReflectionUtils.findMethod(targetClass, method.getName(), method.getParameterTypes());
}
return (specificMethod != null ? specificMethod : method);
}
+ /**
+ * Determine whether the given method is overridable in the given target class.
+ * @param method the method to check
+ * @param targetClass the target class to check against
+ */
+ private static boolean isOverridable(Method method, Class targetClass) {
+ if (Modifier.isPrivate(method.getModifiers())) {
+ return false;
+ }
+ if (Modifier.isPublic(method.getModifiers()) || Modifier.isProtected(method.getModifiers())) {
+ return true;
+ }
+ return getPackageName(method.getDeclaringClass()).equals(getPackageName(targetClass));
+ }
+
/**
* Return a static method of a class.
* @param methodName the static method name
diff --git a/org.springframework.core/src/main/java/org/springframework/util/ReflectionUtils.java b/org.springframework.core/src/main/java/org/springframework/util/ReflectionUtils.java
index 6173671bcff..170c948f940 100644
--- a/org.springframework.core/src/main/java/org/springframework/util/ReflectionUtils.java
+++ b/org.springframework.core/src/main/java/org/springframework/util/ReflectionUtils.java
@@ -433,12 +433,12 @@ public abstract class ReflectionUtils {
* class and superclasses.
* The same named method occurring on subclass and superclass will appear
* twice, unless excluded by a {@link MethodFilter}.
- * @param targetClass class to start looking at
+ * @param clazz class to start looking at
* @param mc the callback to invoke for each method
* @see #doWithMethods(Class, MethodCallback, MethodFilter)
*/
- public static void doWithMethods(Class> targetClass, MethodCallback mc) throws IllegalArgumentException {
- doWithMethods(targetClass, mc, null);
+ public static void doWithMethods(Class> clazz, MethodCallback mc) throws IllegalArgumentException {
+ doWithMethods(clazz, mc, null);
}
/**
@@ -446,14 +446,15 @@ public abstract class ReflectionUtils {
* class and superclasses.
*
The same named method occurring on subclass and superclass will appear
* twice, unless excluded by the specified {@link MethodFilter}.
- * @param targetClass class to start looking at
+ * @param clazz class to start looking at
* @param mc the callback to invoke for each method
* @param mf the filter that determines the methods to apply the callback to
*/
- public static void doWithMethods(Class> targetClass, MethodCallback mc, MethodFilter mf)
+ public static void doWithMethods(Class> clazz, MethodCallback mc, MethodFilter mf)
throws IllegalArgumentException {
// Keep backing up the inheritance hierarchy.
+ Class> targetClass = clazz;
do {
Method[] methods = targetClass.getDeclaredMethods();
for (Method method : methods) {
@@ -469,7 +470,8 @@ public abstract class ReflectionUtils {
}
}
targetClass = targetClass.getSuperclass();
- } while (targetClass != null);
+ }
+ while (targetClass != null);
}
/**
@@ -489,26 +491,26 @@ public abstract class ReflectionUtils {
/**
* Invoke the given callback on all fields in the target class, going up the
* class hierarchy to get all declared fields.
- * @param targetClass the target class to analyze
+ * @param clazz the target class to analyze
* @param fc the callback to invoke for each field
*/
- public static void doWithFields(Class> targetClass, FieldCallback fc) throws IllegalArgumentException {
- doWithFields(targetClass, fc, null);
+ public static void doWithFields(Class> clazz, FieldCallback fc) throws IllegalArgumentException {
+ doWithFields(clazz, fc, null);
}
/**
* Invoke the given callback on all fields in the target class, going up the
* class hierarchy to get all declared fields.
- * @param targetClass the target class to analyze
+ * @param clazz the target class to analyze
* @param fc the callback to invoke for each field
* @param ff the filter that determines the fields to apply the callback to
*/
- public static void doWithFields(Class> targetClass, FieldCallback fc, FieldFilter ff)
+ public static void doWithFields(Class> clazz, FieldCallback fc, FieldFilter ff)
throws IllegalArgumentException {
// Keep backing up the inheritance hierarchy.
+ Class> targetClass = clazz;
do {
- // Copy each field declared on this class unless it's static or file.
Field[] fields = targetClass.getDeclaredFields();
for (Field field : fields) {
// Skip static and final fields.
@@ -519,12 +521,13 @@ public abstract class ReflectionUtils {
fc.doWith(field);
}
catch (IllegalAccessException ex) {
- throw new IllegalStateException("Shouldn't be illegal to access field '" + field.getName() + "': "
- + ex);
+ throw new IllegalStateException(
+ "Shouldn't be illegal to access field '" + field.getName() + "': " + ex);
}
}
targetClass = targetClass.getSuperclass();
- } while (targetClass != null && targetClass != Object.class);
+ }
+ while (targetClass != null && targetClass != Object.class);
}
/**
diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java b/org.springframework.orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java
index 862c5e1b5ab..6a062fc5a88 100644
--- a/org.springframework.orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java
+++ b/org.springframework.orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java
@@ -23,6 +23,7 @@ import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
+import java.util.LinkedList;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
@@ -59,7 +60,6 @@ import org.springframework.orm.jpa.ExtendedEntityManagerCreator;
import org.springframework.orm.jpa.SharedEntityManagerCreator;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
-import org.springframework.util.ReflectionUtils;
/**
* BeanPostProcessor that processes {@link javax.persistence.PersistenceUnit}
@@ -302,13 +302,6 @@ public class PersistenceAnnotationBeanPostProcessor extends JndiLocatorSupport
}
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
- InjectionMetadata metadata = findPersistenceMetadata(bean.getClass());
- try {
- metadata.injectFields(bean, beanName);
- }
- catch (Throwable ex) {
- throw new BeanCreationException(beanName, "Injection of persistence fields failed", ex);
- }
return true;
}
@@ -317,10 +310,10 @@ public class PersistenceAnnotationBeanPostProcessor extends JndiLocatorSupport
InjectionMetadata metadata = findPersistenceMetadata(bean.getClass());
try {
- metadata.injectMethods(bean, beanName, pvs);
+ metadata.inject(bean, beanName, pvs);
}
catch (Throwable ex) {
- throw new BeanCreationException(beanName, "Injection of persistence methods failed", ex);
+ throw new BeanCreationException(beanName, "Injection of persistence dependencies failed", ex);
}
return pvs;
}
@@ -346,21 +339,22 @@ public class PersistenceAnnotationBeanPostProcessor extends JndiLocatorSupport
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 elements = new LinkedList();
+ Class> targetClass = clazz;
+
+ do {
+ LinkedList currElements = new LinkedList();
+ for (Field field : targetClass.getDeclaredFields()) {
PersistenceContext pc = field.getAnnotation(PersistenceContext.class);
PersistenceUnit pu = field.getAnnotation(PersistenceUnit.class);
if (pc != null || pu != null) {
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("Persistence annotations are not supported on static fields");
}
- newMetadata.addInjectedField(new PersistenceElement(field, null));
+ currElements.add(new PersistenceElement(field, null));
}
}
- });
- ReflectionUtils.doWithMethods(clazz, new ReflectionUtils.MethodCallback() {
- public void doWith(Method method) {
+ for (Method method : targetClass.getDeclaredMethods()) {
PersistenceContext pc = method.getAnnotation(PersistenceContext.class);
PersistenceUnit pu = method.getAnnotation(PersistenceUnit.class);
if (pc != null || pu != null &&
@@ -372,11 +366,15 @@ public class PersistenceAnnotationBeanPostProcessor extends JndiLocatorSupport
throw new IllegalStateException("Persistence annotation requires a single-arg method: " + method);
}
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method);
- newMetadata.addInjectedMethod(new PersistenceElement(method, pd));
+ currElements.add(new PersistenceElement(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);
}
}