SpringBean动态注册BeanDefinition(三)

本文探讨了SpringBean动态注册BeanDefinition的重要性,包括Bean的注册和实例化过程。重点介绍了BeanDefinition接口及其子接口和抽象类,如AbstractBeanDefinition、RootBeanDefinition、GenericBeanDefinition和ChildBeanDefinition。同时,讨论了在SpringBoot应用中如何将动态注册的Bean加入到同一容器,并解决运行时获取其他Bean的问题。

SpringBean动态注册BeanDefinition(三)

动态注册Bean意义

1.通常在项目开发中将Bean注入IOC方式:在XML中定义、基于注解@Component、@Bean、@Configuration,项目Start时会扫描注册到IOC容器中。
2.如果在启动后,通过动态注册创建Bean,将Bean增强等,如何实现?
3.动态生成Bean替换数据源实现数据链接切换。

Bean注册和实例化

在开始讲述之前,得明白Bean注册和实例化两个概念。
1.注册:IOC容器通过BeanDefinition 将Bean 依赖关系注册到beanDefinitionMap中,BeanDefinition是一个接口,包含Bean Scope、lazy懒加载等 后续会讲,这里只是依赖关系注册并非直接可以使用Bean。

2.实例化:Bean在注册之后,Bean开始生命周期,通过GetBean()方法对beanDefinitionMap中Bean进行初始化生命周期开始,将创建好的Bean放入单列池。

核心接口

BeanDefinition接口
1. 继承关系

继承关系

2.AttributeAccessor接口:定义用于附加和访问元数据的通用协定的接口简单说Bean属性值DDL,可以是任意对象。
public interface AttributeAccessor {
 
	//设置属性的值
	void setAttribute(String name, Object value);
 
	//获得指定属性名称的值
	Object getAttribute(String name);
 
	//删除指定的属性
	Object removeAttribute(String name);
 
  //判断指定属性是否存在
	boolean hasAttribute(String name);
 
	//获取所有属性
	String[] attributeNames();
}
3.BeanMetadataElement接口:获取Bean来源
public interface BeanMetadataElement {
   // 获取Bean来源
	@Nullable
	default Object getSource() {
		return null;
	}

}
4.BeanDefinition 接口:提供Scope单列or原型、Bean角色、父类BeanDefinition XML中的bean parent=""等
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {

// Socpe:单例\原型
 String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
 String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;

// Bean角色
 int ROLE_APPLICATION = 0;  //用户自己定义的 Bean
 int ROLE_SUPPORT = 1;  //来源于配置文件的 Bean
 int ROLE_INFRASTRUCTURE = 2;//Spring 内部的 Bean

//获取超类BeanDefinition XML中的 <bean parent="">
 void setParentName(@Nullable String parentName);
 //获取超类BeanDefinition名称
 @Nullable
 String getParentName();

//设置BeanClassName。XML 中的 <bean class="">
 void setBeanClassName(@Nullable String beanClassName);
  //获取BeanClassName全路径
 @Nullable
 String getBeanClassName();

// 设置Bean Scope 作用域 XML中的 <bean scope=""> 配置。
 void setScope(@Nullable String scope);
  //获取Bean作用域
 @Nullable
 String getScope();

// 设置 Bean 是否懒加载(默认false,立马加载)XML 中的 <bean lazy-init="">
 void setLazyInit(boolean lazyInit);
  //获取 懒加载值 false:立马加载 true 延迟加载
 boolean isLazyInit();

//设置Bean 的依赖对象  XML中的 <bean depends-on="">
 void setDependsOn(@Nullable String... dependsOn);
  //获取依赖对象
 @Nullable
 String[] getDependsOn();

//设置Bean是否是自动装配  默认为 true XML中的 <bean autowire-candidate="">
 void setAutowireCandidate(boolean autowireCandidate);
  //获取Bean自动装配值
 boolean isAutowireCandidate();

//如果找到了多个可注入bean,那么则选择被Primary标记的bean/获取当前 Bean 是否为首选的 Bean  XML中的 <bean primary="">
 void setPrimary(boolean primary);
 boolean isPrimary();

// 设置 FactoryBean 的名字  XML中的<bean factory-bean="">
 void setFactoryBeanName(@Nullable String factoryBeanName);
 @Nullable
 String getFactoryBeanName();

//设置 FactoryMethod 的名字,可以是某个实例的方法(和factoryBean配合使用)也可以是静态方法。 XML 中的<bean factory-method=""> 
 void setFactoryMethodName(@Nullable String factoryMethodName);
 @Nullable
 String getFactoryMethodName();

//返回该 Bean 构造方法的参数值
 ConstructorArgumentValues getConstructorArgumentValues();

//判断 getConstructorArgumentValues 是否是空对象。
 default boolean hasConstructorArgumentValues() {
  return !getConstructorArgumentValues().isEmpty();
 }

//获取普通属性的集合
 MutablePropertyValues getPropertyValues();

//判断 getPropertyValues 是否为空对象
 default boolean hasPropertyValues() {
  return !getPropertyValues().isEmpty();
 }

// 设置 Bean 的初始化方法 XML中的<bean init-method="">
 void setInitMethodName(@Nullable String initMethodName);
  //获取Bean初始化方法名称
 @Nullable
 String getInitMethodName();

// 设置 Bean 的销毁方法  XML中的<bean destroy-method="">
 void setDestroyMethodName(@Nullable String destroyMethodName);
 @Nullable
 String getDestroyMethodName();

//设置 Bean的角色
 void setRole(int role);
 int getRole();

//设置 Bean 的描述
 void setDescription(@Nullable String description);
 @Nullable
 String getDescription();

//用来解析一个Bean对应的类型上的各种信息,比如泛型
 ResolvableType getResolvableType();

//是否为单例
 boolean isSingleton();

// 是否为原型
 boolean isPrototype();

//是否抽象  XML 中的<bean abstract="true">
 boolean isAbstract();

//返回定义 Bean 的资源描述
 @Nullable
 String getResourceDescription();

// 如果当前 BeanDefinition 是一个代理对象,那么该方法可以用来返回原始的 BeanDefinition
 @Nullable
 BeanDefinition getOriginatingBeanDefinition();
}

