java jdpa指定jre_(二)catalina.bat

startup.bat在最后调用catalina.bat,并且传递了start参数,设置了CATALINA_HOME和CURRENT_DIR俩个临时环境变量。那么catalina.bat都做了什么?

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 @echo off2 rem Licensed to the Apache Software Foundation (ASF) under one or more3 rem contributor license agreements. See the NOTICE file distributed with4 rem this work foradditional information regarding copyright ownership.5 rem The ASF licenses this file to You under the Apache License, Version 2.0

6 rem (the "License"); you may not use this file except incompliance with7 rem the License. You may obtain a copy of the License at8 rem9 rem http://www.apache.org/licenses/LICENSE-2.0

10 rem11 rem Unless required by applicable law or agreed to inwriting, software12 rem distributed under the License is distributed on an "AS IS"BASIS,13 rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.14 rem See the License forthe specific language governing permissions and15 rem limitations under the License.16

17 rem ---------------------------------------------------------------------------

18 rem Start/Stop Script forthe CATALINA Server19 rem20 rem Environment Variable Prerequisites21 rem22 rem Do not set the variables in thisscript. Instead put them into a script23 rem setenv.bat in CATALINA_BASE/bin to keep your customizations separate.24 rem25 rem CATALINA_HOME May point at your Catalina "build"directory.26 rem27 rem CATALINA_BASE (Optional) Base directory for resolving dynamicportions28 rem of a Catalina installation. If not present, resolves to29 rem the same directory that CATALINA_HOME points to.30 rem31 rem CATALINA_OPTS (Optional) Java runtime options used when the "start",32 rem "run" or "debug" command isexecuted.33 rem Include here and not inJAVA_OPTS all options, that should34 rem only be used by Tomcat itself, not by the stop process,35 rem the version command etc.36 rem Examples are heap size, GC logging, JMX ports etc.37 rem38 rem CATALINA_TMPDIR (Optional) Directory path location of temporary directory39 rem the JVM should use (java.io.tmpdir). Defaults to40 rem %CATALINA_BASE%\temp.41 rem42 rem JAVA_HOME Must point at your Java Development Kit installation.43 rem Required to run the with the "debug"argument.44 rem45 rem JRE_HOME Must point at your Java Runtime installation.46 rem Defaults to JAVA_HOME ifempty. If JRE_HOME and JAVA_HOME47 rem are both set, JRE_HOME isused.48 rem49 rem JAVA_OPTS (Optional) Java runtime options used when any command50 rem isexecuted.51 rem Include here and not inCATALINA_OPTS all options, that52 rem should be used by Tomcat and also by the stop process,53 rem the version command etc.54 rem Most options should go into CATALINA_OPTS.55 rem56 rem JAVA_ENDORSED_DIRS (Optional) Lists of of semi-colon separated directories57 rem containing some jars inorder to allow replacement of APIs58 rem created outside of the JCP (i.e. DOM and SAX fromW3C).59 rem It can also be used to update the XML parser implementation.60 rem Defaults to $CATALINA_HOME/endorsed.61 rem62 rem JPDA_TRANSPORT (Optional) JPDA transport used when the "jpda start"

63 rem command is executed. The default is "dt_socket".64 rem65 rem JPDA_ADDRESS (Optional) Java runtime options used when the "jpda start"

66 rem command is executed. The default is 8000.67 rem68 rem JPDA_SUSPEND (Optional) Java runtime options used when the "jpda start"

69 rem command isexecuted. Specifies whether JVM should suspend70 rem execution immediately after startup. Default is "n".71 rem72 rem JPDA_OPTS (Optional) Java runtime options used when the "jpda start"

73 rem command isexecuted. If used, JPDA_TRANSPORT, JPDA_ADDRESS,74 rem and JPDA_SUSPEND are ignored. Thus, all required jpda75 rem options MUST be specified. The default is:76 rem77 rem -agentlib:jdwp=transport=%JPDA_TRANSPORT%,78 rem address=%JPDA_ADDRESS%,server=y,suspend=%JPDA_SUSPEND%

79 rem80 rem LOGGING_CONFIG (Optional) Override Tomcat's logging config file

81 rem Example (all one line)82 rem set LOGGING_CONFIG="-Djava.util.logging.config.file=%CATALINA_BASE%\conf\logging.properties"

83 rem84 rem LOGGING_MANAGER (Optional) Override Tomcat's logging manager

85 rem Example (all one line)86 rem set LOGGING_MANAGER="-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager"

87 rem88 rem TITLE (Optional) Specify the title of Tomcat window. The default

89 rem TITLE is Tomcat if it's not specified.

90 rem Example (all one line)91 rem set TITLE=Tomcat.Cluster#1.Server#1 [%DATE% %TIME%]92 rem ---------------------------------------------------------------------------

93

94 setlocal95

96 rem Suppress Terminate batch job on CTRL+C97 if not ""%1"" == ""run"" gotomainEntry98 if "%TEMP%" == "" gotomainEntry99 if exist "%TEMP%\%~nx0.run" gotomainEntry100 echo Y>"%TEMP%\%~nx0.run"

101 if not exist "%TEMP%\%~nx0.run" gotomainEntry102 echo Y>"%TEMP%\%~nx0.Y"

103 call "%~f0" %*

104 rem Use provided errorlevel105 set RETVAL=%ERRORLEVEL%

