diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/RequiredAnnotationBeanPostProcessor.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/RequiredAnnotationBeanPostProcessor.java index cc6c84c8b23..04f7530d6a5 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/RequiredAnnotationBeanPostProcessor.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/RequiredAnnotationBeanPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 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. @@ -27,8 +27,12 @@ import java.util.Set; import org.springframework.beans.BeansException; import org.springframework.beans.PropertyValues; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.BeanInitializationException; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter; +import org.springframework.core.Conventions; import org.springframework.core.Ordered; import org.springframework.core.PriorityOrdered; import org.springframework.core.annotation.AnnotationUtils; @@ -66,12 +70,23 @@ import org.springframework.util.Assert; * @see Required */ public class RequiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter - implements PriorityOrdered { + implements PriorityOrdered, BeanFactoryAware { + + /** + * Bean definition attribute that may indicate whether a given bean is supposed + * to be skipped when performing this post-processor's required property check. + * @see #shouldSkip + */ + public static final String SKIP_REQUIRED_CHECK_ATTRIBUTE = + Conventions.getQualifiedAttributeName(RequiredAnnotationBeanPostProcessor.class, "skipRequiredCheck"); + private Class requiredAnnotationType = Required.class; private int order = Ordered.LOWEST_PRECEDENCE - 1; + private ConfigurableListableBeanFactory beanFactory; + /** Cache for validated bean names, skipping re-validation for the same bean */ private final Set validatedBeanNames = Collections.synchronizedSet(new HashSet()); @@ -97,6 +112,12 @@ public class RequiredAnnotationBeanPostProcessor extends InstantiationAwareBeanP return this.requiredAnnotationType; } + public void setBeanFactory(BeanFactory beanFactory) { + if (beanFactory instanceof ConfigurableListableBeanFactory) { + this.beanFactory = (ConfigurableListableBeanFactory) beanFactory; + } + } + public void setOrder(int order) { this.order = order; } @@ -112,20 +133,36 @@ public class RequiredAnnotationBeanPostProcessor extends InstantiationAwareBeanP throws BeansException { if (!this.validatedBeanNames.contains(beanName)) { - List invalidProperties = new ArrayList(); - for (PropertyDescriptor pd : pds) { - if (isRequiredProperty(pd) && !pvs.contains(pd.getName())) { - invalidProperties.add(pd.getName()); + if (!shouldSkip(this.beanFactory, beanName)) { + List invalidProperties = new ArrayList(); + for (PropertyDescriptor pd : pds) { + if (isRequiredProperty(pd) && !pvs.contains(pd.getName())) { + invalidProperties.add(pd.getName()); + } + } + if (!invalidProperties.isEmpty()) { + throw new BeanInitializationException(buildExceptionMessage(invalidProperties, beanName)); } - } - if (!invalidProperties.isEmpty()) { - throw new BeanInitializationException(buildExceptionMessage(invalidProperties, beanName)); } this.validatedBeanNames.add(beanName); } return pvs; } + /** + * Check whether the given bean definition is not subject to the annotation-based + * required property check as performed by this post-processor. + *

The default implementations check for the presence of the + * {@link #SKIP_REQUIRED_CHECK_ATTRIBUTE} attribute in the bean definition, if any. + * @param beanFactory the BeanFactory to check against + * @param beanName the name of the bean to check against + * @return true to skip the bean; false to process it + */ + protected boolean shouldSkip(ConfigurableListableBeanFactory beanFactory, String beanName) { + return (beanFactory != null && beanFactory.containsBeanDefinition(beanName) && + Boolean.TRUE.equals(beanFactory.getBeanDefinition(beanName).getAttribute(SKIP_REQUIRED_CHECK_ATTRIBUTE))); + } + /** * Is the supplied property required to have a value (that is, to be dependency-injected)? *

This implementation looks for the existence of a diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java index 00c9181636a..3b5f490e22d 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java @@ -28,6 +28,7 @@ import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.annotation.Autowire; +import org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.parsing.SourceExtractor; @@ -123,6 +124,7 @@ class ConfigurationClassBeanDefinitionReader { beanDef.setFactoryBeanName(configClass.getBeanName()); beanDef.setUniqueFactoryMethodName(metadata.getMethodName()); beanDef.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR); + beanDef.setAttribute(RequiredAnnotationBeanPostProcessor.SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE); // consider name and any aliases Map beanAttributes = metadata.getAnnotationAttributes(Bean.class.getName()); diff --git a/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassProcessingTests.java b/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassProcessingTests.java index ecde5ce6f55..5c005e6fed8 100644 --- a/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassProcessingTests.java +++ b/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassProcessingTests.java @@ -23,7 +23,8 @@ import test.beans.TestBean; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor; +import org.springframework.beans.factory.annotation.Required; +import org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.parsing.BeanDefinitionParsingException; import org.springframework.beans.factory.support.DefaultListableBeanFactory; @@ -56,9 +57,11 @@ public class ConfigurationClassProcessingTests { String configBeanName = configClass.getName(); factory.registerBeanDefinition(configBeanName, new RootBeanDefinition(configClass)); } - ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); - pp.postProcessBeanFactory(factory); - factory.addBeanPostProcessor(new AutowiredAnnotationBeanPostProcessor()); + ConfigurationClassPostProcessor ccpp = new ConfigurationClassPostProcessor(); + ccpp.postProcessBeanFactory(factory); + RequiredAnnotationBeanPostProcessor rapp = new RequiredAnnotationBeanPostProcessor(); + rapp.setBeanFactory(factory); + factory.addBeanPostProcessor(rapp); return factory; } @@ -161,13 +164,13 @@ public class ConfigurationClassProcessingTests { static class ConfigWithPrototypeBean { public @Bean TestBean foo() { - TestBean foo = new TestBean("foo"); + TestBean foo = new SpousyTestBean("foo"); foo.setSpouse(bar()); return foo; } public @Bean TestBean bar() { - TestBean bar = new TestBean("bar"); + TestBean bar = new SpousyTestBean("bar"); bar.setSpouse(baz()); return bar; } @@ -178,4 +181,18 @@ public class ConfigurationClassProcessingTests { } } + + private static class SpousyTestBean extends TestBean { + + public SpousyTestBean(String name) { + super(name); + } + + @Override + @Required + public void setSpouse(ITestBean spouse) { + super.setSpouse(spouse); + } + } + }