最新要闻
- 石家庄太和网点电话_石家庄太和网
- 3个ChatGPT插件自动写书爆火!更多躺着赚钱玩法可以问AI自己_全球看热讯
- 3.1级地震发生时上海居民躲床下避难 监控视频显示:剧烈摇晃、余震可能性不大|今头条
- 足球服批发_足球装备批发|热推荐
- 男子赶集买毛蛋到家全变鸭子 后续会饲养:网友点赞赚大了
- 直言理想ONE被问界M7打残 学习华为要看什么书:李想整理分享
- 丰田手把手教经销商诋毁纯电|每日动态
- 矛盾的马斯克!特斯拉4D雷达首拆:千元成本、探测距离300米
- 女学霸边读研边兼职3年赚17万:为了明确自己毕业后适合什么工作
- 港股午评:恒指涨0.83% 恒生科技指数涨1.82% 天天看点
- 热点在线丨会定期删除 微软回应Edge私传图片问题:不含任何用户标识
- 天玑9200+加持 vivo X90s官宣:安卓最强悍的曲面屏旗舰
- 全球聚焦:李想最新发言引围观:我们谁都没兴趣搞、那点销量有啥可搞的
- 温州近视司机深夜高速上镜片突然脱落:两眼一抹黑
- 动态焦点:张雪峰称高考绝对是普通家庭改变命运的最好出路 回应建议报理科
- 2023年农业电商行业发展现状调查及行业未来趋势分析
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
全球热文:解析Spring内置作用域及其在实践中的应用
摘要:本文详细解析了Spring的内置作用域,包括Singleton、Prototype、Request、Session、Application和WebSocket作用域,并通过实例讲解了它们在实际开发中的应用。
本文分享自华为云社区《Spring高手之路4——深度解析Spring内置作用域及其在实践中的应用》,作者:砖业洋__ 。
本文详细解析了Spring的内置作用域,包括Singleton、Prototype、Request、Session、Application和WebSocket作用域,并通过实例讲解了它们在实际开发中的应用。特别是Singleton和Prototype作用域,我们深入讨论了它们的定义、用途以及如何处理相关的线程安全问题。通过阅读本文,读者可以更深入地理解Spring作用域,并在实际开发中更有效地使用
1. Spring的内置作用域
我们来看看Spring内置的作用域类型。在5.x版本中,Spring内置了六种作用域:
(资料图)
- singleton:在IOC容器中,对应的Bean只有一个实例,所有对它的引用都指向同一个对象。这种作用域非常适合对于无状态的Bean,比如工具类或服务类。
- prototype:每次请求都会创建一个新的Bean实例,适合对于需要维护状态的Bean。
- request:在Web应用中,为每个HTTP请求创建一个Bean实例。适合在一个请求中需要维护状态的场景,如跟踪用户行为信息。
- session:在Web应用中,为每个HTTP会话创建一个Bean实例。适合需要在多个请求之间维护状态的场景,如用户会话。
- application:在整个Web应用期间,创建一个Bean实例。适合存储全局的配置数据等。
- websocket:在每个WebSocket会话中创建一个Bean实例。适合WebSocket通信场景。
我们需要重点学习两种作用域:singleton和prototype。在大多数情况下singleton和prototype这两种作用域已经足够满足需求。
2. singleton作用域
2.1 singleton作用域的定义和用途
Singleton是Spring的默认作用域。在这个作用域中,Spring容器只会创建一个实例,所有对该bean的请求都将返回这个唯一的实例。
例如,我们定义一个名为Plaything的类,并将其作为一个bean:
@Componentpublic class Plaything { public Plaything() { System.out.println("Plaything constructor run ..."); }}
在这个例子中,Plaything是一个singleton作用域的bean。无论我们在应用中的哪个地方请求这个bean,Spring都会返回同一个Plaything实例。
下面的例子展示了如何创建一个单实例的Bean:
package com.example.demo.bean;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;@Componentpublic class Kid { private Plaything plaything; @Autowired public void setPlaything(Plaything plaything) { this.plaything = plaything; } public Plaything getPlaything() { return plaything; }}package com.example.demo.bean;import org.springframework.stereotype.Component;@Componentpublic class Plaything { public Plaything() { System.out.println("Plaything constructor run ..."); }}
这里可以在Plaything类加上@Scope(BeanDefinition.SCOPE_SINGLETON),但是因为是默认作用域是Singleton,所以没必要加。
package com.example.demo.configuration;import com.example.demo.bean.Kid;import com.example.demo.bean.Plaything;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationpublic class BeanScopeConfiguration { @Bean public Kid kid1(Plaything plaything1) { Kid kid = new Kid(); kid.setPlaything(plaything1); return kid; } @Bean public Kid kid2(Plaything plaything2) { Kid kid = new Kid(); kid.setPlaything(plaything2); return kid; }}package com.example.demo.application;import com.example.demo.bean.Kid;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.ApplicationContext;import org.springframework.context.annotation.AnnotationConfigApplicationContext;import org.springframework.context.annotation.ComponentScan;@SpringBootApplication@ComponentScan("com.example")public class DemoApplication { public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(DemoApplication.class); context.getBeansOfType(Kid.class).forEach((name, kid) -> { System.out.println(name + " : " + kid.getPlaything()); }); }}
在Spring IoC容器的工作中,扫描过程只会创建bean的定义,真正的bean实例是在需要注入或者通过getBean方法获取时才会创建。这个过程被称为bean的初始化。
这里运行 ctx.getBeansOfType(Kid.class).forEach((name, kid) -> System.out.println(name + " : " + kid.getPlaything())); 时,Spring IoC容器会查找所有的Kid类型的bean定义,然后为每一个找到的bean定义创建实例(如果这个bean定义还没有对应的实例),并注入相应的依赖。
运行结果:
三个 Kid 的 Plaything bean是相同的,说明默认情况下 Plaything 是一个单例bean,整个Spring应用中只有一个 Plaything bean被创建。
为什么会有3个kid?
- Kid:这个是通过在Kid类上标注的@Component注解自动创建的。Spring在扫描时发现这个注解,就会自动在IOC容器中注册这个bean。这个Bean的名字默认是将类名的首字母小写kid。
- kid1:在 BeanScopeConfiguration 中定义,通过kid1(Plaything plaything1)方法创建,并且注入了plaything1。
- kid2:在 BeanScopeConfiguration 中定义,通过kid2(Plaything plaything2)方法创建,并且注入了plaything2。
2.2 singleton作用域线程安全问题
需要注意的是,虽然singleton Bean只会有一个实例,但Spring并不会解决其线程安全问题,开发者需要根据实际场景自行处理。
我们通过一个代码示例来说明在多线程环境中出现singleton Bean的线程安全问题。
首先,我们创建一个名为Counter的singleton Bean,这个Bean有一个count变量,提供increment方法来增加count的值:
package com.example.demo.bean;import org.springframework.stereotype.Component;@Componentpublic class Counter { private int count = 0; public int increment() { return ++count; }}
然后,我们创建一个名为CounterService的singleton Bean,这个Bean依赖于Counter,在increaseCount方法中,我们调用counter.increment方法:
package com.example.demo.service;import com.example.demo.bean.Counter;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;@Servicepublic class CounterService {@Autowired private final Counter counter; public void increaseCount() { counter.increment(); }}
我们在多线程环境中调用counterService.increaseCount方法时,就可能出现线程安全问题。因为counter.increment方法并非线程安全,多个线程同时调用此方法可能会导致count值出现预期外的结果。
要解决这个问题,我们需要使counter.increment方法线程安全。
这里可以使用原子变量,在Counter类中,我们可以使用AtomicInteger来代替int类型的count,因为AtomicInteger类中的方法是线程安全的,且其性能通常优于synchronized关键字。
package com.example.demo.bean;import org.springframework.stereotype.Component;import java.util.concurrent.atomic.AtomicInteger;@Componentpublic class Counter { private AtomicInteger count = new AtomicInteger(0); public int increment() { return count.incrementAndGet(); }}
尽管优化后已经使Counter类线程安全,但在设计Bean时,我们应该尽可能地减少可变状态。这是因为可变状态使得并发编程变得复杂,而无状态的Bean通常更容易理解和测试。
什么是无状态的Bean呢?如果一个Bean不持有任何状态信息,也就是说,同样的输入总是会得到同样的输出,那么这个Bean就是无状态的。反之,则是有状态的Bean。
3. prototype作用域
3.1 prototype作用域的定义和用途
在prototype作用域中,Spring容器会为每个请求创建一个新的bean实例。
例如,我们定义一个名为Plaything的类,并将其作用域设置为prototype:
package com.example.demo.bean;import org.springframework.beans.factory.config.BeanDefinition;import org.springframework.context.annotation.Scope;import org.springframework.stereotype.Component;@Component@Scope(BeanDefinition.SCOPE_PROTOTYPE)public class Plaything { public Plaything() { System.out.println("Plaything constructor run ..."); }}
在这个例子中,Plaything是一个prototype作用域的bean。每次我们请求这个bean,Spring都会创建一个新的Plaything实例。
我们只需要修改上面的Plaything类,其他的类不用动。
打印结果:
这个@Scope(BeanDefinition.SCOPE_PROTOTYPE)可以写成@Scope("prototype"),按照规范,还是利用已有的常量比较好。
3.2 prototype作用域在开发中的例子
以我个人来说,我在excel多线程上传的时候用到过这个,当时是EasyExcel框架,我给一部分关键代码展示一下如何在Spring中使用prototype作用域来处理多线程环境下的任务(实际业务会更复杂),大家可以对比,如果用prototype作用域和使用new对象的形式在实际开发中有什么区别。
使用prototype作用域的例子
@Resourceprivate ApplicationContext context;@PostMapping("/user/upload")public ResultModel upload(@RequestParam("multipartFile") MultipartFile multipartFile) {......ExecutorService es = new ThreadPoolExceutor(10, 16, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(2000), new ThreadPoolExecutor.CallerRunPolicy());......EasyExcel.read(multipartFile.getInputStream(), UserDataUploadVO.class, new PageReadListener(dataList ->{......// 多线程处理上传excel数据Future> future = es.submit(context.getBean(AsyncUploadHandler.class, user, dataList, errorCount));......})).sheet().doRead();......}
AsyncUploadHandler.java
@Component@Scope(BeanDefinition.SCOPE_PROTOTYPE)public class AsyncUploadHandler implements Runnable {private User user;private ListdataList;private AtomicInteger errorCount;@Resourceprivate RedisService redisService;......@Resourceprivate CompanyManagementMapper companyManagementMapper;public AsyncUploadHandler(user, List dataList, AtomicInteger errorCount) {this.user = user;this.dataList = dataList;this.errorCount = errorCount;}@Overridepublic void run() {......}......}
AsyncUploadHandler类是一个prototype作用域的bean,它被用来处理上传的Excel数据。由于并发上传的每个任务可能需要处理不同的数据,并且可能需要在不同的用户上下文中执行,因此每个任务都需要有自己的AsyncUploadHandler bean。这就是为什么需要将AsyncUploadHandler定义为prototype作用域的原因。
由于AsyncUploadHandler是由Spring管理的,我们可以直接使用@Resource注解来注入其他的bean,例如RedisService和CompanyManagementMapper。
把AsyncUploadHandler交给Spring容器管理,里面依赖的容器对象可以直接用@Resource注解注入。如果采用new出来的对象,那么这些对象只能从外面注入好了再传入进去。
不使用prototype作用域改用new对象的例子
@PostMapping("/user/upload")public ResultModel upload(@RequestParam("multipartFile") MultipartFile multipartFile) {......ExecutorService es = new ThreadPoolExceutor(10, 16, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(2000), new ThreadPoolExecutor.CallerRunPolicy());......EasyExcel.read(multipartFile.getInputStream(), UserDataUploadVO.class, new PageReadListener(dataList ->{......// 多线程处理上传excel数据Future> future = es.submit(new AsyncUploadHandler(user, dataList, errorCount, redisService, companyManagementMapper));......})).sheet().doRead();......}
AsyncUploadHandler.java
public class AsyncUploadHandler implements Runnable {private User user;private ListdataList;private AtomicInteger errorCount;private RedisService redisService;private CompanyManagementMapper companyManagementMapper;......public AsyncUploadHandler(user, List dataList, AtomicInteger errorCount, RedisService redisService, CompanyManagementMapper companyManagementMapper) {this.user = user;this.dataList = dataList;this.errorCount = errorCount;this.redisService = redisService;this.companyManagementMapper = companyManagementMapper;}@Overridepublic void run() {......}......}
如果直接新建AsyncUploadHandler对象,则需要手动传入所有的依赖,这会使代码变得更复杂更难以管理,而且还需要手动管理AsyncUploadHandler的生命周期。
4. request作用域(了解)
request作用域:Bean在一个HTTP请求内有效。当请求开始时,Spring容器会为每个新的HTTP请求创建一个新的Bean实例,这个Bean在当前HTTP请求内是有效的,请求结束后,Bean就会被销毁。如果在同一个请求中多次获取该Bean,就会得到同一个实例,但是在不同的请求中获取的实例将会不同。
@Component@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)public class RequestScopedBean { // 在一次Http请求内共享的数据 private String requestData; public void setRequestData(String requestData) { this.requestData = requestData; } public String getRequestData() { return this.requestData; }}
上述Bean在一个HTTP请求的生命周期内是一个单例,每个新的HTTP请求都会创建一个新的Bean实例。
5. session作用域(了解)
session作用域:Bean是在同一个HTTP会话(Session)中是单例的。也就是说,从用户登录开始,到用户退出登录(或者Session超时)结束,这个过程中,不管用户进行了多少次HTTP请求,只要是在同一个会话中,都会使用同一个Bean实例。
@Component@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)public class SessionScopedBean { // 在一个Http会话内共享的数据 private String sessionData; public void setSessionData(String sessionData) { this.sessionData = sessionData; } public String getSessionData() { return this.sessionData; }}
这样的设计对于存储和管理会话级别的数据非常有用,例如用户的登录信息、购物车信息等。因为它们是在同一个会话中保持一致的,所以使用session作用域的Bean可以很好地解决这个问题。
但是实际开发中没人这么干,会话id都会存在数据库,根据会话id就能在各种表中获取数据,避免频繁查库也是把关键信息序列化后存在Redis。
6. application作用域(了解)
application作用域:在整个Web应用的生命周期内,Spring容器只会创建一个Bean实例。这个Bean在Web应用的生命周期内都是有效的,当Web应用停止后,Bean就会被销毁。
@Component@Scope(value = WebApplicationContext.SCOPE_APPLICATION, proxyMode = ScopedProxyMode.TARGET_CLASS)public class ApplicationScopedBean { // 在整个Web应用的生命周期内共享的数据 private String applicationData; public void setApplicationData(String applicationData) { this.applicationData = applicationData; } public String getApplicationData() { return this.applicationData; }}
如果在一个application作用域的Bean上调用setter方法,那么这个变更将对所有用户和会话可见。后续对这个Bean的所有调用(包括getter和setter)都将影响到同一个Bean实例,后面的调用会覆盖前面的状态。
7. websocket作用域(了解)
websocket作用域:Bean 在每一个新的 WebSocket 会话中都会被创建一次,就像 session 作用域的 Bean 在每一个 HTTP 会话中都会被创建一次一样。这个Bean在整个WebSocket会话内都是有效的,当WebSocket会话结束后,Bean就会被销毁。
@Component@Scope(value = "websocket", proxyMode = ScopedProxyMode.TARGET_CLASS)public class WebSocketScopedBean { // 在一个WebSocket会话内共享的数据 private String socketData; public void setSocketData(String socketData) { this.socketData = socketData; } public String getSocketData() { return this.socketData; }}
上述Bean在一个WebSocket会话的生命周期内是一个单例,每个新的WebSocket会话都会创建一个新的Bean实例。
这个作用域需要Spring Websocket模块支持,并且应用需要配置为使用websocket。
点击关注,第一时间了解华为云新鲜技术~
关键词:
全球热文:解析Spring内置作用域及其在实践中的应用
世界观点:pickle
RPG游戏开发日志: 世界观速讯
石家庄太和网点电话_石家庄太和网
3个ChatGPT插件自动写书爆火!更多躺着赚钱玩法可以问AI自己_全球看热讯
3.1级地震发生时上海居民躲床下避难 监控视频显示:剧烈摇晃、余震可能性不大|今头条
全球热点评!盘点C#最有价值的10个语法糖
Cannot Reference “XxxClass.xxx” Before Supertype Constructor Has Been Called
足球服批发_足球装备批发|热推荐
男子赶集买毛蛋到家全变鸭子 后续会饲养:网友点赞赚大了
直言理想ONE被问界M7打残 学习华为要看什么书:李想整理分享
丰田手把手教经销商诋毁纯电|每日动态
矛盾的马斯克!特斯拉4D雷达首拆:千元成本、探测距离300米
女学霸边读研边兼职3年赚17万:为了明确自己毕业后适合什么工作
开源即时通讯IM框架MobileIMSDK的H5端开发快速入门-世界关注
港股午评:恒指涨0.83% 恒生科技指数涨1.82% 天天看点
热点在线丨会定期删除 微软回应Edge私传图片问题:不含任何用户标识
天玑9200+加持 vivo X90s官宣:安卓最强悍的曲面屏旗舰
全球聚焦:李想最新发言引围观:我们谁都没兴趣搞、那点销量有啥可搞的
温州近视司机深夜高速上镜片突然脱落:两眼一抹黑
动态焦点:张雪峰称高考绝对是普通家庭改变命运的最好出路 回应建议报理科
2023年农业电商行业发展现状调查及行业未来趋势分析
用益-今日财经视点:美联储如期暂停加息但放鹰!
深度学习应用篇-元学习[15]:基于度量的元学习:SNAIL、RN、PN、MN
『题解』BZOJ2839 集合计数|天天热点评
热推荐:00-串口和SSH方式登录
戴德梁行亚洲REITs报告:C-REITs市场快速扩容 ESG将成为高质量发展新动力
因为读书值得
每日动态!六合一套装:八喜冰淇淋3.8元/杯抄底(商超8元)
巴黎治鼠患官宣失败:市民将与600万只老鼠共存
世界微速讯:全系降价3万后 蔚来ET5旅行版今日首发上市:这颜值30万你买吗?
每日热门:618最值得入手智能门锁非它莫属:华为智能门锁系列该怎么选?
业内第一!小鹏首宣开放北京城市智能辅助驾驶:不靠高精地图|天天热点
《长安三万里》曝角色海报 7月8日梦回诗意大唐 要闻
顶奢好文:3W字,穿透Spring事务原理、源码,至少读10遍|焦点速读
c++ mutex 每日消息
微头条丨月的暗面——戴冰选集_对于月的暗面——戴冰选集简单介绍
当前快报:大导演冯小刚多年的白癜风“消失”了引围观 这病真能治好?医生释疑
天天热资讯!高速错过出口 丰田亚洲龙缓行被大货车追尾致5死 官方:两司机均分心驾驶
全球聚焦:李想:理想MPV不会是埃尔法“私生子” 开上街比法拉利还瞩目
女子穿联名汉服进入迪士尼被拦 官方回应:网友吵翻 说好的穿衣自由呢
视讯!对象说冷怎么回
北斗GPS卫星授时服务器(NTP授时)应用于地铁自控系统
最新SMS-Activate短信验证码接收教程-观天下
陕西已累计收获小麦1213.63万亩占应收面积的八成以上
环球快资讯:女孩抓蛙一身泥巴被妈妈逮到 毫无畏惧:网友感慨这才是童年
热点评!《王者荣耀》诸葛亮新皮肤上线:最强大的仙尊 能御剑飞行
不花钱给电脑降温?有两种方法可行-全球聚焦
外卖起送价太高、凑单满减浪费?两部门发文
中国移动5G重大突破:无需安装APP 远程操控智能终端
这项重要工程动工,惠及众多荔湾街坊!|早安,荔湾-环球最资讯
ESP32物联网项目开发1-ESP32简介
美联储如市场预期“跳过”加息 2年期与10年期美债利差走阔 讯息
每日消息!雷军科普:金属背壳的手机曾经非常流行 为什么现在却很少有人用了?
曝驱逐舰07七月预售:搭载云辇-C 车长超比亚迪汉
苹果最强影像旗舰!iPhone 15 Pro Max相机曝光|环球即时
每隔俩烽火台设一个许愿栏又有何不可?
读发布!设计与部署稳定的分布式系统(第2版)笔记03_让系统稳定运行 每日精选
观热点:考古女生寄200斤洛阳铲装备吓坏父母 专业的标配
天天微动态丨用iPhone和用安卓的人 为什么谁也不服谁
快讯:药康生物(688046):6月14日北向资金增持1.11万股
焦点速讯:重新整理 .net core 实践篇——— 测试控制器[四十九]
云南一地多次发生巨响震动!官方回应:初判为浅源性地震-环球快资讯
天天观速讯丨英伟达太强 AMD最强显卡尚无客户采购:软件干不过CUDA
小米14设计定了:标准版小直屏 Pro版极窄微曲屏-环球热头条
快看点丨给全世界每个人发钱 OpenAI创始人这么飘了?
比尔·盖茨抵达北京:就健康和环境问题向中国取经|当前简讯
(上接第一版)
无敌时间_关于无敌时间介绍
创业板注册制IPO申请“开闸”满三年:千企竞逐上市梦 创业板加速构建创新资本中心
中央美术学院教授黄永玉去世 为具有重大影响力的艺术家
张柏芝发文呼吁大家关注糖尿病年轻化 减轻职场人群“高甜负担”
多地提前进入“烧烤模式” 新疆东部和南疆盆地有35℃以上高温
重金买下哈利波特老宅的屋主感到后悔 朝圣粉丝令其苦不堪言
歌手苏醒晒出自己和“球王”梅西的合影 兴奋之情溢于言表
腾讯音乐与原神达成深度战略合作 经典之声再度重逢
2023年“惠购湖北”家电消费券即将发放 总计投入财政资金3亿元
Set 接口及其常用方法
对标英伟达A100 国产GPU沐曦MXC500成功点亮:仅用时5个小时|世界消息
奶奶带孙子时邻居反馈“不像他爸” 去做亲子鉴定后崩溃:真不是
微软创始人比尔盖茨抵达北京!2019年以来首次访问
小米米家首款自发电动感单车采用南芯芯片:支持20W有线/无线快充
环球看热讯:买下《哈利波特》电影中房子的屋主后悔了 每天都有粉丝来打卡
澳琴企业首次亮相第九届上交会_热资讯
百度文心一格×京东618:打造电商行业首个AI线下广告,节省80%制作成本|焦点快看
环球视讯!人人有份!湖北将发放3亿元消费券:最大面额500元 京东可用
世界热点评!作别“战国七雄” 中国快递业变天了:进入三足鼎立
阿里巴巴集团公关部将独立成公关公司?知情人士:假的-环球关注
微控制器实时操作系统实践5选择IDE 全球短讯
文心一言 VS 讯飞星火 VS chatgpt (41)-- 算法导论5.4 5题
【环球播资讯】破“3”!30年期国债收益率创2006年以来新低
有关交朋友的成语典故_与朋友有关的成语典故
男子花120万买二手奥迪R8 开了三年发现竟是全损泡水车
精选!商汤元萝卜AI下棋机器人围棋版发布:棋艺达职业九段
150元只能划半小时!杭州西湖划船刺客运营方被罚12万|环球讯息
开发者怒赞macOS 14真香!喊话微软:这三大功能值得Windows 12借鉴
索泰预热全新PGF系列旗舰显卡:或为RTX 4090 环球头条
天天视点!加个电池就是新能源?被欧洲拒绝的混动,才是埋藏最深的骗局!
当前动态:中兴Axon 50 Ultra开启预约:卫星通信疑似取消
当前关注:18岁小伙骑共享单车5000公里到新疆:送给自己的成人礼