106 del /Q "%TEMP%\%~nx0.Y" >NUL 2>&1

107 exit /B %RETVAL%

108 :mainEntry109 del /Q "%TEMP%\%~nx0.run" >NUL 2>&1

110

111 rem Guess CATALINA_HOME ifnot defined112 set "CURRENT_DIR=%cd%"

113 if not "%CATALINA_HOME%" == "" gotogotHome114 set "CATALINA_HOME=%CURRENT_DIR%"

115 if exist "%CATALINA_HOME%\bin\catalina.bat" gotookHome116 cd ..117 set "CATALINA_HOME=%cd%"

118 cd "%CURRENT_DIR%"

119 :gotHome120

121 if exist "%CATALINA_HOME%\bin\catalina.bat" gotookHome122 echo The CATALINA_HOME environment variable isnot defined correctly123 echo This environment variable is needed to run thisprogram124 gotoend125 :okHome126

127 rem Copy CATALINA_BASE from CATALINA_HOME ifnot defined128 if not "%CATALINA_BASE%" == "" gotogotBase129 set "CATALINA_BASE=%CATALINA_HOME%"

130 :gotBase131

132 rem Ensure that any user defined CLASSPATH variables are not used on startup,133 rem but allow them to be specified in setenv.bat, in rare case when it isneeded.134 set CLASSPATH=

135

136 rem Get standard environment variables137 if not exist "%CATALINA_BASE%\bin\setenv.bat" gotocheckSetenvHome138 call "%CATALINA_BASE%\bin\setenv.bat"

139 gotosetenvDone140 :checkSetenvHome141 if exist "%CATALINA_HOME%\bin\setenv.bat" call "%CATALINA_HOME%\bin\setenv.bat"

142 :setenvDone143

144 rem Get standard Java environment variables145 if exist "%CATALINA_HOME%\bin\setclasspath.bat" gotookSetclasspath146 echo Cannot find "%CATALINA_HOME%\bin\setclasspath.bat"

147 echo This file is needed to run thisprogram148 gotoend149 :okSetclasspath150 call "%CATALINA_HOME%\bin\setclasspath.bat" %1

151 if errorlevel 1 gotoend152

153 rem Add on extra jar file to CLASSPATH154 rem Note that there are no quotes as we donot want to introduce random155 rem quotes into the CLASSPATH156 if "%CLASSPATH%" == "" gotoemptyClasspath157 set "CLASSPATH=%CLASSPATH%;"

158 :emptyClasspath159 set "CLASSPATH=%CLASSPATH%%CATALINA_HOME%\bin\bootstrap.jar"

160

161 if not "%CATALINA_TMPDIR%" == "" gotogotTmpdir162 set "CATALINA_TMPDIR=%CATALINA_BASE%\temp"

163 :gotTmpdir164

165 rem Add tomcat-juli.jar to classpath166 rem tomcat-juli.jar can be over-ridden per instance167 if not exist "%CATALINA_BASE%\bin\tomcat-juli.jar" gotojuliClasspathHome168 set "CLASSPATH=%CLASSPATH%;%CATALINA_BASE%\bin\tomcat-juli.jar"

169 gotojuliClasspathDone170 :juliClasspathHome171 set "CLASSPATH=%CLASSPATH%;%CATALINA_HOME%\bin\tomcat-juli.jar"

172 :juliClasspathDone173

174 if not "%LOGGING_CONFIG%" == "" gotonoJuliConfig175 set LOGGING_CONFIG=-Dnop176 if not exist "%CATALINA_BASE%\conf\logging.properties" gotonoJuliConfig177 set LOGGING_CONFIG=-Djava.util.logging.config.file="%CATALINA_BASE%\conf\logging.properties"

178 :noJuliConfig179 set "JAVA_OPTS=%JAVA_OPTS% %LOGGING_CONFIG%"

180

181 if not "%LOGGING_MANAGER%" == "" gotonoJuliManager182 set LOGGING_MANAGER=-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager183 :noJuliManager184 set "JAVA_OPTS=%JAVA_OPTS% %LOGGING_MANAGER%"

185

186 rem ----- Execute The Requested Command ---------------------------------------

187

188 echo Using CATALINA_BASE: "%CATALINA_BASE%"

189 echo Using CATALINA_HOME: "%CATALINA_HOME%"

190 echo Using CATALINA_TMPDIR: "%CATALINA_TMPDIR%"

191 if ""%1"" == ""debug"" gotouse_jdk192 echo Using JRE_HOME: "%JRE_HOME%"

193 gotojava_dir_displayed194 :use_jdk195 echo Using JAVA_HOME: "%JAVA_HOME%"

196 :java_dir_displayed197 echo Using CLASSPATH: "%CLASSPATH%"

198

199 set _EXECJAVA=%_RUNJAVA%

200 set MAINCLASS=org.apache.catalina.startup.Bootstrap201 set ACTION=start202 set SECURITY_POLICY_FILE=

203 set DEBUG_OPTS=

204 set JPDA=

205

206 if not ""%1"" == ""jpda"" gotonoJpda207 set JPDA=jpda208 if not "%JPDA_TRANSPORT%" == "" gotogotJpdaTransport209 set JPDA_TRANSPORT=dt_socket210 :gotJpdaTransport211 if not "%JPDA_ADDRESS%" == "" gotogotJpdaAddress212 set JPDA_ADDRESS=8000

