最新要闻
- 【聚看点】澳弘电子(605058.SH):年产高精密度多层板、高密度互连积层板120万平方米建设项目及研发中心升级改造项目结项
- 真我GT Neo5 SE“最终幻想”配色回归:赛博朋克风满满
- 当前消息!《黑暗荣耀2》蝉联网飞电视剧全球榜第一:观看时间过亿
- 当前热门:又一日系即将退出中国?广汽三菱否认传闻:正常运转
- 世界看点:被谷歌商店下架 拼多多驳斥恶意软件指控!
- 世界聚焦:上市五年市值缩水99.6%!昔日“明星”趣头条宣布退市
- 今头条!北方多地迎沙尘!长春直接下“泥雨”:汽车变泥车
- 环球速讯:OPPO、小米、vivo联盟:跨品牌一键换机新增第三方应用迁移
- 最新消息:史上最强骁龙7系 真我GT Neo5 SE跑分过百万:16+1TB存储确认
- 今日关注:Alphaliner:2月底港口停运船比率达6.4% 增至去年同期3倍
- 4月上映!《长空之王》新预告出炉:王一博驾驶歼20一飞冲天
- 世界今头条!自嗨锅自热煲仔饭4桶到手价32.8元:15分钟速烹美食
- 今年以来最强沙尘来袭!你的城市在下雨 他的城市在下泥
- 邓紫棋你把我灌醉高音最高是多少?邓紫棋你把我灌醉歌词
- 苏菲日记是什么?苏菲日记的读后感怎么写?
- strict怎么读?strict的反义词是什么?
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
你可能不那么知道的Tomcat生命周期管理 | 博学谷狂野架构师
Tomcat生命周期管理
各种组件如何统一管理
Tomcat的架构设计是清晰的、模块化、它拥有很多组件,加入在启动Tomcat时一个一个组件启动,很容易遗漏组件,同时还会对后面的动态组件拓展带来麻烦。如果采用我们传统的方式的话,组件在启动过程中如果发生异常,会很难管理,比如你的下一个组件调用了start方法,但是如果它的上级组件还没有start甚至还没有init的话,Tomcat的启动会非常难管理,因此,Tomcat的设计者提出一个解决方案:用Lifecycle管理启动,停止、关闭。
生命周期统一接口
Tomcat内部架构中各个核心组件有包含与被包含关系,例如:Server包含了Service.Service又包含了Container和Connector,这个结构有一点像数据结构中的树,树的根结点没有父节点,其他节点有且仅有一个父节点,每一个父节点有0至多个子节点。所以,我们可以通过父容器启动它的子容器,这样只要启动根容器,就可以把其他所有的容器都启动,从而达到了统一的启动,停止、关闭的效果。
所有所有组件有一个统一的接口——Lifecycle,把所有的启动、停止、关闭、生命周期相关的方法都组织到一起,就可以很方便管理Tomcat各个容器组件的生命周期。
【资料图】
Lifecycle其实就是定义了一些状态常量和几个方法,主要方法是init,start,stop三个方法。
例如:Tomcat的Server组件的init负责遍历调用其包含所有的Service组件的init方法。
注意:Server只是一个接口,实现类为StandardServer,有意思的是,StandardServer没有init方法,init方法是在哪里,其实是在它的父类LifecycleBase中,这个类就是统一的生命周期管理。
COPYpublic class StandardService extends LifecycleMBeanBase implements Service public abstract class LifecycleMBeanBase extends LifecycleBase implements JmxEnabled
LifecycleBase
COPYpublic abstract class LifecycleBase implements Lifecycle { @Override public final synchronized void init() throws LifecycleException { //这个就是为了防止 组件启动的顺序不对 if (!state.equals(LifecycleState.NEW)) { invalidTransition(Lifecycle.BEFORE_INIT_EVENT); } try { //只打印核心组件 if(this.getClass().getName().startsWith("org.apache.catalina.core")||this.getClass().getName().startsWith("org.apache.catalina.connector")){ System.out.println(this.getClass()+"--init()"); } setStateInternal(LifecycleState.INITIALIZING, null, false); //调用子类的initInternal方法 initInternal(); setStateInternal(LifecycleState.INITIALIZED, null, false); } catch (Throwable t) { handleSubClassException(t, "lifecycleBase.initFail", toString()); } } }
所以StandardServer最终只会调用到initInternal方法,这个方法会初始化子容器Service的init方法
为什么LifecycleBase这么玩,其实很多架构源码都是这么玩的,包括JDK的容器源码都是这么玩的,一个类,有一个接口,同时抽象一个抽象骨架类,把通用的实现放在抽象骨架类中,这样设计就方便组件的管理,使用LifecycleBase骨架抽象类,在抽象方法中就可以进行统一的处理。
LifeCycle源码分析
作用
组件生命周期方法的通用接口。 Catalina组件可以实现此接口(以及它们支持的功能的适当接口),以便提供一致的机制来启动和停止组件
状态图
Tomcat中的事件触发是通过这些状态来判定的。
COPY* start() * ----------------------------- * | | * | init() | * NEW -»-- INITIALIZING | * | | | | ------------------«----------------------- * | | |auto | | | * | | \|/ start() \|/ \|/ auto auto stop() | * | | INITIALIZED --»-- STARTING_PREP --»- STARTING --»- STARTED --»--- | * | | | | | * | |destroy()| | | * | --»-----«-- ------------------------«-------------------------------- ^ * | | | | * | | \|/ auto auto start() | * | | STOPPING_PREP ----»---- STOPPING ------»----- STOPPED -----»----- * | \|/ ^ | ^ * | | stop() | | | * | | -------------------------- | | * | | | | | * | | | destroy() destroy() | | * | | FAILED ----»------ DESTROYING ---«----------------- | * | | ^ | | * | | destroy() | |auto | * | --------»----------------- \|/ | * | DESTROYED | * | | * | stop() | * ----»-----------------------------»------------------------------
接口定义
Lifecycle接口统一管理Tomcat生命周期。一共做了4件事:
- 定义13个string类型常量,用于LifecycleEvent时间的type属性中,用于区分组件发出的LifecycleEvent事件时的状态。
- 定义三个管理监听器的方法,addLifecycleListener、findLifecycleListeners、removeLifecycleListener。
- 定义4个生命周期的方法,init、start、stop、destory,用于执行生命周期的各个阶段的操作。
- 定义了获取当前状态的两个方法,getState、getStateName、用于获取当前的状态。
COPYpublic interface Lifecycle { // 13个状态常量值 public static final String BEFORE_INIT_EVENT = "before_init"; public static final String AFTER_INIT_EVENT = "after_init"; public static final String START_EVENT = "start"; public static final String BEFORE_START_EVENT = "before_start"; public static final String AFTER_START_EVENT = "after_start"; public static final String STOP_EVENT = "stop"; public static final String BEFORE_STOP_EVENT = "before_stop"; public static final String AFTER_STOP_EVENT = "after_stop"; public static final String AFTER_DESTROY_EVENT = "after_destroy"; public static final String BEFORE_DESTROY_EVENT = "before_destroy"; public static final String PERIODIC_EVENT = "periodic"; public static final String CONFIGURE_START_EVENT = "configure_start"; public static final String CONFIGURE_STOP_EVENT = "configure_stop"; // 3个监听器方法 public void addLifecycleListener(LifecycleListener listener); public LifecycleListener[] findLifecycleListeners(); public void removeLifecycleListener(LifecycleListener listener); // 4个生命周期方法 public void init() throws LifecycleException; public void start() throws LifecycleException; public void stop() throws LifecycleException; public void destroy() throws LifecycleException; // 2个当前状态方法 public LifecycleState getState(); public String getStateName();}
默认实现类
COPYpublic abstract class LifecycleBase implements Lifecycle { // 源组件的当前状态,不同状态触发不同事件 private volatile LifecycleState state = LifecycleState.NEW;}
监听器相关方法
事件监听器需要三个参与者:
- 事件对象:用于封装事件的信息,在事件监听器接口的同一方法中作为参数使用,继承自java.util.EventObject类。
- 事件源:触发事件的源头,不同事件源触发不同事件类型。
- 事件监听器:负责监听事件源发出的事件。实现 java.util.EventListener 接口。
COPY// 用于事件通知的已注册LifecycleListener列表private final List lifecycleListeners = new CopyOnWriteArrayList<>();@Overridepublic void addLifecycleListener(LifecycleListener listener) { lifecycleListeners.add(listener);}@Overridepublic LifecycleListener[] findLifecycleListeners() { return lifecycleListeners.toArray(new LifecycleListener[0]);}@Overridepublic void removeLifecycleListener(LifecycleListener listener) { lifecycleListeners.remove(listener);}// 子类根据当前状态触发不同事件,实现不同操作protected void fireLifecycleEvent(String type, Object data) { LifecycleEvent event = new LifecycleEvent(this, type, data); for (LifecycleListener listener : lifecycleListeners) { listener.lifecycleEvent(event); }}
生命周期方法
LifecycleBase 类是Lifecycle 接口的默认实现,所有实现了生命周期的组件都直接或者间接的继承自LifecycleBase。
init方法
COPY@Overridepublic final synchronized void init() throws LifecycleException { // 只有 NEW 状态可以调用 if (!state.equals(LifecycleState.NEW)) { invalidTransition(Lifecycle.BEFORE_INIT_EVENT); } // 设置 生命周期状态 -- INITIALIZING,触发相应事件 setStateInternal(LifecycleState.INITIALIZING, null, false); // 模板方法,由具体子类实现 initInternal(); // 执行完成,设置生命周期状态 -- INITIALIZED,触发相应事件 setStateInternal(LifecycleState.INITIALIZED, null, false);}
Start方法
COPY@Overridepublic final synchronized void start() throws LifecycleException { // 此三种状态不执行 if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) || LifecycleState.STARTED.equals(state)) { return; } // NEW 状态 执行 init 方法 if (state.equals(LifecycleState.NEW)) { init(); // 启动失败,调用 stop 方法 } else if (state.equals(LifecycleState.FAILED)) { stop(); // 其它状态,非法操作 } else if (!state.equals(LifecycleState.INITIALIZED) && !state.equals(LifecycleState.STOPPED)) { throw new LifecycleException() } // 设置启动状态为 STARTING_PREP【开始准备】 setStateInternal(LifecycleState.STARTING_PREP, null, false); startInternal(); // 调用完成后判断组件启动状态 if (state.equals(LifecycleState.FAILED)) { stop(); } else if (!state.equals(LifecycleState.STARTING)) { throw new LifecycleException(); } else { setStateInternal(LifecycleState.STARTED, null, false); }}
stop方法
COPY@Overridepublic final synchronized void stop() throws LifecycleException { // STOPPING_PREP、STOPPING、STOPPED此三种状态不予执行 if (LifecycleState.STOPPING_PREP.equals(state) || LifecycleState.STOPPING.equals(state) || LifecycleState.STOPPED.equals(state)) { return; } // 如果状态为 NEW、修改为 STOPPED if (state.equals(LifecycleState.NEW)) { state = LifecycleState.STOPPED; return; } // 不为 STARTED、FAILED 2种状态,抛出异常 if (!state.equals(LifecycleState.STARTED) && !state.equals(LifecycleState.FAILED)) { throw new LifecycleException(); } try { // 启动失败,触发事件,否则设置生命周期状态 if (state.equals(LifecycleState.FAILED)) { fireLifecycleEvent(BEFORE_STOP_EVENT, null); } else { setStateInternal(LifecycleState.STOPPING_PREP, null, false); } // 调用模板方法 stopInternal(); if (!state.equals(LifecycleState.STOPPING) && !state.equals(LifecycleState.FAILED)) { throw new LifecycleException(); } setStateInternal(LifecycleState.STOPPED, null, false); } catch (Throwable t) { setStateInternal(LifecycleState.FAILED, null, false); throw new LifecycleException(); } finally { if (this instanceof Lifecycle.SingleUse) { setStateInternal(LifecycleState.STOPPED, null, false); destroy(); } }}
destroy方法
COPY@Overridepublic final synchronized void destroy() throws LifecycleException { // 启动失败,先调用 stop 方法 if (LifecycleState.FAILED.equals(state)) { stop(); } // DESTROYING、DESTROYED不执行了 if (LifecycleState.DESTROYING.equals(state) || LifecycleState.DESTROYED.equals(state)) { return; } // 非法状态 if (!state.equals(LifecycleState.STOPPED) && !state.equals(LifecycleState.FAILED) && !state.equals(LifecycleState.NEW) && !state.equals(LifecycleState.INITIALIZED)) { throw new LifecycleException(); } try { setStateInternal(LifecycleState.DESTROYING, null, false); destroyInternal(); setStateInternal(LifecycleState.DESTROYED, null, false); } catch (Throwable t) { setStateInternal(LifecycleState.FAILED, null, false); throw new LifecycleException(); }}
设置状态方法
COPYprivate synchronized void setStateInternal(LifecycleState state, Object data, boolean check) throws LifecycleException { // 检查参数 if (check) { if (state == null) { throw new LifecycleException(); return; } if (!(state == LifecycleState.FAILED || (this.state == LifecycleState.STARTING_PREP && state == LifecycleState.STARTING) || (this.state == LifecycleState.STOPPING_PREP && state == LifecycleState.STOPPING) || (this.state == LifecycleState.FAILED && state == LifecycleState.STOPPING))) { throw new LifecycleException(); } } this.state = state; String lifecycleEvent = state.getLifecycleEvent(); if (lifecycleEvent != null) { fireLifecycleEvent(lifecycleEvent, data); }}
监听机制
事件监听器需要三个参与者:
- 事件对象—用于封装事件的信息,在事件监听器接口的统一方法中作为参数,一般继承 java.util.EventObjecct类。
- 事件源—触发事件对的源头,不同事件源触发不同事件。
- 事件监听器—负责监听事件源发出的事件,发生事件时,事件源调用事件监听器的统一方法处理。监听器一般实现java.util.EventListener接口。
COPYpublic final class LifecycleEvent extends java.util.EventObject { public LifecycleEvent(Lifecycle lifecycle, String type, Object data) { super(lifecycle); this.type = type; this.data = data; }}COPYpublic interface LifecycleListener { public void lifecycleEvent(LifecycleEvent event);}COPYpublic class HostConfig implements LifecycleListener { @Override public void lifecycleEvent(LifecycleEvent event) { try { host = (Host) event.getLifecycle(); if (host instanceof StandardHost) { setCopyXML(((StandardHost) host).isCopyXML()); setDeployXML(((StandardHost) host).isDeployXML()); setUnpackWARs(((StandardHost) host).isUnpackWARs()); setContextClass(((StandardHost) host).getContextClass()); } } catch (ClassCastException e) { return; } // Process the event that has occurred if (event.getType().equals(Lifecycle.PERIODIC_EVENT)) { check(); } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) { beforeStart(); } else if (event.getType().equals(Lifecycle.START_EVENT)) { start(); } else if (event.getType().equals(Lifecycle.STOP_EVENT)) { stop(); } }}COPY// LifecycleBase.startInternalthis.state = state;String lifecycleEvent = state.getLifecycleEvent();if (lifecycleEvent != null) { fireLifecycleEvent(lifecycleEvent, data);}protected void fireLifecycleEvent(String type, Object data) { LifecycleEvent event = new LifecycleEvent(this, type, data); for (LifecycleListener listener : lifecycleListeners) { listener.lifecycleEvent(event); }}
模板方法
四个模板方法,由子类具体实现
COPYprotected abstract void initInternal() throws LifecycleException;protected abstract void startInternal() throws LifecycleException;protected abstract void stopInternal() throws LifecycleException;protected abstract void destroyInternal() throws LifecycleException;
总结
通过提供init、start、stop、destory方法,通过相应的模板方法【模板设计模式】,提供组件的统一生命周期的管理、事件调度。
本文由
传智教育博学谷狂野架构师
教研团队发布。如果本文对您有帮助,欢迎
关注
和点赞
;如果您有任何建议也可留言评论
或私信
,您的支持是我坚持创作的动力。转载请注明出处!
关键词:
-
全球热文:CSIG企业行-走进合合信息成功举行,聚焦生成式人工智能、智能文档处理前沿热点
3月18日,由中国图象图形学学会(CSIG)主办,合合信息、CSIG文档图像分析与识别专业委员会联合承办的“...
来源: -
环球今日讯!webrtc QOS笔记三 RTT计算,SRS增加XR
webrtcQOS笔记三RTT计算,SRS增加XRRTT计算方式WebRTC中目前有两种方式计算RTT:基于媒体流发送端的计算...
来源: -
全球新资讯:国家药监局:2022年抽检11类化妆品20368批次产品 合格率97.60%
2022年,全国药品监督管理部门以问题多发的品种、场所以及近年来抽检不符合规定的产品涉及的企业为重点...
来源: 全球热文:CSIG企业行-走进合合信息成功举行,聚焦生成式人工智能、智能文档处理前沿热点
环球今日讯!webrtc QOS笔记三 RTT计算,SRS增加XR
你可能不那么知道的Tomcat生命周期管理 | 博学谷狂野架构师
全球新资讯:国家药监局:2022年抽检11类化妆品20368批次产品 合格率97.60%
每日速看!【财经分析】投资收益拖累2022年券商业绩 投行业务景气度仍处于高位
【聚看点】澳弘电子(605058.SH):年产高精密度多层板、高密度互连积层板120万平方米建设项目及研发中心升级改造项目结项
真我GT Neo5 SE“最终幻想”配色回归:赛博朋克风满满
当前消息!《黑暗荣耀2》蝉联网飞电视剧全球榜第一:观看时间过亿
当前热门:又一日系即将退出中国?广汽三菱否认传闻:正常运转
世界看点:被谷歌商店下架 拼多多驳斥恶意软件指控!
世界聚焦:上市五年市值缩水99.6%!昔日“明星”趣头条宣布退市
新资讯:火山引擎 VeDI 零售行业解决方案 聚焦精准营销场景提升品牌转化
面试官:ThreadLocal 为什么会内存泄漏吗?是怎么产生的?面试必问!
JavaSE笔记之注解和反射(含案例代码)
当前速递!Android BLE 广播定制
环球报道:关于asp.net core filters生命周期的探究
今头条!北方多地迎沙尘!长春直接下“泥雨”:汽车变泥车
环球速讯:OPPO、小米、vivo联盟:跨品牌一键换机新增第三方应用迁移
最新消息:史上最强骁龙7系 真我GT Neo5 SE跑分过百万:16+1TB存储确认
世界今热点:孤立森林(IForest)代码实现及与PyOD对比
最资讯丨three.js加载环境贴图
天天热讯:java中同步和异步的区别
今日关注:Alphaliner:2月底港口停运船比率达6.4% 增至去年同期3倍
4月上映!《长空之王》新预告出炉:王一博驾驶歼20一飞冲天
世界今头条!自嗨锅自热煲仔饭4桶到手价32.8元:15分钟速烹美食
今年以来最强沙尘来袭!你的城市在下雨 他的城市在下泥
邓紫棋你把我灌醉高音最高是多少?邓紫棋你把我灌醉歌词
苏菲日记是什么?苏菲日记的读后感怎么写?
strict怎么读?strict的反义词是什么?
主题班会的开场白有什么要求?主题班会的开场白怎么写?
河南洛阳旅游景点排名前十名有哪些?河南洛阳美食排行榜前十名
当前简讯:一种基于实时大数据的图指标解决方案
【天天报资讯】智能零售柜商品检测软件(Python+YOLOv5深度学习模型+清新界面)
来了解下GNOME 2 的 Linux 文件管理器 Caja
【状压DP】蒙德里安的梦想
【环球新要闻】害怕开车也不愁 小米汽车新专利:根据驾驶员匹配自动驾驶等级
前沿资讯!漫威《美国队长4:新世界秩序》开拍 片场首曝:“猎鹰”美队来了
每日热门:一语成谶!网传鬼火少年“三部曲”:片头壮行、片尾撞车
俄罗斯伏尔加汽车厂:中国汽车是新款拉达Vesta的主要对手
省钱神器!淘宝内测同款比价功能:最快本周上线
全球速看:记一次 rr 和硬件断点解决内存踩踏问题
天天新资讯:生核桃怎么变熟核桃_生核桃怎么弄熟
魅族20 PRO三款配色公布:绝美朝阳金吸睛
天天快消息!25日继续公测 暴雪承诺《暗黑4》会解决排队问题:服务器顶得住
环球精选!沙尘天气席卷!北京再现火星同款“蓝太阳” 专家揭秘
世界即时:开特斯拉回村被乡亲群嘲“大冤种” 30万买BBA不好吗?
今日热门!微信聊天记录不用头疼了:vivo宣布跨品牌换机数据迁移
基于深度学习的安全帽检测系统(YOLOv5清新界面版,Python代码)
MAC 最新中文版 FCPX 10.6.5 及相关插件下载安装教程 亲测有效
大数据计算引擎 EasyMR:拥抱开源,引领技术创新
当前聚焦:Java面试问题
解放生产力:30+实用AI工具汇总
全球今日讯!谷歌版ChatGPT开放测试:不会编程、不支持中文
全球聚焦:微软研究员质疑Bard数据集包括Gmail邮件:谷歌迅速澄清
2999元 小米米家对开门540L冰箱今晚开售:风冷无霜保鲜
120mg原生高钙 特仑苏纯牛奶3元/盒大促
天天最新:破解大难题!苹果新专利:佩戴太阳镜也可看清iPhone屏幕内容
【报资讯】vue3
世界视点!设计模式-index
精彩看点:商铺上月买卖91宗 涉21亿创半年高
热点评!平板电脑屏幕冷知识:高刷比高分辨率重要
焦点热讯:比尔·盖茨:GPT是1980年以来最革命性的技术进步
今日热讯:极氪009平均订单金额达52.7万元 一台订单收入相当于吉利卖5台
谷歌版ChatGPT Bard开启公测!CEO:肯定会出错、欢迎反馈
世界焦点!漫威《美队4》正式开拍 首曝片场路透:猎鹰美队就位
世界热议:Android性能优化-ListView自适应性能问题
读C#代码整洁之道笔记03_切面关注点、异常处理和线程与并发
当前时讯:放假一天不调休!2023清明节假期火车票今日开售
快资讯丨《霍格沃茨》在线人数锐减 “反恐同”人士冷嘲热讽
环球今日讯!《战争机器》电影确定编剧
世界聚焦:刚买1个月的特斯拉掉漆车主质疑非新车 售后:交付时没问题
热消息:公交司机急刹车致乘客死亡获刑 官方判定当时不需刹车:交通肇事罪
全球最资讯丨新车品鉴:2020广州车展探馆:广汽传祺EMPOWER
每日热闻!【看新股】10家拟上市银行“换道”注册制:利润普遍增长、资产质量分化、资本充足率承压
精选!iOS 17将支持第三方应用商店:看齐安卓
读Java性能权威指南(第2版)笔记24_ Java飞行记录器JFR
洗衣机6个月不清洗比马桶还脏?我麻了
时代的眼泪 25年历史的数码相机权威网站Dpreview关停
当前快报:Go 并发编程(二):锁、Select、Context、定时器
焦点报道:智能生活垃圾检测与分类系统(UI界面+YOLOv5+训练数据集)
当前通讯!Servlet的基本使用
每日资讯:中国移动:命苦
天天速看:鹿客发布掌心锁V5:掌静脉+3D结构光刷脸解锁 3399元起
尼泊尔空难20天就查明原因?为什么东航事故还没有结果?
世界微资讯!腾讯四年磨一剑!结果 让大家看笑话了
OPPO Find X6 Pro:表演一场简单的光影魔术
亨迪药业(301211):3月21日北向资金减持66.63万股
世界最资讯丨uni-app云开发入门
当前资讯!WPF学习-布局
世界热文:全明星爆款APP“偶像驾到”正式上线 主播经济和明星经纪开启新时代
小屏手机真凉了!苹果卖最差的iPhone 14 Plus 出货量也远超13 mini
今日快讯:mysql集群搭建docker-compose方案
上海农商银行完成上海市政府柜台债发售
山姆会员店寿司致多人上吐下泻:官方拒绝三倍赔偿
天天新消息丨致敬葫芦娃?保时捷911 Carrera GTS泰国特别版官图发布
环球最资讯丨14.5英寸超大屏+天玑9000!联想Y900平板正式发布:4999元起
天天观点:13代酷睿+RTX 40!联想拯救者Y7000P 2023电竞本发布:屏幕大升级
13900HX+4090顶配20999元!联想拯救者Y9000P 2023价格公布
环球最新:睡眠报告:山东人上床睡觉时间全国最早、广东人最能熬夜
焦点快看:埃安(埃)