(6)Java.c中的LoadMainClass
位置jdk/src/share/bin/java.c
该方法负责加载main函数所在的类。该方法首先加载sun.launcher.LauncherHelper类,然后调用该类的checkAndLoadMain去找到main函数所在的类,相关代码如下。
jclass cls = GetLauncherHelperClass(env);//这个函数加载了sun.launcher.LauncherHelper类
NULL_CHECK0(cls);
if (JLI_IsTraceLauncher()) {
start = CounterGet();
}
NULL_CHECK0(mid = (*env)->GetStaticMethodID(env, cls,//找到方法id
"checkAndLoadMain",
"(ZILjava/lang/String;)Ljava/lang/Class;"));
str = NewPlatformString(env, name);
result = (*env)->CallStaticObjectMethod(env, cls, mid, USE_STDERR, mode, str);//调用这个静态方法
位置:jdk/src/share/bin/classes/sun/launcher/LauncherHelper.java
Java代码比较直观,我就直接写注释了
</pre><p><pre name="code" class="java"> public static Class<?> checkAndLoadMain(boolean printToStderr,
int mode,
String what) {
initOutput(printToStderr);
// mode决定是直接传入的主类还是jar文件,即区分java -jar xxx.jar 还是 java xxx.class
String cn = null;
switch (mode) {
case LM_CLASS:
cn = what;
break;
case LM_JAR:
cn = getMainClassFromJar(what);//这个函数的原理是抽取jar中的mainfest文件得到其主类
break;
default:
// should never happen
throw new InternalError("" + mode + ": Unknown launch mode");
}
cn = cn.replace('/', '.');
Class<?> mainClass = null;
try {
mainClass = scloader.loadClass(cn);//使用scloader加载,如何加载水比较深,这里就不展开说了。
} catch (NoClassDefFoundError | ClassNotFoundException cnfe) {
if (System.getProperty("os.name", "").contains("OS X")
&& Normalizer.isNormalized(cn, Normalizer.Form.NFD)) {
try {
// On Mac OS X since all names with diacretic symbols are given as decomposed it
// is possible that main class name comes incorrectly from the command line
// and we have to re-compose it
mainClass = scloader.loadClass(Normalizer.normalize(cn, Normalizer.Form.NFC));
} catch (NoClassDefFoundError | ClassNotFoundException cnfe1) {
abort(cnfe, "java.launcher.cls.error1", cn);
}
} else {
abort(cnfe, "java.launcher.cls.error1", cn);
}
}
// set to mainClass
appClass = mainClass;
/*
* Check if FXHelper can launch it using the FX launcher. In an FX app,
* the main class may or may not have a main method, so do this before
* validating the main class.
*/
if (mainClass.equals(FXHelper.class) ||
FXHelper.doesExtendFXApplication(mainClass)) {
// Will abort() if there are problems with the FX runtime
FXHelper.setFXLaunchParameters(what, mode);
return FXHelper.class;
}
validateMainClass(mainClass);//这里对主类有效性就行了验证,代码附在后面。
return mainClass;
}
static void validateMainClass(Class<?> mainClass) {
Method mainMethod;
try {
mainMethod = mainClass.getMethod("main", String[].class);//看看是否存在如下签名的方法 main(String[] argvs)
} catch (NoSuchMethodException nsme) {
// invalid main or not FX application, abort with an error
abort(null, "java.launcher.cls.error4", mainClass.getName(),
FXHelper.JAVAFX_APPLICATION_CLASS_NAME);
return; // Avoid compiler issues
}
int mod = mainMethod.getModifiers();//取得限定符
if (!Modifier.isStatic(mod)) {//如果不是static的则报错
abort(null, "java.launcher.cls.error2", "static",
mainMethod.getDeclaringClass().getName());
}
if (mainMethod.getReturnType() != java.lang.Void.TYPE) {
abort(null, "java.launcher.cls.error3",//如果不是void的也报错
mainMethod.getDeclaringClass().getName());
}
}
二、总结
这两篇博客主要分析了执行java命令具体会发生一些什么事情,通过阅读代码可以了解到配置的环境变量如何起作用,jvm何时初始化,以及主类加载及验证过程。
本片博客起码还有以下几点没有深入的分析到:
jvm如何初始化。
env结构和vm结构的关系及实现。
scloader怎么作用。
LauncherHelper如何加载。
这些问题或许会在后面的博客进行分析。