专栏电商日志财经减肥爱情
投稿投诉
爱情常识
搭配分娩
减肥两性
孕期塑形
财经教案
论文美文
日志体育
养生学堂
电商科学
头戴业界
专栏星座
用品音乐

SpringIOC容器源码分析

  Spring最重要的概念是IOC和AOP,本篇文章其实就是要带领大家来分析下Spring的IOC容器。既然大家平时都要用到Spring,怎么可以不好好了解Spring呢?阅读本文并不能让你成为Spring专家,不过一定有助于大家理解Spring的很多概念,帮助大家排查应用中和Spring相关的一些问题。
  本文采用的源码版本是4。3。11。RELEASE,算是5。0。x前比较新的版本了。为了降低难度,本文所说的所有的内容都是基于xml的配置的方式,实际使用已经很少人这么做了,至少不是纯xml配置,不过从理解源码的角度来看用这种方式来说无疑是最合适的。
  阅读建议:读者至少需要知道怎么配置Spring,了解Spring中的各种概念,少部分内容我还假设读者使用过SpringMVC。本文要说的IOC总体来说有两处地方最重要,一个是创建Bean容器,一个是初始化Bean,如果读者觉得一次性看完本文压力有点大,那么可以按这个思路分两次消化。读者不一定对Spring容器的源码感兴趣,也许附录部分介绍的知识对读者有些许作用。
  希望通过本文可以让读者不惧怕阅读Spring源码,也希望大家能反馈表述错误或不合理的地方。
  引言
  先看下最基本的启动Spring容器的例子:
  java
  publicstaticvoidmain(String〔〕args){
  ApplicationContextcontextnewClassPathXmlApplicationContext(classpath:applicationfile。xml);
  }
  以上代码就可以利用配置文件来启动一个Spring容器了,请使用maven的小伙伴直接在dependencies中加上以下依赖即可,个人比较反对那些不知道要添加什么依赖,然后把Spring的所有相关的东西都加进来的方式。
  java
  org。springframework
  springcontextartifactId
  4。3。11。RELEASE
  dependency
  springcontext会自动将springcore、springbeans、springaop、springexpression这几个基础jar包带进来。
  多说一句,很多开发者入门就直接接触的SpringMVC,对Spring其实不是很了解,Spring是渐进式的工具,并不具有很强的侵入性,它的模块也划分得很合理,即使你的应用不是web应用,或者之前完全没有使用到Spring,而你就想用Spring的依赖注入这个功能,其实完全是可以的,它的引入不会对其他的组件产生冲突。
  废话说完,我们继续。ApplicationContextcontextnewClassPathXmlApplicationContext(。。。)其实很好理解,从名字上就可以猜出一二,就是在ClassPath中寻找xml配置文件,根据xml文件内容来构建ApplicationContext。当然,除了ClassPathXmlApplicationContext以外,我们也还有其他构建ApplicationContext的方案可供选择,我们先来看看大体的继承结构是怎么样的:
  !〔1〕(https:www。javadoop。comblogimagesspringcontext1。png)
  读者可以大致看一下类名,源码分析的时候不至于找不着看哪个类,因为Spring为了适应各种使用场景,提供的各个接口都可能有很多的实现类。对于我们来说,就是揪着一个完整的分支看完。
  当然,读本文的时候读者也不必太担心,每个代码块分析的时候,我都会告诉读者我们在说哪个类第几行。
  我们可以看到,ClassPathXmlApplicationContext兜兜转转了好久才到ApplicationContext接口,同样的,我们也可以使用绿颜色的FileSystemXmlApplicationContext和AnnotationConfigApplicationContext这两个类。
  1、FileSystemXmlApplicationContext的构造函数需要一个xml配置文件在系统中的路径,其他和ClassPathXmlApplicationContext基本上一样。
  2、AnnotationConfigApplicationContext是基于注解来使用的,它不需要配置文件,采用java配置类和各种注解来配置,是比较简单的方式,也是大势所趋吧。
  不过本文旨在帮助大家理解整个构建流程,所以决定使用ClassPathXmlApplicationContext进行分析。
  我们先来一个简单的例子来看看怎么实例化ApplicationContext。
  首先,定义一个接口:
  java
  publicinterfaceMessageService{
  StringgetMessage();
  }
  定义接口实现类:
  java
  publicclassMessageServiceImplimplementsMessageService{
  publicStringgetMessage(){
  returnhelloworld;
  }
  }
  接下来,我们在resources目录新建一个配置文件,文件名随意,通常叫application。xml或applicationxxx。xml就可以了:
  xml
  lt;?xmlversion1。0encodingUTF8?
  beansxmlns:xsihttp:www。w3。org2001XMLSchemainstance
  xmlnshttp:www。springframework。orgschemabeans
  xsi:schemaLocationhttp:www。springframework。orgschemabeanshttp:www。springframework。orgschemabeansspringbeans。xsddefaultautowirebyName
  beans
  这样,我们就可以跑起来了:
  java
  publicclassApp{
  publicstaticvoidmain(String〔〕args){
  用我们的配置文件来启动一个ApplicationContext
  ApplicationContextcontextnewClassPathXmlApplicationContext(classpath:application。xml);
  System。out。println(context启动成功);
  从context中取出我们的Bean,而不是用newMessageServiceImpl()这种方式
  MessageServicemessageServicecontext。getBean(MessageService。class);
  这句将输出:helloworld
  System。out。println(messageService。getMessage());
  }
  }
  以上例子很简单,不过也够引出本文的主题了,就是怎么样通过配置文件来启动Spring的ApplicationContext?也就是我们今天要分析的IOC的核心了。ApplicationContext启动过程中,会负责创建实例Bean,往各个Bean中注入依赖等。
  BeanFactory简介
  BeanFactory,从名字上也很好理解,生产bean的工厂,它负责生产和管理各个bean实例。
  初学者可别以为我之前说那么多和BeanFactory无关,前面说的ApplicationContext其实就是一个BeanFactory。我们来看下和BeanFactory接口相关的主要的继承结构:
  !〔2〕(https:www。javadoop。comblogimagesspringcontext2。png)
  我想,大家看完这个图以后,可能就不是很开心了。ApplicationContext往下的继承结构前面一张图说过了,这里就不重复了。这张图呢,背下来肯定是不需要的,有几个重点和大家说明下就好。
  1。ApplicationContext继承了ListableBeanFactory,这个Listable的意思就是,通过这个接口,我们可以获取多个Bean,大家看源码会发现,最顶层BeanFactory接口的方法都是获取单个Bean的。
  2。ApplicationContext继承了HierarchicalBeanFactory,Hierarchical单词本身已经能说明问题了,也就是说我们可以在应用中起多个BeanFactory,然后可以将各个BeanFactory设置为父子关系。
  3。AutowireCapableBeanFactory这个名字中的Autowire大家都非常熟悉,它就是用来自动装配Bean用的,但是仔细看上图,ApplicationContext并没有继承它,不过不用担心,不使用继承,不代表不可以使用组合,如果你看到ApplicationContext接口定义中的最后一个方法getAutowireCapableBeanFactory()就知道了。
  4。ConfigurableListableBeanFactory也是一个特殊的接口,看图,特殊之处在于它继承了第二层所有的三个接口,而ApplicationContext没有。这点之后会用到。
  5。请先不用花时间在其他的接口和类上,先理解我说的这几点就可以了。
  然后,请读者打开编辑器,翻一下BeanFactory、ListableBeanFactory、HierarchicalBeanFactory、AutowireCapableBeanFactory、ApplicationContext这几个接口的代码,大概看一下各个接口中的方法,大家心里要有底,限于篇幅,我就不贴代码介绍了。
  启动过程分析
  下面将会是冗长的代码分析,记住,一定要自己打开源码来看,不然纯看是很累的。
  第一步,我们肯定要从ClassPathXmlApplicationContext的构造方法说起。
  java
  publicclassClassPathXmlApplicationContextextendsAbstractXmlApplicationContext{
  privateResource〔〕configResources;
  如果已经有ApplicationContext并需要配置成父子关系,那么调用这个构造方法
  publicClassPathXmlApplicationContext(ApplicationContextparent){
  super(parent);
  }
  。。。
  publicClassPathXmlApplicationContext(String〔〕configLocations,booleanrefresh,ApplicationContextparent)
  throwsBeansException{
  super(parent);
  根据提供的路径,处理成配置文件数组(以分号、逗号、空格、tab、换行符分割)
  setConfigLocations(configLocations);
  if(refresh){
  refresh();核心方法
  }
  }
  。。。
  }
  接下来,就是refresh(),这里简单说下为什么是refresh(),而不是init()这种名字的方法。因为ApplicationContext建立起来以后,其实我们是可以通过调用refresh()这个方法重建的,refresh()会将原来的ApplicationContext销毁,然后再重新执行一次初始化操作。
  往下看,refresh()方法里面调用了那么多方法,就知道肯定不简单了,请读者先看个大概,细节之后会详细说。
  java
  Override
  publicvoidrefresh()throwsBeansException,IllegalStateException{
  来个锁,不然refresh()还没结束,你又来个启动或销毁容器的操作,那不就乱套了嘛
  synchronized(this。startupShutdownMonitor){
  准备工作,记录下容器的启动时间、标记已启动状态、处理配置文件中的占位符
  prepareRefresh();
  这步比较关键,这步完成后,配置文件就会解析成一个个Bean定义,注册到BeanFactory中,
  当然,这里说的Bean还没有初始化,只是配置信息都提取出来了,
  注册也只是将这些信息都保存到了注册中心(说到底核心是一个beanNamebeanDefinition的map)
  ConfigurableListableBeanFactorybeanFactoryobtainFreshBeanFactory();
  设置BeanFactory的类加载器,添加几个BeanPostProcessor,手动注册几个特殊的bean
  这块待会会展开说
  prepareBeanFactory(beanFactory);
  try{
  【这里需要知道BeanFactoryPostProcessor这个知识点,Bean如果实现了此接口,
  那么在容器初始化以后,Spring会负责调用里面的postProcessBeanFactory方法。】
  这里是提供给子类的扩展点,到这里的时候,所有的Bean都加载、注册完成了,但是都还没有初始化
  具体的子类可以在这步的时候添加一些特殊的BeanFactoryPostProcessor的实现类或做点什么事
  postProcessBeanFactory(beanFactory);
  调用BeanFactoryPostProcessor各个实现类的postProcessBeanFactory(factory)方法
  invokeBeanFactoryPostProcessors(beanFactory);
  注册BeanPostProcessor的实现类,注意看和BeanFactoryPostProcessor的区别
  此接口两个方法:postProcessBeforeInitialization和postProcessAfterInitialization
  两个方法分别在Bean初始化之前和初始化之后得到执行。注意,到这里Bean还没初始化
  registerBeanPostProcessors(beanFactory);
  初始化当前ApplicationContext的MessageSource,国际化这里就不展开说了,不然没完没了了
  initMessageSource();
  初始化当前ApplicationContext的事件广播器,这里也不展开了
  initApplicationEventMulticaster();
  从方法名就可以知道,典型的模板方法(钩子方法),
  具体的子类可以在这里初始化一些特殊的Bean(在初始化singletonbeans之前)
  onRefresh();
  注册事件监听器,监听器需要实现ApplicationListener接口。这也不是我们的重点,过
  registerListeners();
  重点,重点,重点
  初始化所有的singletonbeans
  (lazyinit的除外)
  finishBeanFactoryInitialization(beanFactory);
  最后,广播事件,ApplicationContext初始化完成
  finishRefresh();
  }
  catch(BeansExceptionex){
  if(logger。isWarnEnabled()){
  logger。warn(Exceptionencounteredduringcontextinitialization
  cancellingrefreshattempt:ex);
  }
  Destroyalreadycreatedsingletonstoavoiddanglingresources。
  销毁已经初始化的singleton的Beans,以免有些bean会一直占用资源
  destroyBeans();
  Resetactiveflag。
  cancelRefresh(ex);
  把异常往外抛
  throwex;
  }
  finally{
  ResetcommonintrospectioncachesinSpringscore,sincewe
  mightnoteverneedmetadataforsingletonbeansanymore。。。
  resetCommonCaches();
  }
  }
  }
  下面,我们开始一步步来肢解这个refresh()方法。
  创建Bean容器前的准备工作
  这个比较简单,直接看代码中的几个注释即可。
  java
  protectedvoidprepareRefresh(){
  记录启动时间,
  将active属性设置为true,closed属性设置为false,它们都是AtomicBoolean类型
  this。startupDateSystem。currentTimeMillis();
  this。closed。set(false);
  this。active。set(true);
  if(logger。isInfoEnabled()){
  logger。info(Refreshingthis);
  }
  Initializeanyplaceholderpropertysourcesinthecontextenvironment
  initPropertySources();
  校验xml配置文件
  getEnvironment()。validateRequiredProperties();
  this。earlyApplicationEventsnewLinkedHashSet();
  }
  创建Bean容器,加载并注册Bean
  我们回到refresh()方法中的下一行obtainFreshBeanFactory()。
  注意,这个方法是全文最重要的部分之一,这里将会初始化BeanFactory、加载Bean、注册Bean等等。
  当然,这步结束后,Bean并没有完成初始化。这里指的是Bean实例并未在这一步生成。
  AbstractApplicationContext。java
  java
  protectedConfigurableListableBeanFactoryobtainFreshBeanFactory(){
  关闭旧的BeanFactory(如果有),创建新的BeanFactory,加载Bean定义、注册Bean等等
  refreshBeanFactory();
  返回刚刚创建的BeanFactory
  ConfigurableListableBeanFactorybeanFactorygetBeanFactory();
  if(logger。isDebugEnabled()){
  logger。debug(BeanfactoryforgetDisplayName():beanFactory);
  }
  returnbeanFactory;
  }
  AbstractRefreshableApplicationContext。java120
  java
  Override
  protectedfinalvoidrefreshBeanFactory()throwsBeansException{
  如果ApplicationContext中已经加载过BeanFactory了,销毁所有Bean,关闭BeanFactory
  注意,应用中BeanFactory本来就是可以多个的,这里可不是说应用全局是否有BeanFactory,而是当前
  ApplicationContext是否有BeanFactory
  if(hasBeanFactory()){
  destroyBeans();
  closeBeanFactory();
  }
  try{
  初始化一个DefaultListableBeanFactory,为什么用这个,我们马上说。
  DefaultListableBeanFactorybeanFactorycreateBeanFactory();
  用于BeanFactory的序列化,我想不部分人应该都用不到
  beanFactory。setSerializationId(getId());
  下面这两个方法很重要,别跟丢了,具体细节之后说
  设置BeanFactory的两个配置属性:是否允许Bean覆盖、是否允许循环引用
  customizeBeanFactory(beanFactory);
  加载Bean到BeanFactory中
  loadBeanDefinitions(beanFactory);
  synchronized(this。beanFactoryMonitor){
  this。beanFactorybeanFactory;
  }
  }
  catch(IOExceptionex){
  thrownewApplicationContextException(IOerrorparsingbeandefinitionsourceforgetDisplayName(),ex);
  }
  }
  看到这里的时候,我觉得读者就应该站在高处看ApplicationContext了,ApplicationContext继承自BeanFactory,但是它不应该被理解为BeanFactory的实现类,而是说其内部持有一个实例化的BeanFactory(DefaultListableBeanFactory)。以后所有的BeanFactory相关的操作其实是委托给这个实例来处理的。
  我们说说为什么选择实例化DefaultListableBeanFactory?前面我们说了有个很重要的接口ConfigurableListableBeanFactory,它实现了BeanFactory下面一层的所有三个接口,我把之前的继承图再拿过来大家再仔细看一下:
  !〔3〕(https:www。javadoop。comblogimagesspringcontext3。png)
  我们可以看到ConfigurableListableBeanFactory只有一个实现类DefaultListableBeanFactory,而且实现类DefaultListableBeanFactory还通过实现右边的AbstractAutowireCapableBeanFactory通吃了右路。所以结论就是,最底下这个家伙DefaultListableBeanFactory基本上是最牛的BeanFactory了,这也是为什么这边会使用这个类来实例化的原因。
  如果你想要在程序运行的时候动态往SpringIOC容器注册新的bean,就会使用到这个类。那我们怎么在运行时获得这个实例呢?
  之前我们说过ApplicationContext接口能获取到AutowireCapableBeanFactory,就是最右上角那个,然后它向下转型就能得到DefaultListableBeanFactory了。
  那怎么拿到ApplicationContext实例呢?如果你不会,说明你没用过Spring。
  在继续往下之前,我们需要先了解BeanDefinition。我们说BeanFactory是Bean容器,那么Bean又是什么呢?
  这里的BeanDefinition就是我们所说的Spring的Bean,我们自己定义的各个Bean其实会转换成一个个BeanDefinition存在于Spring的BeanFactory中。
  所以,如果有人问你Bean是什么的时候,你要知道Bean在代码层面上可以简单认为是BeanDefinition的实例。
  BeanDefinition中保存了我们的Bean信息,比如这个Bean指向的是哪个类、是否是单例的、是否懒加载、这个Bean依赖了哪些Bean等等。
  BeanDefinition接口定义
  我们来看下BeanDefinition的接口定义:
  java
  publicinterfaceBeanDefinitionextendsAttributeAccessor,BeanMetadataElement{
  我们可以看到,默认只提供sington和prototype两种,
  很多读者可能知道还有request,session,globalSession,application,websocket这几种,
  不过,它们属于基于web的扩展。
  StringSCOPESINGLETONConfigurableBeanFactory。SCOPESINGLETON;
  StringSCOPEPROTOTYPEConfigurableBeanFactory。SCOPEPROTOTYPE;
  比较不重要,直接跳过吧
  intROLEAPPLICATION0;
  intROLESUPPORT1;
  intROLEINFRASTRUCTURE2;
  设置父Bean,这里涉及到bean继承,不是java继承。请参见附录的详细介绍
  一句话就是:继承父Bean的配置信息而已
  voidsetParentName(StringparentName);
  获取父Bean
  StringgetParentName();
  设置Bean的类名称,将来是要通过反射来生成实例的
  voidsetBeanClassName(StringbeanClassName);
  获取Bean的类名称
  StringgetBeanClassName();
  设置bean的scope
  voidsetScope(Stringscope);
  StringgetScope();
  设置是否懒加载
  voidsetLazyInit(booleanlazyInit);
  booleanisLazyInit();
  设置该Bean依赖的所有的Bean,注意,这里的依赖不是指属性依赖(如Autowire标记的),
  是dependson属性设置的值。
  voidsetDependsOn(String。。。dependsOn);
  返回该Bean的所有依赖
  String〔〕getDependsOn();
  设置该Bean是否可以注入到其他Bean中,只对根据类型注入有效,
  如果根据名称注入,即使这边设置了false,也是可以的
  voidsetAutowireCandidate(booleanautowireCandidate);
  该Bean是否可以注入到其他Bean中
  booleanisAutowireCandidate();
  主要的。同一接口的多个实现,如果不指定名字的话,Spring会优先选择设置primary为true的bean
  voidsetPrimary(booleanprimary);
  是否是primary的
  booleanisPrimary();
  如果该Bean采用工厂方法生成,指定工厂名称。对工厂不熟悉的读者,请参加附录
  一句话就是:有些实例不是用反射生成的,而是用工厂模式生成的
  voidsetFactoryBeanName(StringfactoryBeanName);
  获取工厂名称
  StringgetFactoryBeanName();
  指定工厂类中的工厂方法名称
  voidsetFactoryMethodName(StringfactoryMethodName);
  获取工厂类中的工厂方法名称
  StringgetFactoryMethodName();
  构造器参数
  ConstructorArgumentValuesgetConstructorArgumentValues();
  Bean中的属性值,后面给bean注入属性值的时候会说到
  MutablePropertyValuesgetPropertyValues();
  是否singleton
  booleanisSingleton();
  是否prototype
  booleanisPrototype();
  如果这个Bean是被设置为abstract,那么不能实例化,
  常用于作为父bean用于继承,其实也很少用。。。。。。
  booleanisAbstract();
  intgetRole();
  StringgetDescription();
  StringgetResourceDescription();
  BeanDefinitiongetOriginatingBeanDefinition();
  }
  这个BeanDefinition其实已经包含很多的信息了,暂时不清楚所有的方法对应什么东西没关系,希望看完本文后读者可以彻底搞清楚里面的所有东西。
  这里接口虽然那么多,但是没有类似getInstance()这种方法来获取我们定义的类的实例,真正的我们定义的类生成的实例到哪里去了呢?别着急,这个要很后面才能讲到。
  有了BeanDefinition的概念以后,我们再往下看refreshBeanFactory()方法中的剩余部分:
  java
  customizeBeanFactory(beanFactory);
  loadBeanDefinitions(beanFactory);
  虽然只有两个方法,但路还很长啊
  customizeBeanFactory
  customizeBeanFactory(beanFactory)比较简单,就是配置是否允许BeanDefinition覆盖、是否允许循环引用。
  java
  protectedvoidcustomizeBeanFactory(DefaultListableBeanFactorybeanFactory){
  if(this。allowBeanDefinitionOverriding!null){
  是否允许Bean定义覆盖
  beanFactory。setAllowBeanDefinitionOverriding(this。allowBeanDefinitionOverriding);
  }
  if(this。allowCircularReferences!null){
  是否允许Bean间的循环依赖
  beanFactory。setAllowCircularReferences(this。allowCircularReferences);
  }
  }
  BeanDefinition的覆盖问题可能会有开发者碰到这个坑,就是在配置文件中定义bean时使用了相同的id或name,默认情况下,allowBeanDefinitionOverriding属性为null,如果在同一配置文件中重复了,会抛错,但是如果不是同一配置文件中,会发生覆盖。
  循环引用也很好理解:A依赖B,而B依赖A。或A依赖B,B依赖C,而C依赖A。
  默认情况下,Spring允许循环依赖,当然如果你在A的构造方法中依赖B,在B的构造方法中依赖A是不行的。
  至于这两个属性怎么配置?我在附录中进行了介绍,尤其对于覆盖问题,很多人都希望禁止出现Bean覆盖,可是Spring默认是不同文件的时候可以覆盖的。
  之后的源码中还会出现这两个属性,读者有个印象就可以了,它们不是非常重要。
  加载Bean:loadBeanDefinitions
  接下来是最重要的loadBeanDefinitions(beanFactory)方法了,这个方法将根据配置,加载各个Bean,然后放到BeanFactory中。
  读取配置的操作在XmlBeanDefinitionReader中,其负责加载配置、解析。
  AbstractXmlApplicationContext。java80
  java
  我们可以看到,此方法将通过一个XmlBeanDefinitionReader实例来加载各个Bean。
  Override
  protectedvoidloadBeanDefinitions(DefaultListableBeanFactorybeanFactory)throwsBeansException,IOException{
  给这个BeanFactory实例化一个XmlBeanDefinitionReader
  XmlBeanDefinitionReaderbeanDefinitionReadernewXmlBeanDefinitionReader(beanFactory);
  Configurethebeandefinitionreaderwiththiscontexts
  resourceloadingenvironment。
  beanDefinitionReader。setEnvironment(this。getEnvironment());
  beanDefinitionReader。setResourceLoader(this);
  beanDefinitionReader。setEntityResolver(newResourceEntityResolver(this));
  初始化BeanDefinitionReader,其实这个是提供给子类覆写的,
  我看了一下,没有类覆写这个方法,我们姑且当做不重要吧
  initBeanDefinitionReader(beanDefinitionReader);
  重点来了,继续往下
  loadBeanDefinitions(beanDefinitionReader);
  }
  现在还在这个类中,接下来用刚刚初始化的Reader开始来加载xml配置,这块代码读者可以选择性跳过,不是很重要。也就是说,下面这个代码块,读者可以很轻松地略过。
  AbstractXmlApplicationContext。java120
  java
  protectedvoidloadBeanDefinitions(XmlBeanDefinitionReaderreader)throwsBeansException,IOException{
  Resource〔〕configResourcesgetConfigResources();
  if(configResources!null){
  往下看
  reader。loadBeanDefinitions(configResources);
  }
  String〔〕configLocationsgetConfigLocations();
  if(configLocations!null){
  2
  reader。loadBeanDefinitions(configLocations);
  }
  }
  上面虽然有两个分支,不过第二个分支很快通过解析路径转换为Resource以后也会进到这里
  Override
  publicintloadBeanDefinitions(Resource。。。resources)throwsBeanDefinitionStoreException{
  Assert。notNull(resources,Resourcearraymustnotbenull);
  intcounter0;
  注意这里是个for循环,也就是每个文件是一个resource
  for(Resourceresource:resources){
  继续往下看
  counterloadBeanDefinitions(resource);
  }
  最后返回counter,表示总共加载了多少的BeanDefinition
  returncounter;
  }
  XmlBeanDefinitionReader303
  Override
  publicintloadBeanDefinitions(Resourceresource)throwsBeanDefinitionStoreException{
  returnloadBeanDefinitions(newEncodedResource(resource));
  }
  XmlBeanDefinitionReader314
  publicintloadBeanDefinitions(EncodedResourceencodedResource)throwsBeanDefinitionStoreException{
  Assert。notNull(encodedResource,EncodedResourcemustnotbenull);
  if(logger。isInfoEnabled()){
  logger。info(LoadingXMLbeandefinitionsfromencodedResource。getResource());
  }
  用一个ThreadLocal来存放配置文件资源
  SetcurrentResourcesthis。resourcesCurrentlyBeingLoaded。get();
  if(currentResourcesnull){
  currentResourcesnewHashSet(4);
  this。resourcesCurrentlyBeingLoaded。set(currentResources);
  }
  if(!currentResources。add(encodedResource)){
  thrownewBeanDefinitionStoreException(
  DetectedcyclicloadingofencodedResourcecheckyourimportdefinitions!);
  }
  try{
  InputStreaminputStreamencodedResource。getResource()。getInputStream();
  try{
  InputSourceinputSourcenewInputSource(inputStream);
  if(encodedResource。getEncoding()!null){
  inputSource。setEncoding(encodedResource。getEncoding());
  }
  核心部分是这里,往下面看
  returndoLoadBeanDefinitions(inputSource,encodedResource。getResource());
  }
  finally{
  inputStream。close();
  }
  }
  catch(IOExceptionex){
  thrownewBeanDefinitionStoreException(
  IOExceptionparsingXMLdocumentfromencodedResource。getResource(),ex);
  }
  finally{
  currentResources。remove(encodedResource);
  if(currentResources。isEmpty()){
  this。resourcesCurrentlyBeingLoaded。remove();
  }
  }
  }
  还在这个文件中,第388行
  protectedintdoLoadBeanDefinitions(InputSourceinputSource,Resourceresource)
  throwsBeanDefinitionStoreException{
  try{
  这里就不看了,将xml文件转换为Document对象
  DocumentdocdoLoadDocument(inputSource,resource);
  继续
  returnregisterBeanDefinitions(doc,resource);
  }
  catch(。。。
  }
  还在这个文件中,第505行
  返回值:返回从当前配置文件加载了多少数量的Bean
  publicintregisterBeanDefinitions(Documentdoc,Resourceresource)throwsBeanDefinitionStoreException{
  BeanDefinitionDocumentReaderdocumentReadercreateBeanDefinitionDocumentReader();
  intcountBeforegetRegistry()。getBeanDefinitionCount();
  这里
  documentReader。registerBeanDefinitions(doc,createReaderContext(resource));
  returngetRegistry()。getBeanDefinitionCount()countBefore;
  }
  DefaultBeanDefinitionDocumentReader90
  Override
  publicvoidregisterBeanDefinitions(Documentdoc,XmlReaderContextreaderContext){
  this。readerContextreaderContext;
  logger。debug(Loadingbeandefinitions);
  Elementrootdoc。getDocumentElement();
  从xml根节点开始解析文件
  doRegisterBeanDefinitions(root);
  }
  经过漫长的链路,一个配置文件终于转换为一颗DOM树了,注意,这里指的是其中一个配置文件,不是所有的,读者可以看到上面有个for循环的。下面开始从根节点开始解析:
  doRegisterBeanDefinitions:
  java
  DefaultBeanDefinitionDocumentReader116
  protectedvoiddoRegisterBeanDefinitions(Elementroot){
  我们看名字就知道,BeanDefinitionParserDelegate必定是一个重要的类,它负责解析Bean定义,
  这里为什么要定义一个parent?看到后面就知道了,是递归问题,
  因为内部是可以定义的,所以这个方法的root其实不一定就是xml的根节点,也可以是嵌套在里面的节点,从源码分析的角度,我们当做根节点就好了
  BeanDefinitionParserDelegateparentthis。delegate;
  this。delegatecreateDelegate(getReaderContext(),root,parent);
  if(this。delegate。isDefaultNamespace(root)){
  这块说的是根节点中的profile是否是当前环境需要的,
  如果当前环境配置的profile不包含此profile,那就直接return了,不对此解析
  不熟悉profile为何物,不熟悉怎么配置profile读者的请移步附录区
  StringprofileSpecroot。getAttribute(PROFILEATTRIBUTE);
  if(StringUtils。hasText(profileSpec)){
  String〔〕specifiedProfilesStringUtils。tokenizeToStringArray(
  profileSpec,BeanDefinitionParserDelegate。MULTIVALUEATTRIBUTEDELIMITERS);
  if(!getReaderContext()。getEnvironment()。acceptsProfiles(specifiedProfiles)){
  if(logger。isInfoEnabled()){
  logger。info(SkippedXMLbeandefinitionfileduetospecifiedprofiles〔profileSpec
  〕notmatching:getReaderContext()。getResource());
  }
  return;
  }
  }
  }
  preProcessXml(root);钩子
  往下看
  parseBeanDefinitions(root,this。delegate);
  postProcessXml(root);钩子
  this。delegateparent;
  }
  preProcessXml(root)和postProcessXml(root)是给子类用的钩子方法,鉴于没有被使用到,也不是我们的重点,我们直接跳过。
  这里涉及到了profile的问题,对于不了解的读者,我在附录中对profile做了简单的解释,读者可以参考一下。
  接下来,看核心解析方法parseBeanDefinitions(root,this。delegate):
  java
  defaultnamespace涉及到的就四个标签、、和,
  其他的属于custom的
  protectedvoidparseBeanDefinitions(Elementroot,BeanDefinitionParserDelegatedelegate){
  if(delegate。isDefaultNamespace(root)){
  NodeListnlroot。getChildNodes();
  for(inti0;inl。getLength();i){
  Nodenodenl。item(i);
  if(nodeinstanceofElement){
  Elementele(Element)node;
  if(delegate。isDefaultNamespace(ele)){
  解析defaultnamespace下面的几个元素
  parseDefaultElement(ele,delegate);
  }
  else{
  解析其他namespace的元素
  delegate。parseCustomElement(ele);
  }
  }
  }
  }
  else{
  delegate。parseCustomElement(root);
  }
  }
  从上面的代码,我们可以看到,对于每个配置来说,分别进入到parseDefaultElement(ele,delegate);和delegate。parseCustomElement(ele);这两个分支了。
  parseDefaultElement(ele,delegate)代表解析的节点是、、、这几个。
  这里的四个标签之所以是default的,是因为它们是处于这个namespace下定义的:
  http:www。springframework。orgschemabeans
  又到初学者科普时间,不熟悉namespace的读者请看下面贴出来的xml,这里的第二行xmlns就是咯。
  xml
  beansxmlns:xsihttp:www。w3。org2001XMLSchemainstance
  xmlnshttp:www。springframework。orgschemabeans
  xsi:schemaLocation
  http:www。springframework。orgschemabeans
  http:www。springframework。orgschemabeansspringbeans。xsd
  defaultautowirebyName
  而对于其他的标签,将进入到delegate。parseCustomElement(element)这个分支。如我们经常会使用到的、、、等。
  这些属于扩展,如果需要使用上面这些非default标签,那么上面的xml头部的地方也要引入相应的namespace和。xsd文件的路径,如下所示。同时代码中需要提供相应的parser来解析,如MvcNamespaceHandler、TaskNamespaceHandler、ContextNamespaceHandler、AopNamespaceHandler等。
  假如读者想分析的实现原理,就应该到ContextNamespaceHandler中找答案。
  xml
  beansxmlns:xsihttp:www。w3。org2001XMLSchemainstance
  xmlnshttp:www。springframework。orgschemabeans
  xmlns:contexthttp:www。springframework。orgschemacontext
  xmlns:mvchttp:www。springframework。orgschemamvc
  xsi:schemaLocation
  http:www。springframework。orgschemabeans
  http:www。springframework。orgschemabeansspringbeans。xsd
  http:www。springframework。orgschemacontext
  http:www。springframework。orgschemacontextspringcontext。xsd
  http:www。springframework。orgschemamvc
  http:www。springframework。orgschemamvcspringmvc。xsd
  defaultautowirebyName
  同理,以后你要是碰到这种标签,那么就应该搜一搜是不是有DubboNamespaceHandler这个处理类。
  回过神来,看看处理default标签的方法:
  java
  privatevoidparseDefaultElement(Elementele,BeanDefinitionParserDelegatedelegate){
  if(delegate。nodeNameEquals(ele,IMPORTELEMENT)){
  处理标签
  importBeanDefinitionResource(ele);
  }
  elseif(delegate。nodeNameEquals(ele,ALIASELEMENT)){
  处理标签定义
  processAliasRegistration(ele);
  }
  elseif(delegate。nodeNameEquals(ele,BEANELEMENT)){
  处理标签定义,这也算是我们的重点吧
  processBeanDefinition(ele,delegate);
  }
  elseif(delegate。nodeNameEquals(ele,NESTEDBEANSELEMENT)){
  如果碰到的是嵌套的标签,需要递归
  doRegisterBeanDefinitions(ele);
  }
  }
  如果每个标签都说,那我不吐血,你们都要吐血了。我们挑我们的重点标签出来说。
  processBeanDefinition解析bean标签
  下面是processBeanDefinition解析标签:
  DefaultBeanDefinitionDocumentReader298
  java
  protectedvoidprocessBeanDefinition(Elementele,BeanDefinitionParserDelegatedelegate){
  将节点中的信息提取出来,然后封装到一个BeanDefinitionHolder中,细节往下看
  BeanDefinitionHolderbdHolderdelegate。parseBeanDefinitionElement(ele);
  下面的几行先不要看,跳过先,跳过先,跳过先,后面会继续说的
  if(bdHolder!null){
  bdHolderdelegate。decorateBeanDefinitionIfRequired(ele,bdHolder);
  try{
  Registerthefinaldecoratedinstance。
  BeanDefinitionReaderUtils。registerBeanDefinition(bdHolder,getReaderContext()。getRegistry());
  }
  catch(BeanDefinitionStoreExceptionex){
  getReaderContext()。error(Failedtoregisterbeandefinitionwithname
  bdHolder。getBeanName(),ele,ex);
  }
  Sendregistrationevent。
  getReaderContext()。fireComponentRegistered(newBeanComponentDefinition(bdHolder));
  }
  }
  继续往下看怎么解析之前,我们先看下标签中可以定义哪些属性:
  Property
  class类的全限定名
  name可指定id、name(用逗号、分号、空格分隔)
  scope作用域
  constructorarguments指定构造参数
  properties设置属性的值
  autowiringmodeno(默认值)、byName、byType、constructor
  lazyinitializationmode是否懒加载(如果被非懒加载的bean依赖了那么其实也就不能懒加载了)
  initializationmethodbean属性设置完成后,会调用这个方法
  destructionmethodbean销毁后的回调方法
  上面表格中的内容我想大家都非常熟悉吧,如果不熟悉,那就是你不够了解Spring的配置了。
  简单地说就是像下面这样子:
  xml
  beanidexampleBeannamename1,name2,name3classcom。javadoop。ExampleBean
  scopesingletonlazyinittrueinitmethodinitdestroymethodcleanup
  property
  bean
  当然,除了上面举例出来的这些,还有factorybean、factorymethod、、、、这几个,大家是不是熟悉呢?自己检验一下自己对Spring中bean的了解程度。
  有了以上这些知识以后,我们再继续往里看怎么解析bean元素,是怎么转换到BeanDefinitionHolder的。
  BeanDefinitionParserDelegate428
  java
  publicBeanDefinitionHolderparseBeanDefinitionElement(Elementele){
  returnparseBeanDefinitionElement(ele,null);
  }
  publicBeanDefinitionHolderparseBeanDefinitionElement(Elementele,BeanDefinitioncontainingBean){
  Stringidele。getAttribute(IDATTRIBUTE);
  StringnameAttrele。getAttribute(NAMEATTRIBUTE);
  ListaliasesnewArrayList();
  将name属性的定义按照逗号、分号、空格切分,形成一个别名列表数组,
  当然,如果你不定义name属性的话,就是空的了
  我在附录中简单介绍了一下id和name的配置,大家可以看一眼,有个20秒就可以了
  if(StringUtils。hasLength(nameAttr)){
  String〔〕nameArrStringUtils。tokenizeToStringArray(nameAttr,MULTIVALUEATTRIBUTEDELIMITERS);
  aliases。addAll(Arrays。asList(nameArr));
  }
  StringbeanNameid;
  如果没有指定id,那么用别名列表的第一个名字作为beanName
  if(!StringUtils。hasText(beanName)!aliases。isEmpty()){
  beanNamealiases。remove(0);
  if(logger。isDebugEnabled()){
  logger。debug(NoXMLidspecifiedusingbeanName
  asbeannameandaliasesasaliases);
  }
  }
  if(containingBeannull){
  checkNameUniqueness(beanName,aliases,ele);
  }
  根据。。。中的配置创建BeanDefinition,然后把配置中的信息都设置到实例中,
  细节后面细说,先知道下面这行结束后,一个BeanDefinition实例就出来了。
  AbstractBeanDefinitionbeanDefinitionparseBeanDefinitionElement(ele,beanName,containingBean);
  到这里,整个标签就算解析结束了,一个BeanDefinition就形成了。
  if(beanDefinition!null){
  如果都没有设置id和name,那么此时的beanName就会为null,进入下面这块代码产生
  如果读者不感兴趣的话,我觉得不需要关心这块代码,对本文源码分析来说,这些东西不重要
  if(!StringUtils。hasText(beanName)){
  try{
  if(containingBean!null){按照我们的思路,这里containingBean是null的
  beanNameBeanDefinitionReaderUtils。generateBeanName(
  beanDefinition,this。readerContext。getRegistry(),true);
  }
  else{
  如果我们不定义id和name,那么我们引言里的那个例子:
  1。beanName为:com。javadoop。example。MessageServiceImpl0
  2。beanClassName为:com。javadoop。example。MessageServiceImpl
  beanNamethis。readerContext。generateBeanName(beanDefinition);
  StringbeanClassNamebeanDefinition。getBeanClassName();
  if(beanClassName!null
  beanName。startsWith(beanClassName)beanName。length()beanClassName。length()
  !this。readerContext。getRegistry()。isBeanNameInUse(beanClassName)){
  把beanClassName设置为Bean的别名
  aliases。add(beanClassName);
  }
  }
  if(logger。isDebugEnabled()){
  logger。debug(NeitherXMLidnornamespecified
  usinggeneratedbeanname〔beanName〕);
  }
  }
  catch(Exceptionex){
  error(ex。getMessage(),ele);
  returnnull;
  }
  }
  String〔〕aliasesArrayStringUtils。toStringArray(aliases);
  返回BeanDefinitionHolder
  returnnewBeanDefinitionHolder(beanDefinition,beanName,aliasesArray);
  }
  returnnull;
  }
  然后,我们再看看怎么根据配置创建BeanDefinition实例的:
  java
  publicAbstractBeanDefinitionparseBeanDefinitionElement(
  Elementele,StringbeanName,BeanDefinitioncontainingBean){
  this。parseState。push(newBeanEntry(beanName));
  StringclassNamenull;
  if(ele。hasAttribute(CLASSATTRIBUTE)){
  classNameele。getAttribute(CLASSATTRIBUTE)。trim();
  }
  try{
  Stringparentnull;
  if(ele。hasAttribute(PARENTATTRIBUTE)){
  parentele。getAttribute(PARENTATTRIBUTE);
  }
  创建BeanDefinition,然后设置类信息而已,很简单,就不贴代码了
  AbstractBeanDefinitionbdcreateBeanDefinition(className,parent);
  设置BeanDefinition的一堆属性,这些属性定义在AbstractBeanDefinition中
  parseBeanDefinitionAttributes(ele,beanName,containingBean,bd);
  bd。setDescription(DomUtils。getChildElementValueByTagName(ele,DESCRIPTIONELEMENT));
  下面的一堆是解析。。。。。。内部的子元素,
  解析出来以后的信息都放到bd的属性中
  解析
  parseMetaElements(ele,bd);
  解析
  parseLookupOverrideSubElements(ele,bd。getMethodOverrides());
  解析
  parseReplacedMethodSubElements(ele,bd。getMethodOverrides());
  解析
  parseConstructorArgElements(ele,bd);
  解析
  parsePropertyElements(ele,bd);
  解析
  parseQualifierElements(ele,bd);
  bd。setResource(this。readerContext。getResource());
  bd。setSource(extractSource(ele));
  returnbd;
  }
  catch(ClassNotFoundExceptionex){
  error(Beanclass〔className〕notfound,ele,ex);
  }
  catch(NoClassDefFoundErrorerr){
  error(Classthatbeanclass〔className〕dependsonnotfound,ele,err);
  }
  catch(Throwableex){
  error(Unexpectedfailureduringbeandefinitionparsing,ele,ex);
  }
  finally{
  this。parseState。pop();
  }
  returnnull;
  }
  到这里,我们已经完成了根据配置创建了一个BeanDefinitionHolder实例。注意,是一个。
  我们回到解析的入口方法:
  java
  protectedvoidprocessBeanDefinition(Elementele,BeanDefinitionParserDelegatedelegate){
  将节点转换为BeanDefinitionHolder,就是上面说的一堆
  BeanDefinitionHolderbdHolderdelegate。parseBeanDefinitionElement(ele);
  if(bdHolder!null){
  如果有自定义属性的话,进行相应的解析,先忽略
  bdHolderdelegate。decorateBeanDefinitionIfRequired(ele,bdHolder);
  try{
  我们把这步叫做注册Bean吧
  BeanDefinitionReaderUtils。registerBeanDefinition(bdHolder,getReaderContext()。getRegistry());
  }
  catch(BeanDefinitionStoreExceptionex){
  getReaderContext()。error(Failedtoregisterbeandefinitionwithname
  bdHolder。getBeanName(),ele,ex);
  }
  注册完成后,发送事件,本文不展开说这个
  getReaderContext()。fireComponentRegistered(newBeanComponentDefinition(bdHolder));
  }
  }
  大家再仔细看一下这块吧,我们后面就不回来说这个了。这里已经根据一个标签产生了一个BeanDefinitionHolder的实例,这个实例里面也就是一个BeanDefinition的实例和它的beanName、aliases这三个信息,注意,我们的关注点始终在BeanDefinition上:
  java
  publicclassBeanDefinitionHolderimplementsBeanMetadataElement{
  privatefinalBeanDefinitionbeanDefinition;
  privatefinalStringbeanName;
  privatefinalString〔〕aliases;
  。。。
  然后我们准备注册这个BeanDefinition,最后,把这个注册事件发送出去。
  下面,我们开始说说注册Bean吧。
  注册Bean
  BeanDefinitionReaderUtils143
  java
  publicstaticvoidregisterBeanDefinition(
  BeanDefinitionHolderdefinitionHolder,BeanDefinitionRegistryregistry)
  throwsBeanDefinitionStoreException{
  StringbeanNamedefinitionHolder。getBeanName();
  注册这个Bean
  registry。registerBeanDefinition(beanName,definitionHolder。getBeanDefinition());
  如果还有别名的话,也要根据别名全部注册一遍,不然根据别名就会找不到Bean了
  String〔〕aliasesdefinitionHolder。getAliases();
  if(aliases!null){
  for(Stringalias:aliases){
  aliasbeanName保存它们的别名信息,这个很简单,用一个map保存一下就可以了,
  获取的时候,会先将alias转换为beanName,然后再查找
  registry。registerAlias(beanName,alias);
  }
  }
  }
  别名注册的放一边,毕竟它很简单,我们看看怎么注册Bean。
  DefaultListableBeanFactory793
  java
  Override
  publicvoidregisterBeanDefinition(StringbeanName,BeanDefinitionbeanDefinition)
  throwsBeanDefinitionStoreException{
  Assert。hasText(beanName,Beannamemustnotbeempty);
  Assert。notNull(beanDefinition,BeanDefinitionmustnotbenull);
  if(beanDefinitioninstanceofAbstractBeanDefinition){
  try{
  ((AbstractBeanDefinition)beanDefinition)。validate();
  }
  catch(BeanDefinitionValidationExceptionex){
  thrownewBeanDefinitionStoreException(。。。);
  }
  }
  old?还记得允许bean覆盖这个配置吗?allowBeanDefinitionOverriding
  BeanDefinitionoldBeanDefinition;
  之后会看到,所有的Bean注册后会放入这个beanDefinitionMap中
  oldBeanDefinitionthis。beanDefinitionMap。get(beanName);
  处理重复名称的Bean定义的情况
  if(oldBeanDefinition!null){
  if(!isAllowBeanDefinitionOverriding()){
  如果不允许覆盖的话,抛异常
  thrownewBeanDefinitionStoreException(beanDefinition。getResourceDescription()。。。
  }
  elseif(oldBeanDefinition。getRole()beanDefinition。getRole()){
  log。。。用框架定义的Bean覆盖用户自定义的Bean
  }
  elseif(!beanDefinition。equals(oldBeanDefinition)){
  log。。。用新的Bean覆盖旧的Bean
  }
  else{
  log。。。用同等的Bean覆盖旧的Bean,这里指的是equals方法返回true的Bean
  }
  覆盖
  this。beanDefinitionMap。put(beanName,beanDefinition);
  }
  else{
  判断是否已经有其他的Bean开始初始化了。
  注意,注册Bean这个动作结束,Bean依然还没有初始化,我们后面会有大篇幅说初始化过程,
  在Spring容器启动的最后,会预初始化所有的singletonbeans
  if(hasBeanCreationStarted()){
  Cannotmodifystartuptimecollectionelementsanymore(forstableiteration)
  synchronized(this。beanDefinitionMap){
  this。beanDefinitionMap。put(beanName,beanDefinition);
  ListupdatedDefinitionsnewArrayList(this。beanDefinitionNames。size()1);
  updatedDefinitions。addAll(this。beanDefinitionNames);
  updatedDefinitions。add(beanName);
  this。beanDefinitionNamesupdatedDefinitions;
  if(this。manualSingletonNames。contains(beanName)){
  SetupdatedSingletonsnewLinkedHashSet(this。manualSingletonNames);
  updatedSingletons。remove(beanName);
  this。manualSingletonNamesupdatedSingletons;
  }
  }
  }
  else{
  最正常的应该是进到这个分支。
  将BeanDefinition放到这个map中,这个map保存了所有的BeanDefinition
  this。beanDefinitionMap。put(beanName,beanDefinition);
  这是个ArrayList,所以会按照bean配置的顺序保存每一个注册的Bean的名字
  this。beanDefinitionNames。add(beanName);
  这是个LinkedHashSet,代表的是手动注册的singletonbean,
  注意这里是remove方法,到这里的Bean当然不是手动注册的
  手动指的是通过调用以下方法注册的bean:
  registerSingleton(StringbeanName,ObjectsingletonObject)
  这不是重点,解释只是为了不让大家疑惑。Spring会在后面手动注册一些Bean,
  如environment、systemProperties等bean,我们自己也可以在运行时注册Bean到容器中的
  this。manualSingletonNames。remove(beanName);
  }
  这个不重要,在预初始化的时候会用到,不必管它。
  this。frozenBeanDefinitionNamesnull;
  }
  if(oldBeanDefinition!nullcontainsSingleton(beanName)){
  resetBeanDefinition(beanName);
  }
  }
  总结一下,到这里已经初始化了Bean容器,配置也相应的转换为了一个个BeanDefinition,然后注册了各个BeanDefinition到注册中心,并且发送了注册事件。
  分割线
  到这里是一个分水岭,前面的内容都还算比较简单,不过应该也比较繁琐,大家要清楚地知道前面都做了哪些事情。
  Bean容器实例化完成后
  说到这里,我们回到refresh()方法,我重新贴了一遍代码,看看我们说到哪了。是的,我们才说完obtainFreshBeanFactory()方法。
  考虑到篇幅,这里开始大幅缩减掉没必要详细介绍的部分,大家直接看下面的代码中的注释就好了。
  java
  Override
  publicvoidrefresh()throwsBeansException,IllegalStateException{
  来个锁,不然refresh()还没结束,你又来个启动或销毁容器的操作,那不就乱套了嘛
  synchronized(this。startupShutdownMonitor){
  准备工作,记录下容器的启动时间、标记已启动状态、处理配置文件中的占位符
  prepareRefresh();
  这步比较关键,这步完成后,配置文件就会解析成一个个Bean定义,注册到BeanFactory中,
  当然,这里说的Bean还没有初始化,只是配置信息都提取出来了,
  注册也只是将这些信息都保存到了注册中心(说到底核心是一个beanNamebeanDefinition的map)
  ConfigurableListableBeanFactorybeanFactoryobtainFreshBeanFactory();
  设置BeanFactory的类加载器,添加几个BeanPostProcessor,手动注册几个特殊的bean
  这块待会会展开说
  prepareBeanFactory(beanFactory);
  try{
  【这里需要知道BeanFactoryPostProcessor这个知识点,Bean如果实现了此接口,
  那么在容器初始化以后,Spring会负责调用里面的postProcessBeanFactory方法。】
  这里是提供给子类的扩展点,到这里的时候,所有的Bean都加载、注册完成了,但是都还没有初始化
  具体的子类可以在这步的时候添加一些特殊的BeanFactoryPostProcessor的实现类或做点什么事
  postProcessBeanFactory(beanFactory);
  调用BeanFactoryPostProcessor各个实现类的postProcessBeanFactory(factory)回调方法
  invokeBeanFactoryPostProcessors(beanFactory);
  注册BeanPostProcessor的实现类,注意看和BeanFactoryPostProcessor的区别
  此接口两个方法:postProcessBeforeInitialization和postProcessAfterInitialization
  两个方法分别在Bean初始化之前和初始化之后得到执行。这里仅仅是注册,之后会看到回调这两方法的时机
  registerBeanPostProcessors(beanFactory);
  初始化当前ApplicationContext的MessageSource,国际化这里就不展开说了,不然没完没了了
  initMessageSource();
  初始化当前ApplicationContext的事件广播器,这里也不展开了
  initApplicationEventMulticaster();
  从方法名就可以知道,典型的模板方法(钩子方法),不展开说
  具体的子类可以在这里初始化一些特殊的Bean(在初始化singletonbeans之前)
  onRefresh();
  注册事件监听器,监听器需要实现ApplicationListener接口。这也不是我们的重点,过
  registerListeners();
  重点,重点,重点
  初始化所有的singletonbeans
  (lazyinit的除外)
  finishBeanFactoryInitialization(beanFactory);
  最后,广播事件,ApplicationContext初始化完成,不展开
  finishRefresh();
  }
  catch(BeansExceptionex){
  if(logger。isWarnEnabled()){
  logger。warn(Exceptionencounteredduringcontextinitialization
  cancellingrefreshattempt:ex);
  }
  Destroyalreadycreatedsingletonstoavoiddanglingresources。
  销毁已经初始化的singleton的Beans,以免有些bean会一直占用资源
  destroyBeans();
  Resetactiveflag。
  cancelRefresh(ex);
  把异常往外抛
  throwex;
  }
  finally{
  ResetcommonintrospectioncachesinSpringscore,sincewe
  mightnoteverneedmetadataforsingletonbeansanymore。。。
  resetCommonCaches();
  }
  }
  }
  准备Bean容器:prepareBeanFactory
  之前我们说过,Spring把我们在xml配置的bean都注册以后,会手动注册一些特殊的bean。
  这里简单介绍下prepareBeanFactory(factory)方法:
  java
  Configurethefactorysstandardcontextcharacteristics,
  suchasthecontextsClassLoaderandpostprocessors。
  parambeanFactorytheBeanFactorytoconfigure
  protectedvoidprepareBeanFactory(ConfigurableListableBeanFactorybeanFactory){
  设置BeanFactory的类加载器,我们知道BeanFactory需要加载类,也就需要类加载器,
  这里设置为加载当前ApplicationContext类的类加载器
  beanFactory。setBeanClassLoader(getClassLoader());
  设置BeanExpressionResolver
  beanFactory。setBeanExpressionResolver(newStandardBeanExpressionResolver(beanFactory。getBeanClassLoader()));
  beanFactory。addPropertyEditorRegistrar(newResourceEditorRegistrar(this,getEnvironment()));
  添加一个BeanPostProcessor,这个processor比较简单:
  实现了Aware接口的beans在初始化的时候,这个processor负责回调,
  这个我们很常用,如我们会为了获取ApplicationContext而implementApplicationContextAware
  注意:它不仅仅回调ApplicationContextAware,
  还会负责回调EnvironmentAware、ResourceLoaderAware等,看下源码就清楚了
  beanFactory。addBeanPostProcessor(newApplicationContextAwareProcessor(this));
  下面几行的意思就是,如果某个bean依赖于以下几个接口的实现类,在自动装配的时候忽略它们,
  Spring会通过其他方式来处理这些依赖。
  beanFactory。ignoreDependencyInterface(EnvironmentAware。class);
  beanFactory。ignoreDependencyInterface(EmbeddedValueResolverAware。class);
  beanFactory。ignoreDependencyInterface(ResourceLoaderAware。class);
  beanFactory。ignoreDependencyInterface(ApplicationEventPublisherAware。class);
  beanFactory。ignoreDependencyInterface(MessageSourceAware。class);
  beanFactory。ignoreDependencyInterface(ApplicationContextAware。class);
  下面几行就是为特殊的几个bean赋值,如果有bean依赖了以下几个,会注入这边相应的值,
  之前我们说过,当前ApplicationContext持有一个BeanFactory,这里解释了第一行。
  ApplicationContext还继承了ResourceLoader、ApplicationEventPublisher、MessageSource
  所以对于这几个依赖,可以赋值为this,注意this是一个ApplicationContext
  那这里怎么没看到为MessageSource赋值呢?那是因为MessageSource被注册成为了一个普通的bean
  beanFactory。registerResolvableDependency(BeanFactory。class,beanFactory);
  beanFactory。registerResolvableDependency(ResourceLoader。class,this);
  beanFactory。registerResolvableDependency(ApplicationEventPublisher。class,this);
  beanFactory。registerResolvableDependency(ApplicationContext。class,this);
  这个BeanPostProcessor也很简单,在bean实例化后,如果是ApplicationListener的子类,
  那么将其添加到listener列表中,可以理解成:注册事件监听器
  beanFactory。addBeanPostProcessor(newApplicationListenerDetector(this));
  这里涉及到特殊的bean,名为:loadTimeWeaver,这不是我们的重点,忽略它
  tips:ltw是AspectJ的概念,指的是在运行期进行织入,这个和SpringAOP不一样,
  感兴趣的读者请参考我写的关于AspectJ的另一篇文章https:www。javadoop。compostaspectj
  if(beanFactory。containsBean(LOADTIMEWEAVERBEANNAME)){
  beanFactory。addBeanPostProcessor(newLoadTimeWeaverAwareProcessor(beanFactory));
  SetatemporaryClassLoaderfortypematching。
  beanFactory。setTempClassLoader(newContextTypeMatchClassLoader(beanFactory。getBeanClassLoader()));
  }
  从下面几行代码我们可以知道,Spring往往很智能就是因为它会帮我们默认注册一些有用的bean,
  我们也可以选择覆盖
  如果没有定义environment这个bean,那么Spring会手动注册一个
  if(!beanFactory。containsLocalBean(ENVIRONMENTBEANNAME)){
  beanFactory。registerSingleton(ENVIRONMENTBEANNAME,getEnvironment());
  }
  如果没有定义systemProperties这个bean,那么Spring会手动注册一个
  if(!beanFactory。containsLocalBean(SYSTEMPROPERTIESBEANNAME)){
  beanFactory。registerSingleton(SYSTEMPROPERTIESBEANNAME,getEnvironment()。getSystemProperties());
  }
  如果没有定义systemEnvironment这个bean,那么Spring会手动注册一个
  if(!beanFactory。containsLocalBean(SYSTEMENVIRONMENTBEANNAME)){
  beanFactory。registerSingleton(SYSTEMENVIRONMENTBEANNAME,getEnvironment()。getSystemEnvironment());
  }
  }
  在上面这块代码中,Spring对一些特殊的bean进行了处理,读者如果暂时还不能消化它们也没有关系,慢慢往下看。
  初始化所有的singletonbeans
  我们的重点当然是finishBeanFactoryInitialization(beanFactory);这个巨头了,这里会负责初始化所有的singletonbeans。
  注意,后面的描述中,我都会使用初始化或预初始化来代表这个阶段,Spring会在这个阶段完成所有的singletonbeans的实例化。
  我们来总结一下,到目前为止,应该说BeanFactory已经创建完成,并且所有的实现了BeanFactoryPostProcessor接口的Bean都已经初始化并且其中的postProcessBeanFactory(factory)方法已经得到回调执行了。而且Spring已经手动注册了一些特殊的Bean,如environment、systemProperties等。
  剩下的就是初始化singletonbeans了,我们知道它们是单例的,如果没有设置懒加载,那么Spring会在接下来初始化所有的singletonbeans。
  AbstractApplicationContext。java834
  java
  初始化剩余的singletonbeans
  protectedvoidfinishBeanFactoryInitialization(ConfigurableListableBeanFactorybeanFactory){
  首先,初始化名字为conversionService的Bean。本着送佛送到西的精神,我在附录中简单介绍了一下ConversionService,因为这实在太实用了
  什么,看代码这里没有初始化Bean啊!
  注意了,初始化的动作包装在beanFactory。getBean(。。。)中,这里先不说细节,先往下看吧
  if(beanFactory。containsBean(CONVERSIONSERVICEBEANNAME)
  beanFactory。isTypeMatch(CONVERSIONSERVICEBEANNAME,ConversionService。class)){
  beanFactory。setConversionService(
  beanFactory。getBean(CONVERSIONSERVICEBEANNAME,ConversionService。class));
  }
  Registeradefaultembeddedvalueresolverifnobeanpostprocessor
  (suchasaPropertyPlaceholderConfigurerbean)registeredanybefore:
  atthispoint,primarilyforresolutioninannotationattributevalues。
  if(!beanFactory。hasEmbeddedValueResolver()){
  beanFactory。addEmbeddedValueResolver(newStringValueResolver(){
  Override
  publicStringresolveStringValue(StringstrVal){
  returngetEnvironment()。resolvePlaceholders(strVal);
  }
  });
  }
  先初始化LoadTimeWeaverAware类型的Bean
  之前也说过,这是AspectJ相关的内容,放心跳过吧
  String〔〕weaverAwareNamesbeanFactory。getBeanNamesForType(LoadTimeWeaverAware。class,false,false);
  for(StringweaverAwareName:weaverAwareNames){
  getBean(weaverAwareName);
  }
  StopusingthetemporaryClassLoaderfortypematching。
  beanFactory。setTempClassLoader(null);
  没什么别的目的,因为到这一步的时候,Spring已经开始预初始化singletonbeans了,
  肯定不希望这个时候还出现bean定义解析、加载、注册。
  beanFactory。freezeConfiguration();
  开始初始化
  beanFactory。preInstantiateSingletons();
  }
  从上面最后一行往里看,我们就又回到DefaultListableBeanFactory这个类了,这个类大家应该都不陌生了吧。
  preInstantiateSingletons
  DefaultListableBeanFactory728
  java
  Override
  publicvoidpreInstantiateSingletons()throwsBeansException{
  if(this。logger。isDebugEnabled()){
  this。logger。debug(Preinstantiatingsingletonsinthis);
  }
  this。beanDefinitionNames保存了所有的beanNames
  ListbeanNamesnewArrayList(this。beanDefinitionNames);
  下面这个循环,触发所有的非懒加载的singletonbeans的初始化操作
  for(StringbeanName:beanNames){
  合并父Bean中的配置,注意中的parent,用的不多吧,
  考虑到这可能会影响大家的理解,我在附录中解释了一下Bean继承,不了解的请到附录中看一下
  RootBeanDefinitionbdgetMergedLocalBeanDefinition(beanName);
  非抽象、非懒加载的singletons。如果配置了abstracttrue,那是不需要初始化的
  if(!bd。isAbstract()bd。isSingleton()!bd。isLazyInit()){
  处理FactoryBean(读者如果不熟悉FactoryBean,请移步附录区了解)
  if(isFactoryBean(beanName)){
  FactoryBean的话,在beanName前面加上‘’符号。再调用getBean,getBean方法别急
  finalFactoryBeanlt;?factory(FactoryBeanlt;?)getBean(FACTORYBEANPREFIXbeanName);
  判断当前FactoryBean是否是SmartFactoryBean的实现,此处忽略,直接跳过
  booleanisEagerInit;
  if(System。getSecurityManager()!nullfactoryinstanceofSmartFactoryBean){
  isEagerInitAccessController。doPrivileged(newPrivilegedAction(){
  Override
  publicBooleanrun(){
  return((SmartFactoryBeanlt;?)factory)。isEagerInit();
  }
  },getAccessControlContext());
  }
  else{
  isEagerInit(factoryinstanceofSmartFactoryBean
  ((SmartFactoryBeanlt;?)factory)。isEagerInit());
  }
  if(isEagerInit){
  getBean(beanName);
  }
  }
  else{
  对于普通的Bean,只要调用getBean(beanName)这个方法就可以进行初始化了
  getBean(beanName);
  }
  }
  }
  到这里说明所有的非懒加载的singletonbeans已经完成了初始化
  如果我们定义的bean是实现了SmartInitializingSingleton接口的,那么在这里得到回调,忽略
  for(StringbeanName:beanNames){
  ObjectsingletonInstancegetSingleton(beanName);
  if(singletonInstanceinstanceofSmartInitializingSingleton){
  finalSmartInitializingSingletonsmartSingleton(SmartInitializingSingleton)singletonInstance;
  if(System。getSecurityManager()!null){
  AccessController。doPrivileged(newPrivilegedAction(){
  Override
  publicObjectrun(){
  smartSingleton。afterSingletonsInstantiated();
  returnnull;
  }
  },getAccessControlContext());
  }
  else{
  smartSingleton。afterSingletonsInstantiated();
  }
  }
  }
  }
  接下来,我们就进入到getBean(beanName)方法了,这个方法我们经常用来从BeanFactory中获取一个Bean,而初始化的过程也封装到了这个方法里。
  getBean
  在继续前进之前,读者应该具备FactoryBean的知识,如果读者还不熟悉,请移步附录部分了解FactoryBean。
  AbstractBeanFactory196
  java
  Override
  publicObjectgetBean(Stringname)throwsBeansException{
  returndoGetBean(name,null,null,false);
  }
  我们在剖析初始化Bean的过程,但是getBean方法我们经常是用来从容器中获取Bean用的,注意切换思路,
  已经初始化过了就从容器中直接返回,否则就先初始化再返回
  SuppressWarnings(unchecked)
  protectedTdoGetBean(
  finalStringname,finalClassrequiredType,finalObject〔〕args,booleantypeCheckOnly)
  throwsBeansException{
  获取一个正统的beanName,处理两种情况,一个是前面说的FactoryBean(前面带‘’),
  一个是别名问题,因为这个方法是getBean,获取Bean用的,你要是传一个别名进来,是完全可以的
  finalStringbeanNametransformedBeanName(name);
  注意跟着这个,这个是返回值
  Objectbean;
  检查下是不是已经创建过了
  ObjectsharedInstancegetSingleton(beanName);
  这里说下args呗,虽然看上去一点不重要。前面我们一路进来的时候都是getBean(beanName),
  所以args传参其实是null的,但是如果args不为空的时候,那么意味着调用方不是希望获取Bean,而是创建Bean
  if(sharedInstance!nullargsnull){
  if(logger。isDebugEnabled()){
  if(isSingletonCurrentlyInCreation(beanName)){
  logger。debug(。。。);
  }
  else{
  logger。debug(ReturningcachedinstanceofsingletonbeanbeanName);
  }
  }
  下面这个方法:如果是普通Bean的话,直接返回sharedInstance,
  如果是FactoryBean的话,返回它创建的那个实例对象
  (FactoryBean知识,读者若不清楚请移步附录)
  beangetObjectForBeanInstance(sharedInstance,name,beanName,null);
  }
  else{
  if(isPrototypeCurrentlyInCreation(beanName)){
  创建过了此beanName的prototype类型的bean,那么抛异常,
  往往是因为陷入了循环引用
  thrownewBeanCurrentlyInCreationException(beanName);
  }
  检查一下这个BeanDefinition在容器中是否存在
  BeanFactoryparentBeanFactorygetParentBeanFactory();
  if(parentBeanFactory!null!containsBeanDefinition(beanName)){
  如果当前容器不存在这个BeanDefinition,试试父容器中有没有
  StringnameToLookuporiginalBeanName(name);
  if(args!null){
  返回父容器的查询结果
  return(T)parentBeanFactory。getBean(nameToLookup,args);
  }
  else{
  NoargsdelegatetostandardgetBeanmethod。
  returnparentBeanFactory。getBean(nameToLookup,requiredType);
  }
  }
  if(!typeCheckOnly){
  typeCheckOnly为false,将当前beanName放入一个alreadyCreated的Set集合中。
  markBeanAsCreated(beanName);
  }
  稍稍总结一下:
  到这里的话,要准备创建Bean了,对于singleton的Bean来说,容器中还没创建过此Bean;
  对于prototype的Bean来说,本来就是要创建一个新的Bean。
  try{
  finalRootBeanDefinitionmbdgetMergedLocalBeanDefinition(beanName);
  checkMergedBeanDefinition(mbd,beanName,args);
  先初始化依赖的所有Bean,这个很好理解。
  注意,这里的依赖指的是dependson中定义的依赖
  String〔〕dependsOnmbd。getDependsOn();
  if(dependsOn!null){
  for(Stringdep:dependsOn){
  检查是不是有循环依赖,这里的循环依赖和我们前面说的循环依赖又不一样,这里肯定是不允许出现的,不然要乱套了,读者想一下就知道了
  if(isDependent(beanName,dep)){
  thrownewBeanCreationException(mbd。getResourceDescription(),beanName,
  CirculardependsonrelationshipbetweenbeanNameanddep);
  }
  注册一下依赖关系
  registerDependentBean(dep,beanName);
  先初始化被依赖项
  getBean(dep);
  }
  }
  如果是singletonscope的,创建singleton的实例
  if(mbd。isSingleton()){
  sharedInstancegetSingleton(beanName,newObjectFactory(){
  Override
  publicObjectgetObject()throwsBeansException{
  try{
  执行创建Bean,详情后面再说
  returncreateBean(beanName,mbd,args);
  }
  catch(BeansExceptionex){
  destroySingleton(beanName);
  throwex;
  }
  }
  });
  beangetObjectForBeanInstance(sharedInstance,name,beanName,mbd);
  }
  如果是prototypescope的,创建prototype的实例
  elseif(mbd。isPrototype()){
  Itsaprototypecreateanewinstance。
  ObjectprototypeInstancenull;
  try{
  beforePrototypeCreation(beanName);
  执行创建Bean
  prototypeInstancecreateBean(beanName,mbd,args);
  }
  finally{
  afterPrototypeCreation(beanName);
  }
  beangetObjectForBeanInstance(prototypeInstance,name,beanName,mbd);
  }
  如果不是singleton和prototype的话,需要委托给相应的实现类来处理
  else{
  StringscopeNamembd。getScope();
  finalScopescopethis。scopes。get(scopeName);
  if(scopenull){
  thrownewIllegalStateException(NoScoperegisteredforscopenamescopeName);
  }
  try{
  ObjectscopedInstancescope。get(beanName,newObjectFactory(){
  Override
  publicObjectgetObject()throwsBeansException{
  beforePrototypeCreation(beanName);
  try{
  执行创建Bean
  returncreateBean(beanName,mbd,args);
  }
  finally{
  afterPrototypeCreation(beanName);
  }
  }
  });
  beangetObjectForBeanInstance(scopedInstance,name,beanName,mbd);
  }
  catch(IllegalStateExceptionex){
  thrownewBeanCreationException(beanName,
  ScopescopeNameisnotactiveforthecurrentthread;consider
  definingascopedproxyforthisbeanifyouintendtorefertoitfromasingleton,
  ex);
  }
  }
  }
  catch(BeansExceptionex){
  cleanupAfterBeanCreationFailure(beanName);
  throwex;
  }
  }
  最后,检查一下类型对不对,不对的话就抛异常,对的话就返回了
  if(requiredType!nullbean!null!requiredType。isInstance(bean)){
  try{
  returngetTypeConverter()。convertIfNecessary(bean,requiredType);
  }
  catch(TypeMismatchExceptionex){
  if(logger。isDebugEnabled()){
  logger。debug(Failedtoconvertbeannametorequiredtype
  ClassUtils。getQualifiedName(requiredType),ex);
  }
  thrownewBeanNotOfRequiredTypeException(name,requiredType,bean。getClass());
  }
  }
  return(T)bean;
  }
  大家应该也猜到了,接下来当然是分析createBean方法:
  java
  protectedabstractObjectcreateBean(StringbeanName,RootBeanDefinitionmbd,Object〔〕args)throwsBeanCreationException;
  第三个参数args数组代表创建实例需要的参数,不就是给构造方法用的参数,或者是工厂Bean的参数嘛,不过要注意,在我们的初始化阶段,args是null。
  这回我们要到一个新的类了AbstractAutowireCapableBeanFactory,看类名,AutowireCapable?类名是不是也说明了点问题了。
  主要是为了以下场景,采用Autowired注解注入属性值:
  java
  publicclassMessageServiceImplimplementsMessageService{
  Autowired
  privateUserServiceuserService;
  publicStringgetMessage(){
  returnuserService。getMessage();
  }
  }
  xml
  以上这种属于混用了xml和注解两种方式的配置方式,Spring会处理这种情况。
  好了,读者要知道这么回事就可以了,继续向前。
  AbstractAutowireCapableBeanFactory447
  java
  Centralmethodofthisclass:createsabeaninstance,
  populatesthebeaninstance,appliespostprocessors,etc。
  seedoCreateBean
  Override
  protectedObjectcreateBean(StringbeanName,RootBeanDefinitionmbd,Object〔〕args)throwsBeanCreationException{
  if(logger。isDebugEnabled()){
  logger。debug(CreatinginstanceofbeanbeanName);
  }
  RootBeanDefinitionmbdToUsembd;
  确保BeanDefinition中的Class被加载
  Classlt;?resolvedClassresolveBeanClass(mbd,beanName);
  if(resolvedClass!null!mbd。hasBeanClass()mbd。getBeanClassName()!null){
  mbdToUsenewRootBeanDefinition(mbd);
  mbdToUse。setBeanClass(resolvedClass);
  }
  准备方法覆写,这里又涉及到一个概念:MethodOverrides,它来自于bean定义中的
  和,如果读者感兴趣,回到bean解析的地方看看对这两个标签的解析。
  我在附录中也对这两个标签的相关知识点进行了介绍,读者可以移步去看看
  try{
  mbdToUse。prepareMethodOverrides();
  }
  catch(BeanDefinitionValidationExceptionex){
  thrownewBeanDefinitionStoreException(mbdToUse。getResourceDescription(),
  beanName,Validationofmethodoverridesfailed,ex);
  }
  try{
  让InstantiationAwareBeanPostProcessor在这一步有机会返回代理,
  在《SpringAOP源码分析》那篇文章中有解释,这里先跳过
  ObjectbeanresolveBeforeInstantiation(beanName,mbdToUse);
  if(bean!null){
  returnbean;
  }
  }
  catch(Throwableex){
  thr。。。

国际观察枪击噩梦循环美国控枪阻力依旧据美国媒体报道,美国宾夕法尼亚州费城当地时间4日发生枪击事件,已造成3人死亡,至少11人受伤。仅三天前,当地时间6月1日,美国多地发生枪击事件。俄克拉荷马州塔尔萨市的一处医院大楼内海贼王1052话(谜语版)路飞获胜消息传遍世界,五老星气到昏厥本周海贼王最新话情报提前偷跑,依旧是阿拉伯情报师带来的谜语图,之所以被称为是谜语图,是因为阿拉伯情报师并不会明说情报内容,只会发一些相关的动态图。譬如上一话发一张发型的图,原来指的西方开会敲打印度,印方3句话驳回,反警告欧洲不要插手中印事务西方质疑印度购买俄罗斯石油是资助冲突,甚至有人出言威胁,称印度在乌克兰问题上的立场会影响将来西方在中印关系上的态度,印方用三句话驳回,同时对欧洲发出警告。6月2日至4日,欧洲智库举评论俄乌冲突掩盖之下的美国页岩油革命本文试图从石油利益关系的角度剖析俄乌冲突的未来走向。全文约3000字,阅读大概10分钟。历史上的美国其实是一个对石油进口依赖特别严重的国家,不是因为本土没有石油,而是由于产地都属于丑闻缠身的这位女明星,竟然翻盘了最近,之前爆出丑闻的女演员徐睿知复出了携新作夏娃重回小荧幕。说夏娃是自带黑红属性的话题剧可一点不为过。这部戏是在6月1日儿童节开播的,前两集却是19禁的设定,因为无论是徐睿知还是女大腚下坠就别尬演轻功戏了,让这11位告诉你,什么才是身姿缥缈在一些武侠玄幻类型的古装影视剧中,通常会需要腾空飞跃空中打斗等的轻功情节。轻功的特点就是要稳和轻,力度也不可或缺,演员在表演飞檐走壁飞天遁地时得让观众觉得你是游刃有余的,而不是随便半岛连射8枚导弹,印度也凑热闹,试射烈火4,巴铁红旗9B不会吃素根据韩国公布的消息显示,半岛在6月5日清晨连射了8枚弹道导弹,日本通过远程预警雷达也监测到了半岛进行的弹道导弹试射活动,日本官方对于半岛试射导弹表达了不满。根据公开的资料,半岛在2在接吻过程中,如果男人出现这些表现,说明他是真心喜欢你我们都知道,接吻是恋人之间最常见的亲密方式。看一个人有多喜欢你,可以从接吻的过程中感受出来。同时也能检验对方是否真的爱你。著名作家张爱玲说过,当我们都老的时候,我希望还能吻着你的牙佛坪法院深入包扶村常态化开展进知解走访活动为深入推进进万家门知万家情解万家难活动,佛坪法院紧扣大宣传大走访大帮扶三大核心,聚焦群众急难愁盼问题,持续排忧解难,不断提升人民群众获得感幸福感安全感。6月2日上午,佛坪法院党组书民间故事男子背母治病,见母蛇难产出手相助,母蛇你娘早死了梁贵家有六兄弟,他排行老六,梁贵出生的第二年,正好赶止蝗灾,地里颗粒无收,父亲怕养不活他,便把他过继给了自己的寡嫂姜氏当儿子。姜氏是个可怜的女人,早年守寡妇,中年又丧子,日子过得十美记公牛对戈贝尔以及米切尔直播吧6月7日讯此前,露天看台记者JakeFischer曾报道称,猛龙对三届最佳防守球员爵士中锋戈贝尔表达了兴趣。TheRinger记者KevinOConnor表示,他也听说了(以
当初看中特别大旁边多出来一个?朱女士反映,她弟弟的新房交付了,却发现车位旁边多出了一个车位,这和选车位时的平面图不一样。1818黄金眼收房后的车位,旁边又多出一个?朱女士介绍,几年前,弟弟在杭州拱墅区的风尚望和县委大院职场小白背黑锅,林志为有点委屈由胡歌吴越等人领衔主演的电视剧县委大院正在热播。在光明县县委大院,同一天来了两个新人,一个是张新成饰演的林志为,一个是胡歌饰演的梅晓歌。同样是新人,但是待遇完全不一样,为了迎接梅晓安徽90后女子被称为扶弟魔天花板卖饼12年给弟弟买房买车头条创作挑战赛最近,被大家称为伏弟魔天花板的安徽潘女士火了。标题为卖饼12年给弟弟买房买车的视频在网上评论点赞量很高。她不但给弟弟买房买车,还说自己现在挣的钱都在父母手里。有人问,我国加大对个体工商户税费政策支持力度助企纾困视频加载中央视网消息国家税务总局12月12日的发布数据显示,近年来,我国加大对个体工商户的税费政策支持力度。今年前十个月,个体工商户享受减税降费3285亿元。国家税务总局数据显示,为何古代夫妻就寝时,需要丫鬟守在床边?你知道有怎样的原因吗?古时候有着很严格的等级尊卑制度,社会上的人们被划为不同的阶层自然也就拥有着不同的社会地位和身份,而处于最底层的当然是那些专门为大户人家或是王公贵族服务的奴仆,最典型的就是我们在影视政策都救不了燃油车?政策对燃油车的激励作用失灵了。根据乘联会公布的最新数据,11月乘用车市场零售量为164。9万辆,同比下降9。2,环比下降10。5。这是自2008年以来,首次出现10月11月环比两连狗狗币和柴犬暴跌,原因如下加密货币领域在2022年见证了一些最大的灾难性下跌。从5月泰拉帝国的沦陷到最近FTX帝国的沦陷,那些在外界看来被视为强大实体的人在几天内崩溃了。一系列事件也对数百万投资者和公司造成嘉靖和万历这对爷孙,几十年不上朝却江山依旧,有何原因与区别?大明王朝近三百年,奇葩任性的皇帝特别多,就比如嘉靖和万历这对爷孙俩,一个不上朝24年,另一个28年不上朝,爷孙俩罢工时间的总和,居然占到了大明国祚的五分之一强。更神奇的是,大明王朝大冷门!世青赛蒯曼陈熠双双输球,中国女乒提前丢失女单金牌!12月13日,让所有中国乒乓球球迷失望了,中国女乒不仅仅女团早早丢失了冠军,现在女单也早早痛失金牌!在两场中中日对阵的半决赛中,比分和结局都让人难以接受1)中国青年队女单一姐蒯曼1建议大家若条件允许,给父母添置这几样小物,不贵却暖心我们小时候生活条件是很不好的,但即使这样父母们依然把最好的给孩子,希望孩子能够过得开心健康,特别是到了上学的时候,宁愿多花钱钱,也会让娃进入到好的学校中。如今,人们的水平逐渐提高,买手机建议一步到位,目前只有这四款手机符合要求,能用到2028年买手机建议一步到位,目前只有这四款手机符合要求,能用到2028年第一款红米K50至尊版目前最便宜的骁龙8Gen1手机,性能超强,大面积VC散热激进的调度策略,性能表现非常好,极限画
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网