Browse Source

@Required does not get processed on beans returned by @Bean factory methods (SPR-5744)

git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@1601 50f2f4bb-b051-0410-bef5-90022cba6387
pull/1/head
Juergen Hoeller 17 years ago
parent
commit
e49820d4b0
  1. 55
      org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/RequiredAnnotationBeanPostProcessor.java
  2. 2
      org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java
  3. 29
      org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassProcessingTests.java

55
org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/RequiredAnnotationBeanPostProcessor.java

@ -1,5 +1,5 @@ @@ -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; @@ -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; @@ -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<? extends Annotation> 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<String> validatedBeanNames = Collections.synchronizedSet(new HashSet<String>());
@ -97,6 +112,12 @@ public class RequiredAnnotationBeanPostProcessor extends InstantiationAwareBeanP @@ -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 @@ -112,20 +133,36 @@ public class RequiredAnnotationBeanPostProcessor extends InstantiationAwareBeanP
throws BeansException {
if (!this.validatedBeanNames.contains(beanName)) {
List<String> invalidProperties = new ArrayList<String>();
for (PropertyDescriptor pd : pds) {
if (isRequiredProperty(pd) && !pvs.contains(pd.getName())) {
invalidProperties.add(pd.getName());
if (!shouldSkip(this.beanFactory, beanName)) {
List<String> invalidProperties = new ArrayList<String>();
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.
* <p>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 <code>true</code> to skip the bean; <code>false</code> 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)?
* <p>This implementation looks for the existence of a

2
org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java

@ -28,6 +28,7 @@ import org.apache.commons.logging.LogFactory; @@ -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 { @@ -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<String, Object> beanAttributes = metadata.getAnnotationAttributes(Bean.class.getName());

29
org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassProcessingTests.java

@ -23,7 +23,8 @@ import test.beans.TestBean; @@ -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 { @@ -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 { @@ -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 { @@ -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);
}
}
}

Loading…
Cancel
Save