Spring框架探秘:XML配置文件和解析原理和Bean的实例化方式

Spring的XML解析原理

  • Spring的作用:

    • 定位: 寻找XML配置文件
    • 加载: 将解析的XML配置加载到内存
    • 实例化: 根据加载的XML配置进行实例化
  • IOC体系图:

    • 所有的IOC都有继承关系,这样设计使得任何一个子类IOC都可以直接使用父类IOC加载的Bean
    • 其中重要的类是BeanFactoryApplicationContext,这是所有IOC的父接口
      • BeanFactory中提供了对于Bean最基本的操作
      • ApplicationContext继承BeanFactory, 同时继承MessageSource, ResourceLoader, ApplicationEventPublisher接口用来提供国际化,资源加载,时间发布高级功能 .Spring加载的XML的类是ApplicationContext的子类
    • 其余重要的类:
      • BeanFactory
        • HierarchicalBeanFactory
        • ListableBeanFactory
          • DefaultListableBeanFactory
        • AliasRegistry
          • SimpleAliasRegistry
      • ResourceLoader
        • ApplicationContext
          • AbstractApplicationContext
            • AbstractRefreshableApplicationContext
              • AbstractRefreshableConfigApplicationContext
                • AbstractXmlApplicationContext
                  • ClassPathXmlApplicationContext
  • Spring中的两种标签: 标签在使用前都需要在Springxml配置文件里声明Namespace URI, 这样才能在解析标签时通过Namespace URI找到对应的NamespaceHandler

    • Spring原生标签: 不带前缀的标签是Spring中的原生标签
    • 自定义标签: 带前缀的标签是自定义标签
<bean class="com.oxford.bean.Bean" id="bean" c:id="beanId" p:name="beanName" />
  • c: 通过构造器给属性赋值,是constructor-arg的简化写法
  • p: 通过属性的setter方法给属性赋值,是property的简化写法
  • Spring自定义扩展标签:
    • 创建一个自定义标签和解析类
    • 指定命名空间和NamespaceHandler
    • META-INFspring.handler文件中指定命名空间和NamespaceHandler的映射关系. 类似Springc标签和p标签一样
  • Spring的XML解析流程:

Spring的Bean实例化原理

  • BeanFactoryPostProcessor的类图:
  • ConfigurationClassPostProcessor类完成对 @Configuration, @Bean等注解的解析注册

Beans

接口实例化

基本概念

  • JavaBean:
    • JavaBean是遵循Sun制定的JavaBean标准的特定编码规范的类:
      • 包含一个默认的公有无参构造器
      • 允许通过gettersetter访问器访问类的成员属性,或者其余遵循特定命名规范的方法访问
      • 实现了java.io.Serializable接口,可序列化
  • POJO:
    • POJO即简单Java对象
    • POJO类的对象不需要通过框架来使用,也不需要在特定的应用服务环境下运行
    • POJO主要用来区分简单Java对象,轻量级Java对象和重量级Java对象
  • SpringBean:
    • SpringBean表示由Spring框架管理的对象,也就是由Spring框架的容器进行初始化,配置和管理的Java对象
    • Spring Bean是通过Spring配置文件或者通过注解定义的,在Spring容器中初始化,然后注入到应用程序中

Spring实例化Bean

  • Spring寻找并解析Bean属性的方式:
    • 注解的方式:
      • Spring在启动后进行初始化时会扫描classpath包下符合要求的class
      • 然后进行解析并注册到BeanFactory
    • xml的方式:
      • 注解通过XmlBeanDefinitionReader解析Bean
    • 一般情况下,主流的方式是使用注解的配置,尤其是在SpringBoot框架中

构造器实例化

  • SpringBean类中的默认构造器创建Bean实例:
    • Spring容器根据Spring配置文件的路径加载配置文件
    • 在加载的同时 ,Spring容器会通过实现Bean类中默认的无参构造器对Bean进行实例化
  • SpringBean构造器实例化示例

静态工厂实例化

  • 工厂实现类中提供一个静态工厂方法创建Bean实例:
    • Spring容器根据Spring配置文件的路径加载配置文件
    • 在加载的同时 ,Spring容器会通过工厂实现类StaticFactoryBean类中的静态方法getBean()Bean进行实例化
  • SpringBean静态工厂实例化示例