核心类

1. 抽象类AbstractBeanDefinition:未提供Bean属性Get/Set,三个实现类GenericBeanDefinition、RootBeanDefinition、ChildBeanDefinition

AbstractBeanDefinition抽象类关系图

2. RootBeanDefinition Spring源码中Bean创建基于RootBeanDefinition创建,不支持setParentName

BeanEntity

@Data
public class BeanEntity implements InitializingBean {

    private String name;

    private String parentName;

    private int age;

    public BeanEntity() {
        System.out.println("BeanEntity被实例化");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("afterPropertiesSet 初始化开始修改数据");
        setName("afterPropertiesSet");
    }
}

注册/获取 BeanEntity

public static void RootBeanDefinition() {
        //所有IOC容器Bean都是存放beanDefinitionMap中
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

        //BeanDefinition子接口RootBeanDefinition 也可以用new RootBeanDefinition()
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(BeanEntity.class);
        beanDefinitionBuilder.addPropertyValue("name", "nijo");
        beanDefinitionBuilder.setScope(SCOPE_SINGLETON);
        //注册
        beanFactory.registerBeanDefinition("beanEntity", beanDefinitionBuilder.getBeanDefinition());
        //获取并实列化
        BeanEntity result=(BeanEntity)beanFactory.getBean("beanEntity");
        System.out.println(result);

    }

结果:
BeanEntity被实例化
afterPropertiesSet 初始化开始修改数据
BeanEntity(name=afterPropertiesSet, parentName=null, age=0)
3.GenericBeanDefinition 弥补RootBeanDefinition不能设置Parent ,支持设置父类

BeanEntity 作为子类

BeanEntityParent 作为父类

public static void GenericBeanDefinition() {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        //父类注册器
        BeanDefinitionBuilder parent = BeanDefinitionBuilder.genericBeanDefinition(BeanEntityParent.class);
        //设置父类属性值
        parent.addPropertyValue("parentName", "parentBeanEntity");
        parent.addPropertyValue("age", 1);
        beanFactory.registerBeanDefinition("beanEntityParent", parent.getBeanDefinition());

        //子类注册器
        BeanDefinitionBuilder child = BeanDefinitionBuilder.genericBeanDefinition(BeanEntity.class);
        //设置属性值
        child.addPropertyValue("name", "child");
        //设置继承关系
        child.setParentName("beanEntityParent");
        beanFactory.registerBeanDefinition("beanEntity", child.getBeanDefinition());

        BeanEntityParent parentBean = (BeanEntityParent) beanFactory.getBean("beanEntityParent");
        System.out.println("parentBean" + parentBean);
        BeanEntity childBean = (BeanEntity) beanFactory.getBean("beanEntity");
        System.out.println("childBena:" + childBean);
    }
    
结果:
parentBeanBeanEntityParent(parentName=parentBeanEntity, age=1)
BeanEntity被实例化
afterPropertiesSet 初始化开始修改数据
childBena:BeanEntity(name=afterPropertiesSet,parentName=parentBeanEntity, age=1)
4.ChildBeanDefinition 可以设置父类 通过SetPaentName 或者子类构造方法注入父类 这里不过多讲解了。

SpringBoot启动加入同一容器

为什么项目运行时 加入注册Bean后,无法获取启动时其他Bean?
	1. 上面代码依靠DefaultListableBeanFactory注入Bean以及Bean生命周期管理,我们是通过new DefaultListableBeanFactory()方式,new 堆上创建新对象并指向变量引用,SpringBoot 启动时DefaultListableBeanFactory非同一个对象,所以无法获取启动时加入容器Bean。可以看getBean源码就会发现Map注册信息时没有数据的。
如何加入Spring运行中容器
1.直接注入DefaultListableBeanFactory
@Autowired
    private DefaultListableBeanFactory defaultListableBeanFactory;

    public void t1(){
        BeanDefinitionBuilder parent = BeanDefinitionBuilder.genericBeanDefinition(BeanEntityParent.class);
        parent.addPropertyValue("parentName", "parentBeanEntity");
        parent.addPropertyValue("age", 1);
        defaultListableBeanFactory.registerBeanDefinition("beanEntityParent", parent.getBeanDefinition());

        BeanEntityParent bean = defaultListableBeanFactory.getBean(BeanEntityParent.class);
        System.out.println("BeanEntityParent"+bean);
        //获取启动时其他Bean
        User user = defaultListableBeanFactory.getBean(User.class);
        System.out.println("user:"+user);
    }
    
结果:
BeanEntityParentBeanEntityParent(parentName=parentBeanEntity, age=1)
user:User{userName='1'}
2.(DefaultListableBeanFactory)ApplicationContext.getBeanFactory
@SpringBootApplication
public class Application {
    private static Test test;
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
        DefaultListableBeanFactory run= (DefaultListableBeanFactory) context.getBeanFactory();
        test=run.getBean(Test.class);
        test.t1();
    }
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值