diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/model/BeanDefinitionRegistrar.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/BeanDefinitionRegistrar.java similarity index 95% rename from org.springframework.config.java/src/main/java/org/springframework/config/java/model/BeanDefinitionRegistrar.java rename to org.springframework.config.java/src/main/java/org/springframework/config/java/BeanDefinitionRegistrar.java index 64e62fb6d6..31002e64ff 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/model/BeanDefinitionRegistrar.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/BeanDefinitionRegistrar.java @@ -1,4 +1,4 @@ -package org.springframework.config.java.model; +package org.springframework.config.java; import java.lang.reflect.Method; diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/annotation/Configuration.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/Configuration.java similarity index 98% rename from org.springframework.config.java/src/main/java/org/springframework/config/java/annotation/Configuration.java rename to org.springframework.config.java/src/main/java/org/springframework/config/java/Configuration.java index 5fe2f126b4..832ba30e9d 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/annotation/Configuration.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/Configuration.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.config.java.annotation; +package org.springframework.config.java; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/model/ConfigurationClass.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/ConfigurationClass.java similarity index 99% rename from org.springframework.config.java/src/main/java/org/springframework/config/java/model/ConfigurationClass.java rename to org.springframework.config.java/src/main/java/org/springframework/config/java/ConfigurationClass.java index 7dcfcae4a8..732e481969 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/model/ConfigurationClass.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/ConfigurationClass.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.config.java.model; +package org.springframework.config.java; import static java.lang.String.*; @@ -24,7 +24,6 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import org.springframework.config.java.annotation.Configuration; import org.springframework.util.Assert; import sun.security.x509.Extension; diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/model/ConfigurationModel.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/ConfigurationModel.java similarity index 98% rename from org.springframework.config.java/src/main/java/org/springframework/config/java/model/ConfigurationModel.java rename to org.springframework.config.java/src/main/java/org/springframework/config/java/ConfigurationModel.java index 4d3394d8b6..5c813c8a78 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/model/ConfigurationModel.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/ConfigurationModel.java @@ -13,14 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.config.java.model; +package org.springframework.config.java; import static java.lang.String.*; import java.util.ArrayList; import java.util.List; -import org.springframework.config.java.annotation.Configuration; /** diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/model/Factory.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/Factory.java similarity index 97% rename from org.springframework.config.java/src/main/java/org/springframework/config/java/model/Factory.java rename to org.springframework.config.java/src/main/java/org/springframework/config/java/Factory.java index 10cb299e2e..bea48c6d2e 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/model/Factory.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/Factory.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.config.java.model; +package org.springframework.config.java; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/model/MalformedJavaConfigurationException.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/MalformedJavaConfigurationException.java similarity index 97% rename from org.springframework.config.java/src/main/java/org/springframework/config/java/model/MalformedJavaConfigurationException.java rename to org.springframework.config.java/src/main/java/org/springframework/config/java/MalformedJavaConfigurationException.java index 00788515bc..a331ccee10 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/model/MalformedJavaConfigurationException.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/MalformedJavaConfigurationException.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.config.java.model; +package org.springframework.config.java; import java.util.ArrayList; import java.util.Arrays; diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/model/ModelClass.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/ModelClass.java similarity index 97% rename from org.springframework.config.java/src/main/java/org/springframework/config/java/model/ModelClass.java rename to org.springframework.config.java/src/main/java/org/springframework/config/java/ModelClass.java index f041ea412a..5f7fcc657b 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/model/ModelClass.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/ModelClass.java @@ -13,10 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.config.java.model; +package org.springframework.config.java; import org.springframework.beans.BeanMetadataElement; -import org.springframework.config.java.annotation.Configuration; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/model/ModelMethod.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/ModelMethod.java similarity index 99% rename from org.springframework.config.java/src/main/java/org/springframework/config/java/model/ModelMethod.java rename to org.springframework.config.java/src/main/java/org/springframework/config/java/ModelMethod.java index f410ff6851..d5b667cc83 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/model/ModelMethod.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/ModelMethod.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.config.java.model; +package org.springframework.config.java; import static java.lang.String.*; import static org.springframework.config.java.Util.*; diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/model/UsageError.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/UsageError.java similarity index 95% rename from org.springframework.config.java/src/main/java/org/springframework/config/java/model/UsageError.java rename to org.springframework.config.java/src/main/java/org/springframework/config/java/UsageError.java index c431f46828..90767b6a41 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/model/UsageError.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/UsageError.java @@ -13,9 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.config.java.model; +package org.springframework.config.java; -import org.springframework.config.java.annotation.Configuration; /** diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/model/Validatable.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/Validatable.java similarity index 87% rename from org.springframework.config.java/src/main/java/org/springframework/config/java/model/Validatable.java rename to org.springframework.config.java/src/main/java/org/springframework/config/java/Validatable.java index 6d0397e113..a5ce87e318 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/model/Validatable.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/Validatable.java @@ -1,4 +1,4 @@ -package org.springframework.config.java.model; +package org.springframework.config.java; import java.util.List; diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/model/Validator.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/Validator.java similarity index 83% rename from org.springframework.config.java/src/main/java/org/springframework/config/java/model/Validator.java rename to org.springframework.config.java/src/main/java/org/springframework/config/java/Validator.java index 40e8ed6796..7246ee0ee7 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/model/Validator.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/Validator.java @@ -1,4 +1,4 @@ -package org.springframework.config.java.model; +package org.springframework.config.java; import java.util.List; 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 45075810be..a5872313f6 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,13 +26,13 @@ import java.util.List; 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.annotation.Configuration; -import org.springframework.config.java.model.ConfigurationClass; -import org.springframework.config.java.model.ConfigurationModel; -import org.springframework.config.java.model.Factory; -import org.springframework.config.java.model.ModelMethod; -import org.springframework.config.java.model.UsageError; -import org.springframework.config.java.model.Validator; +import org.springframework.config.java.Configuration; +import org.springframework.config.java.ConfigurationClass; +import org.springframework.config.java.ConfigurationModel; +import org.springframework.config.java.Factory; +import org.springframework.config.java.ModelMethod; +import org.springframework.config.java.UsageError; +import org.springframework.config.java.Validator; /** diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/BeanRegistrar.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/BeanRegistrar.java index b9812e81b9..1485371f29 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/BeanRegistrar.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/BeanRegistrar.java @@ -13,12 +13,12 @@ import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.config.java.annotation.Configuration; -import org.springframework.config.java.model.BeanDefinitionRegistrar; -import org.springframework.config.java.model.ConfigurationClass; -import org.springframework.config.java.model.MalformedJavaConfigurationException; -import org.springframework.config.java.model.ModelMethod; -import org.springframework.config.java.model.UsageError; +import org.springframework.config.java.BeanDefinitionRegistrar; +import org.springframework.config.java.Configuration; +import org.springframework.config.java.ConfigurationClass; +import org.springframework.config.java.MalformedJavaConfigurationException; +import org.springframework.config.java.ModelMethod; +import org.springframework.config.java.UsageError; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.util.Assert; diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/enhancement/AddAnnotationAdapter.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/enhancement/AddAnnotationAdapter.java index 96ae3bfefe..688ae7333d 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/enhancement/AddAnnotationAdapter.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/enhancement/AddAnnotationAdapter.java @@ -28,7 +28,9 @@ import org.objectweb.asm.MethodVisitor; /** * Transforms a class by adding bytecode for a class-level annotation. * Checks to ensure that the desired annotation is not already present - * before adding. + * before adding. Used by {@link ConfigurationEnhancer} to dynamically add + * an {@link org.aspectj.lang.Aspect} annotation to an enhanced Configuration + * subclass. *

* This class was originally adapted from examples the ASM 3.0 documentation. * diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/enhancement/CglibConfigurationEnhancer.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/enhancement/CglibConfigurationEnhancer.java deleted file mode 100644 index 758f77d673..0000000000 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/enhancement/CglibConfigurationEnhancer.java +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright 2002-2008 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.internal.enhancement; - -import static java.lang.String.*; -import static org.springframework.config.java.Util.*; -import static org.springframework.util.Assert.*; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.LinkedHashSet; - -import net.sf.cglib.core.DefaultGeneratorStrategy; -import net.sf.cglib.proxy.Callback; -import net.sf.cglib.proxy.CallbackFilter; -import net.sf.cglib.proxy.Enhancer; -import net.sf.cglib.proxy.NoOp; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.objectweb.asm.ClassAdapter; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassWriter; -import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.config.java.annotation.Configuration; -import org.springframework.config.java.model.BeanDefinitionRegistrar; -import org.springframework.config.java.model.ConfigurationClass; -import org.springframework.config.java.model.ConfigurationModel; -import org.springframework.config.java.model.ModelMethod; - - - -/** - * Enhances {@link Configuration} classes by generating a CGLIB subclass capable of - * interacting with the Spring container to respect bean semantics. - * - * @see #enhance(String) - * - * @author Chris Beams - */ -public class CglibConfigurationEnhancer implements ConfigurationEnhancer { - - private static final Log log = LogFactory.getLog(CglibConfigurationEnhancer.class); - - private final ArrayList> callbackTypes = - new ArrayList>(); - - private final LinkedHashSet handlers = - new LinkedHashSet(); - - private final ArrayList callbackInstances = - new ArrayList(); - - private final CallbackFilter callbackFilter = - new CallbackFilter() { - public int accept(Method candidateMethod) { - Iterator iter = handlers.iterator(); - for(int i=0; iter.hasNext(); i++) - if(iter.next().accepts(candidateMethod)) - return i; - - throw new IllegalStateException(format("No registered handler is capable of " + - "handling method [%s]. Perhaps you forgot to register a catch-all registrar?", - candidateMethod.getName())); - } - }; - - - /** - * Creates a new {@link CglibConfigurationEnhancer} instance. - */ - public CglibConfigurationEnhancer(DefaultListableBeanFactory beanFactory, ConfigurationModel model) { - notNull(beanFactory, "beanFactory must be non-null"); - notNull(model, "model must be non-null"); - - populateHandlersAndCallbacks(beanFactory, model); - } - - - /** - * Reads the contents of {@code model} in order to populate {@link #handlers}, - * {@link #callbackInstances} and {@link #callbackTypes} appropriately. - * - * @see #callbackFilter - */ - private void populateHandlersAndCallbacks(DefaultListableBeanFactory beanFactory, ConfigurationModel model) { - - for (ConfigurationClass configClass : model.getAllConfigurationClasses()) { - for (ModelMethod method : configClass.getMethods()) { - handlers.add(method.getRegistrar()); - - Callback callback = method.getCallback(); - - if(callback instanceof BeanFactoryAware) - ((BeanFactoryAware)callback).setBeanFactory(beanFactory); - - callbackInstances.add(callback); - } - } - - handlers.add(new InitializingBeanRegistrar()); - callbackInstances.add(new InitializingBeanCallback(beanFactory)); - - // register a 'catch-all' registrar - handlers.add(new BeanDefinitionRegistrar() { - - public boolean accepts(Method method) { - return true; - } - - public void register(ModelMethod method, BeanDefinitionRegistry registry) { - // no-op - } - }); - callbackInstances.add(NoOp.INSTANCE); - - for(Callback callback : callbackInstances) - callbackTypes.add(callback.getClass()); - } - - - /** - * Loads the specified class and generates a CGLIB subclass of it equipped with container-aware - * callbacks capable of respecting scoping and other bean semantics. - */ - public String enhance(String configClassName) { - if (log.isInfoEnabled()) - log.info("Enhancing " + configClassName); - - Class superclass = loadRequiredClass(configClassName); - - Class subclass = createClass(newEnhancer(superclass), superclass); - - subclass = nestOneClassDeeperIfAspect(superclass, subclass); - - if (log.isInfoEnabled()) - log.info(format("Successfully enhanced %s; enhanced class name is: %s", - configClassName, subclass.getName())); - - return subclass.getName(); - } - - /** - * Creates a new CGLIB {@link Enhancer} instance. - */ - private Enhancer newEnhancer(Class superclass) { - Enhancer enhancer = new Enhancer(); - - // because callbackFilter and callbackTypes are dynamically populated - // there's no opportunity for caching. This does not appear to be causing - // any performance problem. - enhancer.setUseCache(false); - - enhancer.setSuperclass(superclass); - enhancer.setInterfaces(new Class[]{InitializingBean.class}); - enhancer.setUseFactory(false); - enhancer.setCallbackFilter(callbackFilter); - enhancer.setCallbackTypes(callbackTypes.toArray(new Class[]{})); - - return enhancer; - } - - /** - * Uses enhancer to generate a subclass of superclass, ensuring that - * {@link #callbackInstances} are registered for the new subclass. - */ - private Class createClass(Enhancer enhancer, Class superclass) { - Class subclass = enhancer.createClass(); - - // see #registerThreadLocalCleanupBeanDefinition - Enhancer.registerCallbacks(subclass, callbackInstances.toArray(new Callback[] {})); - //Enhancer.registerStaticCallbacks(subclass, callbackInstances.toArray(new Callback[] {})); - - return subclass; - } - - /** - * Works around a constraint imposed by the AspectJ 5 annotation-style programming model. See - * comments inline for detail. - * - * @return original subclass instance unless superclass is annnotated with @Aspect, in which - * case a subclass of the subclass is returned - */ - private Class nestOneClassDeeperIfAspect(Class superclass, Class origSubclass) { - boolean superclassIsAnAspect = false; - - // check for @Aspect by name rather than by class literal to avoid - // requiring AspectJ as a runtime dependency. - for(Annotation anno : superclass.getAnnotations()) - if(anno.annotationType().getName().equals("org.aspectj.lang.annotation.Aspect")) - superclassIsAnAspect = true; - - if(!superclassIsAnAspect) - return origSubclass; - - // the superclass is annotated with AspectJ's @Aspect. - // this means that we must create a subclass of the subclass - // in order to avoid some guard logic in Spring core that disallows - // extending a concrete aspect class. - Enhancer enhancer = newEnhancer(origSubclass); - enhancer.setStrategy(new DefaultGeneratorStrategy() { - @Override - protected byte[] transform(byte[] b) throws Exception { - ClassWriter writer = new ClassWriter(false); - ClassAdapter adapter = - new AddAnnotationAdapter(writer, "Lorg/aspectj/lang/annotation/Aspect;"); - ClassReader reader = new ClassReader(b); - reader.accept(adapter, false); - return writer.toByteArray(); - } - }); - - // create a subclass of the original subclass - Class newSubclass = createClass(enhancer, origSubclass); - - return newSubclass; - } - -} diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/enhancement/ConfigurationEnhancer.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/enhancement/ConfigurationEnhancer.java index 10c2184d47..8175ee4ac5 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/enhancement/ConfigurationEnhancer.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/enhancement/ConfigurationEnhancer.java @@ -15,10 +15,223 @@ */ package org.springframework.config.java.internal.enhancement; +import static java.lang.String.*; +import static org.springframework.config.java.Util.*; +import static org.springframework.util.Assert.*; -/** TODO: JAVADOC */ -public interface ConfigurationEnhancer { +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedHashSet; - String enhance(String configClassName); +import net.sf.cglib.core.DefaultGeneratorStrategy; +import net.sf.cglib.proxy.Callback; +import net.sf.cglib.proxy.CallbackFilter; +import net.sf.cglib.proxy.Enhancer; +import net.sf.cglib.proxy.NoOp; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.objectweb.asm.ClassAdapter; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.config.java.BeanDefinitionRegistrar; +import org.springframework.config.java.Configuration; +import org.springframework.config.java.ConfigurationClass; +import org.springframework.config.java.ConfigurationModel; +import org.springframework.config.java.ModelMethod; + + + +/** + * Enhances {@link Configuration} classes by generating a CGLIB subclass capable of + * interacting with the Spring container to respect bean semantics. + * + * @see #enhance(String) + * + * @author Chris Beams + */ +public class ConfigurationEnhancer { + + private static final Log log = LogFactory.getLog(ConfigurationEnhancer.class); + + private final ArrayList> callbackTypes = + new ArrayList>(); + + private final LinkedHashSet registrars = + new LinkedHashSet(); + + private final ArrayList callbackInstances = + new ArrayList(); + + private final CallbackFilter callbackFilter = + new CallbackFilter() { + public int accept(Method candidateMethod) { + Iterator iter = registrars.iterator(); + for(int i=0; iter.hasNext(); i++) + if(iter.next().accepts(candidateMethod)) + return i; + + throw new IllegalStateException(format("No registrar is capable of " + + "handling method [%s]. Perhaps you forgot to add a catch-all registrar?", + candidateMethod.getName())); + } + }; + + + /** + * Creates a new {@link ConfigurationEnhancer} instance. + */ + public ConfigurationEnhancer(DefaultListableBeanFactory beanFactory, ConfigurationModel model) { + notNull(beanFactory, "beanFactory must be non-null"); + notNull(model, "model must be non-null"); + + populateHandlersAndCallbacks(beanFactory, model); + } + + + /** + * Reads the contents of {@code model} in order to populate {@link #registrars}, + * {@link #callbackInstances} and {@link #callbackTypes} appropriately. + * + * @see #callbackFilter + */ + private void populateHandlersAndCallbacks(DefaultListableBeanFactory beanFactory, ConfigurationModel model) { + + for (ConfigurationClass configClass : model.getAllConfigurationClasses()) { + for (ModelMethod method : configClass.getMethods()) { + registrars.add(method.getRegistrar()); + + Callback callback = method.getCallback(); + + if(callback instanceof BeanFactoryAware) + ((BeanFactoryAware)callback).setBeanFactory(beanFactory); + + callbackInstances.add(callback); + } + } + + registrars.add(new InitializingBeanRegistrar()); + callbackInstances.add(new InitializingBeanCallback(beanFactory)); + + // register a 'catch-all' registrar + registrars.add(new BeanDefinitionRegistrar() { + + public boolean accepts(Method method) { + return true; + } + + public void register(ModelMethod method, BeanDefinitionRegistry registry) { + // no-op + } + }); + callbackInstances.add(NoOp.INSTANCE); + + for(Callback callback : callbackInstances) + callbackTypes.add(callback.getClass()); + } + + + /** + * Loads the specified class and generates a CGLIB subclass of it equipped with container-aware + * callbacks capable of respecting scoping and other bean semantics. + */ + public String enhance(String configClassName) { + if (log.isInfoEnabled()) + log.info("Enhancing " + configClassName); + + Class superclass = loadRequiredClass(configClassName); + + Class subclass = createClass(newEnhancer(superclass), superclass); + + subclass = nestOneClassDeeperIfAspect(superclass, subclass); + + if (log.isInfoEnabled()) + log.info(format("Successfully enhanced %s; enhanced class name is: %s", + configClassName, subclass.getName())); + + return subclass.getName(); + } + + /** + * Creates a new CGLIB {@link Enhancer} instance. + */ + private Enhancer newEnhancer(Class superclass) { + Enhancer enhancer = new Enhancer(); + + // because callbackFilter and callbackTypes are dynamically populated + // there's no opportunity for caching. This does not appear to be causing + // any performance problem. + enhancer.setUseCache(false); + + enhancer.setSuperclass(superclass); + enhancer.setInterfaces(new Class[]{InitializingBean.class}); + enhancer.setUseFactory(false); + enhancer.setCallbackFilter(callbackFilter); + enhancer.setCallbackTypes(callbackTypes.toArray(new Class[]{})); + + return enhancer; + } + + /** + * Uses enhancer to generate a subclass of superclass, ensuring that + * {@link #callbackInstances} are registered for the new subclass. + */ + private Class createClass(Enhancer enhancer, Class superclass) { + Class subclass = enhancer.createClass(); + + // see #registerThreadLocalCleanupBeanDefinition + Enhancer.registerCallbacks(subclass, callbackInstances.toArray(new Callback[] {})); + //Enhancer.registerStaticCallbacks(subclass, callbackInstances.toArray(new Callback[] {})); + + return subclass; + } + + /** + * Works around a constraint imposed by the AspectJ 5 annotation-style programming model. See + * comments inline for detail. + * + * @return original subclass instance unless superclass is annnotated with @Aspect, in which + * case a subclass of the subclass is returned + */ + private Class nestOneClassDeeperIfAspect(Class superclass, Class origSubclass) { + boolean superclassIsAnAspect = false; + + // check for @Aspect by name rather than by class literal to avoid + // requiring AspectJ as a runtime dependency. + for(Annotation anno : superclass.getAnnotations()) + if(anno.annotationType().getName().equals("org.aspectj.lang.annotation.Aspect")) + superclassIsAnAspect = true; + + if(!superclassIsAnAspect) + return origSubclass; + + // the superclass is annotated with AspectJ's @Aspect. + // this means that we must create a subclass of the subclass + // in order to avoid some guard logic in Spring core that disallows + // extending a concrete aspect class. + Enhancer enhancer = newEnhancer(origSubclass); + enhancer.setStrategy(new DefaultGeneratorStrategy() { + @Override + protected byte[] transform(byte[] b) throws Exception { + ClassWriter writer = new ClassWriter(false); + ClassAdapter adapter = + new AddAnnotationAdapter(writer, "Lorg/aspectj/lang/annotation/Aspect;"); + ClassReader reader = new ClassReader(b); + reader.accept(adapter, false); + return writer.toByteArray(); + } + }); + + // create a subclass of the original subclass + Class newSubclass = createClass(enhancer, origSubclass); + + return newSubclass; + } + } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/enhancement/InitializingBeanRegistrar.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/enhancement/InitializingBeanRegistrar.java index 692171c21e..d4428d490e 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/enhancement/InitializingBeanRegistrar.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/enhancement/InitializingBeanRegistrar.java @@ -18,8 +18,8 @@ package org.springframework.config.java.internal.enhancement; import java.lang.reflect.Method; import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.config.java.model.BeanDefinitionRegistrar; -import org.springframework.config.java.model.ModelMethod; +import org.springframework.config.java.BeanDefinitionRegistrar; +import org.springframework.config.java.ModelMethod; class InitializingBeanRegistrar implements BeanDefinitionRegistrar { public boolean accepts(Method method) { diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/factory/support/AsmJavaConfigBeanDefinitionReader.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/factory/support/AsmJavaConfigBeanDefinitionReader.java index 2fb2e8d3ac..8df795743b 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/factory/support/AsmJavaConfigBeanDefinitionReader.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/factory/support/AsmJavaConfigBeanDefinitionReader.java @@ -25,13 +25,13 @@ import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.support.BeanDefinitionReader; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.config.java.annotation.Configuration; +import org.springframework.config.java.Configuration; +import org.springframework.config.java.ConfigurationModel; +import org.springframework.config.java.MalformedJavaConfigurationException; +import org.springframework.config.java.UsageError; import org.springframework.config.java.internal.parsing.ConfigurationParser; import org.springframework.config.java.internal.parsing.asm.AsmConfigurationParser; import org.springframework.config.java.internal.parsing.asm.AsmUtils; -import org.springframework.config.java.model.ConfigurationModel; -import org.springframework.config.java.model.MalformedJavaConfigurationException; -import org.springframework.config.java.model.UsageError; import org.springframework.core.io.ClassPathResource; diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/factory/support/ConfigurationModelBeanDefinitionReader.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/factory/support/ConfigurationModelBeanDefinitionReader.java index 9873db70c7..f79e4447f5 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/factory/support/ConfigurationModelBeanDefinitionReader.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/factory/support/ConfigurationModelBeanDefinitionReader.java @@ -32,11 +32,11 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.config.java.annotation.Configuration; -import org.springframework.config.java.model.ConfigurationClass; -import org.springframework.config.java.model.ConfigurationModel; -import org.springframework.config.java.model.Factory; -import org.springframework.config.java.model.ModelMethod; +import org.springframework.config.java.Configuration; +import org.springframework.config.java.ConfigurationClass; +import org.springframework.config.java.ConfigurationModel; +import org.springframework.config.java.Factory; +import org.springframework.config.java.ModelMethod; import org.springframework.config.java.plugin.Extension; import org.springframework.config.java.plugin.ExtensionAnnotationBeanDefinitionRegistrar; import org.springframework.core.annotation.AnnotationUtils; diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/ConfigurationParser.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/ConfigurationParser.java index fe7523325e..53eb4c91d2 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/ConfigurationParser.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/ConfigurationParser.java @@ -15,8 +15,8 @@ */ package org.springframework.config.java.internal.parsing; -import org.springframework.config.java.annotation.Configuration; -import org.springframework.config.java.model.ConfigurationModel; +import org.springframework.config.java.Configuration; +import org.springframework.config.java.ConfigurationModel; /** @@ -26,7 +26,7 @@ import org.springframework.config.java.model.ConfigurationModel; * from the process of registering bean definitions based on the content of that class. * * @see org.springframework.config.java.internal.parsing.asm.AsmConfigurationParser - * @see org.springframework.config.java.model.ConfigurationModel + * @see org.springframework.config.java.ConfigurationModel * @see org.springframework.config.java.internal.factory.support.ConfigurationModelBeanDefinitionReader * * @author Chris Beams @@ -39,7 +39,7 @@ public interface ConfigurationParser { * @param configurationSource representation of a Configuration class, may be java.lang.Class, * ASM representation or otherwise * - * @see org.springframework.config.java.annotation.Configuration + * @see org.springframework.config.java.Configuration */ void parse(Object configurationSource); @@ -52,7 +52,7 @@ public interface ConfigurationParser { * @param configurationId name of this configuration class, probably corresponding to a * bean id * - * @see org.springframework.config.java.annotation.Configuration + * @see org.springframework.config.java.Configuration * @see org.springframework.config.java.process.ConfigurationPostProcessor */ void parse(Object configurationSource, String configurationId); diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/asm/AsmConfigurationParser.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/asm/AsmConfigurationParser.java index f6f76cad1e..62d9128a5d 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/asm/AsmConfigurationParser.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/asm/AsmConfigurationParser.java @@ -18,10 +18,10 @@ package org.springframework.config.java.internal.parsing.asm; import org.objectweb.asm.ClassReader; -import org.springframework.config.java.annotation.Configuration; +import org.springframework.config.java.Configuration; +import org.springframework.config.java.ConfigurationClass; +import org.springframework.config.java.ConfigurationModel; import org.springframework.config.java.internal.parsing.ConfigurationParser; -import org.springframework.config.java.model.ConfigurationClass; -import org.springframework.config.java.model.ConfigurationModel; import org.springframework.util.Assert; diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/asm/ConfigurationClassMethodVisitor.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/asm/ConfigurationClassMethodVisitor.java index 2b89e911c2..4cd2155459 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/asm/ConfigurationClassMethodVisitor.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/asm/ConfigurationClassMethodVisitor.java @@ -28,11 +28,11 @@ import org.objectweb.asm.ClassAdapter; import org.objectweb.asm.Label; import org.objectweb.asm.MethodAdapter; import org.objectweb.asm.Opcodes; -import org.springframework.config.java.annotation.Configuration; -import org.springframework.config.java.model.ConfigurationClass; -import org.springframework.config.java.model.Factory; -import org.springframework.config.java.model.ModelClass; -import org.springframework.config.java.model.ModelMethod; +import org.springframework.config.java.Configuration; +import org.springframework.config.java.ConfigurationClass; +import org.springframework.config.java.Factory; +import org.springframework.config.java.ModelClass; +import org.springframework.config.java.ModelMethod; /** * Visits a single method declared in a given {@link Configuration} class. Determines whether the diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/asm/ConfigurationClassVisitor.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/asm/ConfigurationClassVisitor.java index a5a5d63ffe..6e0d93041b 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/asm/ConfigurationClassVisitor.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/asm/ConfigurationClassVisitor.java @@ -29,9 +29,9 @@ import org.objectweb.asm.ClassAdapter; import org.objectweb.asm.ClassReader; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; -import org.springframework.config.java.annotation.Configuration; -import org.springframework.config.java.model.ConfigurationClass; -import org.springframework.config.java.model.ConfigurationModel; +import org.springframework.config.java.Configuration; +import org.springframework.config.java.ConfigurationClass; +import org.springframework.config.java.ConfigurationModel; import org.springframework.config.java.plugin.Extension; import org.springframework.util.ClassUtils; diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/process/InternalConfigurationPostProcessor.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/process/InternalConfigurationPostProcessor.java index 20479c1035..caf916a3e2 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/process/InternalConfigurationPostProcessor.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/process/InternalConfigurationPostProcessor.java @@ -27,11 +27,10 @@ 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.DefaultListableBeanFactory; -import org.springframework.config.java.annotation.Configuration; -import org.springframework.config.java.internal.enhancement.CglibConfigurationEnhancer; +import org.springframework.config.java.Configuration; +import org.springframework.config.java.ConfigurationModel; import org.springframework.config.java.internal.enhancement.ConfigurationEnhancer; import org.springframework.config.java.internal.factory.support.AsmJavaConfigBeanDefinitionReader; -import org.springframework.config.java.model.ConfigurationModel; import org.springframework.config.java.process.ConfigurationPostProcessor; import org.springframework.core.io.ClassPathResource; import org.springframework.util.ClassUtils; @@ -48,11 +47,15 @@ public class InternalConfigurationPostProcessor implements BeanFactoryPostProces * necessary to fulfill JavaConfig requirements. */ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + if(!(beanFactory instanceof DefaultListableBeanFactory)) + throw new IllegalStateException("beanFactory must be of type " + + DefaultListableBeanFactory.class.getSimpleName()); + ConfigurationModel model = new ConfigurationModel(); parseAnyConfigurationClasses(beanFactory, model); - enhanceAnyConfigurationClasses(beanFactory, model); + enhanceAnyConfigurationClasses((DefaultListableBeanFactory) beanFactory, model); } private void parseAnyConfigurationClasses(ConfigurableListableBeanFactory beanFactory, ConfigurationModel model) { @@ -84,14 +87,9 @@ public class InternalConfigurationPostProcessor implements BeanFactoryPostProces * @see ConfigurationEnhancer * @see BeanFactoryPostProcessor */ - private void enhanceAnyConfigurationClasses(ConfigurableListableBeanFactory clbf, ConfigurationModel model) { - if(!(clbf instanceof DefaultListableBeanFactory)) - throw new IllegalStateException("beanFactory must be of type " - + DefaultListableBeanFactory.class.getSimpleName()); + private void enhanceAnyConfigurationClasses(DefaultListableBeanFactory beanFactory, ConfigurationModel model) { - DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory)clbf; - - ConfigurationEnhancer enhancer = new CglibConfigurationEnhancer(beanFactory, model); + ConfigurationEnhancer enhancer = new ConfigurationEnhancer(beanFactory, model); int configClassesEnhanced = 0; diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/process/ConfigurationPostProcessor.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/process/ConfigurationPostProcessor.java index d763025e6c..f0ee1d88d0 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/process/ConfigurationPostProcessor.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/process/ConfigurationPostProcessor.java @@ -19,7 +19,7 @@ import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.config.java.annotation.Configuration; +import org.springframework.config.java.Configuration; import org.springframework.config.java.internal.process.InternalConfigurationPostProcessor; import org.springframework.core.Ordered; diff --git a/org.springframework.config.java/src/test/java/test/basic/AutowiredConfigurationTests.java b/org.springframework.config.java/src/test/java/test/basic/AutowiredConfigurationTests.java index 2d058a409c..41c08c91f9 100644 --- a/org.springframework.config.java/src/test/java/test/basic/AutowiredConfigurationTests.java +++ b/org.springframework.config.java/src/test/java/test/basic/AutowiredConfigurationTests.java @@ -5,7 +5,8 @@ import static org.junit.Assert.*; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.config.java.annotation.Configuration; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.config.java.Configuration; import org.springframework.config.java.ext.Bean; import org.springframework.context.support.ClassPathXmlApplicationContext; @@ -36,4 +37,26 @@ public class AutowiredConfigurationTests { public @Bean Colour colour() { return Colour.RED; } } + + + public @Test void testValueInjection() { + System.setProperty("myProp", "foo"); + + ClassPathXmlApplicationContext factory = new ClassPathXmlApplicationContext( + "ValueInjectionTests.xml", AutowiredConfigurationTests.class); + + TestBean testBean = factory.getBean("testBean", TestBean.class); + assertThat(testBean.getName(), equalTo("foo")); + } + + @Configuration + static class ValueConfig { + + @Value("#{systemProperties.myProp}") + private String name = "default"; + + public @Bean TestBean testBean() { + return new TestBean(name); + } + } } 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 a9c60041e8..a1927f14f0 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 @@ -9,7 +9,7 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.config.java.annotation.Configuration; +import org.springframework.config.java.Configuration; import org.springframework.config.java.ext.Bean; import org.springframework.config.java.process.ConfigurationPostProcessor; import org.springframework.config.java.util.DefaultScopes; 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 new file mode 100644 index 0000000000..78f8a6a7de --- /dev/null +++ b/org.springframework.config.java/src/test/java/test/basic/ValueInjectionTests.xml @@ -0,0 +1,13 @@ + + + + + + + + +