213 :gotJpdaAddress214 if not "%JPDA_SUSPEND%" == "" gotogotJpdaSuspend215 set JPDA_SUSPEND=n216 :gotJpdaSuspend217 if not "%JPDA_OPTS%" == "" gotogotJpdaOpts218 set JPDA_OPTS=-agentlib:jdwp=transport=%JPDA_TRANSPORT%,address=%JPDA_ADDRESS%,server=y,suspend=%JPDA_SUSPEND%

219 :gotJpdaOpts220 shift221 :noJpda222

223 if ""%1"" == ""debug"" gotodoDebug224 if ""%1"" == ""run"" gotodoRun225 if ""%1"" == ""start"" gotodoStart226 if ""%1"" == ""stop"" gotodoStop227 if ""%1"" == ""configtest"" gotodoConfigTest228 if ""%1"" == ""version"" gotodoVersion229

230 echo Usage: catalina ( commands ... )231 echo commands:232 echo debug Start Catalina ina debugger233 echo debug -security Debug Catalina with a security manager234 echo jpda start Start Catalina under JPDA debugger235 echo run Start Catalina inthe current window236 echo run -security Start inthe current window with security manager237 echo start Start Catalina ina separate window238 echo start -security Start ina separate window with security manager239 echo stop Stop Catalina240 echo configtest Run a basic syntax check on server.xml241 echo version What version of tomcat are you running?

242 gotoend243

244 :doDebug245 shift246 set _EXECJAVA=%_RUNJDB%

247 set DEBUG_OPTS=-sourcepath "%CATALINA_HOME%\..\..\java"

248 if not ""%1"" == ""-security"" gotoexecCmd249 shift250 echo Using Security Manager251 set "SECURITY_POLICY_FILE=%CATALINA_BASE%\conf\catalina.policy"

252 gotoexecCmd253

254 :doRun255 shift256 if not ""%1"" == ""-security"" gotoexecCmd257 shift258 echo Using Security Manager259 set "SECURITY_POLICY_FILE=%CATALINA_BASE%\conf\catalina.policy"

260 gotoexecCmd261

262 :doStart263 shift264 if "%TITLE%" == "" set TITLE=Tomcat265 set _EXECJAVA=start "%TITLE%" %_RUNJAVA%

266 if not ""%1"" == ""-security"" gotoexecCmd267 shift268 echo Using Security Manager269 set "SECURITY_POLICY_FILE=%CATALINA_BASE%\conf\catalina.policy"

270 gotoexecCmd271

272 :doStop273 shift274 set ACTION=stop275 set CATALINA_OPTS=

276 gotoexecCmd277

278 :doConfigTest279 shift280 set ACTION=configtest281 set CATALINA_OPTS=

282 gotoexecCmd283

284 :doVersion285 %_EXECJAVA% -classpath "%CATALINA_HOME%\lib\catalina.jar"org.apache.catalina.util.ServerInfo286 gotoend287

288

289 :execCmd290 rem Get remaining unshifted command line arguments and save them inthe291 set CMD_LINE_ARGS=

