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

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

被折叠的 条评论
为什么被折叠?



