84 changed files with 2397 additions and 3444 deletions
@ -1,45 +0,0 @@
@@ -1,45 +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.beans.factory.annotation; |
||||
|
||||
import java.lang.annotation.Documented; |
||||
import java.lang.annotation.ElementType; |
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
import java.lang.annotation.Target; |
||||
|
||||
|
||||
/** |
||||
* Marker annotation identical in functionality with <aop:scoped-proxy/> tag. Provides a smart |
||||
* proxy backed by a scoped bean, which can be injected into object instances (usually singletons) |
||||
* allowing the same reference to be held while delegating method invocations to the backing, scoped |
||||
* beans. |
||||
* |
||||
* @author Costin Leau |
||||
*/ |
||||
@Target(ElementType.METHOD) |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Documented |
||||
public @interface ScopedProxy { |
||||
|
||||
/** |
||||
* Use CGLib-based class proxies (true) or JDK interface-based (false). |
||||
* |
||||
* Default is CGLib (true). |
||||
* @return |
||||
*/ |
||||
boolean proxyTargetClass() default true; |
||||
} |
||||
@ -1,122 +0,0 @@
@@ -1,122 +0,0 @@
|
||||
/* |
||||
* 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.context.annotation; |
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition; |
||||
import org.springframework.beans.factory.parsing.FailFastProblemReporter; |
||||
import org.springframework.beans.factory.parsing.ProblemReporter; |
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry; |
||||
|
||||
|
||||
/** |
||||
* Abstract superclass for processing {@link Configuration}-annotated classes and registering |
||||
* bean definitions based on {@link Bean}-annotated methods within those classes. |
||||
* |
||||
* <p>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 ConfigurationClassParser} ({@link #createConfigurationParser}), |
||||
* and customizing {@link ConfigurationModel} validation logic ({@link #validateModel}). |
||||
* |
||||
* <p>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 Configuration |
||||
* @see ConfigurationClassPostProcessor |
||||
*/ |
||||
public abstract class AbstractConfigurationClassProcessor { |
||||
|
||||
/** |
||||
* Used to register any problems detected with {@link Configuration} or {@link Bean} |
||||
* declarations. For instance, a Bean method marked as {@literal final} is illegal |
||||
* and would be reported as a problem. Defaults to {@link FailFastProblemReporter}, |
||||
* but is overridable with {@link #setProblemReporter} |
||||
*/ |
||||
private ProblemReporter problemReporter = new FailFastProblemReporter(); |
||||
|
||||
/** |
||||
* 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 ConfigurationClassParser}, allowing for customization of |
||||
* type (ASM/JDT/Reflection) as well as providing specialized ClassLoader during |
||||
* construction. |
||||
* @see #processConfigBeanDefinitions() |
||||
*/ |
||||
protected abstract ConfigurationClassParser createConfigurationParser(); |
||||
|
||||
/** |
||||
* Override the default {@link ProblemReporter}. |
||||
* @param problemReporter custom problem reporter |
||||
*/ |
||||
protected final void setProblemReporter(ProblemReporter problemReporter) { |
||||
this.problemReporter = problemReporter; |
||||
} |
||||
|
||||
/** |
||||
* Get the currently registered {@link ProblemReporter}. |
||||
*/ |
||||
protected final ProblemReporter getProblemReporter() { |
||||
return problemReporter; |
||||
} |
||||
|
||||
/** |
||||
* 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; |
||||
|
||||
// populate a new ConfigurationModel by parsing each @Configuration classes
|
||||
ConfigurationClassParser parser = createConfigurationParser(); |
||||
|
||||
for(String beanName : configBeanDefs.getBeanDefinitionNames()) { |
||||
BeanDefinition beanDef = configBeanDefs.getBeanDefinition(beanName); |
||||
String className = beanDef.getBeanClassName(); |
||||
|
||||
parser.parse(className, beanName); |
||||
} |
||||
|
||||
ConfigurationModel configModel = parser.getConfigurationModel(); |
||||
|
||||
configModel.validate(problemReporter); |
||||
|
||||
// read the model and create bean definitions based on its content
|
||||
return new ConfigurationModelBeanDefinitionReader().loadBeanDefinitions(configModel); |
||||
} |
||||
|
||||
} |
||||
@ -1,115 +0,0 @@
@@ -1,115 +0,0 @@
|
||||
/* |
||||
* 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.context.annotation; |
||||
|
||||
import net.sf.cglib.asm.Constants; |
||||
|
||||
import org.springframework.asm.AnnotationVisitor; |
||||
import org.springframework.asm.ClassAdapter; |
||||
import org.springframework.asm.ClassVisitor; |
||||
import org.springframework.asm.FieldVisitor; |
||||
import org.springframework.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. Used by |
||||
* {@link ConfigurationClassEnhancer} to dynamically add an {@link org.aspectj.lang.Aspect} |
||||
* annotation to an enhanced Configuration subclass. |
||||
* |
||||
* <p>This class was originally adapted from examples the ASM 3.0 documentation. |
||||
* |
||||
* @author Chris Beams |
||||
*/ |
||||
class AddAnnotationAdapter extends ClassAdapter { |
||||
private String annotationDesc; |
||||
private boolean isAnnotationPresent; |
||||
|
||||
/** |
||||
* Creates a new AddAnnotationAdapter instance. |
||||
* |
||||
* @param cv the ClassVisitor delegate |
||||
* @param annotationDesc name of the annotation to be added (in type descriptor format) |
||||
*/ |
||||
public AddAnnotationAdapter(ClassVisitor cv, String annotationDesc) { |
||||
super(cv); |
||||
this.annotationDesc = annotationDesc; |
||||
} |
||||
|
||||
/** |
||||
* Ensures that the version of the resulting class is Java 5 or better. |
||||
*/ |
||||
@Override |
||||
public void visit(int version, int access, String name, String signature, |
||||
String superName, String[] interfaces) { |
||||
int v = (version & 0xFF) < Constants.V1_5 ? Constants.V1_5 : version; |
||||
cv.visit(v, access, name, signature, superName, interfaces); |
||||
} |
||||
|
||||
/** |
||||
* Checks to ensure that the desired annotation is not already present. |
||||
*/ |
||||
@Override |
||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible) { |
||||
if (visible && desc.equals(annotationDesc)) { |
||||
isAnnotationPresent = true; |
||||
} |
||||
return cv.visitAnnotation(desc, visible); |
||||
} |
||||
|
||||
@Override |
||||
public void visitInnerClass(String name, String outerName, String innerName, int access) { |
||||
addAnnotation(); |
||||
cv.visitInnerClass(name, outerName, innerName, access); |
||||
} |
||||
|
||||
@Override |
||||
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { |
||||
addAnnotation(); |
||||
return cv.visitField(access, name, desc, signature, value); |
||||
} |
||||
|
||||
@Override |
||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { |
||||
addAnnotation(); |
||||
return cv.visitMethod(access, name, desc, signature, exceptions); |
||||
} |
||||
|
||||
/** |
||||
* Kicks off the process of actually adding the desired annotation. |
||||
* |
||||
* @see #addAnnotation() |
||||
*/ |
||||
@Override |
||||
public void visitEnd() { |
||||
addAnnotation(); |
||||
cv.visitEnd(); |
||||
} |
||||
|
||||
/** |
||||
* Actually adds the desired annotation. |
||||
*/ |
||||
private void addAnnotation() { |
||||
if (!isAnnotationPresent) { |
||||
AnnotationVisitor av = cv.visitAnnotation(annotationDesc, true); |
||||
if (av != null) { |
||||
av.visitEnd(); |
||||
} |
||||
isAnnotationPresent = true; |
||||
} |
||||
} |
||||
} |
||||
@ -1,66 +0,0 @@
@@ -1,66 +0,0 @@
|
||||
/* |
||||
* 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.context.annotation; |
||||
|
||||
import org.springframework.asm.AnnotationVisitor; |
||||
|
||||
|
||||
/** |
||||
* An empty {@link AnnotationVisitor} that delegates to another AnnotationVisitor. This |
||||
* class can be used as a super class to quickly implement useful annotation adapter |
||||
* classes, just by overriding the necessary methods. Note that for some reason, ASM |
||||
* doesn't provide this class (it does provide MethodAdapter and ClassAdapter), thus |
||||
* we're following the general pattern and adding our own here. |
||||
* |
||||
* @author Chris Beams |
||||
*/ |
||||
class AnnotationAdapter implements AnnotationVisitor { |
||||
|
||||
private AnnotationVisitor delegate; |
||||
|
||||
/** |
||||
* Creates a new AnnotationAdapter instance that will delegate all its calls to |
||||
* <var>delegate</var>. |
||||
* |
||||
* @param delegate In most cases, the delegate will simply be |
||||
* {@link AsmUtils#ASM_EMPTY_VISITOR} |
||||
*/ |
||||
public AnnotationAdapter(AnnotationVisitor delegate) { |
||||
this.delegate = delegate; |
||||
} |
||||
|
||||
public void visit(String arg0, Object arg1) { |
||||
delegate.visit(arg0, arg1); |
||||
} |
||||
|
||||
public AnnotationVisitor visitAnnotation(String arg0, String arg1) { |
||||
return delegate.visitAnnotation(arg0, arg1); |
||||
} |
||||
|
||||
public AnnotationVisitor visitArray(String arg0) { |
||||
return delegate.visitArray(arg0); |
||||
} |
||||
|
||||
public void visitEnum(String arg0, String arg1, String arg2) { |
||||
delegate.visitEnum(arg0, arg1, arg2); |
||||
} |
||||
|
||||
public void visitEnd() { |
||||
delegate.visitEnd(); |
||||
} |
||||
|
||||
} |
||||
@ -1,229 +0,0 @@
@@ -1,229 +0,0 @@
|
||||
/* |
||||
* 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.context.annotation; |
||||
|
||||
import static java.lang.String.*; |
||||
import static org.springframework.util.ClassUtils.*; |
||||
|
||||
import java.io.FileNotFoundException; |
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.lang.annotation.Annotation; |
||||
import java.lang.reflect.Proxy; |
||||
|
||||
import org.apache.commons.logging.Log; |
||||
import org.apache.commons.logging.LogFactory; |
||||
import org.springframework.asm.ClassReader; |
||||
import org.springframework.asm.commons.EmptyVisitor; |
||||
import org.springframework.util.ClassUtils; |
||||
|
||||
|
||||
/** |
||||
* Various utility methods commonly used when interacting with ASM, classloading |
||||
* and creating {@link MutableAnnotation} instances. |
||||
* |
||||
* @author Chris Beams |
||||
*/ |
||||
class AsmUtils { |
||||
|
||||
public static final EmptyVisitor ASM_EMPTY_VISITOR = new EmptyVisitor(); |
||||
|
||||
private static final Log log = LogFactory.getLog(AsmUtils.class); |
||||
|
||||
/** |
||||
* Convert a type descriptor to a classname suitable for classloading with |
||||
* Class.forName(). |
||||
* |
||||
* @param typeDescriptor see ASM guide section 2.1.3 |
||||
*/ |
||||
public static String convertAsmTypeDescriptorToClassName(String typeDescriptor) { |
||||
final String internalName; // See ASM guide section 2.1.2
|
||||
|
||||
if ("V".equals(typeDescriptor)) |
||||
return Void.class.getName(); |
||||
if ("I".equals(typeDescriptor)) |
||||
return Integer.class.getName(); |
||||
if ("Z".equals(typeDescriptor)) |
||||
return Boolean.class.getName(); |
||||
|
||||
// strip the leading array/object/primitive identifier
|
||||
if (typeDescriptor.startsWith("[[")) |
||||
internalName = typeDescriptor.substring(3); |
||||
else if (typeDescriptor.startsWith("[")) |
||||
internalName = typeDescriptor.substring(2); |
||||
else |
||||
internalName = typeDescriptor.substring(1); |
||||
|
||||
// convert slashes to dots
|
||||
String className = internalName.replace('/', '.'); |
||||
|
||||
// and strip trailing semicolon (if present)
|
||||
if (className.endsWith(";")) |
||||
className = className.substring(0, internalName.length() - 1); |
||||
|
||||
return className; |
||||
} |
||||
|
||||
/** |
||||
* @param methodDescriptor see ASM guide section 2.1.4 |
||||
*/ |
||||
public static String getReturnTypeFromAsmMethodDescriptor(String methodDescriptor) { |
||||
String returnTypeDescriptor = methodDescriptor.substring(methodDescriptor.indexOf(')') + 1); |
||||
return convertAsmTypeDescriptorToClassName(returnTypeDescriptor); |
||||
} |
||||
|
||||
/** |
||||
* Creates a new ASM {@link ClassReader} for <var>pathToClass</var>. Appends '.class' to |
||||
* pathToClass before attempting to load. |
||||
* |
||||
* @throws RuntimeException if <var>pathToClass</var>+.class cannot be found on the |
||||
* classpath |
||||
* @throws RuntimeException if an IOException occurs when creating the new ClassReader |
||||
*/ |
||||
public static ClassReader newAsmClassReader(String pathToClass, ClassLoader classLoader) { |
||||
InputStream is = getClassAsStream(pathToClass, classLoader); |
||||
return newAsmClassReader(is); |
||||
} |
||||
|
||||
/** |
||||
* Convenience method that creates and returns a new ASM {@link ClassReader} for the |
||||
* given InputStream <var>is</var>, closing the InputStream after creating the |
||||
* ClassReader and rethrowing any IOException thrown during ClassReader instantiation as |
||||
* an unchecked exception. Logs and ignores any IOException thrown when closing the |
||||
* InputStream. |
||||
* |
||||
* @param is InputStream that will be provided to the new ClassReader instance. |
||||
*/ |
||||
public static ClassReader newAsmClassReader(InputStream is) { |
||||
try { |
||||
return new ClassReader(is); |
||||
} catch (IOException ex) { |
||||
throw new RuntimeException("An unexpected exception occurred while creating ASM ClassReader: " + ex); |
||||
} finally { |
||||
try { |
||||
is.close(); |
||||
} catch (IOException ex) { |
||||
log.error("Ignoring exception thrown while closing InputStream", ex); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Uses the default ClassLoader to load <var>pathToClass</var>. Appends '.class' to |
||||
* pathToClass before attempting to load. |
||||
* |
||||
* @param pathToClass resource path to class, not including .class suffix. e.g.: |
||||
* com/acme/MyClass |
||||
* |
||||
* @return inputStream for <var>pathToClass</var> |
||||
* |
||||
* @throws RuntimeException if <var>pathToClass</var> does not exist |
||||
*/ |
||||
public static InputStream getClassAsStream(String pathToClass, ClassLoader classLoader) { |
||||
String classFileName = pathToClass + ClassUtils.CLASS_FILE_SUFFIX; |
||||
|
||||
InputStream is = classLoader.getResourceAsStream(classFileName); |
||||
|
||||
if (is == null) |
||||
throw new RuntimeException( |
||||
new FileNotFoundException("Class file [" + classFileName + "] not found")); |
||||
|
||||
return is; |
||||
} |
||||
|
||||
/** |
||||
* Loads the specified class using the default class loader, rethrowing any |
||||
* {@link ClassNotFoundException} as an unchecked exception. |
||||
* |
||||
* @param <T> type of class to be returned |
||||
* @param fqClassName fully-qualified class name |
||||
* |
||||
* @return newly loaded class instance |
||||
* |
||||
* @throws IllegalArgumentException if configClassName cannot be loaded. |
||||
* |
||||
* @see #loadClass(String) |
||||
* @see #loadToolingSafeClass(String) |
||||
* @see ClassUtils#getDefaultClassLoader() |
||||
*/ |
||||
@SuppressWarnings("unchecked") |
||||
public static <T> Class<? extends T> loadRequiredClass(String fqClassName) { |
||||
try { |
||||
return (Class<? extends T>) getDefaultClassLoader().loadClass(fqClassName); |
||||
} catch (ClassNotFoundException ex) { |
||||
throw new IllegalArgumentException(format( |
||||
"Class [%s] could not be loaded, check your CLASSPATH.", fqClassName), ex); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Loads the specified class using the default class loader, gracefully handling any |
||||
* {@link ClassNotFoundException} that may be thrown by issuing a WARN level logging |
||||
* statement and return null. This functionality is specifically implemented to |
||||
* accomodate tooling (Spring IDE) concerns, where user-defined types will not be |
||||
* available to the tooling. |
||||
* <p> |
||||
* Because {@link ClassNotFoundException} is compensated for by returning null, callers |
||||
* must take care to handle the null case appropriately. |
||||
* <p> |
||||
* In cases where the WARN logging statement is not desired, use the |
||||
* {@link #loadClass(String)} method, which returns null but issues no logging |
||||
* statements. |
||||
* <p> |
||||
* This method should only ever return null in the case of a user-defined type be |
||||
* processed at tooling time. Therefore, tooling may not be able to represent any custom |
||||
* annotation semantics, but JavaConfig itself will not have any problem loading and |
||||
* respecting them at actual runtime. |
||||
* |
||||
* @param <T> type of class to be returned |
||||
* @param fqClassName fully-qualified class name |
||||
* |
||||
* @return newly loaded class, null if class could not be found. |
||||
* |
||||
* @see #loadClass(String) |
||||
* @see #loadRequiredClass(String) |
||||
* @see ClassUtils#getDefaultClassLoader() |
||||
*/ |
||||
@SuppressWarnings("unchecked") |
||||
public static <T> Class<? extends T> loadToolingSafeClass(String fqClassName, ClassLoader classLoader) { |
||||
try { |
||||
return (Class<? extends T>) classLoader.loadClass(fqClassName); |
||||
} catch (ClassNotFoundException ex) { |
||||
log.warn(format("Unable to load class [%s], likely due to tooling-specific restrictions." |
||||
+ "Attempting to continue, but unexpected errors may occur", fqClassName), ex); |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Creates a {@link MutableAnnotation} for {@code annoType}. JDK dynamic proxies are |
||||
* used, and the returned proxy implements both {@link MutableAnnotation} and annotation |
||||
* type {@code A} |
||||
* |
||||
* @param <A> annotation type that must be supplied and returned |
||||
* @param annoType type of annotation to create |
||||
*/ |
||||
public static <A extends Annotation> A createMutableAnnotation(Class<A> annoType, ClassLoader classLoader) { |
||||
MutableAnnotationInvocationHandler handler = new MutableAnnotationInvocationHandler(annoType); |
||||
Class<?>[] interfaces = new Class<?>[] { annoType, MutableAnnotation.class }; |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
A mutableAnno = (A) Proxy.newProxyInstance(classLoader, interfaces, handler); |
||||
return mutableAnno; |
||||
} |
||||
|
||||
} |
||||
@ -1,108 +0,0 @@
@@ -1,108 +0,0 @@
|
||||
/* |
||||
* 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.context.annotation; |
||||
|
||||
import static java.lang.String.*; |
||||
|
||||
import java.lang.reflect.Method; |
||||
|
||||
import net.sf.cglib.proxy.MethodInterceptor; |
||||
import net.sf.cglib.proxy.MethodProxy; |
||||
|
||||
import org.apache.commons.logging.Log; |
||||
import org.apache.commons.logging.LogFactory; |
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory; |
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory; |
||||
import org.springframework.core.annotation.AnnotationUtils; |
||||
|
||||
|
||||
/** |
||||
* Intercepts the invocation of any {@link Bean}-annotated methods in order to ensure proper |
||||
* handling of bean semantics such as scoping and AOP proxying. |
||||
* |
||||
* @author Chris Beams |
||||
* @see Bean |
||||
* @see ConfigurationClassEnhancer |
||||
*/ |
||||
class BeanMethodInterceptor implements MethodInterceptor { |
||||
|
||||
private static final Log log = LogFactory.getLog(BeanMethodInterceptor.class); |
||||
|
||||
private final DefaultListableBeanFactory beanFactory; |
||||
|
||||
public BeanMethodInterceptor(DefaultListableBeanFactory beanFactory) { |
||||
this.beanFactory = beanFactory; |
||||
} |
||||
|
||||
/** |
||||
* Enhances a {@link Bean @Bean} method to check the supplied BeanFactory for the |
||||
* existence of this bean object. |
||||
*/ |
||||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { |
||||
|
||||
// by default the bean name is the name of the @Bean-annotated method
|
||||
String beanName = method.getName(); |
||||
|
||||
// check to see if the user has explicitly set the bean name
|
||||
Bean bean = method.getAnnotation(Bean.class); |
||||
if(bean != null && bean.name().length > 0) |
||||
beanName = bean.name()[0]; |
||||
|
||||
// determine whether this bean is a scoped-proxy
|
||||
Scope scope = AnnotationUtils.findAnnotation(method, Scope.class); |
||||
boolean isScopedProxy = (scope != null && scope.proxyMode() != ScopedProxyMode.NO); |
||||
String scopedBeanName = ConfigurationModelBeanDefinitionReader.resolveHiddenScopedProxyBeanName(beanName); |
||||
if (isScopedProxy && beanFactory.isCurrentlyInCreation(scopedBeanName)) |
||||
beanName = scopedBeanName; |
||||
|
||||
// to handle the case of an inter-bean method reference, we must explicitly check the
|
||||
// container for already cached instances
|
||||
if (factoryContainsBean(beanName)) { |
||||
// we have an already existing cached instance of this bean -> retrieve it
|
||||
Object cachedBean = beanFactory.getBean(beanName); |
||||
if (log.isInfoEnabled()) |
||||
log.info(format("Returning cached singleton object [%s] for @Bean method %s.%s", |
||||
cachedBean, method.getDeclaringClass().getSimpleName(), beanName)); |
||||
|
||||
return cachedBean; |
||||
} |
||||
|
||||
// actually create and return the bean
|
||||
return proxy.invokeSuper(obj, args); |
||||
} |
||||
|
||||
/** |
||||
* Check the beanFactory to see whether the bean named <var>beanName</var> already |
||||
* exists. Accounts for the fact that the requested bean may be "in creation", i.e.: |
||||
* we're in the middle of servicing the initial request for this bean. From JavaConfig's |
||||
* perspective, this means that the bean does not actually yet exist, and that it is now |
||||
* our job to create it for the first time by executing the logic in the corresponding |
||||
* Bean method. |
||||
* <p> |
||||
* Said another way, this check repurposes |
||||
* {@link ConfigurableBeanFactory#isCurrentlyInCreation(String)} to determine whether |
||||
* the container is calling this method or the user is calling this method. |
||||
* |
||||
* @param beanName name of bean to check for |
||||
* |
||||
* @return true if <var>beanName</var> already exists in beanFactory |
||||
*/ |
||||
private boolean factoryContainsBean(String beanName) { |
||||
return beanFactory.containsBean(beanName) && !beanFactory.isCurrentlyInCreation(beanName); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,163 @@
@@ -0,0 +1,163 @@
|
||||
/* |
||||
* 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.context.annotation; |
||||
|
||||
import java.lang.annotation.Annotation; |
||||
import java.lang.reflect.Field; |
||||
import java.lang.reflect.Array; |
||||
import java.util.List; |
||||
import java.util.ArrayList; |
||||
|
||||
import org.springframework.asm.AnnotationVisitor; |
||||
import org.springframework.asm.Type; |
||||
import org.springframework.asm.commons.EmptyVisitor; |
||||
|
||||
/** |
||||
* ASM {@link AnnotationVisitor} that populates a given {@link ConfigurationClassAnnotation} instance |
||||
* with its attributes. |
||||
* |
||||
* @author Chris Beams |
||||
* @author Juergen Hoeller |
||||
* @since 3.0 |
||||
* @see ConfigurationClassAnnotation |
||||
* @see ConfigurationClassReaderUtils#createMutableAnnotation |
||||
*/ |
||||
class ConfigurationClassAnnotationVisitor implements AnnotationVisitor { |
||||
|
||||
protected final ConfigurationClassAnnotation mutableAnno; |
||||
|
||||
private final ClassLoader classLoader; |
||||
|
||||
|
||||
/** |
||||
* Creates a new {@link ConfigurationClassAnnotationVisitor} instance that will populate the the |
||||
* attributes of the given <var>mutableAnno</var>. Accepts {@link Annotation} instead of |
||||
* {@link ConfigurationClassAnnotation} to avoid the need for callers to typecast. |
||||
* @param mutableAnno {@link ConfigurationClassAnnotation} instance to visit and populate |
||||
* @see ConfigurationClassReaderUtils#createMutableAnnotation |
||||
*/ |
||||
public ConfigurationClassAnnotationVisitor(ConfigurationClassAnnotation mutableAnno, ClassLoader classLoader) { |
||||
this.mutableAnno = mutableAnno; |
||||
this.classLoader = classLoader; |
||||
} |
||||
|
||||
public void visit(String attribName, Object attribValue) { |
||||
Class<?> attribReturnType = mutableAnno.getAttributeType(attribName); |
||||
|
||||
if (attribReturnType.equals(Class.class)) { |
||||
// the attribute type is Class -> load it and set it.
|
||||
String fqClassName = ((Type) attribValue).getClassName(); |
||||
Class<?> classVal = ConfigurationClassReaderUtils.loadToolingSafeClass(fqClassName, classLoader); |
||||
if (classVal == null) { |
||||
return; |
||||
} |
||||
mutableAnno.setAttributeValue(attribName, classVal); |
||||
return; |
||||
} |
||||
|
||||
// otherwise, assume the value can be set literally
|
||||
mutableAnno.setAttributeValue(attribName, attribValue); |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
public void visitEnum(String attribName, String enumTypeDescriptor, String strEnumValue) { |
||||
String enumClassName = ConfigurationClassReaderUtils.convertAsmTypeDescriptorToClassName(enumTypeDescriptor); |
||||
Class<? extends Enum> enumClass = ConfigurationClassReaderUtils.loadToolingSafeClass(enumClassName, classLoader); |
||||
if (enumClass == null) { |
||||
return; |
||||
} |
||||
Enum enumValue = Enum.valueOf(enumClass, strEnumValue); |
||||
mutableAnno.setAttributeValue(attribName, enumValue); |
||||
} |
||||
|
||||
public AnnotationVisitor visitAnnotation(String attribName, String attribAnnoTypeDesc) { |
||||
String annoTypeName = ConfigurationClassReaderUtils.convertAsmTypeDescriptorToClassName(attribAnnoTypeDesc); |
||||
Class<? extends Annotation> annoType = ConfigurationClassReaderUtils.loadToolingSafeClass(annoTypeName, classLoader); |
||||
if (annoType == null) { |
||||
return new EmptyVisitor(); |
||||
} |
||||
ConfigurationClassAnnotation anno = ConfigurationClassReaderUtils.createMutableAnnotation(annoType, classLoader); |
||||
try { |
||||
Field attribute = mutableAnno.getClass().getField(attribName); |
||||
attribute.set(mutableAnno, anno); |
||||
} |
||||
catch (Exception ex) { |
||||
throw new IllegalStateException("Could not reflectively set annotation field", ex); |
||||
} |
||||
return new ConfigurationClassAnnotationVisitor(anno, classLoader); |
||||
} |
||||
|
||||
public AnnotationVisitor visitArray(final String attribName) { |
||||
return new MutableAnnotationArrayVisitor(mutableAnno, attribName, classLoader); |
||||
} |
||||
|
||||
public void visitEnd() { |
||||
} |
||||
|
||||
|
||||
/** |
||||
* ASM {@link AnnotationVisitor} that visits any annotation array values while populating |
||||
* a new {@link ConfigurationClassAnnotation} instance. |
||||
*/ |
||||
private static class MutableAnnotationArrayVisitor implements AnnotationVisitor { |
||||
|
||||
private final List<Object> values = new ArrayList<Object>(); |
||||
|
||||
private final ConfigurationClassAnnotation mutableAnno; |
||||
|
||||
private final String attribName; |
||||
|
||||
private final ClassLoader classLoader; |
||||
|
||||
|
||||
public MutableAnnotationArrayVisitor(ConfigurationClassAnnotation mutableAnno, String attribName, ClassLoader classLoader) { |
||||
this.mutableAnno = mutableAnno; |
||||
this.attribName = attribName; |
||||
this.classLoader = classLoader; |
||||
} |
||||
|
||||
|
||||
public void visit(String na, Object value) { |
||||
values.add(value); |
||||
} |
||||
|
||||
public void visitEnum(String s, String s1, String s2) { |
||||
} |
||||
|
||||
public AnnotationVisitor visitAnnotation(String na, String annoTypeDesc) { |
||||
String annoTypeName = ConfigurationClassReaderUtils.convertAsmTypeDescriptorToClassName(annoTypeDesc); |
||||
Class<? extends Annotation> annoType = ConfigurationClassReaderUtils.loadToolingSafeClass(annoTypeName, classLoader); |
||||
if (annoType == null) { |
||||
return new EmptyVisitor(); |
||||
} |
||||
ConfigurationClassAnnotation anno = ConfigurationClassReaderUtils.createMutableAnnotation(annoType, classLoader); |
||||
values.add(anno); |
||||
return new ConfigurationClassAnnotationVisitor(anno, classLoader); |
||||
} |
||||
|
||||
public AnnotationVisitor visitArray(String s) { |
||||
return new EmptyVisitor(); |
||||
} |
||||
|
||||
public void visitEnd() { |
||||
Class<?> arrayType = mutableAnno.getAttributeType(attribName); |
||||
Object[] array = (Object[]) Array.newInstance(arrayType.getComponentType(), 0); |
||||
mutableAnno.setAttributeValue(attribName, values.toArray(array)); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,207 @@
@@ -0,0 +1,207 @@
|
||||
/* |
||||
* 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.context.annotation; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
import java.util.Set; |
||||
|
||||
import org.apache.commons.logging.Log; |
||||
import org.apache.commons.logging.LogFactory; |
||||
|
||||
import org.springframework.aop.scope.ScopedProxyUtils; |
||||
import org.springframework.beans.factory.config.BeanDefinition; |
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder; |
||||
import org.springframework.beans.factory.support.BeanDefinitionReader; |
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; |
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry; |
||||
import org.springframework.beans.factory.support.GenericBeanDefinition; |
||||
import org.springframework.beans.factory.support.RootBeanDefinition; |
||||
import org.springframework.beans.factory.annotation.Autowire; |
||||
import org.springframework.core.io.Resource; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
/** |
||||
* Reads a given fully-populated configuration model, registering bean definitions |
||||
* with the given {@link BeanDefinitionRegistry} based on its contents. |
||||
* |
||||
* <p>This class was modeled after the {@link BeanDefinitionReader} hierarchy, but does |
||||
* not implement/extend any of its artifacts as a configuration model is not a {@link Resource}. |
||||
* |
||||
* @author Chris Beams |
||||
* @author Juergen Hoeller |
||||
* @since 3.0 |
||||
*/ |
||||
class ConfigurationClassBeanDefinitionReader { |
||||
|
||||
private static final Log logger = LogFactory.getLog(ConfigurationClassBeanDefinitionReader.class); |
||||
|
||||
private final BeanDefinitionRegistry registry; |
||||
|
||||
|
||||
public ConfigurationClassBeanDefinitionReader(BeanDefinitionRegistry registry) { |
||||
Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); |
||||
this.registry = registry; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Reads {@code configurationModel}, registering bean definitions with {@link #registry} |
||||
* based on its contents. |
||||
*/ |
||||
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) { |
||||
for (ConfigurationClass configClass : configurationModel) { |
||||
loadBeanDefinitionsForConfigurationClass(configClass); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Reads a particular {@link ConfigurationClass}, registering bean definitions for the |
||||
* class itself, all its {@link Bean} methods |
||||
*/ |
||||
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass) { |
||||
doLoadBeanDefinitionForConfigurationClass(configClass); |
||||
for (ConfigurationClassMethod method : configClass.getBeanMethods()) { |
||||
loadBeanDefinitionsForModelMethod(method); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Registers the {@link Configuration} class itself as a bean definition. |
||||
*/ |
||||
private void doLoadBeanDefinitionForConfigurationClass(ConfigurationClass configClass) { |
||||
if (configClass.getBeanName() == null) { |
||||
GenericBeanDefinition configBeanDef = new GenericBeanDefinition(); |
||||
configBeanDef.setBeanClassName(configClass.getName()); |
||||
String configBeanName = BeanDefinitionReaderUtils.registerWithGeneratedName(configBeanDef, registry); |
||||
configClass.setBeanName(configBeanName); |
||||
if (logger.isDebugEnabled()) { |
||||
logger.debug(String.format("Registered bean definition for imported @Configuration class %s", configBeanName)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Reads a particular {@link ConfigurationClassMethod}, registering bean definitions with |
||||
* {@link #registry} based on its contents. |
||||
*/ |
||||
private void loadBeanDefinitionsForModelMethod(ConfigurationClassMethod method) { |
||||
RootBeanDefinition beanDef = new ConfigurationClassBeanDefinition(); |
||||
ConfigurationClass configClass = method.getDeclaringClass(); |
||||
beanDef.setFactoryBeanName(configClass.getBeanName()); |
||||
beanDef.setFactoryMethodName(method.getName()); |
||||
beanDef.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR); |
||||
|
||||
// consider name and any aliases
|
||||
Bean bean = method.getRequiredAnnotation(Bean.class); |
||||
List<String> names = new ArrayList<String>(Arrays.asList(bean.name())); |
||||
String beanName = (names.size() > 0) ? names.remove(0) : method.getName(); |
||||
for (String alias : bean.name()) { |
||||
registry.registerAlias(beanName, alias); |
||||
} |
||||
|
||||
// has this already been overriden (i.e.: via XML)?
|
||||
if (registry.containsBeanDefinition(beanName)) { |
||||
BeanDefinition existingBeanDef = registry.getBeanDefinition(beanName); |
||||
// is the existing bean definition one that was created by JavaConfig?
|
||||
if (!(existingBeanDef instanceof ConfigurationClassBeanDefinition)) { |
||||
// no -> then it's an external override, probably XML
|
||||
// overriding is legal, return immediately
|
||||
if (logger.isDebugEnabled()) { |
||||
logger.debug(String.format("Skipping loading bean definition for %s: a definition for bean " + |
||||
"'%s' already exists. This is likely due to an override in XML.", method, beanName)); |
||||
} |
||||
return; |
||||
} |
||||
} |
||||
|
||||
if (method.getAnnotation(Primary.class) != null) { |
||||
beanDef.setPrimary(true); |
||||
} |
||||
|
||||
// is this bean to be instantiated lazily?
|
||||
Lazy lazy = method.getAnnotation(Lazy.class); |
||||
if (lazy != null) { |
||||
beanDef.setLazyInit(lazy.value()); |
||||
} |
||||
else { |
||||
Lazy defaultLazy = configClass.getAnnotation(Lazy.class); |
||||
if (defaultLazy != null) { |
||||
beanDef.setLazyInit(defaultLazy.value()); |
||||
} |
||||
} |
||||
|
||||
DependsOn dependsOn = method.getAnnotation(DependsOn.class); |
||||
if (dependsOn != null && dependsOn.value().length > 0) { |
||||
beanDef.setDependsOn(dependsOn.value()); |
||||
} |
||||
|
||||
Autowire autowire = bean.autowire(); |
||||
if (autowire.isAutowire()) { |
||||
beanDef.setAutowireMode(autowire.value()); |
||||
} |
||||
|
||||
String initMethodName = bean.initMethod(); |
||||
if (StringUtils.hasText(initMethodName)) { |
||||
beanDef.setInitMethodName(initMethodName); |
||||
} |
||||
|
||||
String destroyMethodName = bean.destroyMethod(); |
||||
if (StringUtils.hasText(destroyMethodName)) { |
||||
beanDef.setDestroyMethodName(destroyMethodName); |
||||
} |
||||
|
||||
// consider scoping
|
||||
Scope scope = method.getAnnotation(Scope.class); |
||||
ScopedProxyMode proxyMode = ScopedProxyMode.NO; |
||||
if (scope != null) { |
||||
beanDef.setScope(scope.value()); |
||||
proxyMode = scope.proxyMode(); |
||||
if (proxyMode == ScopedProxyMode.DEFAULT) { |
||||
proxyMode = ScopedProxyMode.NO; |
||||
} |
||||
} |
||||
|
||||
// replace the original bean definition with the target one, if necessary
|
||||
BeanDefinition beanDefToRegister = beanDef; |
||||
if (proxyMode != ScopedProxyMode.NO) { |
||||
BeanDefinitionHolder proxyDef = ScopedProxyUtils.createScopedProxy( |
||||
new BeanDefinitionHolder(beanDef, beanName), registry, proxyMode == ScopedProxyMode.TARGET_CLASS); |
||||
beanDefToRegister = proxyDef.getBeanDefinition(); |
||||
} |
||||
|
||||
if (logger.isDebugEnabled()) { |
||||
logger.debug(String.format("Registering bean definition for @Bean method %s.%s()", configClass.getName(), beanName)); |
||||
} |
||||
|
||||
registry.registerBeanDefinition(beanName, beanDefToRegister); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* {@link RootBeanDefinition} marker subclass used to signify that a bean definition created |
||||
* by JavaConfig as opposed to any other configuration source. Used in bean overriding cases |
||||
* where it's necessary to determine whether the bean definition was created externally |
||||
* (e.g. via XML). |
||||
*/ |
||||
@SuppressWarnings("serial") |
||||
private class ConfigurationClassBeanDefinition extends RootBeanDefinition { |
||||
} |
||||
|
||||
} |
||||
@ -1,138 +0,0 @@
@@ -1,138 +0,0 @@
|
||||
/* |
||||
* 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.context.annotation; |
||||
|
||||
import static org.springframework.context.annotation.AsmUtils.*; |
||||
import static org.springframework.util.ClassUtils.*; |
||||
|
||||
import java.lang.annotation.Annotation; |
||||
import java.util.ArrayList; |
||||
|
||||
import org.springframework.asm.AnnotationVisitor; |
||||
import org.springframework.asm.ClassAdapter; |
||||
import org.springframework.asm.Label; |
||||
import org.springframework.asm.MethodAdapter; |
||||
import org.springframework.asm.MethodVisitor; |
||||
import org.springframework.asm.Opcodes; |
||||
|
||||
|
||||
/** |
||||
* ASM {@link MethodVisitor} that visits a single method declared in a given |
||||
* {@link Configuration} class. Determines whether the method is a {@link Bean} |
||||
* method and if so, adds it to the {@link ConfigurationClass}. |
||||
* |
||||
* @author Chris Beams |
||||
*/ |
||||
class ConfigurationClassMethodVisitor extends MethodAdapter { |
||||
|
||||
private final ConfigurationClass configClass; |
||||
private final String methodName; |
||||
private final int modifiers; |
||||
private final ModelClass returnType; |
||||
private final ArrayList<Annotation> annotations = new ArrayList<Annotation>(); |
||||
private final ClassLoader classLoader; |
||||
|
||||
private int lineNumber; |
||||
|
||||
/** |
||||
* Creates a new {@link ConfigurationClassMethodVisitor} instance. |
||||
* |
||||
* @param configClass model object to which this method will be added |
||||
* @param methodName name of the method declared in the {@link Configuration} class
|
||||
* @param methodDescriptor ASM representation of the method signature |
||||
* @param modifiers modifiers for this method |
||||
*/ |
||||
public ConfigurationClassMethodVisitor(ConfigurationClass configClass, String methodName, |
||||
String methodDescriptor, int modifiers, ClassLoader classLoader) { |
||||
super(ASM_EMPTY_VISITOR); |
||||
|
||||
this.configClass = configClass; |
||||
this.methodName = methodName; |
||||
this.classLoader = classLoader; |
||||
this.modifiers = modifiers; |
||||
this.returnType = initReturnTypeFromMethodDescriptor(methodDescriptor); |
||||
} |
||||
|
||||
/** |
||||
* Visits a single annotation on this method. Will be called once for each annotation |
||||
* present (regardless of its RetentionPolicy). |
||||
*/ |
||||
@Override |
||||
public AnnotationVisitor visitAnnotation(String annoTypeDesc, boolean visible) { |
||||
String annoClassName = convertAsmTypeDescriptorToClassName(annoTypeDesc); |
||||
|
||||
Class<? extends Annotation> annoClass = loadToolingSafeClass(annoClassName, classLoader); |
||||
|
||||
if (annoClass == null) |
||||
return super.visitAnnotation(annoTypeDesc, visible); |
||||
|
||||
Annotation annotation = createMutableAnnotation(annoClass, classLoader); |
||||
|
||||
annotations.add(annotation); |
||||
|
||||
return new MutableAnnotationVisitor(annotation, classLoader); |
||||
} |
||||
|
||||
/** |
||||
* Provides the line number of this method within its declaring class. In reality, this |
||||
* number is always inaccurate - <var>lineNo</var> represents the line number of the |
||||
* first instruction in this method. Method declaration line numbers are not in any way |
||||
* tracked in the bytecode. Any tooling or output that reads this value will have to |
||||
* compensate and estimate where the actual method declaration is. |
||||
*/ |
||||
@Override |
||||
public void visitLineNumber(int lineNo, Label start) { |
||||
this.lineNumber = lineNo; |
||||
} |
||||
|
||||
/** |
||||
* Parses through all {@link #annotations} on this method in order to determine whether |
||||
* it is a {@link Bean} method and if so adds it to the enclosing {@link #configClass}. |
||||
*/ |
||||
@Override |
||||
public void visitEnd() { |
||||
for (Annotation anno : annotations) { |
||||
if (Bean.class.equals(anno.annotationType())) { |
||||
// this method is annotated with @Bean -> add it to the ConfigurationClass model
|
||||
Annotation[] annoArray = annotations.toArray(new Annotation[] {}); |
||||
BeanMethod method = new BeanMethod(methodName, modifiers, returnType, annoArray); |
||||
method.setSource(lineNumber); |
||||
configClass.addBeanMethod(method); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Determines return type from ASM <var>methodDescriptor</var> and determines whether |
||||
* that type is an interface. |
||||
*/ |
||||
private ModelClass initReturnTypeFromMethodDescriptor(String methodDescriptor) { |
||||
final ModelClass returnType = new ModelClass(getReturnTypeFromAsmMethodDescriptor(methodDescriptor)); |
||||
|
||||
// detect whether the return type is an interface
|
||||
newAsmClassReader(convertClassNameToResourcePath(returnType.getName()), classLoader).accept( |
||||
new ClassAdapter(ASM_EMPTY_VISITOR) { |
||||
@Override |
||||
public void visit(int arg0, int arg1, String arg2, String arg3, String arg4, String[] arg5) { |
||||
returnType.setInterface((arg1 & Opcodes.ACC_INTERFACE) == Opcodes.ACC_INTERFACE); |
||||
} |
||||
}, false); |
||||
|
||||
return returnType; |
||||
} |
||||
} |
||||
@ -0,0 +1,376 @@
@@ -0,0 +1,376 @@
|
||||
/* |
||||
* 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.context.annotation; |
||||
|
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import static java.lang.String.*; |
||||
import java.lang.annotation.Annotation; |
||||
import java.lang.reflect.InvocationHandler; |
||||
import java.lang.reflect.Method; |
||||
import java.lang.reflect.Proxy; |
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
import org.apache.commons.logging.Log; |
||||
import org.apache.commons.logging.LogFactory; |
||||
|
||||
import org.springframework.asm.ClassReader; |
||||
import org.springframework.beans.factory.BeanDefinitionStoreException; |
||||
import static org.springframework.core.annotation.AnnotationUtils.*; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.ClassUtils; |
||||
import org.springframework.util.ReflectionUtils; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
/** |
||||
* Various utility methods commonly used when interacting with ASM, classloading |
||||
* and creating {@link ConfigurationClassAnnotation} instances. |
||||
* |
||||
* @author Chris Beams |
||||
* @since 3.0 |
||||
*/ |
||||
class ConfigurationClassReaderUtils { |
||||
|
||||
private static final Log logger = LogFactory.getLog(ConfigurationClassReaderUtils.class); |
||||
|
||||
/** |
||||
* Convert a type descriptor to a classname suitable for classloading with |
||||
* Class.forName(). |
||||
* |
||||
* @param typeDescriptor see ASM guide section 2.1.3 |
||||
*/ |
||||
public static String convertAsmTypeDescriptorToClassName(String typeDescriptor) { |
||||
final String internalName; // See ASM guide section 2.1.2
|
||||
|
||||
if ("V".equals(typeDescriptor)) |
||||
return Void.class.getName(); |
||||
if ("I".equals(typeDescriptor)) |
||||
return Integer.class.getName(); |
||||
if ("Z".equals(typeDescriptor)) |
||||
return Boolean.class.getName(); |
||||
|
||||
// strip the leading array/object/primitive identifier
|
||||
if (typeDescriptor.startsWith("[[")) |
||||
internalName = typeDescriptor.substring(3); |
||||
else if (typeDescriptor.startsWith("[")) |
||||
internalName = typeDescriptor.substring(2); |
||||
else |
||||
internalName = typeDescriptor.substring(1); |
||||
|
||||
// convert slashes to dots
|
||||
String className = internalName.replace('/', '.'); |
||||
|
||||
// and strip trailing semicolon (if present)
|
||||
if (className.endsWith(";")) |
||||
className = className.substring(0, internalName.length() - 1); |
||||
|
||||
return className; |
||||
} |
||||
|
||||
/** |
||||
* @param methodDescriptor see ASM guide section 2.1.4 |
||||
*/ |
||||
public static String getReturnTypeFromAsmMethodDescriptor(String methodDescriptor) { |
||||
String returnTypeDescriptor = methodDescriptor.substring(methodDescriptor.indexOf(')') + 1); |
||||
return convertAsmTypeDescriptorToClassName(returnTypeDescriptor); |
||||
} |
||||
|
||||
/** |
||||
* Create a new ASM {@link ClassReader} for <var>pathToClass</var>. Appends '.class' to |
||||
* pathToClass before attempting to load. |
||||
*/ |
||||
public static ClassReader newAsmClassReader(String pathToClass, ClassLoader classLoader) { |
||||
InputStream is = getClassAsStream(pathToClass, classLoader); |
||||
return newAsmClassReader(is); |
||||
} |
||||
|
||||
/** |
||||
* Convenience method that creates and returns a new ASM {@link ClassReader} for the |
||||
* given InputStream <var>is</var>, closing the InputStream after creating the |
||||
* ClassReader and rethrowing any IOException thrown during ClassReader instantiation as |
||||
* an unchecked exception. Logs and ignores any IOException thrown when closing the |
||||
* InputStream. |
||||
* |
||||
* @param is InputStream that will be provided to the new ClassReader instance. |
||||
*/ |
||||
public static ClassReader newAsmClassReader(InputStream is) { |
||||
try { |
||||
return new ClassReader(is); |
||||
} |
||||
catch (IOException ex) { |
||||
throw new BeanDefinitionStoreException("An unexpected exception occurred while creating ASM ClassReader: " + ex); |
||||
} |
||||
finally { |
||||
try { |
||||
is.close(); |
||||
} |
||||
catch (IOException ex) { |
||||
// ignore
|
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Uses the default ClassLoader to load <var>pathToClass</var>. Appends '.class' to |
||||
* pathToClass before attempting to load. |
||||
* @param pathToClass resource path to class, not including .class suffix. e.g.: com/acme/MyClass |
||||
* @return inputStream for <var>pathToClass</var> |
||||
* @throws RuntimeException if <var>pathToClass</var> does not exist |
||||
*/ |
||||
public static InputStream getClassAsStream(String pathToClass, ClassLoader classLoader) { |
||||
String classFileName = pathToClass + ClassUtils.CLASS_FILE_SUFFIX; |
||||
InputStream is = classLoader.getResourceAsStream(classFileName); |
||||
if (is == null) { |
||||
throw new IllegalStateException("Class file [" + classFileName + "] not found"); |
||||
} |
||||
return is; |
||||
} |
||||
|
||||
/** |
||||
* Loads the specified class using the default class loader, gracefully handling any |
||||
* {@link ClassNotFoundException} that may be thrown by issuing a WARN level logging |
||||
* statement and return null. This functionality is specifically implemented to |
||||
* accomodate tooling (Spring IDE) concerns, where user-defined types will not be |
||||
* available to the tooling. |
||||
* <p> |
||||
* Because {@link ClassNotFoundException} is compensated for by returning null, callers |
||||
* must take care to handle the null case appropriately. |
||||
* <p> |
||||
* In cases where the WARN logging statement is not desired, use the |
||||
* {@link #loadClass(String)} method, which returns null but issues no logging |
||||
* statements. |
||||
* <p> |
||||
* This method should only ever return null in the case of a user-defined type be |
||||
* processed at tooling time. Therefore, tooling may not be able to represent any custom |
||||
* annotation semantics, but JavaConfig itself will not have any problem loading and |
||||
* respecting them at actual runtime. |
||||
* |
||||
* @param <T> type of class to be returned |
||||
* @param fqClassName fully-qualified class name |
||||
* |
||||
* @return newly loaded class, null if class could not be found. |
||||
* |
||||
* @see #loadClass(String) |
||||
* @see #loadRequiredClass(String) |
||||
* @see ClassUtils#getDefaultClassLoader() |
||||
*/ |
||||
@SuppressWarnings("unchecked") |
||||
public static <T> Class<? extends T> loadToolingSafeClass(String fqClassName, ClassLoader classLoader) { |
||||
try { |
||||
return (Class<? extends T>) classLoader.loadClass(fqClassName); |
||||
} |
||||
catch (ClassNotFoundException ex) { |
||||
logger.warn(String.format("Unable to load class [%s], likely due to tooling-specific restrictions." |
||||
+ "Attempting to continue, but unexpected errors may occur", fqClassName), ex); |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Creates a {@link ConfigurationClassAnnotation} for {@code annoType}. JDK dynamic proxies are used, |
||||
* and the returned proxy implements both {@link ConfigurationClassAnnotation} and the annotation type. |
||||
* @param annoType annotation type that must be supplied and returned |
||||
* @param annoType type of annotation to create |
||||
*/ |
||||
@SuppressWarnings("unchecked") |
||||
public static ConfigurationClassAnnotation createMutableAnnotation(Class<? extends Annotation> annoType, ClassLoader classLoader) { |
||||
MutableAnnotationInvocationHandler handler = new MutableAnnotationInvocationHandler(annoType); |
||||
Class<?>[] interfaces = new Class<?>[] {annoType, ConfigurationClassAnnotation.class}; |
||||
return (ConfigurationClassAnnotation) Proxy.newProxyInstance(classLoader, interfaces, handler); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Handles calls to {@link ConfigurationClassAnnotation} attribute methods at runtime. Essentially |
||||
* emulates what JDK annotation dynamic proxies do. |
||||
*/ |
||||
private static final class MutableAnnotationInvocationHandler implements InvocationHandler { |
||||
|
||||
private final Class<? extends Annotation> annoType; |
||||
private final Map<String, Object> attributes = new HashMap<String, Object>(); |
||||
private final Map<String, Class<?>> attributeTypes = new HashMap<String, Class<?>>(); |
||||
|
||||
public MutableAnnotationInvocationHandler(Class<? extends Annotation> annoType) { |
||||
// pre-populate the attributes hash will all the names
|
||||
// and default values of the attributes defined in 'annoType'
|
||||
Method[] attribs = annoType.getDeclaredMethods(); |
||||
for (Method attrib : attribs) { |
||||
this.attributes.put(attrib.getName(), getDefaultValue(annoType, attrib.getName())); |
||||
this.attributeTypes.put(attrib.getName(), getAttributeType(annoType, attrib.getName())); |
||||
} |
||||
|
||||
this.annoType = annoType; |
||||
} |
||||
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { |
||||
Assert.isInstanceOf(Annotation.class, proxy); |
||||
|
||||
String methodName = method.getName(); |
||||
|
||||
// first -> check to see if this method is an attribute on our annotation
|
||||
if (attributes.containsKey(methodName)) |
||||
return attributes.get(methodName); |
||||
|
||||
|
||||
// second -> is it a method from java.lang.annotation.Annotation?
|
||||
if (methodName.equals("annotationType")) |
||||
return annoType; |
||||
|
||||
|
||||
// third -> is it a method from java.lang.Object?
|
||||
if (methodName.equals("toString")) |
||||
return format("@%s(%s)", annoType.getName(), getAttribs()); |
||||
|
||||
if (methodName.equals("equals")) |
||||
return isEqualTo(proxy, args[0]); |
||||
|
||||
if (methodName.equals("hashCode")) |
||||
return calculateHashCode(proxy); |
||||
|
||||
|
||||
// finally -> is it a method specified by MutableAnno?
|
||||
if (methodName.equals("setAttributeValue")) { |
||||
attributes.put((String) args[0], args[1]); |
||||
return null; // setAttributeValue has a 'void' return type
|
||||
} |
||||
|
||||
if (methodName.equals("getAttributeType")) |
||||
return attributeTypes.get(args[0]); |
||||
|
||||
throw new UnsupportedOperationException("this proxy does not support method: " + methodName); |
||||
} |
||||
|
||||
/** |
||||
* Conforms to the hashCode() specification for Annotation. |
||||
* |
||||
* @see Annotation#hashCode() |
||||
*/ |
||||
private Object calculateHashCode(Object proxy) { |
||||
int sum = 0; |
||||
|
||||
for (String attribName : attributes.keySet()) { |
||||
Object attribValue = attributes.get(attribName); |
||||
|
||||
final int attribNameHashCode = attribName.hashCode(); |
||||
final int attribValueHashCode; |
||||
|
||||
if (attribValue == null) |
||||
// memberValue may be null when a mutable annotation is being added to a
|
||||
// collection
|
||||
// and before it has actually been visited (and populated) by
|
||||
// MutableAnnotationVisitor
|
||||
attribValueHashCode = 0; |
||||
else if (attribValue.getClass().isArray()) |
||||
attribValueHashCode = Arrays.hashCode((Object[]) attribValue); |
||||
else |
||||
attribValueHashCode = attribValue.hashCode(); |
||||
|
||||
sum += (127 * attribNameHashCode) ^ attribValueHashCode; |
||||
} |
||||
|
||||
return sum; |
||||
} |
||||
|
||||
/** |
||||
* Compares <var>proxy</var> object and <var>other</var> object by comparing the return |
||||
* values of the methods specified by their common {@link Annotation} ancestry. |
||||
* <p/> |
||||
* <var>other</var> must be the same type as or a subtype of <var>proxy</var>. Will |
||||
* return false otherwise. |
||||
* <p/> |
||||
* Eagerly returns true if {@code proxy} == <var>other</var> |
||||
* </p> |
||||
* <p/> |
||||
* Conforms strictly to the equals() specification for Annotation |
||||
* </p> |
||||
* |
||||
* @see Annotation#equals(Object) |
||||
*/ |
||||
private Object isEqualTo(Object proxy, Object other) { |
||||
if (proxy == other) |
||||
return true; |
||||
|
||||
if (other == null) |
||||
return false; |
||||
|
||||
if (!annoType.isAssignableFrom(other.getClass())) |
||||
return false; |
||||
|
||||
for (String attribName : attributes.keySet()) { |
||||
Object thisVal; |
||||
Object thatVal; |
||||
|
||||
try { |
||||
thisVal = attributes.get(attribName); |
||||
thatVal = other.getClass().getDeclaredMethod(attribName).invoke(other); |
||||
} catch (Exception ex) { |
||||
throw new IllegalStateException(ex); |
||||
} |
||||
|
||||
if ((thisVal == null) && (thatVal != null)) |
||||
return false; |
||||
|
||||
if ((thatVal == null) && (thisVal != null)) |
||||
return false; |
||||
|
||||
if (thatVal.getClass().isArray()) { |
||||
if (!Arrays.equals((Object[]) thatVal, (Object[]) thisVal)) { |
||||
return false; |
||||
} |
||||
} else if (thisVal instanceof Double) { |
||||
if (!Double.valueOf((Double) thisVal).equals(Double.valueOf((Double) thatVal))) |
||||
return false; |
||||
} else if (thisVal instanceof Float) { |
||||
if (!Float.valueOf((Float) thisVal).equals(Float.valueOf((Float) thatVal))) |
||||
return false; |
||||
} else if (!thisVal.equals(thatVal)) { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
private String getAttribs() { |
||||
ArrayList<String> attribs = new ArrayList<String>(); |
||||
|
||||
for (String attribName : attributes.keySet()) |
||||
attribs.add(format("%s=%s", attribName, attributes.get(attribName))); |
||||
|
||||
return StringUtils.collectionToDelimitedString(attribs, ", "); |
||||
} |
||||
|
||||
/** |
||||
* Retrieve the type of the given annotation attribute. |
||||
*/ |
||||
private static Class<?> getAttributeType(Class<? extends Annotation> annotationType, String attributeName) { |
||||
Method method = null; |
||||
|
||||
try { |
||||
method = annotationType.getDeclaredMethod(attributeName); |
||||
} catch (Exception ex) { |
||||
ReflectionUtils.handleReflectionException(ex); |
||||
} |
||||
|
||||
return method.getReturnType(); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,59 +0,0 @@
@@ -1,59 +0,0 @@
|
||||
/* |
||||
* 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.context.annotation; |
||||
|
||||
import static java.lang.String.*; |
||||
|
||||
import java.util.LinkedHashSet; |
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition; |
||||
import org.springframework.beans.factory.parsing.ProblemReporter; |
||||
|
||||
|
||||
/** |
||||
* Represents the set of all user-defined {@link Configuration} classes. Once this model |
||||
* is populated using a {@link ConfigurationClassParser}, it can be rendered out to a set of |
||||
* {@link BeanDefinition} objects. This model provides an important layer of indirection |
||||
* between the complexity of parsing a set of classes and the complexity of representing |
||||
* the contents of those classes as BeanDefinitions. |
||||
* |
||||
* @author Chris Beams |
||||
* @see ConfigurationClass |
||||
* @see ConfigurationClassParser |
||||
* @see ConfigurationModelBeanDefinitionReader |
||||
*/ |
||||
@SuppressWarnings("serial") |
||||
final class ConfigurationModel extends LinkedHashSet<ConfigurationClass> { |
||||
|
||||
/** |
||||
* Recurses through the model validating each {@link ConfigurationClass}. |
||||
* |
||||
* @param problemReporter {@link ProblemReporter} against which any validation errors |
||||
* will be registered |
||||
* @see ConfigurationClass#validate |
||||
*/ |
||||
public void validate(ProblemReporter problemReporter) { |
||||
for (ConfigurationClass configClass : this) |
||||
configClass.validate(problemReporter); |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return format("%s containing @Configuration classes: %s", getClass().getSimpleName(), super.toString()); |
||||
} |
||||
|
||||
} |
||||
@ -1,279 +0,0 @@
@@ -1,279 +0,0 @@
|
||||
/* |
||||
* 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.context.annotation; |
||||
|
||||
import static java.lang.String.*; |
||||
import static org.springframework.util.StringUtils.*; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
|
||||
import org.apache.commons.logging.Log; |
||||
import org.apache.commons.logging.LogFactory; |
||||
import org.springframework.aop.framework.autoproxy.AutoProxyUtils; |
||||
import org.springframework.aop.scope.ScopedProxyFactoryBean; |
||||
import org.springframework.beans.factory.BeanFactory; |
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException; |
||||
import org.springframework.beans.factory.config.BeanDefinition; |
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; |
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition; |
||||
import org.springframework.beans.factory.support.BeanDefinitionReader; |
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry; |
||||
import org.springframework.beans.factory.support.GenericBeanDefinition; |
||||
import org.springframework.beans.factory.support.RootBeanDefinition; |
||||
import org.springframework.beans.factory.support.SimpleBeanDefinitionRegistry; |
||||
import org.springframework.core.io.Resource; |
||||
import org.springframework.util.Assert; |
||||
|
||||
|
||||
/** |
||||
* Reads a given fully-populated {@link ConfigurationModel}, registering bean definitions |
||||
* with the given {@link BeanDefinitionRegistry} based on its contents. |
||||
* <p> |
||||
* This class was modeled after the {@link BeanDefinitionReader} hierarchy, but does not |
||||
* implement/extend any of its artifacts as {@link ConfigurationModel} is not a |
||||
* {@link Resource}. |
||||
* |
||||
* @author Chris Beams |
||||
* @see ConfigurationModel |
||||
* @see AbstractConfigurationClassProcessor#processConfigBeanDefinitions() |
||||
*/ |
||||
class ConfigurationModelBeanDefinitionReader { |
||||
|
||||
private static final Log log = LogFactory.getLog(ConfigurationModelBeanDefinitionReader.class); |
||||
|
||||
private BeanDefinitionRegistry registry; |
||||
|
||||
|
||||
/** |
||||
* Reads {@code configurationModel}, registering bean definitions with {@link #registry} |
||||
* based on its contents. |
||||
* |
||||
* @return new {@link BeanDefinitionRegistry} containing {@link BeanDefinition}s read |
||||
* from the model. |
||||
*/ |
||||
public BeanDefinitionRegistry loadBeanDefinitions(ConfigurationModel configurationModel) { |
||||
registry = new SimpleBeanDefinitionRegistry(); |
||||
|
||||
for (ConfigurationClass configClass : configurationModel) |
||||
loadBeanDefinitionsForConfigurationClass(configClass); |
||||
|
||||
return registry; |
||||
} |
||||
|
||||
/** |
||||
* Reads a particular {@link ConfigurationClass}, registering bean definitions for the |
||||
* class itself, all its {@link Bean} methods |
||||
*/ |
||||
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass) { |
||||
doLoadBeanDefinitionForConfigurationClass(configClass); |
||||
|
||||
for (BeanMethod method : configClass.getBeanMethods()) |
||||
loadBeanDefinitionsForModelMethod(method); |
||||
} |
||||
|
||||
/** |
||||
* Registers the {@link Configuration} class itself as a bean definition. |
||||
* @param beanDefs |
||||
*/ |
||||
private void doLoadBeanDefinitionForConfigurationClass(ConfigurationClass configClass) { |
||||
|
||||
GenericBeanDefinition configBeanDef = new GenericBeanDefinition(); |
||||
configBeanDef.setBeanClassName(configClass.getName()); |
||||
|
||||
String configBeanName = configClass.getBeanName(); |
||||
|
||||
// consider the case where it's already been defined (probably in XML)
|
||||
// and potentially has PropertyValues and ConstructorArgs)
|
||||
if (registry.containsBeanDefinition(configBeanName)) { |
||||
if (log.isInfoEnabled()) |
||||
log.info(format("Copying property and constructor arg values from existing bean definition for " |
||||
+ "@Configuration class %s to new bean definition", configBeanName)); |
||||
AbstractBeanDefinition existing = (AbstractBeanDefinition) registry.getBeanDefinition(configBeanName); |
||||
configBeanDef.setPropertyValues(existing.getPropertyValues()); |
||||
configBeanDef.setConstructorArgumentValues(existing.getConstructorArgumentValues()); |
||||
configBeanDef.setResource(existing.getResource()); |
||||
} |
||||
|
||||
if (log.isInfoEnabled()) |
||||
log.info(format("Registering bean definition for @Configuration class %s", configBeanName)); |
||||
|
||||
registry.registerBeanDefinition(configBeanName, configBeanDef); |
||||
} |
||||
|
||||
/** |
||||
* Reads a particular {@link BeanMethod}, registering bean definitions with |
||||
* {@link #registry} based on its contents. |
||||
*/ |
||||
private void loadBeanDefinitionsForModelMethod(BeanMethod method) { |
||||
RootBeanDefinition beanDef = new ConfigurationClassBeanDefinition(); |
||||
|
||||
ConfigurationClass configClass = method.getDeclaringClass(); |
||||
|
||||
beanDef.setFactoryBeanName(configClass.getBeanName()); |
||||
beanDef.setFactoryMethodName(method.getName()); |
||||
|
||||
Bean bean = method.getRequiredAnnotation(Bean.class); |
||||
|
||||
// consider scoping
|
||||
Scope scope = method.getAnnotation(Scope.class); |
||||
if(scope != null) |
||||
beanDef.setScope(scope.value()); |
||||
|
||||
// consider name and any aliases
|
||||
ArrayList<String> names = new ArrayList<String>(Arrays.asList(bean.name())); |
||||
String beanName = (names.size() > 0) ? names.remove(0) : method.getName(); |
||||
for (String alias : bean.name()) |
||||
registry.registerAlias(beanName, alias); |
||||
|
||||
// has this already been overriden (i.e.: via XML)?
|
||||
if (containsBeanDefinitionIncludingAncestry(beanName, registry)) { |
||||
BeanDefinition existingBeanDef = getBeanDefinitionIncludingAncestry(beanName, registry); |
||||
|
||||
// is the existing bean definition one that was created by JavaConfig?
|
||||
if (!(existingBeanDef instanceof ConfigurationClassBeanDefinition)) { |
||||
// no -> then it's an external override, probably XML
|
||||
|
||||
// overriding is legal, return immediately
|
||||
log.info(format("Skipping loading bean definition for %s: a definition for bean " |
||||
+ "'%s' already exists. This is likely due to an override in XML.", method, beanName)); |
||||
return; |
||||
} |
||||
} |
||||
|
||||
if (method.getAnnotation(Primary.class) != null) |
||||
beanDef.setPrimary(true); |
||||
|
||||
// is this bean to be instantiated lazily?
|
||||
Lazy defaultLazy = configClass.getAnnotation(Lazy.class); |
||||
if (defaultLazy != null) |
||||
beanDef.setLazyInit(defaultLazy.value()); |
||||
Lazy lazy = method.getAnnotation(Lazy.class); |
||||
if (lazy != null) |
||||
beanDef.setLazyInit(lazy.value()); |
||||
|
||||
// does this bean have a custom init-method specified?
|
||||
String initMethodName = bean.initMethod(); |
||||
if (hasText(initMethodName)) |
||||
beanDef.setInitMethodName(initMethodName); |
||||
|
||||
// does this bean have a custom destroy-method specified?
|
||||
String destroyMethodName = bean.destroyMethod(); |
||||
if (hasText(destroyMethodName)) |
||||
beanDef.setDestroyMethodName(destroyMethodName); |
||||
|
||||
// is this method annotated with @Scope(scopedProxy=...)?
|
||||
if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) { |
||||
RootBeanDefinition targetDef = beanDef; |
||||
|
||||
// Create a scoped proxy definition for the original bean name,
|
||||
// "hiding" the target bean in an internal target definition.
|
||||
String targetBeanName = resolveHiddenScopedProxyBeanName(beanName); |
||||
RootBeanDefinition scopedProxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class); |
||||
scopedProxyDefinition.getPropertyValues().addPropertyValue("targetBeanName", targetBeanName); |
||||
|
||||
if (scope.proxyMode() == ScopedProxyMode.TARGET_CLASS) |
||||
targetDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE); |
||||
// ScopedFactoryBean's "proxyTargetClass" default is TRUE, so we
|
||||
// don't need to set it explicitly here.
|
||||
else |
||||
scopedProxyDefinition.getPropertyValues().addPropertyValue("proxyTargetClass", Boolean.FALSE); |
||||
|
||||
// The target bean should be ignored in favor of the scoped proxy.
|
||||
targetDef.setAutowireCandidate(false); |
||||
|
||||
// Register the target bean as separate bean in the factory
|
||||
registry.registerBeanDefinition(targetBeanName, targetDef); |
||||
|
||||
// replace the original bean definition with the target one
|
||||
beanDef = scopedProxyDefinition; |
||||
} |
||||
|
||||
if (bean.dependsOn().length > 0) |
||||
beanDef.setDependsOn(bean.dependsOn()); |
||||
|
||||
log.info(format("Registering bean definition for @Bean method %s.%s()", |
||||
configClass.getName(), beanName)); |
||||
|
||||
registry.registerBeanDefinition(beanName, beanDef); |
||||
|
||||
} |
||||
|
||||
private boolean containsBeanDefinitionIncludingAncestry(String beanName, BeanDefinitionRegistry registry) { |
||||
try { |
||||
getBeanDefinitionIncludingAncestry(beanName, registry); |
||||
return true; |
||||
} catch (NoSuchBeanDefinitionException ex) { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
private BeanDefinition getBeanDefinitionIncludingAncestry(String beanName, BeanDefinitionRegistry registry) { |
||||
if(!(registry instanceof ConfigurableListableBeanFactory)) |
||||
return registry.getBeanDefinition(beanName); |
||||
|
||||
ConfigurableListableBeanFactory clbf = (ConfigurableListableBeanFactory) registry; |
||||
|
||||
do { |
||||
if (clbf.containsBeanDefinition(beanName)) |
||||
return registry.getBeanDefinition(beanName); |
||||
|
||||
BeanFactory parent = clbf.getParentBeanFactory(); |
||||
if (parent == null) { |
||||
clbf = null; |
||||
} else if (parent instanceof ConfigurableListableBeanFactory) { |
||||
clbf = (ConfigurableListableBeanFactory) parent; |
||||
// TODO: re-enable
|
||||
// } else if (parent instanceof AbstractApplicationContext) {
|
||||
// clbf = ((AbstractApplicationContext) parent).getBeanFactory();
|
||||
} else { |
||||
throw new IllegalStateException("unknown parent type: " + parent.getClass().getName()); |
||||
} |
||||
} while (clbf != null); |
||||
|
||||
throw new NoSuchBeanDefinitionException( |
||||
format("No bean definition matching name '%s' " + |
||||
"could be found in %s or its ancestry", beanName, registry)); |
||||
} |
||||
|
||||
/** |
||||
* Return the <i>hidden</i> name based on a scoped proxy bean name. |
||||
* |
||||
* @param originalBeanName the scope proxy bean name as declared in the |
||||
* Configuration-annotated class
|
||||
* |
||||
* @return the internally-used <i>hidden</i> bean name |
||||
*/ |
||||
public static String resolveHiddenScopedProxyBeanName(String originalBeanName) { |
||||
Assert.hasText(originalBeanName); |
||||
return TARGET_NAME_PREFIX.concat(originalBeanName); |
||||
} |
||||
|
||||
/** Prefix used when registering the target object for a scoped proxy. */ |
||||
private static final String TARGET_NAME_PREFIX = "scopedTarget."; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* {@link RootBeanDefinition} marker subclass used to signify that a bean definition created |
||||
* by JavaConfig as opposed to any other configuration source. Used in bean overriding cases |
||||
* where it's necessary to determine whether the bean definition was created externally |
||||
* (e.g. via XML). |
||||
*/ |
||||
@SuppressWarnings("serial") |
||||
class ConfigurationClassBeanDefinition extends RootBeanDefinition { |
||||
} |
||||
@ -0,0 +1,54 @@
@@ -0,0 +1,54 @@
|
||||
/* |
||||
* 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.context.annotation; |
||||
|
||||
import java.lang.annotation.Target; |
||||
import java.lang.annotation.ElementType; |
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
import java.lang.annotation.Inherited; |
||||
import java.lang.annotation.Documented; |
||||
|
||||
/** |
||||
* Beans on which the current bean depends. Any beans specified are guaranteed to be |
||||
* created by the container before this bean. Used infrequently in cases where a bean |
||||
* does not explicitly depend on another through properties or constructor arguments, |
||||
* but rather depends on the side effects of another bean's initialization. |
||||
* <p>Note: This attribute will not be inherited by child bean definitions, |
||||
* hence it needs to be specified per concrete bean definition. |
||||
* |
||||
* <p>May be used on any class directly or indirectly annotated with |
||||
* {@link org.springframework.stereotype.Component} or on methods annotated |
||||
* with {@link Bean}. |
||||
* |
||||
* <p>Using {@link DependsOn} at the class level has no effect unless component-scanning |
||||
* is being used. If a {@link DependsOn}-annotated class is declared via XML, |
||||
* {@link DependsOn} annotation metadata is ignored, and |
||||
* {@literal <bean depends-on="..."/>} is respected instead. |
||||
* |
||||
* @author Juergen Hoeller |
||||
* @since 3.0 |
||||
*/ |
||||
@Target({ElementType.TYPE, ElementType.METHOD}) |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Inherited |
||||
@Documented |
||||
public @interface DependsOn { |
||||
|
||||
String[] value() default {}; |
||||
|
||||
} |
||||
@ -1,89 +0,0 @@
@@ -1,89 +0,0 @@
|
||||
/* |
||||
* 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.context.annotation; |
||||
|
||||
import static java.lang.String.*; |
||||
import static org.springframework.context.annotation.AsmUtils.*; |
||||
import static org.springframework.util.ClassUtils.*; |
||||
|
||||
import java.util.ArrayList; |
||||
|
||||
import org.springframework.asm.AnnotationVisitor; |
||||
import org.springframework.asm.ClassReader; |
||||
import org.springframework.asm.Type; |
||||
import org.springframework.beans.factory.parsing.ProblemReporter; |
||||
import org.springframework.util.Assert; |
||||
|
||||
|
||||
/** |
||||
* ASM {@link AnnotationVisitor} implementation that reads an {@link Import} annotation |
||||
* for all its specified classes and then one by one processes each class with a new |
||||
* {@link ConfigurationClassVisitor}. |
||||
* |
||||
* @author Chris Beams |
||||
* @see Import |
||||
* @see ImportStack |
||||
* @see ImportStackHolder |
||||
* @see ConfigurationClassVisitor |
||||
*/ |
||||
class ImportAnnotationVisitor extends AnnotationAdapter { |
||||
|
||||
private final ArrayList<String> classesToImport = new ArrayList<String>(); |
||||
private final ConfigurationModel model; |
||||
private final ProblemReporter problemReporter; |
||||
private final ClassLoader classLoader; |
||||
|
||||
public ImportAnnotationVisitor(ConfigurationModel model, ProblemReporter problemReporter, ClassLoader classLoader) { |
||||
super(ASM_EMPTY_VISITOR); |
||||
this.model = model; |
||||
this.problemReporter = problemReporter; |
||||
this.classLoader = classLoader; |
||||
} |
||||
|
||||
@Override |
||||
public AnnotationVisitor visitArray(String attribName) { |
||||
Assert.isTrue("value".equals(attribName), |
||||
format("expected 'value' attribute, got unknown '%s' attribute", attribName)); |
||||
|
||||
return new AnnotationAdapter(ASM_EMPTY_VISITOR) { |
||||
@Override |
||||
public void visit(String na, Object type) { |
||||
Assert.isInstanceOf(Type.class, type); |
||||
classesToImport.add(((Type) type).getClassName()); |
||||
} |
||||
}; |
||||
} |
||||
|
||||
@Override |
||||
public void visitEnd() { |
||||
for (String classToImport : classesToImport) |
||||
processClassToImport(classToImport); |
||||
|
||||
ImportStackHolder.getImportStack().pop(); |
||||
} |
||||
|
||||
private void processClassToImport(String classToImport) { |
||||
ConfigurationClass configClass = new ConfigurationClass(); |
||||
|
||||
ClassReader reader = newAsmClassReader(convertClassNameToResourcePath(classToImport), classLoader); |
||||
|
||||
reader.accept(new ConfigurationClassVisitor(configClass, model, problemReporter, classLoader), false); |
||||
|
||||
model.add(configClass); |
||||
} |
||||
|
||||
} |
||||
@ -1,83 +0,0 @@
@@ -1,83 +0,0 @@
|
||||
/* |
||||
* 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.context.annotation; |
||||
|
||||
import java.util.Collections; |
||||
import java.util.Comparator; |
||||
import java.util.Iterator; |
||||
import java.util.Stack; |
||||
|
||||
import org.springframework.util.Assert; |
||||
|
||||
|
||||
/** |
||||
* {@link Stack} used for detecting circular use of the {@link Import} annotation. |
||||
* |
||||
* @author Chris Beams |
||||
* @see Import |
||||
* @see ImportStackHolder |
||||
* @see CircularImportException |
||||
*/ |
||||
@SuppressWarnings("serial") |
||||
class ImportStack extends Stack<ConfigurationClass> { |
||||
|
||||
/** |
||||
* Simplified contains() implementation that tests to see if any {@link ConfigurationClass} |
||||
* exists within this stack that has the same name as <var>elem</var>. Elem must be of |
||||
* type ConfigurationClass. |
||||
*/ |
||||
@Override |
||||
public boolean contains(Object elem) { |
||||
Assert.isInstanceOf(ConfigurationClass.class, elem); |
||||
|
||||
ConfigurationClass configClass = (ConfigurationClass) elem; |
||||
|
||||
Comparator<ConfigurationClass> comparator = new Comparator<ConfigurationClass>() { |
||||
public int compare(ConfigurationClass first, ConfigurationClass second) { |
||||
return first.getName().equals(second.getName()) ? 0 : 1; |
||||
} |
||||
}; |
||||
|
||||
int index = Collections.binarySearch(this, configClass, comparator); |
||||
|
||||
return index >= 0 ? true : false; |
||||
} |
||||
|
||||
/** |
||||
* Given a stack containing (in order) |
||||
* <ol> |
||||
* <li>com.acme.Foo</li> |
||||
* <li>com.acme.Bar</li> |
||||
* <li>com.acme.Baz</li> |
||||
* </ol> |
||||
* Returns "Foo->Bar->Baz". In the case of an empty stack, returns empty string. |
||||
*/ |
||||
@Override |
||||
public synchronized String toString() { |
||||
StringBuilder builder = new StringBuilder(); |
||||
|
||||
Iterator<ConfigurationClass> iterator = this.iterator(); |
||||
while (iterator.hasNext()) { |
||||
builder.append(iterator.next().getSimpleName()); |
||||
if (iterator.hasNext()) |
||||
builder.append("->"); |
||||
} |
||||
|
||||
return builder.toString(); |
||||
} |
||||
|
||||
} |
||||
@ -1,48 +0,0 @@
@@ -1,48 +0,0 @@
|
||||
/* |
||||
* 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.context.annotation; |
||||
|
||||
|
||||
|
||||
/** |
||||
* Holder class to expose a thread-bound {@link ImportStack}, used while detecting circular |
||||
* declarations of the {@link Import} annotation. |
||||
* |
||||
* @author Chris Beams |
||||
* @see Import |
||||
* @see ImportStack |
||||
* @see CircularImportException |
||||
*/ |
||||
class ImportStackHolder { |
||||
|
||||
private static ThreadLocal<ImportStack> stackHolder = new ThreadLocal<ImportStack>() { |
||||
@Override |
||||
protected ImportStack initialValue() { |
||||
return new ImportStack(); |
||||
} |
||||
}; |
||||
|
||||
public static ImportStack getImportStack() { |
||||
return stackHolder.get(); |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return "Holder for circular @Import detection stack"; |
||||
} |
||||
|
||||
} |
||||
@ -1,172 +0,0 @@
@@ -1,172 +0,0 @@
|
||||
/* |
||||
* 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.context.annotation; |
||||
|
||||
import static org.springframework.util.ClassUtils.*; |
||||
|
||||
import org.springframework.beans.BeanMetadataElement; |
||||
import org.springframework.beans.factory.parsing.Location; |
||||
import org.springframework.core.io.ClassPathResource; |
||||
import org.springframework.util.ClassUtils; |
||||
|
||||
|
||||
/** |
||||
* Represents a class, free from java reflection, |
||||
* populated by {@link ConfigurationClassParser}. |
||||
* |
||||
* @author Chris Beams |
||||
* @see ConfigurationModel |
||||
* @see ConfigurationClass |
||||
* @see ConfigurationClassParser |
||||
*/ |
||||
class ModelClass implements BeanMetadataElement { |
||||
|
||||
private String name; |
||||
private boolean isInterface; |
||||
private transient Object source; |
||||
|
||||
/** |
||||
* Creates a new and empty ModelClass instance. |
||||
*/ |
||||
public ModelClass() { |
||||
} |
||||
|
||||
/** |
||||
* Creates a new ModelClass instance |
||||
* |
||||
* @param name fully-qualified name of the class being represented |
||||
*/ |
||||
public ModelClass(String name) { |
||||
this(name, false); |
||||
} |
||||
|
||||
/** |
||||
* Creates a new ModelClass instance |
||||
* |
||||
* @param name fully-qualified name of the class being represented |
||||
* @param isInterface whether the represented type is an interface
|
||||
*/ |
||||
public ModelClass(String name, boolean isInterface) { |
||||
this.name = name; |
||||
this.isInterface = isInterface; |
||||
} |
||||
|
||||
/** |
||||
* Returns the fully-qualified name of this class. |
||||
*/ |
||||
public String getName() { |
||||
return name; |
||||
} |
||||
|
||||
/** |
||||
* Sets the fully-qualified name of this class. |
||||
*/ |
||||
public void setName(String className) { |
||||
this.name = className; |
||||
} |
||||
|
||||
/** |
||||
* Returns the non-qualified name of this class. Given com.acme.Foo, returns 'Foo'. |
||||
*/ |
||||
public String getSimpleName() { |
||||
return name == null ? null : ClassUtils.getShortName(name); |
||||
} |
||||
|
||||
/** |
||||
* Returns whether the class represented by this ModelClass instance is an interface. |
||||
*/ |
||||
public boolean isInterface() { |
||||
return isInterface; |
||||
} |
||||
|
||||
/** |
||||
* Signifies that this class is (true) or is not (false) an interface. |
||||
*/ |
||||
public void setInterface(boolean isInterface) { |
||||
this.isInterface = isInterface; |
||||
} |
||||
|
||||
/** |
||||
* Returns a resource path-formatted representation of the .java file that declares this |
||||
* class
|
||||
*/ |
||||
public Object getSource() { |
||||
return source; |
||||
} |
||||
|
||||
/** |
||||
* Set the source location for this class. Must be a resource-path formatted string. |
||||
* |
||||
* @param source resource path to the .java file that declares this class. |
||||
*/ |
||||
public void setSource(Object source) { |
||||
this.source = source; |
||||
} |
||||
|
||||
public Location getLocation() { |
||||
if(getName() == null) |
||||
throw new IllegalStateException("'name' property is null. Call setName() before calling getLocation()"); |
||||
return new Location(new ClassPathResource(convertClassNameToResourcePath(getName())), getSource()); |
||||
} |
||||
|
||||
/** |
||||
* Given a ModelClass instance representing a class com.acme.Foo, this method will |
||||
* return |
||||
* |
||||
* <pre> |
||||
* ModelClass: name=Foo |
||||
* </pre> |
||||
*/ |
||||
@Override |
||||
public String toString() { |
||||
return String.format("%s: name=%s", getClass().getSimpleName(), getSimpleName()); |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
final int prime = 31; |
||||
int result = 1; |
||||
result = (prime * result) + (isInterface ? 1231 : 1237); |
||||
result = (prime * result) + ((name == null) ? 0 : name.hashCode()); |
||||
return result; |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(Object obj) { |
||||
if (this == obj) |
||||
return true; |
||||
|
||||
if (obj == null) |
||||
return false; |
||||
|
||||
if (getClass() != obj.getClass()) |
||||
return false; |
||||
|
||||
ModelClass other = (ModelClass) obj; |
||||
if (isInterface != other.isInterface) |
||||
return false; |
||||
|
||||
if (name == null) { |
||||
if (other.name != null) |
||||
return false; |
||||
} else if (!name.equals(other.name)) |
||||
return false; |
||||
|
||||
return true; |
||||
} |
||||
|
||||
} |
||||
@ -1,77 +0,0 @@
@@ -1,77 +0,0 @@
|
||||
/* |
||||
* 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.context.annotation; |
||||
|
||||
import static org.springframework.context.annotation.AsmUtils.*; |
||||
|
||||
import java.lang.annotation.Annotation; |
||||
import java.lang.reflect.Array; |
||||
import java.util.ArrayList; |
||||
|
||||
import org.springframework.asm.AnnotationVisitor; |
||||
|
||||
|
||||
/** |
||||
* ASM {@link AnnotationVisitor} that visits any annotation array values while populating |
||||
* a new {@link MutableAnnotation} instance. |
||||
* |
||||
* @author Chris Beams |
||||
* @see MutableAnnotation |
||||
* @see AsmUtils#createMutableAnnotation |
||||
*/ |
||||
class MutableAnnotationArrayVisitor extends AnnotationAdapter { |
||||
|
||||
private final ArrayList<Object> values = new ArrayList<Object>(); |
||||
private final MutableAnnotation mutableAnno; |
||||
private final String attribName; |
||||
|
||||
private final ClassLoader classLoader; |
||||
|
||||
public MutableAnnotationArrayVisitor(MutableAnnotation mutableAnno, String attribName, ClassLoader classLoader) { |
||||
super(AsmUtils.ASM_EMPTY_VISITOR); |
||||
|
||||
this.mutableAnno = mutableAnno; |
||||
this.attribName = attribName; |
||||
this.classLoader = classLoader; |
||||
} |
||||
|
||||
@Override |
||||
public void visit(String na, Object value) { |
||||
values.add(value); |
||||
} |
||||
|
||||
@Override |
||||
public AnnotationVisitor visitAnnotation(String na, String annoTypeDesc) { |
||||
String annoTypeName = convertAsmTypeDescriptorToClassName(annoTypeDesc); |
||||
Class<? extends Annotation> annoType = loadToolingSafeClass(annoTypeName, classLoader); |
||||
|
||||
if (annoType == null) |
||||
return super.visitAnnotation(na, annoTypeDesc); |
||||
|
||||
Annotation anno = createMutableAnnotation(annoType, classLoader); |
||||
values.add(anno); |
||||
return new MutableAnnotationVisitor(anno, classLoader); |
||||
} |
||||
|
||||
@Override |
||||
public void visitEnd() { |
||||
Class<?> arrayType = mutableAnno.getAttributeType(attribName); |
||||
Object[] array = (Object[]) Array.newInstance(arrayType.getComponentType(), 0); |
||||
mutableAnno.setAttributeValue(attribName, values.toArray(array)); |
||||
} |
||||
|
||||
} |
||||
@ -1,213 +0,0 @@
@@ -1,213 +0,0 @@
|
||||
/* |
||||
* 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.context.annotation; |
||||
|
||||
import static java.lang.String.*; |
||||
import static org.springframework.core.annotation.AnnotationUtils.*; |
||||
|
||||
import java.lang.annotation.Annotation; |
||||
import java.lang.reflect.InvocationHandler; |
||||
import java.lang.reflect.Method; |
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.HashMap; |
||||
|
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.ReflectionUtils; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
|
||||
/** |
||||
* Handles calls to {@link MutableAnnotation} attribute methods at runtime. Essentially |
||||
* emulates what JDK annotation dynamic proxies do. |
||||
* |
||||
* @author Chris Beams |
||||
* @see MutableAnnotation |
||||
* @see AsmUtils#createMutableAnnotation |
||||
*/ |
||||
final class MutableAnnotationInvocationHandler implements InvocationHandler { |
||||
|
||||
private final Class<? extends Annotation> annoType; |
||||
private final HashMap<String, Object> attributes = new HashMap<String, Object>(); |
||||
private final HashMap<String, Class<?>> attributeTypes = new HashMap<String, Class<?>>(); |
||||
|
||||
public MutableAnnotationInvocationHandler(Class<? extends Annotation> annoType) { |
||||
// pre-populate the attributes hash will all the names
|
||||
// and default values of the attributes defined in 'annoType'
|
||||
Method[] attribs = annoType.getDeclaredMethods(); |
||||
for (Method attrib : attribs) { |
||||
this.attributes.put(attrib.getName(), getDefaultValue(annoType, attrib.getName())); |
||||
this.attributeTypes.put(attrib.getName(), getAttributeType(annoType, attrib.getName())); |
||||
} |
||||
|
||||
this.annoType = annoType; |
||||
} |
||||
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { |
||||
Assert.isInstanceOf(Annotation.class, proxy); |
||||
|
||||
String methodName = method.getName(); |
||||
|
||||
// first -> check to see if this method is an attribute on our annotation
|
||||
if (attributes.containsKey(methodName)) |
||||
return attributes.get(methodName); |
||||
|
||||
|
||||
// second -> is it a method from java.lang.annotation.Annotation?
|
||||
if (methodName.equals("annotationType")) |
||||
return annoType; |
||||
|
||||
|
||||
// third -> is it a method from java.lang.Object?
|
||||
if (methodName.equals("toString")) |
||||
return format("@%s(%s)", annoType.getName(), getAttribs()); |
||||
|
||||
if (methodName.equals("equals")) |
||||
return isEqualTo(proxy, args[0]); |
||||
|
||||
if (methodName.equals("hashCode")) |
||||
return calculateHashCode(proxy); |
||||
|
||||
|
||||
// finally -> is it a method specified by MutableAnno?
|
||||
if (methodName.equals("setAttributeValue")) { |
||||
attributes.put((String) args[0], args[1]); |
||||
return null; // setAttributeValue has a 'void' return type
|
||||
} |
||||
|
||||
if (methodName.equals("getAttributeType")) |
||||
return attributeTypes.get(args[0]); |
||||
|
||||
throw new UnsupportedOperationException("this proxy does not support method: " + methodName); |
||||
} |
||||
|
||||
/** |
||||
* Conforms to the hashCode() specification for Annotation. |
||||
* |
||||
* @see Annotation#hashCode() |
||||
*/ |
||||
private Object calculateHashCode(Object proxy) { |
||||
int sum = 0; |
||||
|
||||
for (String attribName : attributes.keySet()) { |
||||
Object attribValue = attributes.get(attribName); |
||||
|
||||
final int attribNameHashCode = attribName.hashCode(); |
||||
final int attribValueHashCode; |
||||
|
||||
if (attribValue == null) |
||||
// memberValue may be null when a mutable annotation is being added to a
|
||||
// collection
|
||||
// and before it has actually been visited (and populated) by
|
||||
// MutableAnnotationVisitor
|
||||
attribValueHashCode = 0; |
||||
else if (attribValue.getClass().isArray()) |
||||
attribValueHashCode = Arrays.hashCode((Object[]) attribValue); |
||||
else |
||||
attribValueHashCode = attribValue.hashCode(); |
||||
|
||||
sum += (127 * attribNameHashCode) ^ attribValueHashCode; |
||||
} |
||||
|
||||
return sum; |
||||
} |
||||
|
||||
/** |
||||
* Compares <var>proxy</var> object and <var>other</var> object by comparing the return |
||||
* values of the methods specified by their common {@link Annotation} ancestry. |
||||
* <p/> |
||||
* <var>other</var> must be the same type as or a subtype of <var>proxy</var>. Will |
||||
* return false otherwise. |
||||
* <p/> |
||||
* Eagerly returns true if {@code proxy} == <var>other</var> |
||||
* </p> |
||||
* <p/> |
||||
* Conforms strictly to the equals() specification for Annotation |
||||
* </p> |
||||
* |
||||
* @see Annotation#equals(Object) |
||||
*/ |
||||
private Object isEqualTo(Object proxy, Object other) { |
||||
if (proxy == other) |
||||
return true; |
||||
|
||||
if (other == null) |
||||
return false; |
||||
|
||||
if (!annoType.isAssignableFrom(other.getClass())) |
||||
return false; |
||||
|
||||
for (String attribName : attributes.keySet()) { |
||||
Object thisVal; |
||||
Object thatVal; |
||||
|
||||
try { |
||||
thisVal = attributes.get(attribName); |
||||
thatVal = other.getClass().getDeclaredMethod(attribName).invoke(other); |
||||
} catch (Exception ex) { |
||||
throw new RuntimeException(ex); |
||||
} |
||||
|
||||
if ((thisVal == null) && (thatVal != null)) |
||||
return false; |
||||
|
||||
if ((thatVal == null) && (thisVal != null)) |
||||
return false; |
||||
|
||||
if (thatVal.getClass().isArray()) { |
||||
if (!Arrays.equals((Object[]) thatVal, (Object[]) thisVal)) { |
||||
return false; |
||||
} |
||||
} else if (thisVal instanceof Double) { |
||||
if (!Double.valueOf((Double) thisVal).equals(Double.valueOf((Double) thatVal))) |
||||
return false; |
||||
} else if (thisVal instanceof Float) { |
||||
if (!Float.valueOf((Float) thisVal).equals(Float.valueOf((Float) thatVal))) |
||||
return false; |
||||
} else if (!thisVal.equals(thatVal)) { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
private String getAttribs() { |
||||
ArrayList<String> attribs = new ArrayList<String>(); |
||||
|
||||
for (String attribName : attributes.keySet()) |
||||
attribs.add(format("%s=%s", attribName, attributes.get(attribName))); |
||||
|
||||
return StringUtils.collectionToDelimitedString(attribs, ", "); |
||||
} |
||||
|
||||
/** |
||||
* Retrieve the type of the given annotation attribute. |
||||
*/ |
||||
private static Class<?> getAttributeType(Class<? extends Annotation> annotationType, String attributeName) { |
||||
Method method = null; |
||||
|
||||
try { |
||||
method = annotationType.getDeclaredMethod(attributeName); |
||||
} catch (Exception ex) { |
||||
ReflectionUtils.handleReflectionException(ex); |
||||
} |
||||
|
||||
return method.getReturnType(); |
||||
} |
||||
|
||||
} |
||||
@ -1,121 +0,0 @@
@@ -1,121 +0,0 @@
|
||||
/* |
||||
* 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.context.annotation; |
||||
|
||||
import static org.springframework.context.annotation.AsmUtils.*; |
||||
|
||||
import java.lang.annotation.Annotation; |
||||
import java.lang.reflect.Field; |
||||
|
||||
import org.springframework.asm.AnnotationVisitor; |
||||
import org.springframework.asm.Type; |
||||
import org.springframework.util.Assert; |
||||
|
||||
|
||||
/** |
||||
* ASM {@link AnnotationVisitor} that populates a given {@link MutableAnnotation} instance |
||||
* with its attributes. |
||||
* |
||||
* @author Chris Beams |
||||
* @see MutableAnnotation |
||||
* @see MutableAnnotationInvocationHandler |
||||
* @see AsmUtils#createMutableAnnotation |
||||
*/ |
||||
class MutableAnnotationVisitor implements AnnotationVisitor { |
||||
|
||||
protected final MutableAnnotation mutableAnno; |
||||
|
||||
private final ClassLoader classLoader; |
||||
|
||||
/** |
||||
* Creates a new {@link MutableAnnotationVisitor} instance that will populate the the |
||||
* attributes of the given <var>mutableAnno</var>. Accepts {@link Annotation} instead of |
||||
* {@link MutableAnnotation} to avoid the need for callers to typecast. |
||||
* |
||||
* @param mutableAnno {@link MutableAnnotation} instance to visit and populate |
||||
* |
||||
* @throws IllegalArgumentException if <var>mutableAnno</var> is not of type |
||||
* {@link MutableAnnotation} |
||||
* |
||||
* @see AsmUtils#createMutableAnnotation |
||||
*/ |
||||
public MutableAnnotationVisitor(Annotation mutableAnno, ClassLoader classLoader) { |
||||
Assert.isInstanceOf(MutableAnnotation.class, mutableAnno, "annotation must be mutable"); |
||||
this.mutableAnno = (MutableAnnotation) mutableAnno; |
||||
this.classLoader = classLoader; |
||||
} |
||||
|
||||
public AnnotationVisitor visitArray(final String attribName) { |
||||
return new MutableAnnotationArrayVisitor(mutableAnno, attribName, classLoader); |
||||
} |
||||
|
||||
public void visit(String attribName, Object attribValue) { |
||||
Class<?> attribReturnType = mutableAnno.getAttributeType(attribName); |
||||
|
||||
if (attribReturnType.equals(Class.class)) { |
||||
// the attribute type is Class -> load it and set it.
|
||||
String fqClassName = ((Type) attribValue).getClassName(); |
||||
|
||||
Class<?> classVal = loadToolingSafeClass(fqClassName, classLoader); |
||||
|
||||
if (classVal == null) |
||||
return; |
||||
|
||||
mutableAnno.setAttributeValue(attribName, classVal); |
||||
return; |
||||
} |
||||
|
||||
// otherwise, assume the value can be set literally
|
||||
mutableAnno.setAttributeValue(attribName, attribValue); |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
public void visitEnum(String attribName, String enumTypeDescriptor, String strEnumValue) { |
||||
String enumClassName = convertAsmTypeDescriptorToClassName(enumTypeDescriptor); |
||||
|
||||
Class<? extends Enum> enumClass = loadToolingSafeClass(enumClassName, classLoader); |
||||
|
||||
if (enumClass == null) |
||||
return; |
||||
|
||||
Enum enumValue = Enum.valueOf(enumClass, strEnumValue); |
||||
mutableAnno.setAttributeValue(attribName, enumValue); |
||||
} |
||||
|
||||
public AnnotationVisitor visitAnnotation(String attribName, String attribAnnoTypeDesc) { |
||||
String annoTypeName = convertAsmTypeDescriptorToClassName(attribAnnoTypeDesc); |
||||
Class<? extends Annotation> annoType = loadToolingSafeClass(annoTypeName, classLoader); |
||||
|
||||
if (annoType == null) |
||||
return ASM_EMPTY_VISITOR.visitAnnotation(attribName, attribAnnoTypeDesc); |
||||
|
||||
Annotation anno = createMutableAnnotation(annoType, classLoader); |
||||
|
||||
try { |
||||
Field attribute = mutableAnno.getClass().getField(attribName); |
||||
attribute.set(mutableAnno, anno); |
||||
} catch (Exception ex) { |
||||
throw new RuntimeException(ex); |
||||
} |
||||
|
||||
return new MutableAnnotationVisitor(anno, classLoader); |
||||
} |
||||
|
||||
public void visitEnd() { |
||||
} |
||||
|
||||
} |
||||
@ -1,48 +0,0 @@
@@ -1,48 +0,0 @@
|
||||
/* |
||||
* 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.context.annotation; |
||||
|
||||
import static java.lang.String.*; |
||||
import static org.hamcrest.CoreMatchers.*; |
||||
import static org.junit.Assert.*; |
||||
|
||||
import org.junit.Test; |
||||
import org.springframework.context.annotation.ConfigurationClass; |
||||
import org.springframework.context.annotation.ConfigurationModel; |
||||
|
||||
|
||||
/** |
||||
* Unit tests for {@link ConfigurationModel}. |
||||
* |
||||
* @author Chris Beams |
||||
*/ |
||||
public class ConfigurationModelTests { |
||||
|
||||
@Test |
||||
public void testToString() { |
||||
ConfigurationModel model = new ConfigurationModel(); |
||||
assertThat(model.toString(), equalTo( |
||||
"ConfigurationModel containing @Configuration classes: []")); |
||||
|
||||
ConfigurationClass config1 = new ConfigurationClass(); |
||||
config1.setName("test.Config1"); |
||||
model.add(config1); |
||||
|
||||
assertThat(model.toString(), equalTo(format( |
||||
"ConfigurationModel containing @Configuration classes: [%s]", config1))); |
||||
} |
||||
|
||||
} |
||||
@ -1,63 +0,0 @@
@@ -1,63 +0,0 @@
|
||||
/* |
||||
* 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.context.annotation.configuration; |
||||
|
||||
import static org.junit.Assert.*; |
||||
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.*; |
||||
|
||||
import org.junit.Test; |
||||
import org.springframework.aop.support.AopUtils; |
||||
import org.springframework.beans.factory.config.BeanDefinition; |
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.context.annotation.ConfigurationClassPostProcessor; |
||||
|
||||
|
||||
/** |
||||
* Covers the somewhat unlilely case of a {@link Configuration} class being declared |
||||
* as an abstract {@link BeanDefinition}. |
||||
* |
||||
* @author Chris Beams |
||||
* @see BeanDefinition#isAbstract() |
||||
*/ |
||||
public class AbstractBeanDefinitionConfigurationClassTests { |
||||
|
||||
@SuppressWarnings("unused") |
||||
@Test |
||||
public void abstractConfigurationClassBeanDefinitionsAreIgnored() { |
||||
@Configuration class Abstract { @Bean Object foo1() { return null; } } |
||||
@Configuration class Concrete { @Bean Object foo2() { return null; } } |
||||
|
||||
DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); |
||||
factory.registerBeanDefinition("abstract", |
||||
rootBeanDefinition(Abstract.class).setAbstract(true).getBeanDefinition()); |
||||
factory.registerBeanDefinition("concrete", |
||||
rootBeanDefinition(Concrete.class).setAbstract(false).getBeanDefinition()); |
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(factory); |
||||
|
||||
assertTrue("abstract configuration should be CGLIB-enhanced", |
||||
AopUtils.isCglibProxyClassName(factory.getBeanDefinition("abstract").getBeanClassName())); |
||||
assertTrue("concrete configuration should be CGLIB-enhanced", |
||||
AopUtils.isCglibProxyClassName(factory.getBeanDefinition("concrete").getBeanClassName())); |
||||
|
||||
assertFalse("abstract configuration's @Bean method should not be registered", |
||||
factory.containsBeanDefinition("foo1")); |
||||
assertTrue("concrete configuration's @Bean method should be registered", |
||||
factory.containsBeanDefinition("foo2")); |
||||
} |
||||
} |
||||
@ -1,36 +1,36 @@
@@ -1,36 +1,36 @@
|
||||
/* |
||||
* 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.beans.factory.annotation; |
||||
|
||||
import java.lang.annotation.ElementType; |
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
import java.lang.annotation.Target; |
||||
|
||||
/** |
||||
* Marks a method as being a factory-method of the class. Use during component scanning |
||||
* to create a bean definition that has factory-bean and factory-method metadata |
||||
* |
||||
* @author Mark Pollack |
||||
* @since 3.0 |
||||
* @see RequiredAnnotationBeanPostProcessor |
||||
*/ |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Target({ElementType.METHOD}) |
||||
public @interface FactoryMethod { |
||||
|
||||
} |
||||
/* |
||||
* 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.context.annotation5; |
||||
|
||||
import example.scannable.FooDao; |
||||
|
||||
import org.springframework.context.annotation.Lazy; |
||||
import org.springframework.context.annotation.Primary; |
||||
import org.springframework.stereotype.Repository; |
||||
|
||||
/** |
||||
* @author Juergen Hoeller |
||||
*/ |
||||
@Repository |
||||
@Primary @Lazy |
||||
public class OtherFooDao implements FooDao { |
||||
|
||||
public String findFoo(int id) { |
||||
return "other"; |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue