应用办公生活信息教育商业
投稿投诉
商业财经
汽车智能
教育国际
房产环球
信息数码
热点科技
生活手机
晨报新闻
办公软件
科学动态
应用生物
体育时事

Spring源码之容器的功能扩展和refresh方法解析

  容器的功能扩展和refresh方法解析
  在之前文章中我们了解了关于Spring中bean的加载流程,并一直使用BeanFactory接口以及它的默认实现类XmlBeanFactory,在Spring中还提供了另一个接口ApplicationContext,用于扩展BeanFactory中现有的功能。
  首先BeanFactory和ApplicationContext都是用于加载bean的,但是相比之下,ApplicationContext提供了更多的扩展功能,ApplicationContext包含了BeanFactory的所有功能。通常我们会优先使用ApplicationContext。
  我们来看看ApplicationContext多了哪些功能?
  首先看一下写法上的不同。
  使用BeanFactory方式加载XMLfinalBeanFactorybeanFactorynewXmlBeanFactory(newClassPathResource(springconfig。xml));
  使用ApplicationContext方式加载XMLfinalApplicationContextapplicationContextnewClassPathXmlApplicationContext(springconfig。xml);
  我们开始点开ClassPathXmlApplicationContext的构造函数,进行分析。publicClassPathXmlApplicationContext(StringconfigLocation)throwsBeansException{this(newString〔〕{configLocation},true,null);}publicClassPathXmlApplicationContext(String〔〕configLocations,booleanrefresh,NullableApplicationContextparent)throwsBeansException{super(parent);setConfigLocations(configLocations);if(refresh){refresh();}}
  在ClassPathXmlApplicationContext中可以将配置文件路径以数组的形式传入,对解析及功能实现都在refresh()方法中实现。设置配置路径publicvoidsetConfigLocations(NullableString。。。locations){if(locations!null){Assert。noNullElements(locations,Configlocationsmustnotbenull);this。configLocationsnewString〔locations。length〕;for(inti0;ilocations。length;i){this。configLocations〔i〕resolvePath(locations〔i〕)。trim();}}else{this。configLocationsnull;}}
  此函数主要解析给定的路径数组,如果数组中包含特殊符号,如{var},那么在resolvePath方法中会搜寻匹配的系统变量并替换。扩展功能
  设置完路径后,就可以对文件进行解析和各种功能的实现,可以说在refresh方法中几乎包含了ApplicationContext中提供的全部功能,而且此函数的逻辑也十分清晰,可以很容易分析对应层次和逻辑。publicvoidrefresh()throwsBeansException,IllegalStateException{synchronized(this。startupShutdownMonitor){准备刷新的上下文环境,包括设置启动时间,是否激活标识位初始化属性源(propertysource)配置prepareRefresh();初始化BeanFactory并进行xml文件读取ConfigurableListableBeanFactorybeanFactoryobtainFreshBeanFactory();对BeanFactory进行各种功能填充prepareBeanFactory(beanFactory);try{子类覆盖方法做额外的处理postProcessBeanFactory(beanFactory);激活各种BeanFactory处理器invokeBeanFactoryPostProcessors(beanFactory);注册拦截bean创建的bean处理器,只是注册,具体调用在getBean中registerBeanPostProcessors(beanFactory);为上下文初始化Message源,国际化处理initMessageSource();初始化应用消息广播器,并放入applicationEventMulticasterbean中initApplicationEventMulticaster();留给子类来初始化其他的beanonRefresh();在所有注册的bean中查找Listenerbean,注册到消息广播器中registerListeners();初始化剩下的单例bean(非惰性)finishBeanFactoryInitialization(beanFactory);完成刷新过程,通知生命周期处理器LifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人finishRefresh();}catch(BeansExceptionex){if(logger。isWarnEnabled()){logger。warn(Exceptionencounteredduringcontextinitializationcancellingrefreshattempt:ex);}销毁已经初始化的singleton的Beans,以免有些bean会一直占用资源destroyBeans();重置活动标志cancelRefresh(ex);throwex;}finally{重置公共缓存resetCommonCaches();}}}
  我们总结一下初始化的步骤。初始化前的准备工作,例如对系统属性或者环境变量进行准备及验证初始化BeanFactory,并对XML文件进行读取。之前我们说过ClassPathXmlApplicationContext中包含着BeanFactory所提供的一切特征,那么在这一步将会复用BeanFactory中的配置文件读取解析及其他功能,在这一步之后ClassPathXmlApplicationContext就已经包含了BeanFactory所提供的功能,也就是可以对bean进行提取等操作对BeanFactory进行各种功能填充子类覆盖方法做额外的处理。主要用于我们在业务上做进一步扩展激活各种BeanFactory处理器注册拦截bean创建的bena处理器,这里仅仅是注册,真正调用在getBean中为上下文初始化Message源,对不同语言的消息体进行国际化处理初始化应用消息广播器,并放入applicationEventMulticasterbean中留给子类来初始化其他的bean在所有注册的bean中查找listenerbean,注册到消息广播器中初始化剩下的单实例(非惰性)完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent来通知别人环境准备
  prepareRefresh方法主要做些准备工作,比如对系统属性及环境变量的初始化及验证。
  initPropertySources
  该方法里面是一个空实现,主要用于给我们根据需要去重写该方法,并在方法中进行个性化的属性处理及设置。protectedvoidinitPropertySources(){Forsubclasses:donothingbydefault。}validateRequiredProperties该方法主要对属性进行验证。默认情况下什么也没校验。在我们继承了ClassPathXmlApplicationContext类重写了initPropertySources方法后会进行相关校验。加载BeanFactory
  obtainFreshBeanFactory方法主要用来获取BeanFactory,刚才说过ApplicationContext拥有BeanFactory的所有功能,这个方法就是实现BeanFactory的地方,也就是说调用完该方法后,applicationContext就拥有了BeanFactory的功能。protectedConfigurableListableBeanFactoryobtainFreshBeanFactory(){初始化BeanFactory,并进行XML文件读取,将得到的BeanFactory记录到当前实体属性中refreshBeanFactory();返回当前实体的beanFactory属性returngetBeanFactory();}protectedfinalvoidrefreshBeanFactory()throwsBeansException{if(hasBeanFactory()){destroyBeans();closeBeanFactory();}try{DefaultListableBeanFactorybeanFactorycreateBeanFactory();beanFactory。setSerializationId(getId());customizeBeanFactory(beanFactory);loadBeanDefinitions(beanFactory);synchronized(this。beanFactoryMonitor){this。beanFactorybeanFactory;}}catch(IOExceptionex){thrownewApplicationContextException(IOerrorparsingbeandefinitionsourceforgetDisplayName(),ex);}}
  我们进入AbstractRefreshableApplicationContextrefreshBeanFactory()方法中。protectedfinalvoidrefreshBeanFactory()throwsBeansException{判断是否存在beanFactoryif(hasBeanFactory()){销毁所有单例destroyBeans();重置beanFactorycloseBeanFactory();}try{创建beanFactoryDefaultListableBeanFactorybeanFactorycreateBeanFactory();设置序列化idbeanFactory。setSerializationId(getId());定制beanFactory,设置相关属性,包括是否允许覆盖同名称不同定义的对象以及循环依赖customizeBeanFactory(beanFactory);初始化DocumentReader,进行XML读取和解析loadBeanDefinitions(beanFactory);synchronized(this。beanFactoryMonitor){this。beanFactorybeanFactory;}}catch(IOExceptionex){thrownewApplicationContextException(IOerrorparsingbeandefinitionsourceforgetDisplayName(),ex);}}
  总结一下这个方法的流程:创建DefaultListableBeanFactory。声明方式为:BeanFactorybfnewXmlBeanFactory(beanFactoryTest。xml),其中的XmlBeanFactory继承自DefaultListableBeanFactory,并提供了XmlBeanDefinitionReader类型的reader属性,也就是说DefaultListableBeanFactory是容器的基础,必须首先实例化,这里就是实例化DefaultListableBeanFactory的步骤指定序列化ID定制BeanFactory加载BeanDefinition使用全局变量记录BeanFactory类实例定制BeanFactory
  首先我们先了解customizeBeanFactory方法,该方法是在基本容器的基础上,增加了是否允许覆盖、是否允许扩展的设置。protectedvoidcustomizeBeanFactory(DefaultListableBeanFactorybeanFactory){如果不为空,设置beanFactory对象响应的属性,含义:是否允许覆盖同名称的不同定义的对象if(this。allowBeanDefinitionOverriding!null){beanFactory。setAllowBeanDefinitionOverriding(this。allowBeanDefinitionOverriding);}如果属性不为空,设置给beanFactory对象相应属性,含义:是否允许bean之间存在循环依赖if(this。allowCircularReferences!null){beanFactory。setAllowCircularReferences(this。allowCircularReferences);}}
  具体这里只是做了简单的判断,具体设置属性的地方,使用子类覆盖即可。例如:author神秘杰克公众号:Java菜鸟程序员date2022612Description自定义ClassPathXmlApplicationContextpublicclassMyClassPathXmlApplicationContextextendsClassPathXmlApplicationContext{OverrideprotectedvoidcustomizeBeanFactory(DefaultListableBeanFactorybeanFactory){super。setAllowBeanDefinitionOverriding(false);super。setAllowCircularReferences(false);super。customizeBeanFactory(beanFactory);}}加载BeanDefinition
  在初始化了DefaultListableBeanFactory后,我们还需要XmlBeanDefinitionReader来读取XML文件,这个步骤中首先要做的就是初始化XmlBeanDefinitionReader。protectedvoidloadBeanDefinitions(DefaultListableBeanFactorybeanFactory)throwsBeansException,IOException{为指定beanFactory创建XmlBeanDefinitionReaderXmlBeanDefinitionReaderbeanDefinitionReadernewXmlBeanDefinitionReader(beanFactory);进行环境变量的设置beanDefinitionReader。setEnvironment(this。getEnvironment());beanDefinitionReader。setResourceLoader(this);beanDefinitionReader。setEntityResolver(newResourceEntityResolver(this));对beanDefinitionReader进行设置,可以覆盖initBeanDefinitionReader(beanDefinitionReader);loadBeanDefinitions(beanDefinitionReader);}
  初始化了DefaultListableBeanFactory和XmlBeanDefinitionReader后,我们就可以进行配置文件的读取了。最终XmlBeanDefinitionReader所去读的BeanDefinitionHolder都会注册到DefaultListableBeanFactory中。
  经过该方法后类型为DefaultListableBeanFactory中的变量beanFactory已经包含了所有解析好的配置。关于配置文件的读取这一部分之前文章已经讲过,这里就不再赘述。功能扩展
  我们在完成了配置文件解析后,我们接着进入prepareBeanFactory方法。protectedvoidprepareBeanFactory(ConfigurableListableBeanFactorybeanFactory){设置beanFactory的ClassLoader为当前context的ClassLoaderbeanFactory。setBeanClassLoader(getClassLoader());设置beanFactory的表达式语言处理beanFactory。setBeanExpressionResolver(newStandardBeanExpressionResolver(beanFactory。getBeanClassLoader()));为beanFactory增加了一个默认的propertyEditor,主要是对bean的属性等设置管理的一个工具beanFactory。addPropertyEditorRegistrar(newResourceEditorRegistrar(this,getEnvironment()));添加BeanPostProcessorbeanFactory。addBeanPostProcessor(newApplicationContextAwareProcessor(this));设置几个忽略自动装配的接口beanFactory。ignoreDependencyInterface(EnvironmentAware。class);beanFactory。ignoreDependencyInterface(EmbeddedValueResolverAware。class);beanFactory。ignoreDependencyInterface(ResourceLoaderAware。class);beanFactory。ignoreDependencyInterface(ApplicationEventPublisherAware。class);beanFactory。ignoreDependencyInterface(MessageSourceAware。class);beanFactory。ignoreDependencyInterface(ApplicationContextAware。class);设置了几个自动装配的特殊规则beanFactory。registerResolvableDependency(BeanFactory。class,beanFactory);beanFactory。registerResolvableDependency(ResourceLoader。class,this);beanFactory。registerResolvableDependency(ApplicationEventPublisher。class,this);beanFactory。registerResolvableDependency(ApplicationContext。class,this);增加了ApplicationListenerDetector主要是检测bean是否实现了ApplicationListener接口beanFactory。addBeanPostProcessor(newApplicationListenerDetector(this));增加对AspectJ的支持if(beanFactory。containsBean(LOADTIMEWEAVERBEANNAME)){beanFactory。addBeanPostProcessor(newLoadTimeWeaverAwareProcessor(beanFactory));SetatemporaryClassLoaderfortypematching。beanFactory。setTempClassLoader(newContextTypeMatchClassLoader(beanFactory。getBeanClassLoader()));}添加默认的系统环境beanif(!beanFactory。containsLocalBean(ENVIRONMENTBEANNAME)){beanFactory。registerSingleton(ENVIRONMENTBEANNAME,getEnvironment());}if(!beanFactory。containsLocalBean(SYSTEMPROPERTIESBEANNAME)){beanFactory。registerSingleton(SYSTEMPROPERTIESBEANNAME,getEnvironment()。getSystemProperties());}if(!beanFactory。containsLocalBean(SYSTEMENVIRONMENTBEANNAME)){beanFactory。registerSingleton(SYSTEMENVIRONMENTBEANNAME,getEnvironment()。getSystemEnvironment());}}
  该方法主要做了几个方面的扩展:增加了对SpEL语言的支持增加了对属性编辑器的支持增加了一些内置类,比如EnvironmentAware、MessageSourceAware的信息注入设置了依赖功能可忽略的接口注册了一些固定依赖的属性增加AspectJ的支持将相关环境变量及属性注册以单例模式注册BeanFactory的后处理
  BeanFactory作为Spring中容器的基础,用于存放所有已经加载的bean,为了保证程序的高扩展性,Spring针对BeanFactory做了大量的扩展,比如PostProcessor等都是在这里实现的。激活注册的BeanFactoryPostProcessor
  在学习之前,我们先了解一下BeanFactoryPostProcessor的用法。BeanFactoryPostProcessor接口和BeanPostProcessor类似,可以对bean的定义进行处理。也就是说,SpringIOC容器允许BeanFactoryPostProcessor在容器实例化任何bean之前读取配置元数据,并可以修改它。BeanFactoryPostProcessor可以配置多个,通过实现Ordered接口设置order来控制执行顺序。
  需要注意的是如果在容器中定义一个BeanFactoryPostProcessor,它仅仅对此容器中的bean进行后置处理。BeanFactoryPostProcessor不会对其他容器中的bean进行后置处理。
  1。BeanFactoryPostProcessor的典型应用:PropertySourcesPlaceholderConfigurer
  首先我们来看一下配置文件:beanidhelloclasscn。jack。Hellopropertynamemsgvalue{bean。msg}valuepropertybean
  在里面我们使用到了变量引用:{bean。msg},这就是Spring的分散配置,我们可以在配置文件中配置该属性的值。
  application。propertiesbean。msghi
  然后我们再进行配置文件的配置。beanidhelloHandlerclassorg。springframework。context。support。PropertySourcesPlaceholderConfigurerpropertynamelocationslistvalueapplication。propertiesvaluelistpropertybean
  这时候就明白了,我们通过PreferencesPlaceholderConfigurer中进行获取我们的配置信息。我们查看该类可以知道间接性继承了BeanFactoryPostProcessor接口。
  当Spring加载任何实现了这个接口的bean时,都会在bean工厂加载所有bena的配置之后执行postProcessBeanFactory方法。在方法中先后调用了mergeProperties、convertProperties、processProperties这三个方法,分别得到配置、将得到的配置进行转换为合适的类型、最后将配置内容告知BeanFactory。
  正是通过实现BeanFactoryPostProcessor,BeanFactory会在实例化任何bean之前获得配置信息,从而能够正确解析bean配置文件中的变量引用。
  PropertySourcesPlaceholderConfigurer已经取代了PropertyPlaceholderConfigurer,因为汇聚了Environment、多个PropertySource。所以它能够控制取值优先级、顺序,并且还提供了访问的方法,后期再想获取也不是问题。
  2。使用自定义BeanFactoryPostProcessor
  我们自己实现一个自定义BeanFactoryPostProcessor,去除我们不想要显示的属性值的功能来展示自定义BeanFactoryPostProcessor的创建及使用,例如bean定义中我们屏蔽掉‘guapi’、‘shazi’。beansxmlnshttp:www。springframework。orgschemabeansxmlns:xsihttp:www。w3。org2001XMLSchemainstancexsi:schemaLocationhttp:www。springframework。orgschemabeanshttp:www。springframework。orgschemabeansspringbeans。xsdbeanclasscn。jack。ObscenityRemovingBeanFactoryPostProcessoridcustomBeanFactoryPostProcessorpropertynameobscenitiessetvalueguapivaluevalueshazivaluesetpropertybeanbeanclasscn。jack。SimpleBeanidsimpleBeanpropertynameuserNamevaluejackpropertynameaddressvalueguapipropertynameemailvalueshazibeanbeanspublicclassSimpleBean{privateStringuserName;privateStringemail;privateStringaddress;gettersetter}publicclassObscenityRemovingBeanFactoryPostProcessorimplementsBeanFactoryPostProcessor{privatefinalSetStringobscenities;publicObscenityRemovingBeanFactoryPostProcessor(){this。obscenitiesnewHashSet();}将所有bean的参数中含有obscenities集合中的值进行屏蔽OverridepublicvoidpostProcessBeanFactory(ConfigurableListableBeanFactorybeanFactory)throwsBeansException{String〔〕beanNamesbeanFactory。getBeanDefinitionNames();for(StringbeanName:beanNames){finalBeanDefinitionbeanDefinitionbeanFactory。getBeanDefinition(beanName);StringValueResolvervalueResolverstrVal{if(isObscene(strVal)){return;}returnstrVal;};finalBeanDefinitionVisitorbeanDefinitionVisitornewBeanDefinitionVisitor(valueResolver);beanDefinitionVisitor。visitBeanDefinition(beanDefinition);}}publicbooleanisObscene(Objectvalue){StringpotentialObscenityvalue。toString()。toUpperCase();returnthis。obscenities。contains(potentialObscenity);}publicvoidsetObscenities(SetStringobscenities){this。obscenities。clear();for(Stringobscenity:obscenities){this。obscenities。add(obscenity。toUpperCase());}}}
  启动类:publicclassTest{publicstaticvoidmain(String〔〕args){ApplicationContextacnewClassPathXmlApplicationContext(beanFactory。xml);SimpleBeansimpleBean(SimpleBean)ac。getBean(simpleBean);System。out。println(simpleBean);}}
  输出结果:SimpleBean{userNamejack,email,address}
  我们通过ObscenityRemovingBeanFactoryPostProcessor我们很好的屏蔽掉了我们不想要显示的属性。激活BeanFactoryPostProcessor
  我们在了解了BeanFactoryPostProcessor的用法之后就可以继续回到我们的refresh方法中继续研究源码了。
  进入invokeBeanFactoryPostProcessors方法中。protectedvoidinvokeBeanFactoryPostProcessors(ConfigurableListableBeanFactorybeanFactory){PostProcessorRegistrationDelegate。invokeBeanFactoryPostProcessors(beanFactory,getBeanFactoryPostProcessors());DetectaLoadTimeWeaverandprepareforweaving,iffoundinthemeantime(e。g。throughanBeanmethodregisteredbyConfigurationClassPostProcessor)if(beanFactory。getTempClassLoader()nullbeanFactory。containsBean(LOADTIMEWEAVERBEANNAME)){beanFactory。addBeanPostProcessor(newLoadTimeWeaverAwareProcessor(beanFactory));beanFactory。setTempClassLoader(newContextTypeMatchClassLoader(beanFactory。getBeanClassLoader()));}}
  我们继续进入具体重载方法中invokeBeanFactoryPostProcessors。publicstaticvoidinvokeBeanFactoryPostProcessors(ConfigurableListableBeanFactorybeanFactory,ListBeanFactoryPostProcessorbeanFactoryPostProcessors){将已经执行过的BeanFactoryPostProcessor存储在processedBeans,防止重复执行SetStringprocessedBeansnewHashSet();对BeanDefinitionRegistry类型进行处理if(beanFactoryinstanceofBeanDefinitionRegistry){BeanDefinitionRegistryregistry(BeanDefinitionRegistry)beanFactory;用来存放BeanFactoryPostProcessor对象ListBeanFactoryPostProcessorregularPostProcessorsnewArrayList();用来存放BeanDefinitionRegistryPostProcessor对象方便统一执行实现了BeanDefinitionRegistryPostProcessor接口父类的方法ListBeanDefinitionRegistryPostProcessorregistryProcessorsnewArrayList();处理外部定义的BeanFactoryPostProcessor,将BeanDefinitionRegistryPostProcessor与BeanFactoryPostProcessor区分开for(BeanFactoryPostProcessorpostProcessor:beanFactoryPostProcessors){if(postProcessorinstanceofBeanDefinitionRegistryPostProcessor){BeanDefinitionRegistryPostProcessorregistryProcessor(BeanDefinitionRegistryPostProcessor)postProcessor;对于BeanDefinitionRegistryPostProcessor类型,需要先调用此方法,再添加到集合中registryProcessor。postProcessBeanDefinitionRegistry(registry);registryProcessors。add(registryProcessor);}else{记录常规BeanFactoryPostProcessorregularPostProcessors。add(postProcessor);}}存放当前需要执行的BeanDefinitionRegistryPostProcessorListBeanDefinitionRegistryPostProcessorcurrentRegistryProcessorsnewArrayList();调用实现PriorityOrdered的BeanDefinitionRegistryPostProcessor。获取所有实现了BeanDefinitionRegistryPostProcessor接口的类名String〔〕postProcessorNamesbeanFactory。getBeanNamesForType(BeanDefinitionRegistryPostProcessor。class,true,false);for(StringppName:postProcessorNames){判断当前类是否实现了PriorityOrdered接口if(beanFactory。isTypeMatch(ppName,PriorityOrdered。class)){将BeanDefinitionRegistryPostProcessor类型存入currentRegistryProcessors中currentRegistryProcessors。add(beanFactory。getBean(ppName,BeanDefinitionRegistryPostProcessor。class));提前存放到processedBeans,避免重复执行,但是此处还未执行processedBeans。add(ppName);}}对currentRegistryProcessors集合中的BeanDefinitionRegistryPostProcessor类型进行排序sortPostProcessors(currentRegistryProcessors,beanFactory);添加到registryProcessors集合,用于后续执行父接口的postProcessBeanFactory方法registryProcessors。addAll(currentRegistryProcessors);遍历集合,执行BeanDefinitionRegistryPostProcessor。postProcessBeanDefinitionRegistry()方法invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors,registry);执行完毕后清空该集合currentRegistryProcessors。clear();接着,调用实现Ordered的BeanDefinitionRegistryPostProcessors这里再次获取BeanDefinitionRegistryPostProcessor,是因为有可能在上面方法执行过程中添加了BeanDefinitionRegistryPostProcessor而下面处理BeanFactoryPostProcessor的时候又不需要重复获取了是为什么呢?因为添加BeanFactoryPostProcessor与BeanDefinitionRegistryPostProcessor只能在BeanDefinitionRegistryPostProcessor中添加,在BeanFactoryPostProcessor是无法添加的for(StringppName:postProcessorNames){判断当前bean没有被执行过,并且实现了Ordered接口if(!processedBeans。contains(ppName)beanFactory。isTypeMatch(ppName,Ordered。class)){如果BeanFactory中没有该Bean则会去创建该BeancurrentRegistryProcessors。add(beanFactory。getBean(ppName,BeanDefinitionRegistryPostProcessor。class));processedBeans。add(ppName);}}sortPostProcessors(currentRegistryProcessors,beanFactory);registryProcessors。addAll(currentRegistryProcessors);invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors,registry);currentRegistryProcessors。clear();最后处理没有实现Ordered与PriorityOrdered接口的BeanDefinitionRegistryPostProcessorbooleanreiteratetrue;while(reiterate){reiteratefalse;再次获取BeanDefinitionRegistryPostProcessorpostProcessorNamesbeanFactory。getBeanNamesForType(BeanDefinitionRegistryPostProcessor。class,true,false);for(StringppName:postProcessorNames){if(!processedBeans。contains(ppName)){将本次要执行的BeanDefinitionRegistryPostProcessor存放到currentRegistryProcessorscurrentRegistryProcessors。add(beanFactory。getBean(ppName,BeanDefinitionRegistryPostProcessor。class));processedBeans。add(ppName);reiteratetrue;}}sortPostProcessors(currentRegistryProcessors,beanFactory);registryProcessors。addAll(currentRegistryProcessors);invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors,registry);currentRegistryProcessors。clear();}现在,调用到目前为止处理的所有处理器的postProcessBeanFactory回调。invokeBeanFactoryPostProcessors(registryProcessors,beanFactory);invokeBeanFactoryPostProcessors(regularPostProcessors,beanFactory);}else{BeanFactory如果不归属于BeanDefinitionRegistry类型,则直接执行beanFactoryPostProcessorinvokeBeanFactoryPostProcessors(beanFactoryPostProcessors,beanFactory);}String〔〕postProcessorNamesbeanFactory。getBeanNamesForType(BeanFactoryPostProcessor。class,true,false);用于存放实现了priorityOrdered接口的BeanFactoryPostProcessorListBeanFactoryPostProcessorpriorityOrderedPostProcessorsnewArrayList();用于存放实现了ordered接口的BeanFactoryPostProcessor名称ListStringorderedPostProcessorNamesnewArrayList();用于存放无排序的BeanFactoryPostProcessor名称ListStringnonOrderedPostProcessorNamesnewArrayList();for(StringppName:postProcessorNames){如果已经执行过了,则不做处理if(processedBeans。contains(ppName)){skipalreadyprocessedinfirstphaseabove}如果实现了PriorityOrdered则添加elseif(beanFactory。isTypeMatch(ppName,PriorityOrdered。class)){priorityOrderedPostProcessors。add(beanFactory。getBean(ppName,BeanFactoryPostProcessor。class));}如果实现了Ordered则添加elseif(beanFactory。isTypeMatch(ppName,Ordered。class)){orderedPostProcessorNames。add(ppName);}如果没有排序则添加到指定集合else{nonOrderedPostProcessorNames。add(ppName);}}首先调用实现PriorityOrdered的BeanFactoryPostProcessor。sortPostProcessors(priorityOrderedPostProcessors,beanFactory);invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors,beanFactory);然后调用实现Ordered的BeanFactoryPostProcessors。ListBeanFactoryPostProcessororderedPostProcessorsnewArrayList(orderedPostProcessorNames。size());for(StringpostProcessorName:orderedPostProcessorNames){orderedPostProcessors。add(beanFactory。getBean(postProcessorName,BeanFactoryPostProcessor。class));}sortPostProcessors(orderedPostProcessors,beanFactory);invokeBeanFactoryPostProcessors(orderedPostProcessors,beanFactory);最后调用其他没有排序的BeanFactoryPostProcessor。ListBeanFactoryPostProcessornonOrderedPostProcessorsnewArrayList(nonOrderedPostProcessorNames。size());for(StringpostProcessorName:nonOrderedPostProcessorNames){nonOrderedPostProcessors。add(beanFactory。getBean(postProcessorName,BeanFactoryPostProcessor。class));}invokeBeanFactoryPostProcessors(nonOrderedPostProcessors,beanFactory);清空缓存beanFactory。clearMetadataCache();}注册BeanPostProcessor
  了解了BeanFactoryPostProcessors的调用后,我们现在来了解下BeanPostProcessor,这里仅仅是注册,并不是调用。真正的调用在bean实例化阶段进行的。
  在BeanFactory中并没有实现后处理器的自动注册功能,所以在调用的时候如果没有进行主动注册则是不能够使用的。但是在ApplicationContext中添加了主动注册功能。
  比如自定义这样的后处理器:publicclassMyInstantiationAwareBeanPostProcessorimplementsInstantiationAwareBeanPostProcessor{OverridepublicObjectpostProcessBeforeInstantiation(Classlt;?beanClass,StringbeanName)throwsBeansException{System。out。println();returnnull;}}beanclasscn。jack。MyInstantiationAwareBeanPostProcessor
  在使用ApplicationContext方式获取bean的时候会在获取之前打印出,在BeanFactory方式进行bean的加载是不会有该打印的。
  这个特性就是在registerBeanPostProcessors中实现的。publicstaticvoidregisterBeanPostProcessors(ConfigurableListableBeanFactorybeanFactory,AbstractApplicationContextapplicationContext){获取所有实现BeanPostProcessor接口的类String〔〕postProcessorNamesbeanFactory。getBeanNamesForType(BeanPostProcessor。class,true,false);注册一个BeanPostProcessorChecker,用来记录bean在BeanPostProcessor实例化时的信息intbeanProcessorTargetCountbeanFactory。getBeanPostProcessorCount()1postProcessorNames。length;beanFactory。addBeanPostProcessor(newBeanPostProcessorChecker(beanFactory,beanProcessorTargetCount));区分实现不同接口的BeanPostProcessorsListBeanPostProcessorpriorityOrderedPostProcessorsnewArrayList();ListBeanPostProcessorinternalPostProcessorsnewArrayList();ListStringorderedPostProcessorNamesnewArrayList();ListStringnonOrderedPostProcessorNamesnewArrayList();根据不同类型进行addfor(StringppName:postProcessorNames){if(beanFactory。isTypeMatch(ppName,PriorityOrdered。class)){BeanPostProcessorppbeanFactory。getBean(ppName,BeanPostProcessor。class);priorityOrderedPostProcessors。add(pp);if(ppinstanceofMergedBeanDefinitionPostProcessor){internalPostProcessors。add(pp);}}elseif(beanFactory。isTypeMatch(ppName,Ordered。class)){orderedPostProcessorNames。add(ppName);}else{nonOrderedPostProcessorNames。add(ppName);}}排序后执行注册实现了PriorityOrdered的BeanPostProcessorssortPostProcessors(priorityOrderedPostProcessors,beanFactory);registerBeanPostProcessors(beanFactory,priorityOrderedPostProcessors);注册实现Ordered接口的BeanPostProcessorsListBeanPostProcessororderedPostProcessorsnewArrayList(orderedPostProcessorNames。size());for(StringppName:orderedPostProcessorNames){拿到ppName对应的BeanPostProcessor实例对象BeanPostProcessorppbeanFactory。getBean(ppName,BeanPostProcessor。class);将ppName对应的BeanPostProcessor实例对象添加到orderedPostProcessors,准备执行注册orderedPostProcessors。add(pp);if(ppinstanceofMergedBeanDefinitionPostProcessor){如果ppName对应的bean实例也实现了MergedBeanDefinitionPostProcessor接口,则添加到该集合中internalPostProcessors。add(pp);}}对orderedPostProcessors进行排序并注册sortPostProcessors(orderedPostProcessors,beanFactory);registerBeanPostProcessors(beanFactory,orderedPostProcessors);注册所有常规的BeanPostProcessors,过程同上ListBeanPostProcessornonOrderedPostProcessorsnewArrayList(nonOrderedPostProcessorNames。size());for(StringppName:nonOrderedPostProcessorNames){BeanPostProcessorppbeanFactory。getBean(ppName,BeanPostProcessor。class);nonOrderedPostProcessors。add(pp);if(ppinstanceofMergedBeanDefinitionPostProcessor){internalPostProcessors。add(pp);}}registerBeanPostProcessors(beanFactory,nonOrderedPostProcessors);注册所有mergedBeanDefinitionPostProcessor类型的BeanPostProcessor,并非重复注册在beanFactory。addBeanPostProcessor中会先移除已经存在的BeanPostProcessorsortPostProcessors(internalPostProcessors,beanFactory);registerBeanPostProcessors(beanFactory,internalPostProcessors);添加ApplicationListener探测器beanFactory。addBeanPostProcessor(newApplicationListenerDetector(applicationContext));}初始化消息资源
  在initMessageSource中主要功能是提取配置文件中的messageSource,并将其记录在Spring容器中,也就是ApplicationContext中。如果用户未设置资源文件的话,则获取Spring默认的配置delegatingMessageSource。protectedvoidinitMessageSource(){ConfigurableListableBeanFactorybeanFactorygetBeanFactory();Bean的名称必须要是messageSourceif(beanFactory。containsLocalBean(MESSAGESOURCEBEANNAME)){MESSAGESOURCEBEANNAMEmessageSourcethis。messageSourcebeanFactory。getBean(MESSAGESOURCEBEANNAME,MessageSource。class);MakeMessageSourceawareofparentMessageSource。if(this。parent!nullthis。messageSourceinstanceofHierarchicalMessageSource){HierarchicalMessageSourcehms(HierarchicalMessageSource)this。messageSource;if(hms。getParentMessageSource()null){OnlysetparentcontextasparentMessageSourceifnoparentMessageSourceregisteredalready。hms。setParentMessageSource(getInternalParentMessageSource());}}if(logger。isTraceEnabled()){logger。trace(UsingMessageSource〔this。messageSource〕);}}else{如果用户并没有定义配置文件,那么使用临时的DelegatingMessageSource以便于作为调用getMessage的返回DelegatingMessageSourcedmsnewDelegatingMessageSource();dms。setParentMessageSource(getInternalParentMessageSource());this。messageSourcedms;beanFactory。registerSingleton(MESSAGESOURCEBEANNAME,this。messageSource);if(logger。isTraceEnabled()){logger。trace(NoMESSAGESOURCEBEANNAMEbean,using〔this。messageSource〕);}}}
  这里规定资源文件必须为messageSource,否则就会获取不到自定义资源配置。初始化ApplicationEventMulticaster
  initApplicationEventMulticaster方法实现比较简单,存在两种情况:如果用户自定义了事件广播器,那么就是用用户自定义的事件广播器如果用户没有自定义事件广播器,那么使用默认的ApplicationEventMulticasterprotectedvoidinitApplicationEventMulticaster(){ConfigurableListableBeanFactorybeanFactorygetBeanFactory();判断容器中是否存在BeanDefinitionName为applicationEventMulticaster的bd,也就是自定义的事件监听多路广播器,必须实现ApplicationEventMulticaster接口if(beanFactory。containsLocalBean(APPLICATIONEVENTMULTICASTERBEANNAME)){this。applicationEventMulticasterbeanFactory。getBean(APPLICATIONEVENTMULTICASTERBEANNAME,ApplicationEventMulticaster。class);if(logger。isTraceEnabled()){logger。trace(UsingApplicationEventMulticaster〔this。applicationEventMulticaster〕);}}else{如果没有,则默认采用SimpleApplicationEventMulticasterthis。applicationEventMulticasternewSimpleApplicationEventMulticaster(beanFactory);beanFactory。registerSingleton(APPLICATIONEVENTMULTICASTERBEANNAME,this。applicationEventMulticaster);if(logger。isTraceEnabled()){logger。trace(NoAPPLICATIONEVENTMULTICASTERBEANNAMEbean,using〔this。applicationEventMulticaster。getClass()。getSimpleName()〕);}}}
  最后,作为广播器,一定是用于存放监听器并在合适的时候调用监听器,我们进入默认的广播器实现类SimpleApplicationEventMulticaster中看一下。
  看到如下方法:publicvoidmulticastEvent(finalApplicationEventevent,NullableResolvableTypeeventType){ResolvableTypetype(eventType!null?eventType:resolveDefaultEventType(event));ExecutorexecutorgetTaskExecutor();for(ApplicationListenerlt;?listener:getApplicationListeners(event,type)){if(executor!null){executor。execute(()invokeListener(listener,event));}else{invokeListener(listener,event);}}}
  可以推断,当产生Spring事件的时候会默认使用SimpleApplicationEventMulticaster的multicastEvent来广播事件,遍历所有监听器,并使用监听器中的onApplicationEvent方法来进行监听器的处理。而对于每个监听器来说其实都可以获取到产生的事件,但是是否进行处理则由事件监听器决定。注册监听器
  我们反复提到了监听器,我们接下来看一下Spring注册监听器的时候又做了哪些逻辑操作?protectedvoidregisterListeners(){首先注册静态的指定的监听器,注册的是特殊的事件监听器,而不是配置中的beanfor(ApplicationListenerlt;?listener:getApplicationListeners()){getApplicationEventMulticaster()。addApplicationListener(listener);}这里不会初始化FactoryBean,我们需要保留所有的普通bean不会实例化这些bean,让后置处理器可以感知到它们String〔〕listenerBeanNamesgetBeanNamesForType(ApplicationListener。class,true,false);for(StringlistenerBeanName:listenerBeanNames){getApplicationEventMulticaster()。addApplicationListenerBean(listenerBeanName);}现在有了事件广播组,发布之前的应用事件SetearlyEventsToProcessthis。earlyApplicationEvents;this。earlyApplicationEventsnull;if(earlyEventsToProcess!null){for(ApplicationEventearlyEvent:earlyEventsToProcess){getApplicationEventMulticaster()。multicastEvent(earlyEvent);}}}
  只是将一些特殊的监听器注册到广播组中,那些在bean配置文件中实现了ApplicationListener接口的类还没有实例化,所以此时只是将name保存到了广播组中,将这些监听器注册到广播组中的操作时在bean的后置处理器中完成的,那时候bean的实例化已经完成了。初始化非延迟加载单例
  完成BeanFactory的初始化工作,其中包括ConversionService的设置、配置冻结以及非延迟加载的bean的初始化工作。protectedvoidfinishBeanFactoryInitialization(ConfigurableListableBeanFactorybeanFactory){初始化此上下文的转换服务if(beanFactory。containsBean(CONVERSIONSERVICEBEANNAME)beanFactory。isTypeMatch(CONVERSIONSERVICEBEANNAME,ConversionService。class)){beanFactory。setConversionService(beanFactory。getBean(CONVERSIONSERVICEBEANNAME,ConversionService。class));}如果beanFactory之前没有注册解析器,则注册默认的解析器,例如{}解析成真正的属性:主要用于注解属性值的解析if(!beanFactory。hasEmbeddedValueResolver()){beanFactory。addEmbeddedValueResolver(strValgetEnvironment()。resolvePlaceholders(strVal));}处理EnableLoadTimeWeaving或context:loadtimeweaver标记的类String〔〕weaverAwareNamesbeanFactory。getBeanNamesForType(LoadTimeWeaverAware。class,false,false);for(StringweaverAwareName:weaverAwareNames){getBean(weaverAwareName);}临时类加载器设置为空beanFactory。setTempClassLoader(null);冻结所有的bean定义,说明注册的bean定义将不被修改或者进一步处理beanFactory。freezeConfiguration();初始化剩下的单例实例(非惰性)beanFactory。preInstantiateSingletons();}
  首先,我们先了解下ConversionService类所提供的作用。
  1。ConversionService的设置
  之前我们提到可以用自定义类型转换器把String类型转换为Date,在Spring中也提供了使用Converter来进行转换。
  2。冻结配置
  冻结所有的bean定义,说明注册的bean定义将不被修改或者进行任何一步的处理。publicvoidfreezeConfiguration(){this。configurationFrozentrue;this。frozenBeanDefinitionNamesStringUtils。toStringArray(this。beanDefinitionNames);}
  3。初始化延迟加载
  ApplicationContext实现的默认行为就是在启动时将所有单例bean提前进行实例化。提前实例化也就意味着作为初始化过程的一部分,ApplicationContext实例会创建并配置所有单例bean,这个实例化过程就是在finishBeanFactoryInitialization方法中的preInstantiateSingletons方法中完成的。publicvoidpreInstantiateSingletons()throwsBeansException{if(logger。isTraceEnabled()){logger。trace(Preinstantiatingsingletonsinthis);}创建beanDefinitionNames的副本beanNames用于后续的遍历,以允许init等方法注册新的bean定义ListStringbeanNamesnewArrayList(this。beanDefinitionNames);遍历beanNames,触发所有非懒加载单例bean的初始化for(StringbeanName:beanNames){获取beanName对应的MergedBeanDefinitionRootBeanDefinitionbdgetMergedLocalBeanDefinition(beanName);bd对应的不是抽象类并且是单例并且不是懒加载if(!bd。isAbstract()bd。isSingleton()!bd。isLazyInit()){判断是否为FactoryBeanif(isFactoryBean(beanName)){通过前缀和beanName拿到BeanObjectbeangetBean(FACTORYBEANPREFIXbeanName);如果为FactoryBeanif(beaninstanceofFactoryBean){finalFactoryBeanlt;?factory(FactoryBeanlt;?)bean;判断这个FactoryBean是否希望急切的初始化booleanisEagerInit;if(System。getSecurityManager()!nullfactoryinstanceofSmartFactoryBean){isEagerInitAccessController。doPrivileged((PrivilegedActionBoolean)((SmartFactoryBeanlt;?)factory)::isEagerInit,getAccessControlContext());}else{isEagerInit(factoryinstanceofSmartFactoryBean((SmartFactoryBeanlt;?)factory)。isEagerInit());}如果希望急切的初始化,则通过beanName获取bean实例if(isEagerInit){getBean(beanName);}}}else{如果beanName对应的bean不是FactoryBean,只是普通Bean,通过beanName获取bean实例getBean(beanName);}}}遍历beanNames,触发所有SmartInitializingSingleton的后初始化回调for(StringbeanName:beanNames){拿到beanName对应的bean实例ObjectsingletonInstancegetSingleton(beanName);判断singletonInstance是否实现了SmartInitializingSingleton接口if(singletonInstanceinstanceofSmartInitializingSingleton){finalSmartInitializingSingletonsmartSingleton(SmartInitializingSingleton)singletonInstance;触发SmartInitializingSingleton实现类的afterSingletonsInstantiated方法if(System。getSecurityManager()!null){AccessController。doPrivileged((PrivilegedActionObject)(){smartSingleton。afterSingletonsInstantiated();returnnull;},getAccessControlContext());}else{smartSingleton。afterSingletonsInstantiated();}}}}finishRefresh
  在Spring中提供了Lifecycle接口,该接口包含startstop方法,实现此接口后Spring会保证在启动时候调用其start方法开始声明周期,并在Spring关闭时候调用stop方法来结束声明周期。通常用来配置后台程序,在启动后一直运行(比如MQ)而ApplicationContext最后一步finishRefresh方法就是实现这一功能。protectedvoidfinishRefresh(){清除资源缓存clearResourceCaches();1。为此上下文初始化生命周期处理器initLifecycleProcessor();2。首先将刷新完毕事件传播到生命周期处理器(触发isAutoStartup方法返回true的SmartLifecycle的start方法)getLifecycleProcessor()。onRefresh();3。推送上下文刷新完毕事件到相应的监听器publishEvent(newContextRefreshedEvent(this));ParticipateinLiveBeansViewMBean,ifactive。LiveBeansView。registerApplicationContext(this);}
  1。initLifecycleProcessor
  当ApplicationContext启动或停止时,它会通过LifecycleProcessor来和所有声明的bean的周期做状态更新,而在LifecycleProcessor的使用前首先进行初始化。protectedvoidinitLifecycleProcessor(){ConfigurableListableBeanFactorybeanFactorygetBeanFactory();判断BeanFactory是否已经存在生命周期处理器(beanNamelifecycleProcessor)if(beanFactory。containsLocalBean(LIFECYCLEPROCESSORBEANNAME)){this。lifecycleProcessorbeanFactory。getBean(LIFECYCLEPROCESSORBEANNAME,LifecycleProcessor。class);if(logger。isTraceEnabled()){logger。trace(UsingLifecycleProcessor〔this。lifecycleProcessor〕);}}else{如果不存在,则使用DefaultLifecycleProcessorDefaultLifecycleProcessordefaultProcessornewDefaultLifecycleProcessor();defaultProcessor。setBeanFactory(beanFactory);this。lifecycleProcessordefaultProcessor;并将DefaultLifecycleProcessor作为默认的生命周期处理器,注册到BeanFactory中beanFactory。registerSingleton(LIFECYCLEPROCESSORBEANNAME,this。lifecycleProcessor);if(logger。isTraceEnabled()){logger。trace(NoLIFECYCLEPROCESSORBEANNAMEbean,using〔this。lifecycleProcessor。getClass()。getSimpleName()〕);}}}
  2。onRefresh
  启动所有实现了Lifecycle接口的beanpublicvoidonRefresh(){startBeans(true);this。runningtrue;}privatevoidstartBeans(booleanautoStartupOnly){MapString,LifecyclelifecycleBeansgetLifecycleBeans();MapInteger,LifecycleGroupphasesnewHashMap();lifecycleBeans。forEach((beanName,bean){if(!autoStartupOnly(beaninstanceofSmartLifecycle((SmartLifecycle)bean)。isAutoStartup())){intphasegetPhase(bean);LifecycleGroupgroupphases。get(phase);if(groupnull){groupnewLifecycleGroup(phase,this。timeoutPerShutdownPhase,lifecycleBeans,autoStartupOnly);phases。put(phase,group);}group。add(beanName,bean);}});if(!phases。isEmpty()){ListIntegerkeysnewArrayList(phases。keySet());Collections。sort(keys);for(Integerkey:keys){phases。get(key)。start();}}}
  3。publishEvent
  当完成ApplicationContext初始化的时候,要通过Spring中的发布机制来发出ContextRefreshedEvent事件,以保证对应的监听器可以做进一步的逻辑处理。protectedvoidpublishEvent(Objectevent,NullableResolvableTypeeventType){Assert。notNull(event,Eventmustnotbenull);如有必要,将事件装饰为ApplicationEventApplicationEventapplicationEvent;if(eventinstanceofApplicationEvent){applicationEvent(ApplicationEvent)event;}else{applicationEventnewPayloadApplicationEvent(this,event);if(eventTypenull){eventType((PayloadApplicationEventlt;?)applicationEvent)。getResolvableType();}}Multicastrightnowifpossibleorlazilyoncethemulticasterisinitializedif(this。earlyApplicationEvents!null){this。earlyApplicationEvents。add(applicationEvent);}else{使用事件广播器广播事件到相应的监听器getApplicationEventMulticaster()。multicastEvent(applicationEvent,eventType);}通过parent发布事件if(this。parent!null){if(this。parentinstanceofAbstractApplicationContext){((AbstractApplicationContext)this。parent)。publishEvent(event,eventType);}else{this。parent。publishEvent(event);}}}
  到这里,refresh方法就解析完成了。下一步就是AOP了。
  如果本文对你有帮助,别忘记给我个3连,点赞,转发,评论,
  咱们下期见!答案获取方式:已赞已评已关
  学习更多JAVA知识与技巧,关注与私信博主(03)
  原文出处:https:segmentfault。coma1190000041999221

