diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/Scopes.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/StandardScopes.java similarity index 95% rename from org.springframework.config.java/src/main/java/org/springframework/config/java/Scopes.java rename to org.springframework.config.java/src/main/java/org/springframework/config/java/StandardScopes.java index 18782484f41..bc79b411759 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/Scopes.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/StandardScopes.java @@ -26,9 +26,9 @@ package org.springframework.config.java; * @author Chris Beams * @since 3.0 */ -public class Scopes { +public class StandardScopes { - private Scopes() { + private StandardScopes() { } public static final String SINGLETON = "singleton"; diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/Bean.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/Bean.java index 4f90824c28e..720d49afbed 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/Bean.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/Bean.java @@ -26,7 +26,7 @@ import org.springframework.beans.factory.annotation.Autowire; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.config.java.Configuration; -import org.springframework.config.java.Scopes; +import org.springframework.config.java.StandardScopes; /** @@ -87,7 +87,7 @@ public @interface Bean { * Scope: whether the bean is a singleton, prototype or custom scope. Default is * singleton. */ - String scope() default Scopes.SINGLETON; + String scope() default StandardScopes.SINGLETON; /** * Bean autowire strategy. diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/support/AbstractConfigurationClassProcessor.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/support/AbstractConfigurationClassProcessor.java index be58c0fb34c..a6dd7fde638 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/support/AbstractConfigurationClassProcessor.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/support/AbstractConfigurationClassProcessor.java @@ -1,47 +1,108 @@ +/* + * 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.config.java.support; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.config.java.Configuration; import org.springframework.config.java.ConfigurationModel; +import org.springframework.config.java.MalformedConfigurationException; +import org.springframework.config.java.ext.Bean; import org.springframework.config.java.internal.parsing.ConfigurationParser; + +/** + * Abstract superclass for processing {@link Configuration}-annotated classes and registering + * bean definitions based on {@link Bean}-annotated methods within those classes. + * + *

Provides template method {@link #processConfigBeanDefinitions()} that orchestrates calling each + * of several abstract methods to be overriden by concrete implementations that allow for + * customizing how {@link Configuration} classes are found ({@link #getConfigurationBeanDefinitions}), + * customizing the creation of a {@link ConfigurationParser} ({@link #createConfigurationParser}), + * and customizing {@link ConfigurationModel} validation logic ({@link #validateModel}). + * + *

This class was expressly designed with tooling in mind. Spring IDE will maintain it's + * own implementation of this class but still take advantage of the generic parsing algorithm + * defined here by {@link #processConfigBeanDefinitions()}. + * + * @author Chris Beams + * @since 3.0 + * @see ConfigurationClassPostProcessor + */ public abstract class AbstractConfigurationClassProcessor { - + /** + * Populate and return a registry containing all {@link Configuration} bean definitions + * to be processed. + * + * @param includeAbstractBeanDefs whether abstract Configuration bean definitions should + * be included in the resulting BeanDefinitionRegistry. Usually false, but called as true + * during the enhancement phase. + * @see #processConfigBeanDefinitions() + */ protected abstract BeanDefinitionRegistry getConfigurationBeanDefinitions(boolean includeAbstractBeanDefs); + /** + * Create and return a new {@link ConfigurationParser}, allowing for customization of + * type (ASM/JDT/Reflection) as well as providing specialized ClassLoader during + * construction. + * @see #processConfigBeanDefinitions() + */ protected abstract ConfigurationParser createConfigurationParser(); + /** + * Validate the given model and handle any errors. Implementations may choose to throw + * {@link MalformedConfigurationException}, or in the case of tooling register problems + * with the UI. + * @param configModel {@link ConfigurationModel} to validate + */ protected abstract void validateModel(ConfigurationModel configModel); - protected BeanDefinitionRegistry processConfigBeanDefinitions() { + /** + * Build and validate a {@link ConfigurationModel} based on the registry of + * {@link Configuration} classes provided by {@link #getConfigurationBeanDefinitions}, + * then, based on the content of that model, create and register bean definitions + * against a new {@link BeanDefinitionRegistry}, then return the registry. + * + * @return registry containing one bean definition per {@link Bean} method declared + * within the Configuration classes + */ + protected final BeanDefinitionRegistry processConfigBeanDefinitions() { BeanDefinitionRegistry configBeanDefs = getConfigurationBeanDefinitions(false); + // return an empty registry immediately if no @Configuration classes were found if(configBeanDefs.getBeanDefinitionCount() == 0) - return configBeanDefs; // nothing to do - don't waste any more cycles + return configBeanDefs; - ConfigurationModel configModel = createConfigurationModelFor(configBeanDefs); + // populate a new ConfigurationModel by parsing each @Configuration classes + ConfigurationParser parser = createConfigurationParser(); + + for(String beanName : configBeanDefs.getBeanDefinitionNames()) { + BeanDefinition beanDef = configBeanDefs.getBeanDefinition(beanName); + String className = beanDef.getBeanClassName(); + + parser.parse(className, beanName); + } + ConfigurationModel configModel = parser.getConfigurationModel(); + + // validate the ConfigurationModel validateModel(configModel); - return renderModelAsBeanDefinitions(configModel); - } - - private ConfigurationModel createConfigurationModelFor(BeanDefinitionRegistry configBeanDefinitions) { - - ConfigurationParser parser = createConfigurationParser(); - - for(String beanName : configBeanDefinitions.getBeanDefinitionNames()) { - BeanDefinition beanDef = configBeanDefinitions.getBeanDefinition(beanName); - String className = beanDef.getBeanClassName(); - - parser.parse(className, beanName); - } - - return parser.getConfigurationModel(); - } - - private BeanDefinitionRegistry renderModelAsBeanDefinitions(ConfigurationModel configModel) { + // read the model and create bean definitions based on its content return new ConfigurationModelBeanDefinitionReader().loadBeanDefinitions(configModel); } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationPostProcessor.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationClassPostProcessor.java similarity index 88% rename from org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationPostProcessor.java rename to org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationClassPostProcessor.java index a5707c1f8c3..7c598cb712f 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationPostProcessor.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationClassPostProcessor.java @@ -25,7 +25,6 @@ import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; -import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.DefaultListableBeanFactory; @@ -33,6 +32,7 @@ import org.springframework.config.java.Configuration; import org.springframework.config.java.ConfigurationModel; import org.springframework.config.java.MalformedConfigurationException; import org.springframework.config.java.UsageError; +import org.springframework.config.java.ext.Bean; import org.springframework.config.java.internal.enhancement.ConfigurationEnhancer; import org.springframework.config.java.internal.parsing.ConfigurationParser; import org.springframework.core.Ordered; @@ -46,11 +46,23 @@ import org.springframework.util.StringUtils; /** * {@link BeanFactoryPostProcessor} used for bootstrapping processing of * {@link Configuration @Configuration} classes. + *

+ * Registered by default when using {@literal } or + * {@literal }. Otherwise, may be declared manually as + * with any other BeanFactoryPostProcessor. + *

+ * This post processor is {@link Ordered#HIGHEST_PRECEDENCE} as it's important + * that any {@link Bean} methods declared in Configuration classes have their + * respective bean definitions registered before any other BeanFactoryPostProcessor + * executes. + * + * @author Chris Beams + * @since 3.0 */ -public class ConfigurationPostProcessor extends AbstractConfigurationClassProcessor - implements Ordered, BeanFactoryPostProcessor { +public class ConfigurationClassPostProcessor extends AbstractConfigurationClassProcessor + implements Ordered, BeanFactoryPostProcessor { - private static final Log logger = LogFactory.getLog(ConfigurationPostProcessor.class); + private static final Log logger = LogFactory.getLog(ConfigurationClassPostProcessor.class); /** * A well-known class in the CGLIB API used when testing to see if CGLIB @@ -68,8 +80,7 @@ public class ConfigurationPostProcessor extends AbstractConfigurationClassProces /** - * @return the order in which this {@link BeanPostProcessor} will be executed. Returns - * {@link Ordered#HIGHEST_PRECEDENCE}. + * @return {@link Ordered#HIGHEST_PRECEDENCE}. */ public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; @@ -95,7 +106,7 @@ public class ConfigurationPostProcessor extends AbstractConfigurationClassProces /** * @return a ConfigurationParser that uses the enclosing BeanFactory's - * classLoader to load all Configuration class artifacts. + * ClassLoader to load all Configuration class artifacts. */ @Override protected ConfigurationParser createConfigurationParser() { @@ -103,7 +114,8 @@ public class ConfigurationPostProcessor extends AbstractConfigurationClassProces } /** - * @return map of all non-abstract {@link BeanDefinition}s in the enclosing {@link #beanFactory} + * @return map of all non-abstract {@link BeanDefinition}s in the + * enclosing {@link #beanFactory} */ @Override protected BeanDefinitionRegistry getConfigurationBeanDefinitions(boolean includeAbstractBeanDefs) { diff --git a/org.springframework.config.java/src/test/java/org/springframework/config/java/support/ConfigurationPostProcessorTests.java b/org.springframework.config.java/src/test/java/org/springframework/config/java/support/ConfigurationPostProcessorTests.java index 2158e9a3834..e16f03c04e5 100644 --- a/org.springframework.config.java/src/test/java/org/springframework/config/java/support/ConfigurationPostProcessorTests.java +++ b/org.springframework.config.java/src/test/java/org/springframework/config/java/support/ConfigurationPostProcessorTests.java @@ -14,17 +14,17 @@ import org.springframework.config.java.ext.Bean; import org.springframework.util.ClassUtils; /** - * Unit tests for {@link ConfigurationPostProcessor} + * Unit tests for {@link ConfigurationClassPostProcessor} * * @author Chris Beams */ public class ConfigurationPostProcessorTests { - private static final String ORIG_CGLIB_TEST_CLASS = ConfigurationPostProcessor.CGLIB_TEST_CLASS; + private static final String ORIG_CGLIB_TEST_CLASS = ConfigurationClassPostProcessor.CGLIB_TEST_CLASS; private static final String BOGUS_CGLIB_TEST_CLASS = "a.bogus.class"; /** - * CGLIB is an optional dependency for Core Spring. If users attempt + * CGLIB is an optional dependency for Spring. If users attempt * to use {@link Configuration} classes, they'll need it on the classpath; * if Configuration classes are present in the bean factory and CGLIB * is not present, an instructive exception should be thrown. @@ -39,17 +39,17 @@ public class ConfigurationPostProcessorTests { factory.registerBeanDefinition("config1", rootBeanDefinition(Config.class).getBeanDefinition()); - ConfigurationPostProcessor cpp = new ConfigurationPostProcessor(); + ConfigurationClassPostProcessor cpp = new ConfigurationClassPostProcessor(); // temporarily set the cglib test class to something bogus - ConfigurationPostProcessor.CGLIB_TEST_CLASS = BOGUS_CGLIB_TEST_CLASS; + ConfigurationClassPostProcessor.CGLIB_TEST_CLASS = BOGUS_CGLIB_TEST_CLASS; try { cpp.postProcessBeanFactory(factory); } catch (RuntimeException ex) { assertTrue(ex.getMessage().contains("CGLIB is required to process @Configuration classes")); } finally { - ConfigurationPostProcessor.CGLIB_TEST_CLASS = ORIG_CGLIB_TEST_CLASS; + ConfigurationClassPostProcessor.CGLIB_TEST_CLASS = ORIG_CGLIB_TEST_CLASS; } } @@ -59,7 +59,7 @@ public class ConfigurationPostProcessorTests { * of {@link Configuration} classes. * * This test will fail if any CGLIB classes are classloaded before the call - * to {@link ConfigurationPostProcessor#enhanceConfigurationClasses} + * to {@link ConfigurationClassPostProcessor#enhanceConfigurationClasses} */ @Test public void testCglibClassesAreLoadedJustInTimeForEnhancement() throws Exception { @@ -93,7 +93,7 @@ public class ConfigurationPostProcessorTests { * Enhanced {@link Configuration} classes are only necessary for respecting * certain bean semantics, like singleton-scoping, scoped proxies, etc. * - * Technically, {@link ConfigurationPostProcessor} could fail to enhance the + * Technically, {@link ConfigurationClassPostProcessor} could fail to enhance the * registered Configuration classes, and many use cases would still work. * Certain cases, however, like inter-bean singleton references would not. * We test for such a case below, and in doing so prove that enhancement is @@ -104,7 +104,7 @@ public class ConfigurationPostProcessorTests { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); beanFactory.registerBeanDefinition("config", rootBeanDefinition(SingletonBeanConfig.class).getBeanDefinition()); - new ConfigurationPostProcessor().postProcessBeanFactory(beanFactory); + new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory); Foo foo = (Foo) beanFactory.getBean("foo"); Bar bar = (Bar) beanFactory.getBean("bar"); assertThat(foo, sameInstance(bar.foo)); diff --git a/org.springframework.config.java/src/test/java/test/basic/AutowiredConfigurationTests.xml b/org.springframework.config.java/src/test/java/test/basic/AutowiredConfigurationTests.xml index ffdd961b422..4cb14bec881 100644 --- a/org.springframework.config.java/src/test/java/test/basic/AutowiredConfigurationTests.xml +++ b/org.springframework.config.java/src/test/java/test/basic/AutowiredConfigurationTests.xml @@ -7,7 +7,7 @@ - + diff --git a/org.springframework.config.java/src/test/java/test/basic/BasicTests.java b/org.springframework.config.java/src/test/java/test/basic/BasicTests.java index 8e499b05798..32f6890c076 100644 --- a/org.springframework.config.java/src/test/java/test/basic/BasicTests.java +++ b/org.springframework.config.java/src/test/java/test/basic/BasicTests.java @@ -11,9 +11,9 @@ import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.config.java.Configuration; import org.springframework.config.java.MalformedConfigurationException; -import org.springframework.config.java.Scopes; +import org.springframework.config.java.StandardScopes; import org.springframework.config.java.ext.Bean; -import org.springframework.config.java.support.ConfigurationPostProcessor; +import org.springframework.config.java.support.ConfigurationClassPostProcessor; import test.beans.ITestBean; import test.beans.TestBean; @@ -24,7 +24,7 @@ public class BasicTests { /** * Creates a new {@link BeanFactory}, populates it with a {@link BeanDefinition} for * each of the given {@link Configuration} configClasses, and then - * post-processes the factory using JavaConfig's {@link ConfigurationPostProcessor}. + * post-processes the factory using JavaConfig's {@link ConfigurationClassPostProcessor}. * When complete, the factory is ready to service requests for any {@link Bean} methods * declared by configClasses. * @@ -42,7 +42,7 @@ public class BasicTests { .getBeanDefinition()); } - new ConfigurationPostProcessor().postProcessBeanFactory(factory); + new ConfigurationClassPostProcessor().postProcessBeanFactory(factory); factory.addBeanPostProcessor(new AutowiredAnnotationBeanPostProcessor()); @@ -96,7 +96,7 @@ public class BasicTests { return bar; } - @Bean(scope = Scopes.PROTOTYPE) + @Bean(scope = StandardScopes.PROTOTYPE) public TestBean baz() { return new TestBean("bar"); } diff --git a/org.springframework.config.java/src/test/java/test/basic/ValueInjectionTests.xml b/org.springframework.config.java/src/test/java/test/basic/ValueInjectionTests.xml index d89442c0191..b7e0868c4e1 100644 --- a/org.springframework.config.java/src/test/java/test/basic/ValueInjectionTests.xml +++ b/org.springframework.config.java/src/test/java/test/basic/ValueInjectionTests.xml @@ -7,7 +7,7 @@ - +