Browse Source
+ Tested compatibility with @Value (works, but noticed and filed an unrelated-to-javaconfig improvement. See SPR-5530) git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@704 50f2f4bb-b051-0410-bef5-90022cba6387pull/1/head
28 changed files with 318 additions and 310 deletions
@ -1,4 +1,4 @@ |
|||||||
package org.springframework.config.java.model; |
package org.springframework.config.java; |
||||||
|
|
||||||
import java.lang.reflect.Method; |
import java.lang.reflect.Method; |
||||||
|
|
||||||
@ -1,4 +1,4 @@ |
|||||||
package org.springframework.config.java.model; |
package org.springframework.config.java; |
||||||
|
|
||||||
import java.util.List; |
import java.util.List; |
||||||
|
|
||||||
@ -1,4 +1,4 @@ |
|||||||
package org.springframework.config.java.model; |
package org.springframework.config.java; |
||||||
|
|
||||||
import java.util.List; |
import java.util.List; |
||||||
|
|
||||||
@ -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<Class<? extends Callback>> callbackTypes = |
|
||||||
new ArrayList<Class<? extends Callback>>(); |
|
||||||
|
|
||||||
private final LinkedHashSet<BeanDefinitionRegistrar> handlers = |
|
||||||
new LinkedHashSet<BeanDefinitionRegistrar>(); |
|
||||||
|
|
||||||
private final ArrayList<Callback> callbackInstances = |
|
||||||
new ArrayList<Callback>(); |
|
||||||
|
|
||||||
private final CallbackFilter callbackFilter = |
|
||||||
new CallbackFilter() { |
|
||||||
public int accept(Method candidateMethod) { |
|
||||||
Iterator<BeanDefinitionRegistrar> 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; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
@ -0,0 +1,13 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<beans xmlns="http://www.springframework.org/schema/beans" |
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||||
|
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd |
||||||
|
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" |
||||||
|
xmlns:context="http://www.springframework.org/schema/context"> |
||||||
|
|
||||||
|
<context:annotation-config/> |
||||||
|
|
||||||
|
<bean class="org.springframework.config.java.process.ConfigurationPostProcessor"/> |
||||||
|
|
||||||
|
<bean class="test.basic.AutowiredConfigurationTests$ValueConfig"/> |
||||||
|
</beans> |
||||||
Loading…
Reference in new issue