ConfigurationEnhancer.java 6.0 KB
Newer Older
1
/*
C
Chris Beams 已提交
2
 * Copyright 2002-2009 the original author or authors.
3 4 5 6 7 8 9 10 11 12 13 14 15
 *
 * 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.
 */
C
Chris Beams 已提交
16

17
package org.springframework.context.annotation;
18

19
import static java.lang.String.*;
20
import static org.springframework.context.annotation.AsmUtils.*;
21

22 23 24
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
25

26 27 28 29
import net.sf.cglib.core.DefaultGeneratorStrategy;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
30
import net.sf.cglib.proxy.NoOp;
31

32 33
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
34 35 36
import org.springframework.asm.ClassAdapter;
import org.springframework.asm.ClassReader;
import org.springframework.asm.ClassWriter;
37
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
38 39
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
40 41 42 43 44 45 46 47 48


/**
 * 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
49
 * @see ConfigurationClassPostProcessor
50
 */
51
class ConfigurationEnhancer {
52

53 54 55
	private static final Log log = LogFactory.getLog(ConfigurationEnhancer.class);

	private final ArrayList<Callback> callbackInstances = new ArrayList<Callback>();
56 57
	private final ArrayList<Class<? extends Callback>> callbackTypes = new ArrayList<Class<? extends Callback>>();
	private final CallbackFilter callbackFilter;
58 59 60 61 62


	/**
	 * Creates a new {@link ConfigurationEnhancer} instance.
	 */
63
	public ConfigurationEnhancer(DefaultListableBeanFactory beanFactory) {
64
		Assert.notNull(beanFactory, "beanFactory must be non-null");
65

66 67
		callbackInstances.add(new BeanMethodInterceptor(beanFactory));
		callbackInstances.add(NoOp.INSTANCE);
68 69 70

		for (Callback callback : callbackInstances)
			callbackTypes.add(callback.getClass());
71 72 73 74 75 76 77 78

		// set up the callback filter to return the index of the BeanMethodInterceptor when
		// handling a @Bean-annotated method; otherwise, return index of the NoOp callback.
		callbackFilter = new CallbackFilter() {
			public int accept(Method candidateMethod) {
				return (AnnotationUtils.findAnnotation(candidateMethod, Bean.class) != null) ? 0 : 1;
			}
		};
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
	}


	/**
	 * Loads the specified class and generates a CGLIB subclass of it equipped with
	 * container-aware callbacks capable of respecting scoping and other bean semantics.
	 * 
	 * @return fully-qualified name of the enhanced subclass
	 */
	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())
C
Chris Beams 已提交
99 100
			log.info(format("Successfully enhanced %s; enhanced class name is: %s",
			                configClassName, subclass.getName()));
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163

		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.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();

		Enhancer.registerCallbacks(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);
C
Chris Beams 已提交
164
				ClassAdapter adapter = new AddAnnotationAdapter(writer, "Lorg/aspectj/lang/annotation/Aspect;");
165 166 167 168 169 170 171 172 173 174 175 176
				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;
	}

177
}