《jdk8u源码分析》5.SelectVersion
作者:互联网
C Language Reference > Parsing C Command-Line Arguments
src/share/bin/java.c::SelectVersion
/*
* The SelectVersion() routine ensures that an appropriate version of
* the JRE is running. The specification for the appropriate version
* is obtained from either the manifest of a jar file (preferred) or
* from command line options.
* The routine also parses splash screen command line options and
* passes on their values in private environment variables.
*/
static void
SelectVersion(int argc, char **argv, char **main_class)
{
char *arg;
char **new_argv;
char **new_argp;
char *operand;
char *version = NULL;
char *jre = NULL;
int jarflag = 0;
int headlessflag = 0;
int restrict_search = -1; /* -1 implies not known */
manifest_info info;
char env_entry[MAXNAMELEN + 24] = ENV_ENTRY "=";
char *splash_file_name = NULL;
char *splash_jar_name = NULL;
char *env_in;
int res;
/*
* If the version has already been selected, set *main_class
* with the value passed through the environment (if any) and
* simply return.
*/
//该环节变量_JAVA_VERSION_SET存储jar中META-INF/MANIFEST.MF文件的Main-Class,避免重复读取
if ((env_in = getenv(ENV_ENTRY)) != NULL) {
if (*env_in != '\0')
*main_class = JLI_StringDup(env_in);
return;
}
/*
* Scan through the arguments for options relevant to multiple JRE
* support. For reference, the command line syntax is defined as:
*
* SYNOPSIS
* java [options] class [argument...]
*
* java [options] -jar file.jar [argument...]
*
* As the scan is performed, make a copy of the argument list with
* the version specification options (new to 1.5) removed, so that
* a version less than 1.5 can be exec'd.
*
* Note that due to the syntax of the native Windows interface
* CreateProcess(), processing similar to the following exists in
* the Windows platform specific routine ExecJRE (in java_md.c).
* Changes here should be reproduced there.
*/
//过滤命令行参数保存到新的变量中,最后替换原有变量
//移除-version, -jre-restrict-search, -no-jre-restrict-search
//设置version, restrict_search, jarflag, headlessflag, splash_file_name等变量的值
new_argv = JLI_MemAlloc((argc + 1) * sizeof(char*));
new_argv[0] = argv[0];
new_argp = &new_argv[1];
argc--;
argv++;
while ((arg = *argv) != 0 && *arg == '-') {
if (JLI_StrCCmp(arg, "-version:") == 0) {
version = arg + 9;
} else if (JLI_StrCmp(arg, "-jre-restrict-search") == 0) {
restrict_search = 1;//在版本搜索中包括/排除用户专用JRE(已过时)
} else if (JLI_StrCmp(arg, "-no-jre-restrict-search") == 0) {
restrict_search = 0;
} else {
//判断是否是从jar包启动
if (JLI_StrCmp(arg, "-jar") == 0)
jarflag = 1;
/* deal with "unfortunate" classpath syntax */
//argv[7] = -classpath
//argv[8] = xx1.jar;xx2.jar;...
//argv[9] = com.johnjoe.study.Test
//argv[10] = -cp
//argv[11] = xxx.jar
//因为argv中-classpath和-cp的特殊处理,参考MSDN《C Language Reference》
if ((JLI_StrCmp(arg, "-classpath") == 0 || JLI_StrCmp(arg, "-cp") == 0) &&
(argc >= 2)) {
*new_argp++ = arg;//存储-classpath | -cp
//命令行参数自减,参数数组指向下一个元素,即jar包目录
//赋值给arg, 下面会继续执行:*new_argp++ = arg;
argc--;
argv++;
arg = *argv;
}
/*
* Checking for headless toolkit option in the some way as AWT does:
* "true" means true and any other value means false
*/
//Headless模式是系统的一种配置模式。在该模式下,系统缺少了显示设备、键盘或鼠标。
if (JLI_StrCmp(arg, "-Djava.awt.headless=true") == 0) {
headlessflag = 1;
} else if (JLI_StrCCmp(arg, "-Djava.awt.headless=") == 0) {
headlessflag = 0;
} else if (JLI_StrCCmp(arg, "-splash:") == 0) {
splash_file_name = arg+8;
}
*new_argp++ = arg;
}
argc--;
argv++;
}
//-jar语法:命令与参数之间有一个空格,如果是jar包启动此处可获取到jar的完全路径
if (argc <= 0) { /* No operand? Possibly legit with -[full]version */
operand = NULL;
} else {
argc--;
*new_argp++ = operand = *argv++;
}
//处理剩余的参数,即Java main函数的入参,个人觉得有点多余,最后置空
while (argc-- > 0) /* Copy over [argument...] */
*new_argp++ = *argv++;
*new_argp = NULL;
/*
* If there is a jar file, read the manifest. If the jarfile can't be
* read, the manifest can't be read from the jar file, or the manifest
* is corrupt, issue the appropriate error messages and exit.
*
* Even if there isn't a jar file, construct a manifest_info structure
* containing the command line information. It's a convenient way to carry
* this data around.
*/
//如果是jar包启动,并且jar路径不为空,读取MANIFEST.MF文件信息到info中
if (jarflag && operand) {
if ((res = JLI_ParseManifest(operand, &info)) != 0) {
if (res == -1)
//无法访问jar file
JLI_ReportErrorMessage(JAR_ERROR2, operand);
else
//jar file无效或已损坏
JLI_ReportErrorMessage(JAR_ERROR3, operand);
exit(1);
}
/*
* Command line splash screen option should have precedence
* over the manifest, so the manifest data is used only if
* splash_file_name has not been initialized above during command
* line parsing
*/
//根据配置决定是否设置闪屏图片(即启动界面图片,例如:AWT、android)
//设置窗体查询启动jar
if (!headlessflag && !splash_file_name && info.splashscreen_image_file_name) {
splash_file_name = info.splashscreen_image_file_name;
splash_jar_name = operand;
}
} else {
//非jar包启动置空
info.manifest_version = NULL;
info.main_class = NULL;
info.jre_version = NULL;
info.jre_restrict_search = 0;
}
/*
* Passing on splash screen info in environment variables
*/
//根据配置决定是否设置闪屏图片,如果需要,添加环境变量:_JAVA_SPLASH_FILE,变量在JVMInit中的ShowSplashScreen会用到
if (splash_file_name && !headlessflag) {
char* splash_file_entry = JLI_MemAlloc(JLI_StrLen(SPLASH_FILE_ENV_ENTRY "=")+JLI_StrLen(splash_file_name)+1);
JLI_StrCpy(splash_file_entry, SPLASH_FILE_ENV_ENTRY "=");
JLI_StrCat(splash_file_entry, splash_file_name);
putenv(splash_file_entry);
}
//根据配置决定是否设置窗体查询启动jar,如果需要,添加环境变量:_JAVA_SPLASH_JAR,变量在JVMInit中的ShowSplashScreen会用到
if (splash_jar_name && !headlessflag) {
char* splash_jar_entry = JLI_MemAlloc(JLI_StrLen(SPLASH_JAR_ENV_ENTRY "=")+JLI_StrLen(splash_jar_name)+1);
JLI_StrCpy(splash_jar_entry, SPLASH_JAR_ENV_ENTRY "=");
JLI_StrCat(splash_jar_entry, splash_jar_name);
putenv(splash_jar_entry);
}
/*
* The JRE-Version and JRE-Restrict-Search values (if any) from the
* manifest are overwritten by any specified on the command line.
*/
//命令行中配置了version,用命令行配置的version重写MANIFEST.MF中的jre version
if (version != NULL)
info.jre_version = version;
//命令行中配置了-jre-restrict-search/-no-jre-restrict-search,用命令行配置的restrict_search重写MANIFEST.MF中的jre_restrict_search
if (restrict_search != -1)
info.jre_restrict_search = restrict_search;
/*
* "Valid" returns (other than unrecoverable errors) follow. Set
* main_class as a side-effect of this routine.
*/
if (info.main_class != NULL)
*main_class = JLI_StringDup(info.main_class);
/*
* If no version selection information is found either on the command
* line or in the manifest, simply return.
*/
//如果-version也未定义或为空,且jar中META-INF/MANIFEST.MF的JRE-Version属性未定义或为空,直接释放内存并返回
if (info.jre_version == NULL) {
JLI_FreeManifest();
JLI_MemFree(new_argv);
return;
}
/*
* Check for correct syntax of the version specification (JSR 56).
*/
//校验jre version的有效性:
// simple-element ::= version-id | version-id modifier
// modifier ::= '+' | '*'
// version-id ::= string ( separator string )*
// string ::= char ( char )*
// char ::= Any ASCII character except a space, an ampersand, a separator or a modifier
// separator ::= '.' | '-' | '_'
//1) Doesn't contain a space, an ampersand or a modifier.
//2) Doesn't begin or end with a separator.
//3) Doesn't contain two adjacent separators.
//1.不能包含:' ' | '+' | '*'
//2.不能以:'.' | '-' | '_' 开始或结束
//3.不能包含两个连续的分隔符:'.' | '-' | '_'
if (!JLI_ValidVersionString(info.jre_version)) {
JLI_ReportErrorMessage(SPC_ERROR1, info.jre_version);
exit(1);
}
/*
* Find the appropriate JVM on the system. Just to be as forgiving as
* possible, if the standard algorithms don't locate an appropriate
* jre, check to see if the one running will satisfy the requirements.
* This can happen on systems which haven't been set-up for multiple
* JRE support.
*/
//读取注册表获取最佳jre目录地址
jre = LocateJRE(&info);
JLI_TraceLauncher("JRE-Version = %s, JRE-Restrict-Search = %s Selected = %s\n",
(info.jre_version?info.jre_version:"null"),
(info.jre_restrict_search?"true":"false"), (jre?jre:"null"));
//如果jre目录地址为NULL,退出程序
if (jre == NULL) {
if (JLI_AcceptableRelease(GetFullVersion(), info.jre_version)) {
JLI_FreeManifest();
JLI_MemFree(new_argv);
return;
} else {
//"Error: Unable to locate JRE meeting specification \"%s\""
JLI_ReportErrorMessage(CFG_ERROR4, info.jre_version);
exit(1);
}
}
/*
* If I'm not the chosen one, exec the chosen one. Returning from
* ExecJRE indicates that I am indeed the chosen one.
*
* The private environment variable _JAVA_VERSION_SET is used to
* prevent the chosen one from re-reading the manifest file and
* using the values found within to override the (potential) command
* line flags stripped from argv (because the target may not
* understand them). Passing the MainClass value is an optimization
* to avoid locating, expanding and parsing the manifest extra
* times.
*/
//MANIFEST.MF中Main-Class不为NULL,且长度小于MAX_PATH,拼接环境变量
if (info.main_class != NULL) {
//MAXNAMELEN最终引用系统的MAX_PATH
//windows 260
//MAC 256
if (JLI_StrLen(info.main_class) <= MAXNAMELEN) {
(void)JLI_StrCat(env_entry, info.main_class);
} else {
//"Error: main-class: attribute exceeds system limits of %d bytes\n" GEN_ERROR
JLI_ReportErrorMessage(CLS_ERROR5, MAXNAMELEN);
exit(1);
}
}
//将META-INF/MANIFEST.MF中Main-Class存储到当前用户的环境变量_JAVA_VERSION_SET中在方法开始时调用,防止重复加载
(void)putenv(env_entry);
//尝试执行JRE
ExecJRE(jre, new_argv);
JLI_FreeManifest();
JLI_MemFree(new_argv);
return;
}
src/share/bin/java.h::ENV_ENTRY
#define ENV_ENTRY "_JAVA_VERSION_SET"
标签:info,SelectVersion,jre,version,splash,jar,源码,jdk8u,JLI 来源: https://blog.csdn.net/weixin_37477523/article/details/88121221