在Spring获取Context的源代码中,我们看到了对ClassUtil的方法调用,通过给定ClassName和ClassLoader进行Class的加载:
if (contextClassName != null) {
try {
return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load custom context class [" + contextClassName + "]", ex);
}
}
ClassUtil.forName()
ClassUtil.forName是仅供于Spring内部使用的获取Class对象的方法,来看一下源码:
public static Class<?> forName(String name, @Nullable ClassLoader classLoader)
throws ClassNotFoundException, LinkageError {
Assert.notNull(name, "Name must not be null");
//缓存的name.length<=7简单类class,里面会先对长度做判断
Class<?> clazz = resolvePrimitiveClassName(name);
if (clazz == null) {
//java.lang.*等其他简单类class
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 Class.forName(name, false, clToUse);
}
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 Class.forName(innerClassName, false, clToUse);
}
catch (ClassNotFoundException ex2) {
// Swallow - let original exception get through
}
}
throw ex;
}
}
首先 对于缓存的Class一块,在类的静态块中就能看出其逻辑:
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);
primitiveWrapperTypeMap.put(Void.class, void.class);
// Map entry iteration is less expensive to initialize than forEach with lambdas
for (Map.Entry<Class<?>, Class<?>> entry : primitiveWrapperTypeMap.entrySet()) {
primitiveTypeToWrapperMap.put(entry.getValue(), entry.getKey());
registerCommonClasses(entry.getKey());
}
Set<Class<?>> primitiveTypes = new HashSet<>(32);
primitiveTypes.addAll(primitiveWrapperTypeMap.values());
Collections.addAll(primitiveTypes, boolean[].class, byte[].class, char[].class,
double[].class, float[].class, int[].class, long[].class, short[].class);
for (Class<?> primitiveType : primitiveTypes) {
primitiveTypeNameMap.put(primitiveType.getName(), primitiveType);
}
//这个方法的实现略,是将传入的对象放入commonClassCache中
registerCommonClasses(Boolean[].class, Byte[].class, Character[].class, Double[].class,
Float[].class, Integer[].class, Long[].class, Short[].class);
registerCommonClasses(Number.class, Number[].class, String.class, String[].class,
Class.class, Class[].class, Object.class, Object[].class);
registerCommonClasses(Throwable.class, Exception.class, RuntimeException.class,
Error.class, StackTraceElement.class, StackTraceElement[].class);
registerCommonClasses(Enum.class, Iterable.class, Iterator.class, Enumeration.class,
Collection.class, List.class, Set.class, Map.class, Map.Entry.class, Optional.class);
Class<?>[] javaLanguageInterfaceArray = {Serializable.class, Externalizable.class,
Closeable.class, AutoCloseable.class, Cloneable.class, Comparable.class};
registerCommonClasses(javaLanguageInterfaceArray);
javaLanguageInterfaces = new HashSet<>(Arrays.asList(javaLanguageInterfaceArray));
}
在上面的resolvePrimitiveClassName方法中,先对长度做了一个判断,因为较长的packagename会影响执行的性能:
@Nullable
public static Class<?> resolvePrimitiveClassName(@Nullable 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() <= 7) {
// Could be a primitive - likely.
result = primitiveTypeNameMap.get(name);
}
return result;
}
ContextClassLoader
最终加载Class依旧是通过ClassLoader的,先来看一下获取ClassLoader的方法实现:
public static ClassLoader getDefaultClassLoader() {
ClassLoader cl = null;
try {
//优先获取ContextClassLoader
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.
//获取类本身的ClassLoader
cl = ClassUtils.class.getClassLoader();
if (cl == null) {
// getClassLoader() returning null indicates the bootstrap ClassLoader
try {
//全都没有 则获取SystemClassLoader(默认是AppClassLoader)
cl = ClassLoader.getSystemClassLoader();
}
catch (Throwable ex) {
// Cannot access system ClassLoader - oh well, maybe the caller can live with null...
}
}
}
return cl;
}
此处优先使用了ContextClassLoader作为
此外,ContextClassLoader的读取策略是默认读取父线程的(即创建当前线程的线程)contextClassLoader,当然 这是可以覆盖的,在由父线程调用的init方法中可以看到:
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
//…………部分代码略
//读取父线程的contextClassLoader
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
//…………部分代码略
}
SPI和JNDI
ContextClassLoader其实并不是为我们日常开发提供的,JDK设计ContextClassLoader起初是由于
Connection conn=null;
try {
//手动加载驱动类,对应不同数据库其驱动也不相同
Class.forName("com.mysql.jdbc.Driver",
true, Thread.currentThread().getContextClassLoader());
conn=DriverManager.getConnection("jdbc:mysql://dbserver?user=dolly&password=dagger");
/* use the connection here */
c.close();
}
catch(Exception e) {
e.printStackTrace();
}
finally {
if(conn!=null) {
try {
conn.close();
} catch(SQLException e) {}
}
}
在JDK中,想要在没有AppClassLoader实现的环境下进行jdbc的加载,就需要用到ContextClassLoader了。所以在DriverManager的获取数据库连接方法中:
private static Connection getConnection(
String url, java.util.Properties info, Class<?> caller) throws SQLException {
/*
* When callerCl is null, we should check the application's
* (which is invoking this class indirectly)
* classloader, so that the JDBC driver class outside rt.jar
* can be loaded from here.
*/
ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
synchronized(DriverManager.class) {
// synchronize loading of the correct classloader.
if (callerCL == null) {
//使用ContextClassLoader
callerCL = Thread.currentThread().getContextClassLoader();
}
}
//……
}

