接上一篇,本篇针对ClassUtils.forName()方法进行研究。源码是这样事儿的:
public static Class> forName(String name, ClassLoader classLoader) throws ClassNotFoundException, LinkageError {
Assert.notNull(name, "Name must not be null");
Class> clazz = resolvePrimitiveClassName(name);
if (clazz == null) {
clazz = commonClassCache.get(name);
}
if (clazz != null) {
return clazz;
}
// "java.lang.String[]" style arrays
if (name.endsWith(ARRAY_SUFFIX)) {
String elementClassName = name.substring(0, name.length() - ARRAY_SUFFIX.length());
Class> elementClass = forName(elementClassName, classLoader);
return Array.newInstance(elementClass, 0).getClass();
}
// "[Ljava.lang.String;" style arrays
if (name.startsWith(NON_PRIMITIVE_ARRAY_PREFIX) && name.endsWith(";")) {
String elementName = name.substring(NON_PRIMITIVE_ARRAY_PREFIX.length(), name.length() - 1);
Class> elementClass = forName(elementName, classLoader);
return Array.newInstance(elementClass, 0).getClass();
}
// "[[I" or "[[Ljava.lang.String;" style arrays
if (name.startsWith(INTERNAL_ARRAY_PREFIX)) {
String elementName = name.substring(INTERNAL_ARRAY_PREFIX.length());
Class> elementClass = forName(elementName, classLoader);
return Array.newInstance(elementClass, 0).getClass();
}
ClassLoader clToUse = classLoader;
if (clToUse == null) {
clToUse = getDefaultClassLoader();
}
try {
return (clToUse != null ? clToUse.loadClass(name) : Class.forName(name));
}
catch (ClassNotFoundException ex) {
int lastDotIndex = name.lastIndexOf(PACKAGE_SEPARATOR);
if (lastDotIndex != -1) {
String innerClassName =
name.substring(0, lastDotIndex) + INNER_CLASS_SEPARATOR + name.substring(lastDotIndex + 1);
try {
return (clToUse != null ? clToUse.loadClass(innerClassName) : Class.forName(innerClassName));
}
catch (ClassNotFoundException ex2) {
// Swallow - let original exception get through
}
}
throw ex;
}
}
1.先看这个方法的两个参数,一个是要待获取类的类名:String name,一个是类加载器:ClassLoader classLoader。对于类加载器,从JVM的角度进行一下介绍:众所周知,java的类加载过程可以分为三个大步骤:装载、链接和初始化,类加载器就是来实现类加载的功能的。java为我们提供了三种不同类型的类加载器,BootstrapClassLoader、ExtensionClassLoader和AppClassLoader,三种类加载器分别针对核心类、扩展类和classPath下以及我们自定义的jar进行加载。类加载器根据双亲委派模型进行工作,即当一个类被加载时,先交由父加载器进行,如果父加载器无法找到这个类,再交给子加载器进行加载。当然,我们可以自定义子加载器进行个性化加载。如下图所示。ClassUtils.forName()这个方法通过反射机制实现动态加载,当然要指定一个类加载器了。
2.说完参数,继续往下走。首先映入眼帘的是一个name不能为空的断言,这个也是调用的spring自己封装的断言类Assert。接下来,调用resolvePrimitiveClassName,如果name指定的是java的一个简单类型,那么就根据jvm的命名规则返回对应的类。进入resolvePrimitiveClassName(String name)方法里面,源码在这儿:
public static Class> resolvePrimitiveClassName(String name) {
Class> result = null;
// Most class names will be quite long, considering that they
// SHOULD sit in a package, so a length check is worthwhile.
if (name != null && name.length() <= 8) {
// Could be a primitive - likely.
result = primitiveTypeNameMap.get(name);
}
return result;
}
可以看到,有一个很巧妙的地方,因为java中大多数的类名加上包名都很长,而简单类型的相对要短很多,因此这个方法先根据name的长度对大多数的类进行过滤;接着就是从缓存里取值——primitiveTypeNameMap.get(name)。primitiveTypeNameMap是ClassUtils维护的一个map,用于存储涉及简单类型的所有的类和名称的关系映射,该map存储东西的源码如下:
static {
primitiveWrapperTypeMap.put(Boolean.class, boolean.class);
primitiveWrapperTypeMap.put(Byte.class, byte.class);
primitiveWrapperTypeMap.put(Character.class, char.class);
primitiveWrapperTypeMap.put(Double.class, double.class);
primitiveWrapperTypeMap.put(Float.class, float.class);
primitiveWrapperTypeMap.put(Integer.class, int.class);
primitiveWrapperTypeMap.put(Long.class, long.class);
primitiveWrapperTypeMap.put(Short.class, short.class);
for (Map.Entry, Class>> entry : primitiveWrapperTypeMap.entrySet()) {
primitiveTypeToWrapperMap.put(entry.getValue(), entry.getKey());
registerCommonClasses(entry.getKey());
}
Set> primitiveTypes = new HashSet>(32);
primitiveTypes.addAll(primitiveWrapperTypeMap.values());
primitiveTypes.addAll(Arrays.asList(new Class>[] {
boolean[].class, byte[].class, char[].class, double[].class,
float[].class, int[].class, long[].class, short[].class}));
primitiveTypes.add(void.class);
for (Class> primitiveType : primitiveTypes) {
primitiveTypeNameMap.put(primitiveType.getName(), primitiveType);
}
打断点调试可以看到,primitiveTypeNameMap存放的内容是这个:{void=void, double=double, byte=byte, [B=class [B, [C=class [C, [D=class [D, [F=class [F, float=float, int=int, long=long, [I=class [I, [J=class [J, boolean=boolean, char=char, [S=class [S, short=short, [Z=class [Z},我们可以看到,除了存放简单类型,还存放了简单类型的数组([C等类似表达是JNI字段描述符的表达,比如[C代表char[],下图是对应表)。
3.显然,javax.inject.provider不是简单类型,接着往下走,如果不是基本类型,就会从commonClassCache里去取值,commonClassCathe存储了所有Java.lang包中的类,显而易见我们这个不在lang包里,继续往下走。
4.对类是否为数组对象、二进制数组对象和二维数组对象进行判断,显然都不是,那么断点来到这里:
ClassLoader clToUse = classLoader;
搞一个类加载器。继续往下走,如果传进来的类加载器是null,就弄一个默认的类加载器,进方法看看:
public static ClassLoader getDefaultClassLoader() {
ClassLoader cl = null;
try {
cl = Thread.currentThread().getContextClassLoader();
}
catch (Throwable ex) {
// Cannot access thread context ClassLoader - falling back...
}
if (cl == null) {
// No thread context class loader -> use class loader of this class.
cl = ClassUtils.class.getClassLoader();
if (cl == null) {
// getClassLoader() returning null indicates the bootstrap ClassLoader
try {
cl = ClassLoader.getSystemClassLoader();
}
catch (Throwable ex) {
// Cannot access system ClassLoader - oh well, maybe the caller can live with null...
}
}
}
return cl;
}
如果可以的话,先返回contextClassLoader,也就是先默认获取线程上下文类加载器,如果当前线程的如果拿不到,就返回加载ClassUtils的加载器,如果还拿不到,就返回java提供的systemClassLoader。接着回去看。我们调用forName方法的时候传进去的classLoader是这个:sun.misc.Launcher$AppClassLoader@18b4aac2,这个是AppclassLoader,然后发现,javax.inject.provider无法通过AppclassLoader拿到,报ClassNotFoundException异常,catch到以后接着往下走。
5.catch到以后,程序就将name代表的class作为内部类进行处理,也就是说name变为javax.inject$Provider,然后调用classloader传入的加载器对其进行加载,也就是AppCLassLoader;最后抛出异常,由上一层继续捕捉,至此ClassUtils.forName()执行完毕。