292 :setArgs293 if ""%1""=="""" gotodoneSetArgs294 set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1

295 shift296 gotosetArgs297 :doneSetArgs298

299 rem Execute Java with the applicable properties300 if not "%JPDA%" == "" gotodoJpda301 if not "%SECURITY_POLICY_FILE%" == "" gotodoSecurity302 %_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%

303 gotoend304 :doSecurity305 %_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Djava.security.manager -Djava.security.policy=="%SECURITY_POLICY_FILE%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%

306 gotoend307 :doJpda308 if not "%SECURITY_POLICY_FILE%" == "" gotodoSecurityJpda309 %_EXECJAVA% %JAVA_OPTS% %JPDA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%

310 gotoend311 :doSecurityJpda312 %_EXECJAVA% %JAVA_OPTS% %JPDA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Djava.security.manager -Djava.security.policy=="%SECURITY_POLICY_FILE%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%

313 gotoend314

315 :end

View Code

line    1  @echo off  关闭批处理输出

line  94  setlocal  开始设置临时环境变量

line  97  if not ""%1"" == ""run"" goto mainEntry  如果第一个参数不是run的话,goto mainEntry节点(第一个参数默认start,所以会直接goto mianEntry节点);否则继续。

line  98-108  如果第一个参数是run的话才会被执行,其中加黑的103,105,107并未理解。

line  98  if "%TEMP%" == "" goto mainEntry  如果没有TEMP环境变量的话,goto mianEntry节点;否则继续。我试了一下echo %TEMP%,得到的是*\AppData\Local\Temp,所以这个和startup.bat中的%OS%一样是存在的,继续line99

line  99  if exist "%TEMP%\%~nx0.run" goto mainEntry  如果在%TEMP% 目录下有 %~nx0.run,goto mainEntry,但是%~nx0.run是什么呢?搜索了一下,%0就是本BAT的绝对路径,%~ 扩充删除任何引号,%~nx ::n文件名 ::x扩展名。试着写了个test.bat,内容是echo "%TEMP%\%~nx0.run",执行结果:*\AppData\Local\Temp\test.bat.run,所以这一行是如果在%TEMP%下有catalina.bat.run,goto mainEntry节点,我查了下对应目录下并没有catalina.bat.run,继续line100。

line100  echo Y>"%TEMP%\%~nx0.run"  创建catalina.bat.run并写入Y。

line101  if not exist "%TEMP%\%~nx0.run" goto mainEntry  如果%TEMP%下仍没有catalina.bat.run,goto mainEntry,但line100创建并写入了Y,继续下一行。

line102  echo Y>"%TEMP%\%~nx0.Y"  创建catalina.bat.Y并写入Y。

line103  call "%~f0" %*

line105  set RETVAL=%ERRORLEVEL%  试了下echo %ERRORLEVEL%,执行结果:0,但是搜索了一下%ERRORLEVEL%是返回的错误码,0是成功,其他是失败。

line106  del /Q "%TEMP%\%~nx0.Y" >NUL 2>&1  del /Q是安静模式删除,不要求确认。NUL是DOS下的一个虚拟设备,相当于一个文件(输入时立即结束,输出时仅作模拟)。2>&1,2是值错误输出,&1是标准输出,意思就是将错误输出重定向到标准输出中。>NUL 2>&1,就是将错误输入重定向到标准输出,重定向到NUL中。这行作用就是安静模式删除*\AppData\Local\Temp\test.bat.Y。

DEL命令:

DEL [/P] [/F] [/S] [/Q] [/A[[:]attributes]] names

/P 删除每一个文件之前提示确认。/F 强制删除只读文件。/S 从所有子目录删除指定文件。/Q 安静模式。删除全局通配符时,不要求确认。/A 根据属性选择要删除的文件。

line107  exit /B %RETVAL%  ,在cmd中输入了一下help exit,得到如下结果:

退出 CMD.EXE 程序(命令解释器)或当前批处理脚本。

EXIT [/B] [exitCode]/B 指定要退出当前批处理脚本而不是 CMD.EXE。如果从一个

批处理脚本外执行,则会退出 CMD.EXE

exitCode 指定一个数字号码。如果指定了/B,将 ERRORLEVEL

设成那个数字。如果退出 CMD.EXE,则用那个数字设置

过程退出代码。

line108  :mainEntry  :mainEntry节点

line109  del /Q "%TEMP%\%~nx0.run" >NUL 2>&1  安静模式删除*\AppData\Local\Temp\test.bat.run。

为了搞明白103~107,我在catalina.bat中加入了几行输出,把catalina.bat.run的创建和删除注释掉了,如下:

1 if not ""%1"" == ""run"" gotomainEntry2 if "%TEMP%" == "" gotomainEntry3 if exist "%TEMP%\%~nx0.run" gotomainEntry4 rem echo Y>"%TEMP%\%~nx0.run"

5 rem if not exist "%TEMP%\%~nx0.run" gotomainEntry6 echo Y>"%TEMP%\%~nx0.Y"

7 echo ======================================1

8 call "%~f0" %*

9 echo ======================================2

10 rem Use provided errorlevel11 set RETVAL=%ERRORLEVEL%

12 echo ====================================="%ERRORLEVEL%"

13 echo ====================================="%RETVAL%

14 del /Q "%TEMP%\%~nx0.Y" >NUL 2>&1

15 exit /B %RETVAL%

16 :mainEntry17 rem del /Q "%TEMP%\%~nx0.run" >NUL 2>&1

我直接启动catalina.bat run,用run做第一个参数,因为如果%TEMP%下没有catalina.bat.run的话会创建,然后我注释了line5,这样不会goto mainEntry节点,而是继续执行。line7会有输出,而后就没有了。line8 ,catalina.bat使用Y参数自己调用自己,然后走到line1,因为参数不是run而是Y,goto mainEntry节点。之后我将catalina.bat.Y修改为run试了下,会停留在line7一直输出=====1。那么line9~line15什么时候会执行呢?

我启动catalina.bat run,然后运行shutdown.bat,line9输出,%ERRORLEVEL%是0,catalina.bat.Y被删除,catalina.bat被停掉。简单扫了眼shutdown.bat,这个batch file 在最后call catalina.bat stop,即停掉了catalina.bat。

line111~125  和startup.bat做的事情差不多,如果是从startup.bat启动的话,CURRENT_DIR和CATALINA_HOME俩个变量在startup里已经设置过,依然有效。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

rem Guess CATALINA_HOME ifnot definedset "CURRENT_DIR=%cd%"

if not "%CATALINA_HOME%" == "" gotogotHomeset "CATALINA_HOME=%CURRENT_DIR%"

if exist "%CATALINA_HOME%\bin\catalina.bat" gotookHome

cd ..set "CATALINA_HOME=%cd%"cd"%CURRENT_DIR%":gotHomeif exist "%CATALINA_HOME%\bin\catalina.bat" gotookHome

echo The CATALINA_HOME environment variableisnot defined correctly

echo This environment variableis needed to run thisprogramgotoend

:okHome

111~125

line127~130  如果没有CATALINA_BASE这个环境变量的话,把CATALINA_HOME的值给它(CATALINA_HOME这个环境变量我都没有提前配置,CATALINA_BASE肯定也不会配置)。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

rem Copy CATALINA_BASE from CATALINA_HOME ifnot definedif not "%CATALINA_BASE%" == "" gotogotBaseset "CATALINA_BASE=%CATALINA_HOME%":gotBase

127~130

line132~134  set CLASSPATH=  设置CLASSPATH临时环境变量,注释为:Ensure that any user defined CLASSPATH variables are not used on startup, but allow them to be specified in setenv.bat, in rare case when it is needed. 大意是:确认在启动时不使用任何用户定义的CLASSPATH,但是在允许在setenv.bat里指定它们(CLASSPATH variables)。搜索了一下setenv.bat发现可以指定另外的JAVA_HOME和JAVA_OPTS等等环境变量。

line136~142  如果用户在%CATALINA_HOME%\bin或者%CATALINA_BASE%\bin下建了setenv.bat就调用setenv.bat。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

rem Get standard environment variablesif not exist "%CATALINA_BASE%\bin\setenv.bat" gotocheckSetenvHome

call"%CATALINA_BASE%\bin\setenv.bat"

gotosetenvDone

:checkSetenvHomeif exist "%CATALINA_HOME%\bin\setenv.bat" call "%CATALINA_HOME%\bin\setenv.bat":setenvDone

136~142

line144~151  调用%CATALINA_HOME%\bin\setclasspath.bat,获取java相关环境变量。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

rem Get standard Java environment variablesif exist "%CATALINA_HOME%\bin\setclasspath.bat" gotookSetclasspath

echo Cannot find"%CATALINA_HOME%\bin\setclasspath.bat"echo This fileis needed to run thisprogramgotoend

:okSetclasspath

call"%CATALINA_HOME%\bin\setclasspath.bat" %1

if errorlevel 1 goto end

144~151

line145  if exist "%CATALINA_HOME%\bin\setclasspath.bat" goto okSetclasspath  如果文件存在,goto okSetclasspath节点,否则执行line146~147后goto end节点退出本次批处理。

line150  call "%CATALINA_HOME%\bin\setclasspath.bat" %1  调用%CATALINA_HOME%\bin\setclasspath.bat,参数是catalina.bat的第一个参数(即从startup.bat拿到的start)。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 @echo off2 rem Licensed to the Apache Software Foundation (ASF) under one or more3 rem contributor license agreements. See the NOTICE file distributed with4 rem this work foradditional information regarding copyright ownership.5 rem The ASF licenses this file to You under the Apache License, Version 2.0

6 rem (the "License"); you may not use this file except incompliance with7 rem the License. You may obtain a copy of the License at8 rem9 rem http://www.apache.org/licenses/LICENSE-2.0

10 rem11 rem Unless required by applicable law or agreed to inwriting, software12 rem distributed under the License is distributed on an "AS IS"BASIS,13 rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.14 rem See the License forthe specific language governing permissions and15 rem limitations under the License.16

17 rem ---------------------------------------------------------------------------

18 rem Set JAVA_HOME or JRE_HOME if not already set, ensure any provided settings19 rem are valid and consistent with the selected start-up options and setup the20 rem endorsed directory.21 rem ---------------------------------------------------------------------------

22

23 rem Make sure prerequisite environment variables are set

24

25 rem In debug mode we need a real JDK (JAVA_HOME)26 if ""%1"" == ""debug"" gotoneedJavaHome27

28 rem Otherwise either JRE or JDK are fine29 if not "%JRE_HOME%" == "" gotogotJreHome30 if not "%JAVA_HOME%" == "" gotogotJavaHome31 echo Neither the JAVA_HOME nor the JRE_HOME environment variable isdefined32 echo At least one of these environment variable is needed to run thisprogram33 gotoexit34

35 :needJavaHome36 rem Check ifwe have a usable JDK37 if "%JAVA_HOME%" == "" gotonoJavaHome38 if not exist "%JAVA_HOME%\bin\java.exe" gotonoJavaHome39 if not exist "%JAVA_HOME%\bin\javaw.exe" gotonoJavaHome40 if not exist "%JAVA_HOME%\bin\jdb.exe" gotonoJavaHome41 if not exist "%JAVA_HOME%\bin\javac.exe" gotonoJavaHome42 set "JRE_HOME=%JAVA_HOME%"

43 gotookJava44

45 :noJavaHome46 echo The JAVA_HOME environment variable isnot defined correctly.47 echo It is needed to run this program indebug mode.48 echo NB: JAVA_HOME should point to a JDK not a JRE.49 gotoexit50

51 :gotJavaHome52 rem No JRE given, use JAVA_HOME asJRE_HOME53 set "JRE_HOME=%JAVA_HOME%"

54

55 :gotJreHome56 rem Check ifwe have a usable JRE57 if not exist "%JRE_HOME%\bin\java.exe" gotonoJreHome58 if not exist "%JRE_HOME%\bin\javaw.exe" gotonoJreHome59 gotookJava60

61 :noJreHome62 rem Needed at least a JRE63 echo The JRE_HOME environment variable isnot defined correctly64 echo This environment variable is needed to run thisprogram65 gotoexit66

67 :okJava68 rem Don't override the endorsed dir if the user has set it previously

69 if not "%JAVA_ENDORSED_DIRS%" == "" gotogotEndorseddir70 rem Set the default -Djava.endorsed.dirs argument71 set "JAVA_ENDORSED_DIRS=%CATALINA_HOME%\endorsed"

72 :gotEndorseddir73

74 rem Don't override _RUNJAVA if the user has set it previously

75 if not "%_RUNJAVA%" == "" gotogotRunJava76 rem Set standard command forinvoking Java.77 rem Also note the quoting asJRE_HOME may contain spaces.78 set _RUNJAVA="%JRE_HOME%\bin\java.exe"

79 :gotRunJava80

81 rem Don't override _RUNJDB if the user has set it previously

82 rem Also note the quoting asJAVA_HOME may contain spaces.83 if not "%_RUNJDB%" == "" gotogotRunJdb84 set _RUNJDB="%JAVA_HOME%\bin\jdb.exe"

85 :gotRunJdb86

87 gotoend88

89 :exit90 exit /b 1

91

92 :end93 exit /b 0

setclasspath.bat

setclasspath.bat比较简单。In debug mode we need a real JDK (JAVA_HOME),如果没有JRE_HOME和JAVA_HOME,goto exit等等,设置了%JAVA_ENDORSED_DIRS%、%_RUNJAVA%和%_RUNJDB%,执行完毕返回结果0或1。

line151  if errorlevel 1 goto end  如果setclasspath.bat的结果是1,goto end节点,退出本次批处理。

line153~159  如果有%CLASSPATH%的话,CLASSPATH=";" + %CATALINA_HOME%\bin\bootstrap.jar;否则CLASSPATH=%CATALINA_HOME%\bin\bootstrap.jar。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

rem Add on extra jar file to CLASSPATH

rem Note that there are no quotesas we donot want to introduce random

rem quotes into the CLASSPATHif "%CLASSPATH%" == "" gotoemptyClasspathset "CLASSPATH=%CLASSPATH%;":emptyClasspathset "CLASSPATH=%CLASSPATH%%CATALINA_HOME%\bin\bootstrap.jar"

153~159

line161~163  如果没有%CATALINA_TMPDIR%环境变量的话,设置%CATALINA_TMPDIR%=%CATALINA_BASE%\temp。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

if not "%CATALINA_TMPDIR%" == "" gotogotTmpdirset "CATALINA_TMPDIR=%CATALINA_BASE%\temp":gotTmpdir

161~163

line165~172  把tomcat-juli.jar写进CLASSPATH里。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

rem Add tomcat-juli.jar to classpath

rem tomcat-juli.jar can be over-ridden per instanceif not exist "%CATALINA_BASE%\bin\tomcat-juli.jar" gotojuliClasspathHomeset "CLASSPATH=%CLASSPATH%;%CATALINA_BASE%\bin\tomcat-juli.jar"

gotojuliClasspathDone

:juliClasspathHomeset "CLASSPATH=%CLASSPATH%;%CATALINA_HOME%\bin\tomcat-juli.jar":juliClasspathDone

165~172

line174~184  对日志的处理,给%JAVA_OPTS%加上了%LOGGING_CONFIG% %LOGGING_MANAGER%。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

if not "%LOGGING_CONFIG%" == "" gotonoJuliConfigset LOGGING_CONFIG=-Dnopif not exist "%CATALINA_BASE%\conf\logging.properties" gotonoJuliConfigset LOGGING_CONFIG=-Djava.util.logging.config.file="%CATALINA_BASE%\conf\logging.properties":noJuliConfigset "JAVA_OPTS=%JAVA_OPTS% %LOGGING_CONFIG%"

if not "%LOGGING_MANAGER%" == "" gotonoJuliManagerset LOGGING_MANAGER=-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager

:noJuliManagerset "JAVA_OPTS=%JAVA_OPTS% %LOGGING_MANAGER%"

174~184

line186  rem ----- Execute The Requested Command   执行请求。

line188~190  输出了CATALINA_BASE、CATALINA_HOME和CATALINA_TMPDIR,从上边可以看出,如果没有设置CATALINA_BASE环境变量的话,CATALINA_BASE=CATALINA_HOME,而CATALINA_TMPDIR=在CATALINA_BASE\temp,而CATALINA_HOME就是catalina.bat的目录(startup.bat一般和catalina.bat在一起)。

line190~197  如果第一个参数是debug,输出%JAVA_HOME%;否则输出%JRE_HOME%,输出拼接的%CLASSPATH%。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

echo Using CATALINA_BASE: "%CATALINA_BASE%"echo Using CATALINA_HOME:"%CATALINA_HOME%"echo Using CATALINA_TMPDIR:"%CATALINA_TMPDIR%"

if ""%1"" == ""debug"" gotouse_jdk

echo Using JRE_HOME:"%JRE_HOME%"

gotojava_dir_displayed

:use_jdk

echo Using JAVA_HOME:"%JAVA_HOME%":java_dir_displayed

echo Using CLASSPATH:"%CLASSPATH%"

188~197

line199~204  设置了6个变量。_EXECJAVA=%_RUNJAVA%,%_RUNJAVA%这个环境变量是在setclasspath.bat中设置的,值为%JRE_HOME%\bin\java.exe;MAINCLASS=org.apache.catalina.startup.Bootstrap,这个类在%CATALINA_HOME%\bin\bootstarp.jar;ACTION=start;其他3个变量没有赋值。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

set _EXECJAVA=%_RUNJAVA%

set MAINCLASS=org.apache.catalina.startup.Bootstrapset ACTION=startset SECURITY_POLICY_FILE=

set DEBUG_OPTS=

set JPDA=

199~204

line206~221  是对jdpa的一些操作,搜了一下jdpa的简介,是调试java的api。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

if not ""%1"" == ""jpda"" gotonoJpdaset JPDA=jpdaif not "%JPDA_TRANSPORT%" == "" gotogotJpdaTransportset JPDA_TRANSPORT=dt_socket

:gotJpdaTransportif not "%JPDA_ADDRESS%" == "" gotogotJpdaAddressset JPDA_ADDRESS=8000:gotJpdaAddressif not "%JPDA_SUSPEND%" == "" gotogotJpdaSuspendset JPDA_SUSPEND=n

:gotJpdaSuspendif not "%JPDA_OPTS%" == "" gotogotJpdaOptsset JPDA_OPTS=-agentlib:jdwp=transport=%JPDA_TRANSPORT%,address=%JPDA_ADDRESS%,server=y,suspend=%JPDA_SUSPEND%:gotJpdaOpts

shift

:noJpda

206~221

jdpa简介:

Java平台调试器架构(英语:Java Platform Debugger Architecture,JPDA)是一组用于调试Java代码的API。

Java调试器接口(Java Debugger Interface,JDI)——定义了一个高层次Java接口,开发人员可以利用JDI轻松编写远程调试工具。

Java虚拟机工具接口(Java Virtual Machine Tools Interface,JVMTI)——定义了一个原生(native)接口,可以对运行在Java虚拟机的应用程序检查状态、控制运行。

Java虚拟机调试接口(JVMDI)——JVMDI在J2SE5.0中被JVMTI取代,并在Java SE 6中被移除。

Java调试线协议(JDWP)——定义了调试对象(一个 Java 应用程序)和调试器进程之间的通信协议。

line223~242  判断catalina.bat的第一个参数,goto 对应的节点。如果第一个参数不是debug、run、start、stop、configtest、version,goto end,本次批处理结束。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

if ""%1"" == ""debug"" gotodoDebugif ""%1"" == ""run"" gotodoRunif ""%1"" == ""start"" gotodoStartif ""%1"" == ""stop"" gotodoStopif ""%1"" == ""configtest"" gotodoConfigTestif ""%1"" == ""version"" gotodoVersion

echo Usage: catalina ( commands ... )

echo commands:

echo debug Start Catalinaina debugger

echo debug-security Debug Catalina with a security manager

echo jpda start Start Catalina under JPDA debugger

echo run Start Catalinainthe current window

echo run-security Start inthe current window with security manager

echo start Start Catalinaina separate window

echo start-security Start ina separate window with security manager

echo stop Stop Catalina

echo configtest Run a basic syntax check on server.xml

echo version What version of tomcat are you running?

goto end

223~242

line244~252  doDebug节点

line245  shift  表示批处理文件中替换参数左移一个位置,后面的替换参数陆续填补上去。

line246  set _EXECJAVA=%_RUNJDB%  这个变量在line199已经设置过,%_RUNJAVA%这个环境变量是在setclasspath.bat中设置的,值为%JRE_HOME%\bin\java.exe。

line247  set DEBUG_OPTS=-sourcepath "%CATALINA_HOME%\..\..\java"

line248  if not ""%1"" == ""-security"" goto execCmd  因为245有一个shift命令,所以此处的%1是catalina.bat启动时的第二个参数。如果%1不是-security的话,goto execCmd节点;否则继续。

line249  shift  参数再次左移

line251  set "SECURITY_POLICY_FILE=%CATALINA_BASE%\conf\catalina.policy"  这个SECURITY_POLICY_FILE赋值。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

:doDebug

shiftset _EXECJAVA=%_RUNJDB%

set DEBUG_OPTS=-sourcepath "%CATALINA_HOME%\..\..\java"

if not ""%1"" == ""-security"" gotoexecCmd

shift

echo Using Security Managerset "SECURITY_POLICY_FILE=%CATALINA_BASE%\conf\catalina.policy"

goto execCmd

debug节点244~252

line254~260  doRun节点,如果启动时第二个参数是-security,set "SECURITY_POLICY_FILE=%CATALINA_BASE%\conf\catalina.policy";否则直接goto execCmd节点。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

:doRun

shiftif not ""%1"" == ""-security"" gotoexecCmd

shift

echo Using Security Managerset "SECURITY_POLICY_FILE=%CATALINA_BASE%\conf\catalina.policy"

goto execCmd

dorun节点254~260

line262~270  doStart节点。设置TITLE=TOMCAT,设置_EXECJAVA=start "TOMCAT" %_RUNJAVA%,这样cmd窗口的title会变为“TOMCAT”。如果启动时第二个参数是-security,set "SECURITY_POLICY_FILE=%CATALINA_BASE%\conf\catalina.policy";否则直接goto execCmd节点。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

:doStart

shiftif "%TITLE%" == "" set TITLE=Tomcatset _EXECJAVA=start "%TITLE%" %_RUNJAVA%

if not ""%1"" == ""-security"" gotoexecCmd

shift

echo Using Security Managerset "SECURITY_POLICY_FILE=%CATALINA_BASE%\conf\catalina.policy"

goto execCmd

dostart262~270

line272~276  doStop节点,改变ACTION=stop,CATALINA_OPTS=,goto execCmd节点。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

:doStop

shiftset ACTION=stopset CATALINA_OPTS=

goto execCmd

dostop272~270

line278~282  doConfigTest节点,改变ACTION=configtest,CATALINA_OPTS=,goto execCmd节点。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

:doConfigTest

shiftset ACTION=configtestset CATALINA_OPTS=

goto execCmd

doConfigTest278~282

line284~286  doVersion节点,执行line285后goto end节点,本次批处理结束。

line285  %_EXECJAVA% -classpath "%CATALINA_HOME%\lib\catalina.jar" org.apache.catalina.util.ServerInfo  等同于java -classpath "*\catalina.jar" org.apache.catalina.util.ServerInfo

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

:doVersion%_EXECJAVA% -classpath "%CATALINA_HOME%\lib\catalina.jar"org.apache.catalina.util.ServerInfogoto end

doVersion284~286

反编译后的ServerInfo(片段):

...public static voidmain(String[] args) {

System.out.println("Server version:" +getServerInfo());

System.out.println("Server built:" +getServerBuilt());

System.out.println("Server number:" +getServerNumber());

System.out.println("OS Name:" + System.getProperty("os.name"));

System.out.println("OS Version:" + System.getProperty("os.version"));

System.out.println("Architecture:" + System.getProperty("os.arch"));

System.out.println("JVM Version:" + System.getProperty("java.runtime.version"));

System.out.println("JVM Vendor:" + System.getProperty("java.vm.vendor"));

}

/**

* 从ServerInfo.properties读取服务器信息,如果读取失败会有默认返回。

*/static {Properties props= newProperties();

InputStreamis = null;try{is = ServerInfo.class.getResourceAsStream("/org/apache/catalina/util/ServerInfo.properties"); // 这个配置文件里只有server.info, server.built, server.number3个属性

props.load(is);

serverInfo= props.getProperty("server.info");

serverBuilt= props.getProperty("server.built");

serverNumber= props.getProperty("server.number");

}catch(Throwable t) {

ExceptionUtils.handleThrowable(t);

}finally{if (is != null)try{is.close();

}catch(IOException e) {

}

}if (serverInfo == null)

serverInfo= "Apache Tomcat 7.0.x-dev";if (serverBuilt == null)

serverBuilt= "unknown";if (serverNumber == null)

serverNumber= "7.0.x";

}

...

line289~297  execCmd节点,拼接剩余的参数给CMD_LINE_ARGS,和startup.bat中一样。

line299~313  以4种方式之一启动bootstarp.jar,启动后都goto end节点,本次批处理结束,设置的临时环境变量失效。

line302  %_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%  这种启动方式是没有jdpa和SECURITY_POLICY_FILE的启动。这种模式的%_EXECJAVA%=start "Tomcat" "%JRE_HOME%\bin\java.exe";%JAVA_OPTS%是line174~184对日志的处理;CATALINA_OPTS="";%DEBUG_OPTS%="";%JAVA_ENDORSED_DIRS%在setclasspath.bat设置为%CATALINA_HOME%\endorsed;%CLASSPATH%是拼接了bootstrap.jar和tomcat-juli.jar俩个jar包的绝对路径;%MAINCLASS%="org.apache.catalina.startup.Bootstrap"是bootstrap.jar的要启动的main方法所在类;%CMD_LINE_ARGS%是catalina.bat启动时传入参数拼接而成(从第三个参数开始拼接,因为doStart节点的line263和line267有2处shift),一般为空;%ACTION%=start。简言,就是启动了bootstrap.jar,main方法是org.apache.catalina.startup.Bootstrap,参数肯定会有一个start(如果有其他参数,这个会在最后)。

line305  %_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Djava.security.manager -Djava.security.policy=="%SECURITY_POLICY_FILE%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%  这种启动方式是没有jdpa但是有SECURITY_POLICY_FILE的启动。比line302多了-Djava.security.policy=="%SECURITY_POLICY_FILE%",%SECURITY_POLICY_FILE%=%CATALINA_BASE%\conf\catalina.policy,catalina.policy是大约TOMCAT的权限控制之类。

line309  %_EXECJAVA% %JAVA_OPTS% %JPDA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%  这种启动方式有jdpa无SECURITY_POLICY_FILE,其中%JPDA_OPTS%在line206~221设置。

line312  %_EXECJAVA% %JAVA_OPTS% %JPDA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Djava.security.manager -Djava.security.policy=="%SECURITY_POLICY_FILE%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%  这种启动方式有jpda和SECURITY_POLICY_FILE

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

rem Execute Java with the applicable propertiesif not "%JPDA%" == "" gotodoJpdaif not "%SECURITY_POLICY_FILE%" == "" gotodoSecurity%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%

gotoend

:doSecurity%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Djava.security.manager -Djava.security.policy=="%SECURITY_POLICY_FILE%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%

gotoend

:doJpdaif not "%SECURITY_POLICY_FILE%" == "" gotodoSecurityJpda%_EXECJAVA% %JAVA_OPTS% %JPDA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%

gotoend

:doSecurityJpda%_EXECJAVA% %JAVA_OPTS% %JPDA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Djava.security.manager -Djava.security.policy=="%SECURITY_POLICY_FILE%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%

goto end

299~313

line315  :end  end节点。

这个批处理比startup.bat复杂的多,tomcat允许我们在%CATALINA_BASE%\bin\setenv.bat设置环境变量,读取%CATALINA_HOME%\bin\setclasspath.bat获取java相关的环境变量,拼接classpath,设置日志相关,设置JPDA_OPTS相关,%CATALINA_BASE%\conf\catalina.policy的权限控制,如果要查看版本信息,会启动%CATALINA_HOME%\lib\catalina.jar下的org.apache.catalina.util.ServerInfo等等。最终,也是一般情况(startup.bat启动catalina.bat)会在最后启动bootstrap.jar的org.apache.catalina.startup.Bootstrap类,参数为start。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值