范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

彻底讲透SpringBean生命周期,看完直接吊打面试官

  前言
  本篇文章主要是要介绍如何在Spring IoC 容器中 如何管理Spring Bean生命周期。
  在应用开发中,常常需要执行一些特定的初始化工作,这些工作都是相对比较固定的,比如建立数据库连接,打开网络连接等,同时,在结束服务时,也有一些相对固定的销毁工作需要执行。为了便于这些工作的设计,Spring IoC容器提供了相关的功能,可以让应用定制Bean的初始化和销毁过程。  Spring Bean 生命周期
  图片描述
  先来看看 Spring Bean 的生命周期流程图。结合图看后面的描述会更轻松一点哦。
  文字描述  Bean容器在配置文件中找到Spring Bean的定义。  Bean容器使用Java Reflection API创建Bean的实例。  如果声明了任何属性,声明的属性就会被设置。如果属性本身是Bean,则将对其进行解析和设置。  如果Bean类实现   BeanNameAware   接口,则将通过传递Bean的名称来调用  setBeanName()   方法。 如果Bean类实现   BeanClassLoaderAware   接口,则将通过传递加载此Bean的ClassLoader对象的实例来调用  setBeanClassLoader()   方法。 如果Bean类实现   BeanFactoryAware   接口,则将通过传递BeanFactory对象的实例来调用  setBeanFactory()   方法。 如果有任何与BeanFactory关联的BeanPostProcessors对象已加载Bean,则将在设置Bean属性之前调用   postProcessBeforeInitialization()   方法。 如果Bean类实现了   InitializingBean   接口,则在设置了配置文件中定义的所有Bean属性后,将调用  afterPropertiesSet()   方法。 如果配置文件中的Bean定义包含   init-method   属性,则该属性的值将解析为Bean类中的方法名称,并将调用该方法。 如果为Bean Factory对象附加了任何Bean 后置处理器,则将调用   postProcessAfterInitialization()   方法。 如果Bean类实现   DisposableBean   接口,则当Application不再需要Bean引用时,将调用  destroy()   方法。 如果配置文件中的Bean定义包含   destroy-method   属性,那么将调用Bean类中的相应方法定义。 实例演示
  接下来,我们用一个简单的DEMO来演示一下,整个生命周期的流转过程,加深你的印象。  定义一个   Person   类,实现了  DisposableBean, InitializingBean, BeanFactoryAware, BeanNameAware   这4个接口,同时还有自定义的  init-method   和  destroy-method   。这里,如果不了解这几个接口的读者,可以先去看看这几个接口的定义。 public class Person implements DisposableBean, InitializingBean, BeanFactoryAware, BeanNameAware {      private String name;      Person() {         System.out.println("Constructor of person bean is invoked!");     }      public String getName() {         return name;     }     public void setName(String name) {         this.name = name;     }      @Override     public void setBeanFactory(BeanFactory beanFactory) throws BeansException {         System.out.println("setBeanFactory method of person is invoked");     }      @Override     public void setBeanName(String name) {         System.out.println("setBeanName method of person is invoked");     }      public void init() {         System.out.println("custom init method of person bean is invoked!");     }      //Bean initialization code  equals to     @Override     public void afterPropertiesSet() throws Exception {         System.out.println("afterPropertiesSet method of person bean is invoked!");     }      //Bean destruction code     @Override     public void destroy() throws Exception {         System.out.println("DisposableBean Destroy method of person bean is invoked!");     }      public void destroyMethod() {         System.out.println("custom Destroy method of person bean is invoked!");     } }定义一个   MyBeanPostProcessor   实现  BeanPostProcessor   接口。 public class MyBeanPostProcessor implements BeanPostProcessor {     @Override     public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {         System.out.println("post Process Before Initialization is invoked");         return bean;     }      @Override     public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {         System.out.println("post Process after Initialization is invoked");         return bean;     } } 配置文件, 指定   init-method   和  destroy-method   属性 <?xml version="1.0" encoding="UTF-8"?>                           启动容器、销毁容器  public class Main {     public static void main(String[] args) {         ApplicationContext context = new ClassPathXmlApplicationContext("spring-config-1.xml");         ((ClassPathXmlApplicationContext) context).destroy();     } } 输出  Constructor of person bean is invoked! setBeanName method of person is invoked setBeanFactory method of person is invoked post Process Before Initialization is invoked afterPropertiesSet method of person bean is invoked! custom init method of person bean is invoked! post Process after Initialization is invoked DisposableBean Destroy method of person bean is invoked! custom Destroy method of person bean is invoked!
  可以看到这个结果和我们上面描述的一样。  源码解析
  下面我们从源码角度来看看,上述描述的调用是如何实现的。
  容器初始化
  Spring IoC 依赖注入的阶段,创建Bean有三个关键步骤  createBeanInstance() 实例化  populateBean(); 属性装配  initializeBean() 处理Bean初始化之后的各种回调事件
  其中,   initializeBean()   负责处理Bean初始化后的各种回调事件。 protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {   if (System.getSecurityManager() != null) {    AccessController.doPrivileged(new PrivilegedAction() {     @Override     public Object run() {      invokeAwareMethods(beanName, bean);      return null;     }    }, getAccessControlContext());   }   else {             // 涉及到的回调接口点进去一目了然,代码都是自解释的             // BeanNameAware、BeanClassLoaderAware或BeanFactoryAware    invokeAwareMethods(beanName, bean);   }    Object wrappedBean = bean;   if (mbd == null || !mbd.isSynthetic()) {             // BeanPostProcessor 的 postProcessBeforeInitialization 回调    wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);   }    try {             // init-methods             // 或者是实现了InitializingBean接口,会调用afterPropertiesSet() 方法    invokeInitMethods(beanName, wrappedBean, mbd);   }   catch (Throwable ex) {    throw new BeanCreationException(      (mbd != null ? mbd.getResourceDescription() : null),      beanName, "Invocation of init method failed", ex);   }   if (mbd == null || !mbd.isSynthetic()) {             // BeanPostProcessor 的 postProcessAfterInitialization 回调    wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);   }   return wrappedBean;  }
  其中   invokeAwareMethods   会先调用一系列的  ***Aware   接口实现 private void invokeAwareMethods(final String beanName, final Object bean) {   if (bean instanceof Aware) {    if (bean instanceof BeanNameAware) {     ((BeanNameAware) bean).setBeanName(beanName);    }    if (bean instanceof BeanClassLoaderAware) {     ((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());    }    if (bean instanceof BeanFactoryAware) {     ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);    }   }  }
  然后再执行   BeanPostProcessor   的  postProcessBeforeInitialization   回调  @Override  public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)    throws BeansException {    Object result = existingBean;   for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {    result = beanProcessor.postProcessBeforeInitialization(result, beanName);    if (result == null) {     return result;    }   }   return result;  }
  然后再调用 初始化方法,其中包括   InitializingBean   的  afterPropertiesSet   方法和指定的  init-method   方法, protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)    throws Throwable {    boolean isInitializingBean = (bean instanceof InitializingBean);   if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {    if (logger.isDebugEnabled()) {     logger.debug("Invoking afterPropertiesSet() on bean with name "" + beanName + """);    }    if (System.getSecurityManager() != null) {     try {      AccessController.doPrivileged(new PrivilegedExceptionAction() {       @Override       public Object run() throws Exception {        ((InitializingBean) bean).afterPropertiesSet();        return null;       }      }, getAccessControlContext());     }     catch (PrivilegedActionException pae) {      throw pae.getException();     }    }    else {     ((InitializingBean) bean).afterPropertiesSet();    }   }    if (mbd != null) {    String initMethodName = mbd.getInitMethodName();    if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&      !mbd.isExternallyManagedInitMethod(initMethodName)) {     invokeCustomInitMethod(beanName, bean, mbd);    }   }  }
  最后再执行   BeanPostProcessor   的  postProcessAfterInitialization   回调  @Override  public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)    throws BeansException {    Object result = existingBean;   for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {    result = beanProcessor.postProcessAfterInitialization(result, beanName);    if (result == null) {     return result;    }   }   return result;  }
  好的,到这里我们介绍了Spring 容器初始化过程Bean加载过程当中的各种回调实现,下面介绍Spring 容器销毁阶段。
  容器关闭
  与Bean初始化类似,当容器关闭时,可以看到对Bean销毁方法的调用。销毁过程是这样的。顺着   close()-> doClose() -> destroyBeans() -> destroySingletons() -> destroySingleton() -> destroyBean() -> bean.destroy()   , 会看到最终调用Bean的销毁方法。 protected void destroyBean(String beanName, DisposableBean bean) {   // 忽略    // Actually destroy the bean now...   if (bean != null) {    try {     bean.destroy();    }    catch (Throwable ex) {     logger.error("Destroy method on bean with name "" + beanName + "" threw an exception", ex);    }   }    // 忽略  } protected void destroyBean(String beanName, DisposableBean bean) {   // 忽略    // Actually destroy the bean now...   if (bean != null) {    try {     bean.destroy();    }    catch (Throwable ex) {     logger.error("Destroy method on bean with name "" + beanName + "" threw an exception", ex);    }   }    // 忽略  }
  这里注意哦,这个Bean的类型实际上是   DisposableBeanAdapter   ,  DisposableBeanAdapter   是管理Spring Bean的销毁的,实际上这里运用了适配器模式。再来看看  destroy()   的具体方法。 @Override  public void destroy() {   if (!CollectionUtils.isEmpty(this.beanPostProcessors)) {    for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {     processor.postProcessBeforeDestruction(this.bean, this.beanName);    }   }    if (this.invokeDisposableBean) {    if (logger.isDebugEnabled()) {     logger.debug("Invoking destroy() on bean with name "" + this.beanName + """);    }    try {     if (System.getSecurityManager() != null) {      AccessController.doPrivileged(new PrivilegedExceptionAction() {       @Override       public Object run() throws Exception {        ((DisposableBean) bean).destroy();        return null;       }      }, acc);     }     else {                     // 调用 DisposableBean 的 destroy()方法      ((DisposableBean) bean).destroy();     }    }    catch (Throwable ex) {     String msg = "Invocation of destroy method failed on bean with name "" + this.beanName + """;     if (logger.isDebugEnabled()) {      logger.warn(msg, ex);     }     else {      logger.warn(msg + ": " + ex);     }    }   }    if (this.destroyMethod != null) {             // 调用 设置的destroyMethod    invokeCustomDestroyMethod(this.destroyMethod);   }   else if (this.destroyMethodName != null) {    Method methodToCall = determineDestroyMethod();    if (methodToCall != null) {     invokeCustomDestroyMethod(methodToCall);    }   }  }
  BeanPostProcessor 是什么时候注册到容器的?
  前面只介绍了BeanPostProcessor类在 Spring Bean 生命周期中的回调实现,却没有说明 BeanPostProcessor 是什么时候注册到容器的。下面我们来介绍下。
  在Spring IoC 容器初始化的时候,容器会做一些初始化操作,其中就包括了BeanPostProcessor的register过程。
  这里直接放源码吧。
  源码位置   AbstractApplicationContext#refresh()  @Override  public void refresh() throws BeansException, IllegalStateException {   synchronized (this.startupShutdownMonitor) {    // Prepare this context for refreshing.    prepareRefresh();     // Tell the subclass to refresh the internal bean factory.    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();     // Prepare the bean factory for use in this context.    prepareBeanFactory(beanFactory);     try {     // Allows post-processing of the bean factory in context subclasses.     postProcessBeanFactory(beanFactory);      // Invoke factory processors registered as beans in the context.     invokeBeanFactoryPostProcessors(beanFactory);      // Register bean processors that intercept bean creation.                 // 在这里     registerBeanPostProcessors(beanFactory);    // ....忽略   }  }  protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {   PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);  }
  源码位置   PostProcessorRegistrationDelegate#registerBeanPostProcessors()  public static void registerBeanPostProcessors(    ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {    String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false); // step1   // Register BeanPostProcessorChecker that logs an info message when   // a bean is created during BeanPostProcessor instantiation, i.e. when   // a bean is not eligible for getting processed by all BeanPostProcessors.   int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;   beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));  // step2   // Separate between BeanPostProcessors that implement PriorityOrdered,   // Ordered, and the rest.   List priorityOrderedPostProcessors = new ArrayList();   List internalPostProcessors = new ArrayList();   List orderedPostProcessorNames = new ArrayList();   List nonOrderedPostProcessorNames = new ArrayList();   for (String ppName : postProcessorNames) {    if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {     BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);     priorityOrderedPostProcessors.add(pp);     if (pp instanceof MergedBeanDefinitionPostProcessor) {      internalPostProcessors.add(pp);     }    }    else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {     orderedPostProcessorNames.add(ppName);    }    else {     nonOrderedPostProcessorNames.add(ppName);    }   } // step3   // First, register the BeanPostProcessors that implement PriorityOrdered.   sortPostProcessors(priorityOrderedPostProcessors, beanFactory);   registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);    // Next, register the BeanPostProcessors that implement Ordered.   List orderedPostProcessors = new ArrayList();   for (String ppName : orderedPostProcessorNames) {    BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);    orderedPostProcessors.add(pp);    if (pp instanceof MergedBeanDefinitionPostProcessor) {     internalPostProcessors.add(pp);    }   }   sortPostProcessors(orderedPostProcessors, beanFactory);   registerBeanPostProcessors(beanFactory, orderedPostProcessors);    // Now, register all regular BeanPostProcessors.   List nonOrderedPostProcessors = new ArrayList();   for (String ppName : nonOrderedPostProcessorNames) {    BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);    nonOrderedPostProcessors.add(pp);    if (pp instanceof MergedBeanDefinitionPostProcessor) {     internalPostProcessors.add(pp);    }   }   registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);    // Finally, re-register all internal BeanPostProcessors.   sortPostProcessors(internalPostProcessors, beanFactory);   registerBeanPostProcessors(beanFactory, internalPostProcessors);    // Re-register post-processor for detecting inner beans as ApplicationListeners,   // moving it to the end of the processor chain (for picking up proxies etc).   beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));  }
  上述过程可以分成四步:  通过   beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);   方法获取beanFactory里继承了BeanPostProcessor接口的name的集合; 把后置器beans分为   PriorityOrdered、Ordered、nonOrdered   三大类,前两类是增加了排序条件的后置器;(Spring可以通过  PriorityOrdered   和  Ordered   接口控制处理器的优先级),这里实际上还有一类是  MergedBeanDefinitionPostProcessor   ,不是核心点,不展开讲。 第三步可以分为以下小步  priorityOrderedPostProcessors   ,先排序后注册 orderedPostProcessors   ,先排序后注册 注册   nonOrderedPostProcessors   ,就是一般的处理器 internalPostProcessors   ,先排序后注册 注册一个   ApplicationListenerDetector   的 processor
  DisposableBeanAdapter 什么时候注册到容器的?
  DisposableBeanAdapter   和上文的  BeanPostProcessor   的抽象层级不同,这个是和Bean绑定的,所以它的注册时机是在Spring Bean的依赖注入阶段 。
  源码位置:   AbstractAutowireCapableBeanFactory#doCreateBean()  protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)    throws BeanCreationException {   // 省略前面的超多步骤,想了解的可以去看源码或者我的那篇文章    // Register bean as disposable.      // 这里就是DisposableBeanAdapter的注册步骤了   try {    registerDisposableBeanIfNecessary(beanName, bean, mbd);   }   catch (BeanDefinitionValidationException ex) {    throw new BeanCreationException(      mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);   }    return exposedObject;  }
  源码位置:   AbstractBeanFactory#registerDisposableBeanIfNecessary()  protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {   AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);   if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {    if (mbd.isSingleton()) {                 // 注册一个DisposableBean实现,该实现将执行给定bean的所有销毁工作。                 // 包括:DestructionAwareBeanPostProcessors,DisposableBean接口,自定义destroy方法。     registerDisposableBean(beanName,       new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));    }    else {     // A bean with a custom scope...     Scope scope = this.scopes.get(mbd.getScope());     if (scope == null) {      throw new IllegalStateException("No Scope registered for scope name "" + mbd.getScope() + """);     }     scope.registerDestructionCallback(beanName,       new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));    }   }  }结语
  至此,Spring Bean的整个生命周期算是讲解完了,从容器初始化到容器销毁,以及回调事件的注册时机等方面都说明了一下,希望能对你有所帮助。
  下面是整理的一些资料希望对小伙伴们有所帮助
  如果本文对你有帮助,别忘记给我个3连 ,点赞,转发,评论,,咱们下期见。
  收藏 等于白嫖,点赞才是真情。
微信支付怎么变成服务了?有个朋友问我,为毛微信支付改成了服务,是不哪里搞错了?说早上点早餐找了半天没找的付款入口,还发我截屏给我看,我也对比了下自己的。我自己的没变,老样子,还是支付。我朋友手机微信截屏我性骚扰丑闻促成4000亿元交易?微软收购动视暴雪始末科蒂克卷入性骚扰丑闻凤凰网科技讯北京时间1月19日消息,微软公司昨晚突然宣布豪掷687亿美元(约合4365亿元)收购游戏巨头动视暴雪。在此之前,这笔爆炸性交易没有走漏一点风声,它是字节跳动布局房地产近日,天眼查App说明,北京福旺房地产经纪有限公司发生工商变更,新增字节跳动关联公司北京好房有幸信息技术有限公司为股东,持股比例100。公司的原股东退出,法定代表人也由吴存胜变更为短信验证码的安全隐患在当下这个年代,几乎是人人都在用手机,只要在用手机,就没有不知道短信验证码的。随着手机更新换代的速度变快,各大APP也随之诞生。一般要使用APP,都需要进行登录认证,最常见的就是将三星4纳米工艺Exynos2200移动处理器发布,ARMv9AMD架构三星发布了5G移动SOC芯片三星Exynos2200处理器,这款芯片功能强大采用ARMv9架构,与AMD长期合作,这款芯片包含了AMDRDNA2图形架构的GPU功能芯片,支持硬件加深度体验丨iQOO9Pro距离全能旗舰只有一步之遥?iQOO数字旗舰自诞生之日起,便拥有了性能怪兽的江湖称号!历经数代更新,也开始寻求新的突破和蜕变那就是全面的硬件升级融合更智能的系统!所以,当iQOO9Pro出现在我手中时,我对它软件产品商标注册有没有必要?应该注册哪几类APP注册关键类主要分为9类35类38类和42类,很多人由于注册了这几个关键类商标就歹意攻击不在此类别的商标的APP,也有人由于没有注册到这几类商标而堕入担忧,这都是不正确的。ap18G512G现货充足,6000mAh坐镇,就是价格稍微贵了些ROG作为全球知名的高端主板品牌,旗下产品,无论是电脑配件还是手机,都是以性能为优先级,想要体验超频的快乐和极致的流畅度,ROG游戏手机无疑是最好的选择,ROG游戏手机5s也是目前江苏银行布局金融元宇宙近日,江苏银行基于对互联网金融发展和数字化金融底层逻辑的研判,结合自身优势,开始积极布局金融元宇宙,为客户提供更加优质的金融服务体验。元宇宙是指利用科技手段进行链接与创造的,与现实NFT与元宇宙,价值还是陷阱新年伊始,NFT的热度有增无减。国内外明星艺术家互联网大厂纷纷入局,NFT,一时成为了投资(投机)领域的显贵。毫无疑问,NFT使艺术品的价值实现更加便利。通过NFT,把现实世界的资在世界手机舞台上,传音控股打造来自中国的标杆企业手机行业经历飞速变革和发展,衍生出一批中国科技公司,它们在全球形成了巨大的品牌优势。据市场分析机构DIGITIMESResearch数据显示,2021年下半年,手机全球销量排名从高
从蔚来事件说起,汽车汉化有必要吗出品丨破浪图文组文丨赵妙琳责编丨章丽娟这些天来,相信不少人在网上冲浪时都和我一般,被关于90后企业家驾驶蔚来ES8在沈海高速发生交通事故的新闻和讨论刷屏。最新消息显示,死者家属已向新能源汽车的发展方向新能源车该有的样子从诞生石油开始,石油这种不可缺少也不可再生的能源成为了人类社会发展不可缺少资源,敝端不可再生,污染环境。新能源汽车从根本上解决了对石油的依赖,减少了一氧化碳丶废气数据分析,除了Excel数据透视表,还有什么工具?现在市面上有很多做数据分析可视化图表的工具。我们公司采购过,所以对这块比较了解,列出当时选型时主要考虑过的一些工具,给题主和其他人做参考,以下顺序随机,无优劣之分。1。Tablea荣耀平板V7Pro上手评测既有出色影音体验,又是不错的创作工具最近这两年,安卓平板市场逐渐回暖。主要是因为,随着性能的提升,安卓平板摆脱了影音播放工具的限制,在移动办公等方面能力有了提升,使用场景更多。荣耀在平板领域有着自己深刻的洞察,历代多重磅!亚马逊德国站地址核查信件详解与解决方案图片来源乐鱼跨境官方微信公众号这两天有很多卖家收到了一封和德国站有关的亚马逊信件ImportantVerifyyourbusinessnameandaddressassociate一面镜子真的能将家打造成健身房?Fitmore智能健身镜体验近几年人们的健身锻炼的意识越来越强,而且健身房也是遍地开花,很多人选择健身房锻炼的主要原因有两个健身器材和专业的教练。但这两年因为受疫情的影响,很多人都不愿意去人多的地方,多数人会个人信息保护法正式通过,互联网平台将迎大变机哥发现,最近这几年提到互联网上的个人信息时,大家都变得特别敏感。例如,在昨天机哥那篇讨论生物识别的文章底下,就有机友谈到了隐私问题。说起来,机哥觉得大家伙能有这样的思考,确实是一呼声最高的屏下指纹没了?苹果我行我素,未来将使用屏下FaceID作为数一数二的苹果公司,凭借着优秀的的生态和强大的品牌影响力吸引了一大批粉丝,因此苹果的每一次发布新品也基本上成为众多厂商的风向标了,但苹果旗下的产品并非是十全十美的,而苹果虽然是国产全面反击,美国院士阻碍华为,反而导致自身5G技术全面落后其实大家都明白,华为手机现在硬件受阻,其原因就是我们的5G技术领先于美国。虽然他们的专家也曾规劝过,阻碍华为,反而导致自身5G技术全面落后。但他们依然一意孤行,期望通过给华为制造麻中国能否研发量子计算机从而实现对美国芯片的弯道超车?不要老说弯道超车了,事到如今谁见到了所谓的弯道超车了?量子通讯计算机等目前都还是基础理论阶段,到实际应用还很远,根本就是远水不解近渴的事。川普既然敢对中国那么多家高科技公司舞动大棒新能源车的真正灵魂,揭秘ZEEKR001三电系统硬核实力什么是一台新能源车的灵魂?是造车理念?是前卫造型?还是大空间长续航?作为当下关注度最高的新能源来说,让大家选择它的理由有很多,但唯独离不开的就是一台新能源的三电技术。那么究竟好的三