Java开发三年,竟然连动态注册bean都不知道?

干货点:

看完该篇文章我们可以了解到为何要动态注册bean、动态注册bean的方式、注册和实例化的区别、spring是什么时候实例化bean的。

描述:

前阵子在给项目写组件的时候研究了下spring组件拓展流程,其中遇见了动态注册bean的操作,对于长期使用spring容器管理bean的同学来说动态注册bean应该是活久见系列,为此打算以组件中使用到的方式讲讲动态注册方面的相关解说。

应用场景

在自定义组件的时候,需要自定义命名空间注册器,其中需要提供一个配置解析器ConfigDefinitionParser,配置解析器需要继承AbstractBeanDefinitionParser,

其中需要重写函数parseInternal,返回的是AbstractBeanDefinition类型的数据。为了返回AbstractBeanDefinition,我借鉴了其他spring内部组件的写法,这边就用到了动态注册bean了。

在进入动态注册bean的相关解说前之前有必要大致描述下spring容器注册bean的相关知识!

了解下DefaultListableBeanFactory

DefaultListableBeanFactory是spring容器注册bean的核心,是spring注册及加载bean的默认实现。

一句话来描述其应用那就是:在spring项目启动的时候通过DefaultListableBeanFactory注册所有的BeanDefinition后放入definition表,后面在我们使用的时候就可以直接从表中加载了。

了解下BeanDefinition

在说到动态注册bean之前,要先提下BeanDefinition
在这里插入图片描述
这是从官方文档截出来的,对此我的理解是,BeanDefinition是bean在实例化之前存在spring容器中的一种状态。

