Consolidated Util and MutableAnnotationUtils classes into existing AsmUtils

This commit is contained in:
Chris Beams
2009-03-23 07:01:01 +00:00
parent 7f96f57375
commit 22b25e0d7b
14 changed files with 162 additions and 300 deletions

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -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))

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);

View File

@@ -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 {

View File

@@ -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);
}

View File

@@ -37,7 +37,7 @@ import org.springframework.util.StringUtils;
*
* @author Chris Beams
* @see MutableAnnotation
* @see MutableAnnotationUtils
* @see AsmUtils#createMutableAnnotation
*/
final class MutableAnnotationInvocationHandler implements InvocationHandler {

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -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;
}
}