Spring源码学习(四)——ClassUtils.forName()

    接上一篇,本篇针对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()这个方法通过反射机制实现动态加载,当然要指定一个类加载器了。

Spring源码学习(四)——ClassUtils.forName()_第1张图片

    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[],下图是对应表)。

Spring源码学习(四)——ClassUtils.forName()_第2张图片

    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()执行完毕。

你可能感兴趣的:(#,Spring源码学习)