分不清注册和实例化的同学们在这里内心应该是 ━━( ̄ー ̄*|||━━ wocao,实例化和注册,什么跟什么 。

别急,在文章最后会点一下实例化和注册的哈!

大致了解了这两个之后便可以真正讲解如何动态注册bean了

使用 BeanDefinitionBuilder注册bean

举个可以运行的栗子

package com.example.testdemo.beanDefinitionBuilderTest.main;


public class TestBean {

    private String str;

    private TestBean2 testBean2;

    public void setStr(String str) {
        this.str = str;
    }

    public void setTestBean2(TestBean2 testBean2) {
        this.testBean2 = testBean2;
    }

    @Override
    public String toString() {
        return "TestBean{" +
            "str='" + str + '\'' +
            ", testBean2=" + testBean2 +
            '}';
    }
}

package com.example.testdemo.beanDefinitionBuilderTest.main;

public class TestBean2 {

    private int a;
    
    public void setA(int a) {
        this.a = a;
    }

    @Override
    public String toString() {
        return "TestBean2{" +
            "a=" + a +
            '}';
    }
}

主要main类,可分为三个步骤:定义、注册、再取出

package com.example.testdemo.beanDefinitionBuilderTest.main;

import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;

public class BeanDefinitionBuilderExample {

    public static void main(String[] args) {
        init();
    }

    private static void init() {

        // 构建DefaultListableBeanFactory
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

        // 注册testBean2
        registerTestBean2(beanFactory);
        // 注册testBean
        registerTestBean1(beanFactory);

        // 从DefaultListableBeanFactory中读取bean
        TestBean bean = beanFactory.getBean(TestBean.class);
        System.out.println(bean);
    }

    private static void registerTestBean1(DefaultListableBeanFactory beanFactory) {

        // 构建TestBean
        BeanDefinitionBuilder b1 = BeanDefinitionBuilder.rootBeanDefinition(TestBean.class);
        // 添加属性
        b1.addPropertyValue("str", "myStringValue");
        // 添加testBean2引用
        b1.addPropertyReference("testBean2", "testBean2");

        // 注册TestBean
        beanFactory.registerBeanDefinition("testBean", b1.getBeanDefinition());
    }

    private static void registerTestBean2(DefaultListableBeanFactory beanFactory) {

        // 构建TestBean2
        BeanDefinitionBuilder b2 = BeanDefinitionBuilder.rootBeanDefinition(TestBean2.class);
        // 添加属性
        b2.addPropertyValue("a", 1);

        // 注册TestBean2
        beanFactory.registerBeanDefinition("testBean2", b2.getBeanDefinition());
    }
}

运行后输出

TestBean{str=‘myStringValue’, testBean2=TestBean2{a=1}}

关于如何给BeanDefinitionBuilder添加各种成员属性和构造传参可以直接查看api地址:

docs.spring.io/spring/docs…

使用 BeanFactoryPostProcessor注册bean

BeanFactoryPostProcessor允许自定义BeanDefinition,使用的GenericBeanDefinition,然后再注册入DefaultListableBeanFactory。

举个可以运行的栗子

这里只给出main类,和上面的例子的区别在于注册函数的不同

package com.example.testdemo.beanDefinitionBuilderTest.main;

import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;

public class BeanDefinitionBuilderExample {

    public static void main(String[] args) {
        init();
    }

    private static void init() {

        // 构建DefaultListableBeanFactory
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

        // 注册testBean2
        registerTestBean2(beanFactory);
        // 注册testBean
        registerTestBean1(beanFactory);

        // 从DefaultListableBeanFactory中读取bean
        TestBean bean = beanFactory.getBean(TestBean.class);
        System.out.println(bean);
    }

    rivate static void registerTestBean1(DefaultListableBeanFactory beanFactory) {

        // 构建TestBean
        GenericBeanDefinition bd = new GenericBeanDefinition();
        bd.setBeanClass(TestBean.class);
        // 添加属性
        bd.getPropertyValues().add("str", "myStringValue");
        // 添加testBean2引用
        bd.getPropertyValues().add("testBean2", beanFactory.getBean("testBean2"));

        // 注册TestBean
        beanFactory.registerBeanDefinition("testBean", bd);
    }

    private static void registerTestBean2(DefaultListableBeanFactory beanFactory) {

        // 构建TestBean2
        GenericBeanDefinition bd = new GenericBeanDefinition();
        bd.setBeanClass(TestBean2.class);
        // 添加属性
        bd.getPropertyValues().add("a", 1);

        // 注册TestBean2
        beanFactory.registerBeanDefinition("testBean2", bd);
    }
}

运行后输出

TestBean{str=‘myStringValue’, testBean2=TestBean2{a=1}}

可以看到注册bean的时候对GenericBeanDefinition的处理是可以自定义了,最终运行效果也是一样的。

关于注册和实例化

注册和实例化其实就是spring容器管理bean的一个过程,先有注册bean才有实例化bean。

那么spring什么时候实例化bean呢?这里可以分为2种情况

  • 如果我们使用BeanFactory作为bean的工厂类,如我上面那样的,则所有的bean都是在第一次使用该Bean的时候实例化,也就是从容器get出来的时候。
  • 如果我们使用ApplicationContext作为bean的工厂类,则又分为以下几种情况:
  • 如果bean的scope是singleton的,也就是单例,并且lazy-init为false(默认是false,所以可以不用设置),意思就是懒初始化,则ApplicationContext启动的时候就实例Bean,并且将实例化后的Bean放在缓存中,下次再使用该Bean的时候,直接从这个缓存中。
  • 如果bean的scope是singleton的,而lazy-init为true,则该Bean的实例化是在第一次使用该Bean的时候进行实例化。
  • 如果bean的scope是prototype的,则该Bean的实例化是在第一次使用该Bean的时候进行实例化。

ApplicationContext由BeanFactory类派生而来,所以提供了更多面向实际应用的功能。以上可以看出spring容器其实是很懒的!!!

写给粉丝

最后,关于本篇文章如果有任何错误、疑问或者补充欢迎大家留言或私信我。另外,我今天给大家分享一份BATJ面试资料,还有一套珍藏的关于Java架构方面的资料一并分享给大家,截图只展示了部分。

如果对我写的文章感兴趣,或者想获取架构进阶以及大厂面试资料的可以加入我的 Java架构社区 免费获取,并相互交流学习!
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值