实例工厂实例化

  • 工厂实现类直接使用成员方法创建Bean实例,同时在配置文件中:
    • 需要实例化的Bean不是通过class属性直接指向实例化的类
    • 而是通过class属性配置实例工厂
    • 然后通过factory-bean属性指定一个实例工厂
    • 最后使用factory-method属性指定使用factory-bean配置的实例工厂中的哪一个方法
      • Spring容器根据Spring配置文件的路径加载配置文件
      • 在加载的同时 ,Spring容器会通过配置的实例工厂类InstanceBeanFactory中的成员getBean()Bean进行实例化
  • SpringBean实例工厂实例化示例

代理Bean操作

  • 实现一个动态代理接口,并且在Spring容器初始化完成之前将该代理对象注册到Spring容器中.实现可以通过 @Autowired等方法从Spring中获取该代理对象
  • Spring中代理Bean操作示例

Spring中动态注入和删除Bean

  • 动态注入和删除Bean:
    • 使用getBean() 获取对象的操作,这些对象都是程序中事先定义好的
    • 有时候需要动态地加入和删除对象. 尽管可以采用配置文件或者注解的方式,但是每次都需要重启服务
    • 为了避免以上问题,需要在Spring中对Bean进行动态地管理,包括Bean对象的注入和删除
  • Spring中动态管理Bean实现思路:
    • Spring中管理Bean对象的是BeanFactory, 具体的是DefaultListableBeanFactory
      • 通过获取到Bean类的ApplicationContext对象获取到BeanFactory
    • 在类DefaultListableBeanFactory中有registerBeanDefinition() 方法使用一个BeanDefinition参数注入Bean
      • 通过BeanDefinitionBuilder可以构建一个Bean类的BeanDefinition
  • Spring中动态注入Bean步骤:
    • 获取Bean类的ApplicationContext
    • 通过ApplicationContext获取BeanFactory
    • 通过BeanDefinitionBuilder获取Bean类的BeanDefinition
    • 通过BeanFactoryregisterBeanDefinition() 注入Bean类的BeanDefinition
    • 注入完成后,可以通过ApplicationContextgetBean() 获取Bean对象
// 1. 获取Bean类的ApplicationContext
ApplicationContext ctx = SpringApplication.run(Bean.class, args);
// 2. 通过ApplicationContext获取BeanFactory
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) ctx.getAutowireCapableBeanFactory();
// 3. 获取并创建BeanDefinition
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(Bean.class);
beanDefinitionBuilder.addPropertyValue("name", "Oxford"); 
BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
// 4. 通过BeanFactory的registerBeanDefinition()注入Bean类的BeanDefinition
defaultListableBeanFactory.registerBeanDefinition("beanInterface", beanDefinition);
// 获取动态注册的Bean
Bean beanInterface = ctx.getBean(Bean.class);
  • Spring中动态删除Bean步骤:
    • 获取Bean类的ApplicationContext
    • 通过ApplicationContext获取BeanFactory
    • 通过BeanFactoryremoveBeanDefinition() 删除已经注入的Bean类的BeanDefinition
// 1. 获取Bean类的ApplicationContext
ApplicationContext ctx = SpringApplication.run(Bean.class, args);
// 2. 通过ApplicationContext获取BeanFactory
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) ctx.getAutowireCapableBeanFactory();
// 通过BeanFactory的removeBeanDefinition()删除已经注入的BeanDefinition
defaultListableBeanFactory.removeBeanDefinition("beanInterface");
  • 动态注入Bean出现的问题:
    • 多次注入同一个Bean类时,如果BeanName一样,后注入的Bean会覆盖先注入的Bean
    • 多次注入同一个Bean类时,如果BeanName不一样,就会产生两个Bean
      • 此时不能使用ApplicationContext.getBean(Bean.class) 方法通过ByType方式获取注入的Bean, 会产生报错
      • 此时可以使用ApplicationContext.getBean("beanInterface") 方法通过ByName的方式获取重复注入的Bean
    • 可以使用以下单例方式解决重复注入Bean的问题:
    // 使得Bean对象完成Spring初始化过程中的所有增强器检验,但是不重新创建Bean对象
    applicationContext.getAutowireCapableBeanFactory().applyBeanPostProcessorsAfterInitialization(beanClass, beanClass.getClass().getName());
    // 将Bean对象以单例的形式注入到容器中. 此时通过 beanClass.getClass() 和 beanClass.getClass().getName() 都可以获取到Spring容器中注入的Bean对象
    defaultListableBeanFactory.registerSingleton(beanClass.getClass().getName(), beanClass);
    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,504评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,434评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,089评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,378评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,472评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,506评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,519评论 3 413
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,292评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,738评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,022评论 2 329
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,194评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,873评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,536评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,162评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,413评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,075评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,080评论 2 352

推荐阅读更多精彩内容