Consolidated Util and MutableAnnotationUtils classes into existing AsmUtils
This commit is contained in:
@@ -37,7 +37,7 @@ class AnnotationAdapter implements AnnotationVisitor {
|
||||
* <var>delegate</var>.
|
||||
*
|
||||
* @param delegate In most cases, the delegate will simply be
|
||||
* {@link AsmUtils#EMPTY_VISITOR}
|
||||
* {@link AsmUtils#ASM_EMPTY_VISITOR}
|
||||
*/
|
||||
public AnnotationAdapter(AnnotationVisitor delegate) {
|
||||
this.delegate = delegate;
|
||||
|
||||
@@ -16,46 +16,42 @@
|
||||
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
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.
|
||||
* Various utility methods commonly used when interacting with ASM, classloading
|
||||
* and creating {@link MutableAnnotation} instances.
|
||||
*
|
||||
* @author Chris Beams
|
||||
*/
|
||||
class AsmUtils {
|
||||
|
||||
public static final EmptyVisitor EMPTY_VISITOR = new EmptyVisitor();
|
||||
public static final EmptyVisitor ASM_EMPTY_VISITOR = new EmptyVisitor();
|
||||
|
||||
private static final Log log = LogFactory.getLog(AsmUtils.class);
|
||||
|
||||
/**
|
||||
* @param className a standard, dot-delimeted, fully-qualified Java class name
|
||||
* @return internal version of className, as per ASM guide section 2.1.2
|
||||
* "Internal Names"
|
||||
*/
|
||||
public static String convertClassNameToInternalName(String className) {
|
||||
return className.replace('.', '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 convertTypeDescriptorToClassName(String typeDescriptor) {
|
||||
public static String convertAsmTypeDescriptorToClassName(String typeDescriptor) {
|
||||
final String internalName; // See ASM guide section 2.1.2
|
||||
|
||||
// TODO: SJC-242 should catch all possible cases. use case statement and switch on
|
||||
// char
|
||||
// TODO: SJC-242 converting from primitive to object here won't be intuitive to
|
||||
// users
|
||||
if ("V".equals(typeDescriptor))
|
||||
return Void.class.getName();
|
||||
if ("I".equals(typeDescriptor))
|
||||
@@ -84,9 +80,9 @@ class AsmUtils {
|
||||
/**
|
||||
* @param methodDescriptor see ASM guide section 2.1.4
|
||||
*/
|
||||
public static String getReturnTypeFromMethodDescriptor(String methodDescriptor) {
|
||||
public static String getReturnTypeFromAsmMethodDescriptor(String methodDescriptor) {
|
||||
String returnTypeDescriptor = methodDescriptor.substring(methodDescriptor.indexOf(')') + 1);
|
||||
return convertTypeDescriptorToClassName(returnTypeDescriptor);
|
||||
return convertAsmTypeDescriptorToClassName(returnTypeDescriptor);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,22 +93,9 @@ class AsmUtils {
|
||||
* classpath
|
||||
* @throws RuntimeException if an IOException occurs when creating the new ClassReader
|
||||
*/
|
||||
public static ClassReader newClassReader(String pathToClass, ClassLoader classLoader) {
|
||||
InputStream is = Util.getClassAsStream(pathToClass, classLoader);
|
||||
return newClassReader(is);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method that simply returns a new ASM {@link ClassReader} instance based
|
||||
* on the supplied <var>bytes</var> byte array. This method is exactly equivalent to
|
||||
* calling new ClassReader(byte[]), and is mainly provided for symmetry with usage of
|
||||
* {@link #newClassReader(InputStream)}.
|
||||
*
|
||||
* @param bytes byte array that will be provided as input to the new ClassReader
|
||||
* instance.
|
||||
*/
|
||||
public static ClassReader newClassReader(byte[] bytes) {
|
||||
return new ClassReader(bytes);
|
||||
public static ClassReader newAsmClassReader(String pathToClass, ClassLoader classLoader) {
|
||||
InputStream is = getClassAsStream(pathToClass, classLoader);
|
||||
return newAsmClassReader(is);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,7 +107,7 @@ class AsmUtils {
|
||||
*
|
||||
* @param is InputStream that will be provided to the new ClassReader instance.
|
||||
*/
|
||||
public static ClassReader newClassReader(InputStream is) {
|
||||
public static ClassReader newAsmClassReader(InputStream is) {
|
||||
try {
|
||||
return new ClassReader(is);
|
||||
} catch (IOException ex) {
|
||||
@@ -138,4 +121,108 @@ class AsmUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import static org.springframework.context.annotation.support.AsmUtils.*;
|
||||
import static org.springframework.context.annotation.support.MutableAnnotationUtils.*;
|
||||
import static org.springframework.context.annotation.support.Util.*;
|
||||
import static org.springframework.util.ClassUtils.*;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
@@ -62,7 +60,7 @@ class ConfigurationClassMethodVisitor extends MethodAdapter {
|
||||
*/
|
||||
public ConfigurationClassMethodVisitor(ConfigurationClass configClass, String methodName,
|
||||
String methodDescriptor, int modifiers, ClassLoader classLoader) {
|
||||
super(AsmUtils.EMPTY_VISITOR);
|
||||
super(ASM_EMPTY_VISITOR);
|
||||
|
||||
this.configClass = configClass;
|
||||
this.methodName = methodName;
|
||||
@@ -77,14 +75,14 @@ class ConfigurationClassMethodVisitor extends MethodAdapter {
|
||||
*/
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String annoTypeDesc, boolean visible) {
|
||||
String annoClassName = AsmUtils.convertTypeDescriptorToClassName(annoTypeDesc);
|
||||
String annoClassName = convertAsmTypeDescriptorToClassName(annoTypeDesc);
|
||||
|
||||
Class<? extends Annotation> annoClass = loadToolingSafeClass(annoClassName, classLoader);
|
||||
|
||||
if (annoClass == null)
|
||||
return super.visitAnnotation(annoTypeDesc, visible);
|
||||
|
||||
Annotation annotation = createMutableAnnotation(annoClass);
|
||||
Annotation annotation = createMutableAnnotation(annoClass, classLoader);
|
||||
|
||||
annotations.add(annotation);
|
||||
|
||||
@@ -126,11 +124,11 @@ class ConfigurationClassMethodVisitor extends MethodAdapter {
|
||||
* that type is an interface.
|
||||
*/
|
||||
private ModelClass initReturnTypeFromMethodDescriptor(String methodDescriptor) {
|
||||
final ModelClass returnType = new ModelClass(getReturnTypeFromMethodDescriptor(methodDescriptor));
|
||||
final ModelClass returnType = new ModelClass(getReturnTypeFromAsmMethodDescriptor(methodDescriptor));
|
||||
|
||||
// detect whether the return type is an interface
|
||||
newClassReader(convertClassNameToResourcePath(returnType.getName()), classLoader).accept(
|
||||
new ClassAdapter(AsmUtils.EMPTY_VISITOR) {
|
||||
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);
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import static java.lang.String.*;
|
||||
import static org.springframework.context.annotation.support.MutableAnnotationUtils.*;
|
||||
import static org.springframework.context.annotation.support.AsmUtils.*;
|
||||
import static org.springframework.util.ClassUtils.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -60,7 +60,7 @@ class ConfigurationClassVisitor extends ClassAdapter {
|
||||
|
||||
public ConfigurationClassVisitor(ConfigurationClass configClass, ConfigurationModel model,
|
||||
ProblemReporter problemReporter, ClassLoader classLoader) {
|
||||
super(AsmUtils.EMPTY_VISITOR);
|
||||
super(ASM_EMPTY_VISITOR);
|
||||
this.configClass = configClass;
|
||||
this.model = model;
|
||||
this.problemReporter = problemReporter;
|
||||
@@ -101,7 +101,7 @@ class ConfigurationClassVisitor extends ClassAdapter {
|
||||
ConfigurationClassVisitor visitor =
|
||||
new ConfigurationClassVisitor(configClass, model, problemReporter, classLoader);
|
||||
|
||||
ClassReader reader = AsmUtils.newClassReader(superTypeDesc, classLoader);
|
||||
ClassReader reader = newAsmClassReader(superTypeDesc, classLoader);
|
||||
reader.accept(visitor, false);
|
||||
}
|
||||
|
||||
@@ -118,10 +118,10 @@ class ConfigurationClassVisitor extends ClassAdapter {
|
||||
*/
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String annoTypeDesc, boolean visible) {
|
||||
String annoTypeName = AsmUtils.convertTypeDescriptorToClassName(annoTypeDesc);
|
||||
String annoTypeName = convertAsmTypeDescriptorToClassName(annoTypeDesc);
|
||||
|
||||
if (Configuration.class.getName().equals(annoTypeName)) {
|
||||
Configuration mutableConfiguration = createMutableAnnotation(Configuration.class);
|
||||
Configuration mutableConfiguration = createMutableAnnotation(Configuration.class, classLoader);
|
||||
configClass.setConfigurationAnnotation(mutableConfiguration);
|
||||
return new MutableAnnotationVisitor(mutableConfiguration, classLoader);
|
||||
}
|
||||
@@ -182,7 +182,7 @@ class ConfigurationClassVisitor extends ClassAdapter {
|
||||
new ConfigurationClassVisitor(innerConfigClass, new ConfigurationModel(), problemReporter, classLoader);
|
||||
ccVisitor.setProcessInnerClasses(false);
|
||||
|
||||
ClassReader reader = AsmUtils.newClassReader(name, classLoader);
|
||||
ClassReader reader = newAsmClassReader(name, classLoader);
|
||||
reader.accept(ccVisitor, false);
|
||||
|
||||
if (innerClasses.containsKey(outerName))
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import static java.lang.String.*;
|
||||
import static org.springframework.context.annotation.support.Util.*;
|
||||
import static org.springframework.context.annotation.support.AsmUtils.*;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import static org.springframework.context.annotation.support.AsmUtils.*;
|
||||
|
||||
import org.springframework.asm.ClassReader;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||
@@ -71,7 +73,7 @@ public class ConfigurationParser {
|
||||
|
||||
String resourcePath = ClassUtils.convertClassNameToResourcePath(className);
|
||||
|
||||
ClassReader configClassReader = AsmUtils.newClassReader(Util.getClassAsStream(resourcePath, classLoader));
|
||||
ClassReader configClassReader = newAsmClassReader(getClassAsStream(resourcePath, classLoader));
|
||||
|
||||
ConfigurationClass configClass = new ConfigurationClass();
|
||||
configClass.setBeanName(configurationId);
|
||||
|
||||
@@ -49,7 +49,7 @@ class ImportAnnotationVisitor extends AnnotationAdapter {
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
public ImportAnnotationVisitor(ConfigurationModel model, ProblemReporter problemReporter, ClassLoader classLoader) {
|
||||
super(AsmUtils.EMPTY_VISITOR);
|
||||
super(ASM_EMPTY_VISITOR);
|
||||
this.model = model;
|
||||
this.problemReporter = problemReporter;
|
||||
this.classLoader = classLoader;
|
||||
@@ -60,7 +60,7 @@ class ImportAnnotationVisitor extends AnnotationAdapter {
|
||||
Assert.isTrue("value".equals(attribName),
|
||||
format("expected 'value' attribute, got unknown '%s' attribute", attribName));
|
||||
|
||||
return new AnnotationAdapter(AsmUtils.EMPTY_VISITOR) {
|
||||
return new AnnotationAdapter(ASM_EMPTY_VISITOR) {
|
||||
@Override
|
||||
public void visit(String na, Object type) {
|
||||
Assert.isInstanceOf(Type.class, type);
|
||||
@@ -80,7 +80,7 @@ class ImportAnnotationVisitor extends AnnotationAdapter {
|
||||
private void processClassToImport(String classToImport) {
|
||||
ConfigurationClass configClass = new ConfigurationClass();
|
||||
|
||||
ClassReader reader = newClassReader(convertClassNameToResourcePath(classToImport), classLoader);
|
||||
ClassReader reader = newAsmClassReader(convertClassNameToResourcePath(classToImport), classLoader);
|
||||
|
||||
reader.accept(new ConfigurationClassVisitor(configClass, model, problemReporter, classLoader), false);
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
*
|
||||
* <p>Note: the visibility of this interface would be reduced to package-private save for an
|
||||
* obscure restriction of JDK dynamic proxies.
|
||||
* {@link MutableAnnotationUtils#createMutableAnnotation(Class)} creates a proxy based on
|
||||
* {@link AsmUtils#createMutableAnnotation} creates a proxy based on
|
||||
* two interfaces: this one, and whatever annotation is currently being parsed. The
|
||||
* restriction is that both interfaces may not be package-private if they are in separate
|
||||
* packages. In order to avoid unnecessarily restricting the visibility options for
|
||||
@@ -39,9 +39,10 @@ import org.springframework.context.annotation.Configuration;
|
||||
* not to use this annotation outside this package.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @see MutableAnnotationUtils
|
||||
* @since 3.0
|
||||
* @see MutableAnnotationVisitor
|
||||
* @see MutableAnnotationInvocationHandler
|
||||
* @see AsmUtils#createMutableAnnotation
|
||||
*/
|
||||
public interface MutableAnnotation {
|
||||
|
||||
|
||||
@@ -16,8 +16,7 @@
|
||||
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import static org.springframework.context.annotation.support.MutableAnnotationUtils.*;
|
||||
import static org.springframework.context.annotation.support.Util.*;
|
||||
import static org.springframework.context.annotation.support.AsmUtils.*;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Array;
|
||||
@@ -32,7 +31,7 @@ import org.springframework.asm.AnnotationVisitor;
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @see MutableAnnotation
|
||||
* @see MutableAnnotationUtils
|
||||
* @see AsmUtils#createMutableAnnotation
|
||||
*/
|
||||
class MutableAnnotationArrayVisitor extends AnnotationAdapter {
|
||||
|
||||
@@ -43,7 +42,7 @@ class MutableAnnotationArrayVisitor extends AnnotationAdapter {
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
public MutableAnnotationArrayVisitor(MutableAnnotation mutableAnno, String attribName, ClassLoader classLoader) {
|
||||
super(AsmUtils.EMPTY_VISITOR);
|
||||
super(AsmUtils.ASM_EMPTY_VISITOR);
|
||||
|
||||
this.mutableAnno = mutableAnno;
|
||||
this.attribName = attribName;
|
||||
@@ -57,13 +56,13 @@ class MutableAnnotationArrayVisitor extends AnnotationAdapter {
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String na, String annoTypeDesc) {
|
||||
String annoTypeName = AsmUtils.convertTypeDescriptorToClassName(annoTypeDesc);
|
||||
String annoTypeName = convertAsmTypeDescriptorToClassName(annoTypeDesc);
|
||||
Class<? extends Annotation> annoType = loadToolingSafeClass(annoTypeName, classLoader);
|
||||
|
||||
if (annoType == null)
|
||||
return super.visitAnnotation(na, annoTypeDesc);
|
||||
|
||||
Annotation anno = createMutableAnnotation(annoType);
|
||||
Annotation anno = createMutableAnnotation(annoType, classLoader);
|
||||
values.add(anno);
|
||||
return new MutableAnnotationVisitor(anno, classLoader);
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @see MutableAnnotation
|
||||
* @see MutableAnnotationUtils
|
||||
* @see AsmUtils#createMutableAnnotation
|
||||
*/
|
||||
final class MutableAnnotationInvocationHandler implements InvocationHandler {
|
||||
|
||||
|
||||
@@ -1,43 +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.support;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Proxy;
|
||||
|
||||
|
||||
class MutableAnnotationUtils {
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
MutableAnnotationInvocationHandler handler = new MutableAnnotationInvocationHandler(annoType);
|
||||
ClassLoader classLoader = MutableAnnotationUtils.class.getClassLoader();
|
||||
Class<?>[] interfaces = new Class<?>[] { annoType, MutableAnnotation.class };
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
A mutableAnno = (A) Proxy.newProxyInstance(classLoader, interfaces, handler);
|
||||
return mutableAnno;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,8 +16,7 @@
|
||||
|
||||
package org.springframework.context.annotation.support;
|
||||
|
||||
import static org.springframework.context.annotation.support.MutableAnnotationUtils.*;
|
||||
import static org.springframework.context.annotation.support.Util.*;
|
||||
import static org.springframework.context.annotation.support.AsmUtils.*;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
@@ -34,7 +33,7 @@ import org.springframework.util.Assert;
|
||||
* @author Chris Beams
|
||||
* @see MutableAnnotation
|
||||
* @see MutableAnnotationInvocationHandler
|
||||
* @see MutableAnnotationUtils
|
||||
* @see AsmUtils#createMutableAnnotation
|
||||
*/
|
||||
class MutableAnnotationVisitor implements AnnotationVisitor {
|
||||
|
||||
@@ -52,7 +51,7 @@ class MutableAnnotationVisitor implements AnnotationVisitor {
|
||||
* @throws IllegalArgumentException if <var>mutableAnno</var> is not of type
|
||||
* {@link MutableAnnotation}
|
||||
*
|
||||
* @see MutableAnnotationUtils#createMutableAnnotation(Class)
|
||||
* @see AsmUtils#createMutableAnnotation
|
||||
*/
|
||||
public MutableAnnotationVisitor(Annotation mutableAnno, ClassLoader classLoader) {
|
||||
Assert.isInstanceOf(MutableAnnotation.class, mutableAnno, "annotation must be mutable");
|
||||
@@ -86,7 +85,7 @@ class MutableAnnotationVisitor implements AnnotationVisitor {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void visitEnum(String attribName, String enumTypeDescriptor, String strEnumValue) {
|
||||
String enumClassName = AsmUtils.convertTypeDescriptorToClassName(enumTypeDescriptor);
|
||||
String enumClassName = convertAsmTypeDescriptorToClassName(enumTypeDescriptor);
|
||||
|
||||
Class<? extends Enum> enumClass = loadToolingSafeClass(enumClassName, classLoader);
|
||||
|
||||
@@ -98,13 +97,13 @@ class MutableAnnotationVisitor implements AnnotationVisitor {
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitAnnotation(String attribName, String attribAnnoTypeDesc) {
|
||||
String annoTypeName = AsmUtils.convertTypeDescriptorToClassName(attribAnnoTypeDesc);
|
||||
String annoTypeName = convertAsmTypeDescriptorToClassName(attribAnnoTypeDesc);
|
||||
Class<? extends Annotation> annoType = loadToolingSafeClass(annoTypeName, classLoader);
|
||||
|
||||
if (annoType == null)
|
||||
return AsmUtils.EMPTY_VISITOR.visitAnnotation(attribName, attribAnnoTypeDesc);
|
||||
return ASM_EMPTY_VISITOR.visitAnnotation(attribName, attribAnnoTypeDesc);
|
||||
|
||||
Annotation anno = createMutableAnnotation(annoType);
|
||||
Annotation anno = createMutableAnnotation(annoType, classLoader);
|
||||
|
||||
try {
|
||||
Field attribute = mutableAnno.getClass().getField(attribName);
|
||||
|
||||
@@ -1,179 +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.support;
|
||||
|
||||
import static java.lang.String.*;
|
||||
import static org.springframework.util.ClassUtils.*;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import sun.security.x509.Extension;
|
||||
|
||||
|
||||
/**
|
||||
* Misc utils
|
||||
*
|
||||
* @author Chris Beams
|
||||
*/
|
||||
class Util {
|
||||
|
||||
private static final Log log = LogFactory.getLog(Util.class);
|
||||
|
||||
private Util() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns instance of type T by invoking its default or no-arg constructor.
|
||||
* <p>
|
||||
* Any reflection-related issues are re-thrown as unchecked.
|
||||
*/
|
||||
public static <T> T getInstance(Class<? extends T> clazz) {
|
||||
try {
|
||||
Constructor<? extends T> noArgCtor = clazz.getDeclaredConstructor();
|
||||
ReflectionUtils.makeAccessible(noArgCtor);
|
||||
return noArgCtor.newInstance();
|
||||
} catch (Exception ex) {
|
||||
ReflectionUtils.handleReflectionException(ex);
|
||||
throw new IllegalStateException(format("Unexpected reflection exception - %s: %s", ex.getClass()
|
||||
.getName(), ex.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the specified class using the default class loader, gracefully handling any
|
||||
* {@link ClassNotFoundException} that may be thrown. This functionality is specifically
|
||||
* implemented to accomodate tooling (Spring IDE) concerns, where user-defined types
|
||||
* will not be
|
||||
*
|
||||
* @param <T> type of class to be returned
|
||||
* @param fqClassName fully-qualified class name
|
||||
*
|
||||
* @return newly loaded class instance, null if class could not be found
|
||||
*
|
||||
* @see #loadRequiredClass(String)
|
||||
* @see #loadToolingSafeClass(String)
|
||||
* @see ClassUtils#getDefaultClassLoader()
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Class<? extends T> loadClass(String fqClassName) {
|
||||
try {
|
||||
return (Class<? extends T>) ClassUtils.getDefaultClassLoader().loadClass(fqClassName);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* ASM class reading is used throughout JavaConfig, but there are certain cases where
|
||||
* classloading cannot be avoided - specifically in cases where users define their own
|
||||
* {@link Extension} annotations. This method should therefore be
|
||||
* used sparingly but consistently where required.
|
||||
* <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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user