再见了,NBA!湖人和勇士都不要他!曾经单场砍下26分25篮据篮球记者AlbertoDeRoa报道,前NBA球员肯尼思法里德加盟墨西哥联赛,正式签约SolesdeMexicali队。两周之前勇士试训了法里德等人,但球队选择签约2018年……音质很能打,续航有惊喜,塞那M13SPro无线音箱使用体验上个月,我入手了一款塞那V12SPro便携音箱,整体的使用感受非常不错,我也把它的使用体验跟大家分享了出来,我身边的许多朋友对这款音响也是赞不绝口,还有很多人问我,塞那有没有同……小S二女儿广告大片出炉,妆发成熟超有气质,网友直呼像30多岁10月5日晚,小S二女儿许韶恩在个人社交平台分享了一组为某品牌拍摄的广告大片。之前许韶恩就曾和妈妈小S一起拍过广告大片,此次又独自一人出镜,简直太有范儿了。看来小妮子不久的将来……不用卫生巾自制洗衣液零垃圾旅行,网红博主的绿色生活阿娃再一次出发了。阿娃在流浪是一名环保博主,她一直在日常生活中坚持着零废弃理念。这一次她与几个伙伴一起在国内开启了新一轮的零垃圾旅行。与一般的旅行不同,阿娃认为,这种旅行……银行账户进账多少会被查呢?那个人银行账户进账多少会被查呢?根据中国人民银行2016年的第三号文件发布金融机构大额交易和可疑交易报告管理办法当中给出了一个明确的答案,大额交易肯定是会被严查的。简单一点来说……Python快速排序和归并排序快速排序(Quicksort)快速排序,是每次找一个数字(一般是列表的第一个元素)作为中间值,将小于这个中间值的元素都放在左边,比这个中间值大的放在右边。然后,对左边和右……OPPOFindX5成销量之王,FindX3无奈沦落至白菜价OPPOFindX5这款手机,现在价格不错。3000出头的价格就可以得到一部性能好,拍照好的手机。配置方面,OPPOFindX5搭载高通骁龙888处理器,采用5nm工艺,拥有八……首战输30分,威少不背锅!不过比输球更可怕的是湖人两大致命弱今天是湖人新赛季的第一场季前赛,主场对阵实力平平的国王,结果却令人大跌眼镜,全场比赛下来湖人整整输了30分!要知道这场比赛湖人主力悉数登场,虽然詹眉以及威少都只出战了15……做时代的漫游者,去往未来度假上海时装周记者周芳颖编辑楼婍沁XUZHIFlneur漫游者在瞬息万变的城市生活中,总有这样一群漫游者,静静观察熙熙攘攘的人群。波德莱尔称他们为现代生活的画家,在漫无目的……放射性元素会发生衰变,它为什么要衰变?内部矛盾太大了在元素周期表中,43号元素、61号元素以及83号以后的所有元素都是放射性元素,而这些元素都会发生衰变,简言之就是从一种元素变成另外一种元素,那么它为什么要变呢?目前人类已……全仓交通银行的第21天【全仓实盘】此生只买交通银行,全仓实盘以铜为镜可以正衣冠,以人为镜可以明得失。希望大家自我为镜可以明白炒股之长远收益在于银行股。最近经常看到有朋友留言说……2款全新四轮电动车,搭载3000W变频电机,油电两用,续航超阅读本文前,请您先点击上面的关注二字,可以快速订阅我们的最新内容,感谢支持老年代步车市场这2年发展非常迅速,一方面是人口老龄化带来的红利,另一方面也离不开产品升级。不少厂……
WOT重要提醒丨免费翻牌活动的兑换码在哪用?令牌在哪换奖励?本文首发于微信公众号坦克零距离,文章转载权限坦克空间站UP为个人专栏文章作者,非官方人员,在头条同步更新文章翡翠岛交易会寻宝活动各位车长老爷们大家好啊,我是阿……IEA月报2023年全球石油需求将创历史新高,OPEC减产加看好中国需求复苏,IEA继续上调全球石油需求,但警告称OPEC减产可能会加剧下半年石油供应短缺。4月14日周五,IEA公布了最新全球石油市场月报。在报告中,IEA称202……旅交会上阿克苏文旅主题馆透出浓浓文化味石榴云新疆日报讯左手握着刚刚能看出形状的木勺,右手拿一把雕刻刀,艾尔肯吾甫尔现场制作自治区级非物质文化遗产项目柯坪木勺。一旁的木墩上,摆着做好的木勺,造型圆润、线条柔顺。很快,……喝茶不一定要多讲究,但这些好习惯尽量培养当喝茶成为我们日常生活的一部分以后,能否培养一个好的喝茶习惯,就显得至关重要了。因为要是没有一个良好的习惯,可能喝茶对于自己来说,不见得会是一个充满享受的事情,而有些鸡肋……桃花峪谷涧流溪影桃花笑春风来源:【泰安日报最泰安】泰安日报社最泰安讯(记者刘小东陈阳)人间四月,处处芳菲。春日的泰山西麓桃花峪,变身桃花的世界,十里桃花夹岸开,开出一条醉美花路,开出一个桃源梦境。……就长月烬明的宣发,和流浪地球粉丝唠嗑几句《长月烬明》魔神大战太顶了长月烬明流浪地球同一特效团队虽然不喜欢古偶仙侠剧,但我还是承认《长月烬明》是一部用心之作:服化道不错,突破了古偶仙侠剧的流水线造型,创造性……杭州抛房潮来了杭州一大批房主,正在降价抛房。3月,杭州二手房成交量破万套。时隔2年,这样的数据,让很多人兴奋不已。但殊不知,以价换量,才是残酷现实。看似市场回暖,实则……诺基亚想退出鼎桥,东方材料想接手,华为发布公告进行了表态最近有个和鼎桥(TDTECH)相关的事情火了,事情大体上是这样的有家名叫新东方新材料的上市公司(东方材料)发布公告,宣布准备把诺基亚手里鼎桥51的股权拿下,合计要花费21……有没有你的菜?怀化推出5条春季旅游线路华声在线4月6日讯(全媒体记者刘涛黄煌)离立夏还有一个月时间,留给你春游的时间不多了,再等下去花儿真的要谢了!4月6日,在2023年湖南(春季)乡村文化旅游节暨湖南省春季村晚示……血压高的人,常吃这10种食物,可能是高血压的前兆如果不可避免地出现高血压,那么在接下来的10种食物中,可以注意沉积于血管壁上的钙盐和胆固醇。长期大量酗酒更容易诱发动脉硬化,并加重高血压。食用狗肉或躁动浮阳或加重痰火或助热性血……深圳男篮,将躺进总决赛?广州龙狮崔永熙神奇不再,无法限制吴前头条创作挑战赛辽宁媒体人导演我躺哪表示:浙江男篮、广州龙狮继续这么上对抗的话,深圳男篮可要躺进总决赛了。有球迷调侃道:深圳男篮说你们打着,我们先去度个假。CBA季后……持续升级新颖体验,FindN2Flip推动竖折叠的大外屏趋势在如今的智能市场中,新形态的折叠屏手机也逐渐被消费者所接受。目前,市面上拥有横向折叠和竖向折叠等两种主流形态,它们都各具特色。其中,竖向折叠屏手机都会配备一块外屏,以提供丰富的……
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网