catalina.bat脚本启动Tomcat源码分析 快资讯
概述
Tomcat 的三个最重要的启动脚本:
(资料图片)
startup.batcatalina.batsetclasspath.bat
上一篇咱们分析了 startup.bat 脚本
这一篇咱们来分析 catalina.bat 脚本. 至于 setclasspath.bat 这个脚本, 相信看完这一篇, 就可以自己看懂这个脚本了. 可以点击 [Tomcat 源码分析系列] (附件) : setclasspath.bat 脚本 查看附注释的 setclasspath.bat 脚本
catalina.bat
这个脚本的代码有点多, 就单独弄了一篇博客展示 catalina.bat 脚本中的内容. 点击 [Tomcat 源码分析系列] (附件) : catalina.bat 脚本 查看.
下面咱们就按照脚本中的内容一行行的来分析.
@echo offsetlocal
这两个命令可以参考上一篇的文章的解释
第一块脚本代码
rem Suppress Terminate batch job on CTRL+Cif not ""%1"" == ""run"" goto mainEntryif "%TEMP%" == "" goto mainEntryif exist "%TEMP%\%~nx0.run" goto mainEntryecho Y>"%TEMP%\%~nx0.run"if not exist "%TEMP%\%~nx0.run" goto mainEntryecho Y>"%TEMP%\%~nx0.Y"call "%~f0" %* <"%temp%\%~nx0.y"rem use="" provided="" errorlevelset="" retval="%ERRORLEVEL%del" q="">NUL 2>&1exit /B %RETVAL%:mainEntrydel /Q "%TEMP%\%~nx0.run" >NUL 2>&1
脚本的作用
判断用户是否使用
catalina.bat run
来启动 Tomcat 的. 如果用户使用 startup.bat 脚本启动 Tomcat, 那么这段脚本不会被执行.
这段代码看起来很乱, 慢慢分析.
第一行:
注释, 意思就是: 禁止使用 CTRL+C 来终止批处理任务, 也不知道是怎么禁止的.
第二行:
if not ""%1"" == ""run"" goto mainEntry
首先明白这里的这个 "%1" 这个变量代表的是什么? 正常情况下, 这个脚本是被 startup.bat 脚本调用的, 被调用的同时传递了一个 start 参数过来(上一篇分析得出的). 在 批处理命令 中 %1 就表示命令之后的第一个参数, 在这里指的就是 start. 所以 "%1" = start. 如果用户用 catalina.bat run 命令启动 Tomcat 的话, 那么这里的 "%1" = run.
第三行:
if "%TEMP%" == "" goto mainEntry
这里的 %TEMP% 很有可能被认为是 空, 其实这里可以读取到系统的环境变量. 所以, 这里的 %TEMP% 就是系统的环境变量值, 通常装完 windows 系统的话, 系统会自动配置上这个环境变量. 所以这里一般是有值的. 大家可以去系统的环境变量看一下它指向那个目录, 一般就是 C:\Users\用户名\AppData\Local\Temp. 注意: AppData 是一个隐藏目录.
第四行:
if exist "%TEMP%\%~nx0.run" goto mainEntry
这里又出现了一个新的东西 %~nx0 . 在批处理中, 我们知道 %1 表示的是程序之后的第一个参数, 那么 %0 呢? %0 表示这个可执行程序的名称, %~nx0 的话就是程序的名称+扩展名 在这里就是 catalina.bat . 大家可以写一个小脚本(test.bat)验证一下: (我的脚本放在 D 盘下)
脚本内容:
@echo offecho "%~nx0"echo "%1"
执行结果:
PS D:\> .\test.bat Hello"test.bat""Hello"PS D:\>
第五行:
echo Y>"%TEMP%\%~nx0.run"
这段代码很简单, 就是写入字符 Y 到 %TEMP%\catalina.bat.run 文件中.
第六行:
if not exist "%TEMP%\%~nx0.run" goto mainEntry
又判断了一下 %TEMP%\catalina.bat.run 文件是否存在.
第七行:
echo Y>"%TEMP%\%~nx0.Y"
同第五行, 写入 Y 到 %TEMP%\catalina.bat.Y . 如果文件不存在, 则新建一个.
第八行:
call "%~f0" %* <"%TEMP%\%~nx0.Y"
这一行有点意思. 又出现了两个新的东西: (因为 markdown 语法限制, 把下面代码写到代码块里)
- "%~f0" : 简单说就是表示当前命令的绝对路径.- "%*" : 我们知道 %1 表示第一个参数, 依次类推, %2 表示第二个.... 那么 %* 就很好理解了, 代表所有参数.
验证一下
脚本内容:
@echo offecho "%*"echo "%~f0"
执行结果:
PS D:\> .\test.bat Hello World"Hello World""D:\test.bat"PS D:\>
那么后面的 <"%TEMP%\%~nx0.Y" 意思就是读取 %TEMP%\catalina.bat.Y 文件中的内容. 之后又通过 call 进行调用. 我们自己写一个例子, 在 D 盘建立 test.bat 文件, 再建立 catalina.bat.Y 文件
脚本内容:
call "%~f0" %* < D:/catalina.bat.Y
catalina.bat.Y 文件内容
Y
执行结果:
........D:\>call "D:\test.bat" Hello World 0call "D:\test.bat" Hello World 0call "D:\test.bat" Hello World 0call "D:\test.bat" Hello World 0call "D:\test.bat" Hello World 0call "D:\test.bat" Hello World 0call "D:\test.bat" Hello World 0<D: catalina.bat.y******="" u="" s="" i="" n="" exceeds="" limits="" ******recursion="" count="593," stack="" usage="90" percent******="" c="" h="" processing="" is="" a="" b="" o="" r="" t="" e="" d="" ******
最上面省略了很多重复代码, 从这里发现它不断地调用自己本身, 直到超出了堆栈的限制才停止. 我们如果加上 @echo off 的话
@echo offcall "%~f0" %* < D:/catalina.bat.Y
结果只会出现
D:\>.\test.bat Hello World****** B A T C H R E C U R S I O N exceeds STACK limits ******Recursion Count=593, Stack Usage=90 percent****** B A T C H PROCESSING IS A B O R T E D ******
我们这里只需要明白这些命令的作用就可以, 稍后我们会总结 Tomcat 执行这些命令的目的.
第十行:
set RETVAL=%ERRORLEVEL%
我们如果了解 Linux 的话都知道, 每个命令的执行都会返回一个执行完成之后的退出码. Linux执行完一条命令之后用 echo $? 来查看上一条命令的退出码. 在 Windows 中也是一样的, 命令执行完之后都有自己的退出码. 这里的 %ERRORLEVEL% 就是取的上面的 call 命令的退出码. 赋值给一个变量 RETVAL
第十一行:
del /Q "%TEMP%\%~nx0.Y" >NUL 2>&1
这里又出现了一个 del 命令, 很容易联想到 delete, 那么 /Q 是什么意思呢? 静默删除, 不会给你任何提示, 就比如 Linux 中的 rm -f 一样, 这里是删除 %TEMP%\catalina.bat.Y 这个文件. 后面的 >NUL 2>&1 又是什么意思呢? 于 Linux 中的输出流的重定向原理是一样的. (因为 markdown 语法限制, 把下面代码写到代码块里)
- >NUL : 表示将输出重定向到 NUL 中, 你什么也看不到- 2>&1 : 2:错误输出, &1: 标准输出, 意思就是将错误消息输出到标准输出中.- >NUL 2>&1 : 就是先将错误消息输出到标准输出中, 然后再输出到 NUL 中.
第十二行:
exit /B %RETVAL%
退出当前批处理, /B 指定退出时的编号, 把 RETVAL 最为 退出码, 也就是 call 执行的命令 的退出码.
最后两行:
:mainEntrydel /Q "%TEMP%\%~nx0.run" >NUL 2>&1
定义一个 mainEntry 标签, 然后删除 临时目录中的 catalina.bat.run 文件.
总结第一段脚本的功能
简单说, 这段代码的作用就是调用本身, 判断临时目录中的文件是否存在来避免二次回调自己. 感觉写的好复杂.
下面就进入 Tomcat 的正式启动过程, 并没有开始执行 main 方法
第二段脚本代码
rem Guess CATALINA_HOME if not definedset "CURRENT_DIR=%cd%"if not "%CATALINA_HOME%" == "" goto gotHomeset "CATALINA_HOME=%CURRENT_DIR%"if exist "%CATALINA_HOME%\bin\catalina.bat" goto okHomecd ..set "CATALINA_HOME=%cd%"cd "%CURRENT_DIR%":gotHomeif exist "%CATALINA_HOME%\bin\catalina.bat" goto okHomeecho The CATALINA_HOME environment variable is not defined correctlyecho This environment variable is needed to run this programgoto end:okHomerem Copy CATALINA_BASE from CATALINA_HOME if not definedif not "%CATALINA_BASE%" == "" goto gotBaseset "CATALINA_BASE=%CATALINA_HOME%":gotBase
这段脚本还是比较简单的, 主要是设置了两个环境变量 CATALINA_HOME 和 CATALINA_BASE . 如果没有配置 CATALINA_BASE 环境变量的话, 直接引用 CATALINA_HOME 的值 静下心来稍微看一下就懂了.
第三段脚本代码
rem Ensure that neither CATALINA_HOME nor CATALINA_BASE contains a semi-colonrem as this is used as the separator in the classpath and Java provides norem mechanism for escaping if the same character appears in the path. Check thisrem by replacing all occurrences of ";" with "" and checking that neitherrem CATALINA_HOME nor CATALINA_BASE have changedif "%CATALINA_HOME%" == "%CATALINA_HOME:;=%" goto homeNoSemicolonecho Using CATALINA_HOME: "%CATALINA_HOME%"echo Unable to start as CATALINA_HOME contains a semicolon (;) charactergoto end:homeNoSemicolonif "%CATALINA_BASE%" == "%CATALINA_BASE:;=%" goto baseNoSemicolonecho Using CATALINA_BASE: "%CATALINA_BASE%"echo Unable to start as CATALINA_BASE contains a semicolon (;) charactergoto end:baseNoSemicolon
这里主要是判断 CATALINA_HOME 环境变量的值 和 CATALINA_BASE 环境变量的值是否以 分号为结尾, 如果以 分号为结尾的话, 就报错退出.
第四段脚本代码
rem Ensure that any user defined CLASSPATH variables are not used on startup,rem but allow them to be specified in setenv.bat, in rare case when it is needed.set CLASSPATH=rem Get standard environment variablesif not exist "%CATALINA_BASE%\bin\setenv.bat" goto checkSetenvHomecall "%CATALINA_BASE%\bin\setenv.bat"goto setenvDone:checkSetenvHomeif exist "%CATALINA_HOME%\bin\setenv.bat" call "%CATALINA_HOME%\bin\setenv.bat":setenvDonerem Get standard Java environment variablesif exist "%CATALINA_HOME%\bin\setclasspath.bat" goto okSetclasspathecho Cannot find "%CATALINA_HOME%\bin\setclasspath.bat"echo This file is needed to run this programgoto end:okSetclasspathcall "%CATALINA_HOME%\bin\setclasspath.bat" %1if errorlevel 1 goto end
设置一个临时环境变量: CLASSPATH. 如果 Tomcat 的 bin 目录下面存在 setnv.bat 脚本的话, 就执行它. 通常情况下是没有的. 继而又判断 setclasspath.bat 脚本是否存在, 如果不存在的话, 直接报错, 停止启动 Tomcat. 如果存在的话, 就去调用它, 并把 第一个参数传进去.
setclasspath.bat 这个脚本主要设置了几个环境变量
JAVA_HOMEJRE_HOMEJAVA_ENDORSED_DIRS = %CATALINA_HOME%\endorsed_RUNJAVA = %JRE_HOME%\bin\java.exe_RUNJDB = %JAVA_HOME%\bin\jdb.exe
第五段脚本代码
rem Add on extra jar file to CLASSPATHrem Note that there are no quotes as we do not want to introduce randomrem quotes into the CLASSPATHif "%CLASSPATH%" == "" goto emptyClasspathset "CLASSPATH=%CLASSPATH%;":emptyClasspathset "CLASSPATH=%CLASSPATH%%CATALINA_HOME%\bin\bootstrap.jar"if not "%CATALINA_TMPDIR%" == "" goto gotTmpdirset "CATALINA_TMPDIR=%CATALINA_BASE%\temp":gotTmpdirrem Add tomcat-juli.jar to classpathrem tomcat-juli.jar can be over-ridden per instanceif not exist "%CATALINA_BASE%\bin\tomcat-juli.jar" goto juliClasspathHomeset "CLASSPATH=%CLASSPATH%;%CATALINA_BASE%\bin\tomcat-juli.jar"goto juliClasspathDone:juliClasspathHomeset "CLASSPATH=%CLASSPATH%;%CATALINA_HOME%\bin\tomcat-juli.jar":juliClasspathDone
这段代码主要做了三件事:
把 Tomcat bin 目录下的 bootstrap.jar 加入到环境变量中设置 CATALINA_TMPDIR 环境变量的值为 Tomcat 目录下的 temp 目录把 Tomcat bin 目录下的 tomcat-juli.jar 加入到环境变量中
第六段脚本代码
if not "%JSSE_OPTS%" == "" goto gotJsseOptsset JSSE_OPTS="-Djdk.tls.ephemeralDHKeySize=2048":gotJsseOptsset "JAVA_OPTS=%JAVA_OPTS% %JSSE_OPTS%"rem Register custom URL handlersrem Do this here so custom URL handles (specifically "war:...") can be used in the security policyset "JAVA_OPTS=%JAVA_OPTS% -Djava.protocol.handler.pkgs=org.apache.catalina.webresources"if not "%LOGGING_CONFIG%" == "" goto noJuliConfigset LOGGING_CONFIG=-Dnopif not exist "%CATALINA_BASE%\conf\logging.properties" goto noJuliConfigset LOGGING_CONFIG=-Djava.util.logging.config.file="%CATALINA_BASE%\conf\logging.properties":noJuliConfigset "JAVA_OPTS=%JAVA_OPTS% %LOGGING_CONFIG%"if not "%LOGGING_MANAGER%" == "" goto noJuliManagerset LOGGING_MANAGER=-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager:noJuliManagerset "JAVA_OPTS=%JAVA_OPTS% %LOGGING_MANAGER%"
主要是追加一系列的启动参数到 JAVA_OPTS 这个环境变量中.
第八段脚本代码
echo Using CATALINA_BASE: "%CATALINA_BASE%"echo Using CATALINA_HOME: "%CATALINA_HOME%"echo Using CATALINA_TMPDIR: "%CATALINA_TMPDIR%"if ""%1"" == ""debug"" goto use_jdkecho Using JRE_HOME: "%JRE_HOME%"goto java_dir_displayed:use_jdkecho Using JAVA_HOME: "%JAVA_HOME%":java_dir_displayedecho Using CLASSPATH: "%CLASSPATH%"
主要是打印相关的环境变量信息.
第九段脚本代码
set _EXECJAVA=%_RUNJAVA%set MAINCLASS=org.apache.catalina.startup.Bootstrapset ACTION=startset SECURITY_POLICY_FILE=set DEBUG_OPTS=set JPDA=
设置一些列的环境变量:
_RUNJAVA : %JRE_HOME%\bin\java.exeMAINCLASS : 指定了 Tomcat 的启动类, 没错 main 方法就是在这个类里面.ACTION : 动作: 就是启动SECURITY_POLICY_FILE : 安全策略文件, 如果启动的时候加上了 -security 参数的话, 下面会对这个参数指定到 Tomcat 的 conf 目录下的 catalina.policy 文件.JPDA : 这个参数可以百度一下, 我们平时几乎用不到.
第十段代码
if not ""%1"" == ""jpda"" goto noJpdaset JPDA=jpdaif not "%JPDA_TRANSPORT%" == "" goto gotJpdaTransportset JPDA_TRANSPORT=dt_socket:gotJpdaTransportif not "%JPDA_ADDRESS%" == "" goto gotJpdaAddressset JPDA_ADDRESS=localhost:8000:gotJpdaAddressif not "%JPDA_SUSPEND%" == "" goto gotJpdaSuspendset JPDA_SUSPEND=n:gotJpdaSuspendif not "%JPDA_OPTS%" == "" goto gotJpdaOptsset JPDA_OPTS=-agentlib:jdwp=transport=%JPDA_TRANSPORT%,address=%JPDA_ADDRESS%,server=y,suspend=%JPDA_SUSPEND%:gotJpdaOptsshift:noJpda
好像直接从第一行跳到了最后一行, 没错, 一般我没启动的时候没有加 jpda 参数的话, 这里会直接跳过, 里面的脚本是关于 JPDA 的设置等.
第十一段脚本代码
if ""%1"" == ""debug"" goto doDebugif ""%1"" == ""run"" goto doRunif ""%1"" == ""start"" goto doStartif ""%1"" == ""stop"" goto doStopif ""%1"" == ""configtest"" goto doConfigTestif ""%1"" == ""version"" goto doVersionecho Usage: catalina ( commands ... )echo commands:echo debug Start Catalina in a debuggerecho debug -security Debug Catalina with a security managerecho jpda start Start Catalina under JPDA debuggerecho run Start Catalina in the current windowecho run -security Start in the current window with security managerecho start Start Catalina in a separate windowecho start -security Start in a separate window with security managerecho stop Stop Catalinaecho configtest Run a basic syntax check on server.xmlecho version What version of tomcat are you running?goto end
好似一个 switch 开关.
如果我们用 startup.bat 启动 Tomcat 的话, 这里的 "%1" 的值是 start如果通过 catalina.bat run 启动 Tomcat 的话, 这里的 "%1" 的值是 run
第十二段脚本代码
:doRunshiftif not ""%1"" == ""-security"" goto execCmdshiftecho Using Security Managerset "SECURITY_POLICY_FILE=%CATALINA_BASE%\conf\catalina.policy"goto execCmd:doStartshiftif "%TITLE%" == "" set TITLE=Tomcatset _EXECJAVA=start "%TITLE%" %_RUNJAVA%if not ""%1"" == ""-security"" goto execCmdshiftecho Using Security Managerset "SECURITY_POLICY_FILE=%CATALINA_BASE%\conf\catalina.policy"goto execCmd
首先分析一下其中的两个 shift 命令 第一个 shift 是把 start 或者 run 参数移除, 然后下面 还是利用 "%1" 来取参数, 这时候, 取出来的就是参数列表中的第二个. 第二个 shift 是在第二个参数移除掉.
我们再来比较一下 start 和 run 的启动区别.
差别
if "%TITLE%" == "" set TITLE=Tomcatset _EXECJAVA=start "%TITLE%" %_RUNJAVA%
如果是 startup.bat 脚本启动的话, 会启动一个新的 cmd 窗口, 并且把 cmd 的 title 设置为 Tomcat.如果是 catalina.bat run 启动的话, 不会新建 cmd 窗口, 也不会设置 cmd 的 title.
最后都跳到了 execCmd 标签处.
第十三段脚本代码
:execCmdrem Get remaining unshifted command line arguments and save them in theset CMD_LINE_ARGS=:setArgsif ""%1""=="""" goto doneSetArgsset CMD_LINE_ARGS=%CMD_LINE_ARGS% %1shiftgoto setArgs:doneSetArgs
这里还是利用 "%1" 来取出启动命令之后的参数, 如果存的话, 追加到 CMD_LINE_ARGS 环境变量上, 并把这个参数移除. 通常情况下, 我们这里是不会有什么参数了, -security 这个参数我们都不会追加.
继续往下走.
第十四段脚本代码
rem Execute Java with the applicable propertiesif not "%JPDA%" == "" goto doJpdaif not "%SECURITY_POLICY_FILE%" == "" goto doSecurity%_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%goto end
很明显, 我们的 %JPDA% 没有值, 不会跳转; 由于我们没有加 -security 参数, 所以 %SECURITY_POLICY_FILE% 没有值, 不会跳转.
下面这段长命令就是来启动 BootStrap 类, 并把相应的参数传进去.
只要把对应的环境变量替换为它们的值, 就可以解析出这个长命令的内容. 相信你可以的. Be patient!
总结一下
首先判断一下用户直接使用 catalina.bat run 来启动 Tocmat设置 CATALINA_HOME 和 CATALINA_BASE 环境变量值验证 CATALINA_HOME 和 CATALINA_BASE 环境变量值的正确性调用 setnv.bat 脚本调用 setclasspath.bat 脚本添加 bootstrap.jar 和 tomcat-juli.jar 到 CLASSPATH 中设置 CATALINA_TMPDIR 临时目录的值为 Tomcat 目录下的 temp追加一系列的参数到 JAVA_OPTS 中整合相关的启动信息, 参数启动 Tomcat
标签:
相关推荐:
最新新闻:
- 天天观热点:成像系统设计中的激光扫描系统——多重组态
- 云桌面是什么?如何正确认识云桌面?-世界热点
- catalina.bat脚本启动Tomcat源码分析 快资讯
- 地下城与勇士缔造者刷图怎么加点?地下城与勇士缔造者刷图加点攻略_时快讯
- U盘数据丢失了怎么办?恢复数据的详细步骤在这里:环球时快讯
- 戴尔笔记本电脑多少钱?戴尔笔记本电脑报价及图片
- Autorun.inf Autorun.inf进程是什么?Autorun.inf病毒到底能干什么?
- 【课程】E2PROMM电路设计 74ls138译码器设计流程图|快资讯
- 快看:ipod和iphone有什么区别?iPod和iPhone区别介绍
- 天天播报:python可以自学吗?如何从零基础开始学的Python?
- 美图秀秀app漫画特效在哪?漫画滤镜使用教程
- yandex注册验证码怎么填?怎么玩英雄联盟外服手游?|每日短讯
- 库克20亿奖金漫天飞舞!苹果iPhone12系列电池容量曝光
- 成都建筑设计公司有哪些?成都建筑设计公司排名:全球即时
- bak文件怎么打开?cad文件被误删了怎么办?|当前热议
- Ubuntu常用指令有哪些?Ubuntu常用指令及修改快捷键大全_焦点速看
- 给查询的字段起别名方式:resultMap自定义映射|全球时快讯
- ps磨皮有哪些技巧?ps手工磨皮教程_世界时快讯
- DVR监控软件 V1.0基本参数介绍 DVR监控软件 V1.0安装方法_每日观点
- 快速入门!ANSYS2020版本入门教程
- 全球快资讯丨为什么手机显示移动4g?上网还是很慢?
- 小岛秀夫曾想与周润发合作:也很喜欢张国荣、徐克和吴宇森|时讯
- 环球信息:三星3650怎么刷机?三星S3650C刷机教程
- 全球最新:常州plc培训:PLC发展过程及自动化前景
- 电脑小常识之电脑硬件教程 计算机硬件的最基础知识
- 颈椎按摩器品牌排行如何选购?颈椎按摩器品牌排行详情介绍_今日最新
- vr视频怎么看?准备工具有哪些?VR眼镜怎么用?
- 世界快报:兰博基尼Antares怎么样?兰博基尼Antares评测
- 【世界时快讯】学习3d建模没有基础可以学吗?学习3D建模需要多少钱?
- 诺基亚c7最新报价是多少?诺基亚c7最新报价参考
- 2019上海劳力士大师赛:梅德维德夫与兹维列夫会师男单决赛
- 环球最资讯丨linux下搭建Nexus3Maven私服 史上最全搭建MAVEN私服上传并使用JAR包教程
- 瑞幸咖啡被吐槽三四口喝完剩下全是冰 客服:门店按配方比例制作
- 【数据分析】门店客流统计的常见技术及其优劣势分析-世界速讯
- 【热闻】word如何在任意页开始添加页码?excel如何将光标定位到下一页?
- toto智能坐便器有哪些型号?toto智能坐便器型号介绍
- 华为p40怎么升级成鸿蒙系统?华为p40升级鸿蒙系统完整版教程-快看点
- 每日动态!如何实现自动化?IT实现工作流程自动化的8个步骤
- cad中怎么输入文字?CAD输入文本文字技巧教程 环球播报
- 基于产品间共性的“软件”产品线代表了什么?产品线及系统演化
- 华为HiLink是什么?华为路由器hilink怎么用?
- 阿里旺旺有什么功能?阿里旺旺7.10.08C官方版软件下载
- 如何发微信到朋友圈?详细方法步骤如下
- 想给孩子存钱怎样存合算?儿童银行卡的存款类型有哪些?_今日报
- 拼多多预售可以取消吗?拼多多预售怎么取消? 每日头条
- 网吧服务器维护工具怎么下载?网吧维护管理助手使用方法:天天观热点
- 天天热头条丨什么品牌的净水器好?摩恩达净水器推荐
- 全球视点!【技术方案】脉冲电子围栏防护系统设计方案
- 【欧洲杯】德国队4-2击败葡萄牙 卫冕冠军球队出局
- 网易游戏成立新动画品牌Anici 正在制作多部作品-世界独家
- 苹果用户大喜!iPhone折旧率赚大了-独家焦点
- 新成就解锁!九号电动国内累计出货量突破150万辆,增速创纪录
- 今日热讯:真保值!iPhone折旧率仅为安卓一半 三年的旧iPhone仅折旧11.3%
- 小米发放20亿股份奖励:4110人能领取
- 成龙新片《龙马精神》新海报:成龙和赤兔眼神炙热:微速讯
- 征服者康演员因殴打女性被捕 现已被担保释放
- 世界快资讯:小岛秀夫观看经典电影《英雄本色》 曾想制作周润发为主角的游戏
- 滚动:外星人x14 R2国行价格公布 RTX4050版定价14999元
- 加拿大华裔店员遭种族歧视 涉歧视者曾多次发表种族言论:环球简讯
- 苹果MR头显售价超20000元_全球观察
- 苹果MR头显销量目标曝光:太大胆!
- 百亿补贴史低价!2T固态硬盘仅529元
- iOS 17升级不再小打小闹了 世界观焦点
- 最新消息:曝《刺客信条:幻景》将延期发售!仍采用旧引擎开发
- 热消息:《暗黑破坏神4》国外主播单挑世界Boss!甚至击杀了两次
- 《辐射》编剧被前女友控告虐待 诽谤诉讼现已获赔|焦点要闻
- 每日消息!《英雄连3》明日发布免费大型更新“蓝宝石豺狼行动”
- 世界资讯:彭博社报道苹果高层相聚加州 或于近日公开AR/VR头显
- 国产游戏《动物派对》通过ESRB评级 青少年级别13+
- 国产科幻独立游戏《逐光星火》新预告 4月12日发售_环球热资讯
- 小阔条纹蝶内容概括300_小阔条纹蝶内容概括
- 天天速看:性能可期!AMD R7 7800X3D测试曝光,比5800X3D快37.3%
- 环球热文:复刻MacOS?微软Win12原型系统用户界面曝光:浮动任务栏亮相
- 全球速读:独一无二!一加11限定版用上了前所未有的材质
- 小李子新片《花月杀手》定档!10.6北美首映