BeanDefinition的概述及使用

  • Post author:
  • Post category:其他


写在前面

注:本文章使用的 SpringBoot 版本为 2.2.4.RELEASE,其 Spring 版本为 5.2.3.RELEASE



前言

BeanDefinition是Spring中非常重要的概念,可以说 它是IoC容器中我们定义的各种Bean的”真身”。它主要用来描述Bean,内部拥有很多属性及方法。我们通过注解操作Bean或在Bean上设置的一些属性均能在BeanDefinition类中找到其身影。Spring也是借助BeanDefinition来生成Bean的。

接下来我们会分三个部分介绍BeanDefinition,分别是BeanDefinition类中常见属性释义,BeanDefinition的分类以及BeanDefinition的使用



BeanDefinition类的属性释义

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {

	//单例作用域 属性
	String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;

	//原型作用域 属性
	String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;


	// 这三个属性 用于标识Bean的类别或来源
	//代表是用户自己定义的Bean
	int ROLE_APPLICATION = 0;
	//代表Bean来自于配置文件
	int ROLE_SUPPORT = 1;
	//代表Bean来自于Spring内部
	int ROLE_INFRASTRUCTURE = 2;


	// 设置parentName,bd允许有层级 因此可以指定父bd的名称
	void setParentName(@Nullable String parentName);

	//获取parentName
	@Nullable
	String getParentName();

	// 这个比较重要 设置BeanClassName,代表bd是属于哪个类型的
	void setBeanClassName(@Nullable String beanClassName);

	//获取BeanClassName
	@Nullable
	String getBeanClassName();

	// 设置Bean的作用域 是单例、原型还是其他
	void setScope(@Nullable String scope);

	
	@Nullable
	String getScope();

	//设置是否懒加载,与@Lazy相似
	void setLazyInit(boolean lazyInit);

	boolean isLazyInit();

	//设置依赖的Bean,即加载当前bd之前,需要先加载依赖的bd 才会有当前bd。这与@DependsOn注解效果类似
	void setDependsOn(@Nullable String... dependsOn);

	@Nullable
	String[] getDependsOn();

	//设置bean是否可以依赖注入,可参考@AutoWired
	void setAutowireCandidate(boolean autowireCandidate);
	boolean isAutowireCandidate();

	//设置当前bean是否为主要候选Bean,一般同一类型的Bean存在多个实例时,这个字段很有效
	void setPrimary(boolean primary);
	boolean isPrimary();

	// 设置工厂BeanName,这可以跟FactoryMethod一起来用 帮忙创建Bean
	void setFactoryBeanName(@Nullable String factoryBeanName);
	@Nullable
	String getFactoryBeanName();

	//设置FactoryMethod
	void setFactoryMethodName(@Nullable String factoryMethodName);
	@Nullable
	String getFactoryMethodName();

	//获取构造函数参数
	ConstructorArgumentValues getConstructorArgumentValues();

	/**
	 * Return if there are constructor argument values defined for this bean.
	 * @since 5.0.2
	 */
	default boolean hasConstructorArgumentValues() {
		return !getConstructorArgumentValues().isEmpty();
	}

	//获取属性参数
	MutablePropertyValues getPropertyValues();

	default boolean hasPropertyValues() {
		return !getPropertyValues().isEmpty();
	}

	//设置Bean的初始化方法,这与@InitMethod类似
	void setInitMethodName(@Nullable String initMethodName);
	@Nullable
	String getInitMethodName();

	//设置Bean销毁前方法,这与@DestroyMethod类似
	void setDestroyMethodName(@Nullable String destroyMethodName);
	@Nullable
	String getDestroyMethodName();

	//设置Bean的类别 具体取值 在前面属性部分介绍了
	void setRole(int role);
	int getRole();

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


	// Read-only attributes

	//这个暂时不太清楚其用途,有知道的小伙伴也可以分享一下 
	ResolvableType getResolvableType();

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

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

	//当前Bean是否为抽象类
	boolean isAbstract();

	@Nullable
	String getResourceDescription();

	@Nullable
	BeanDefinition getOriginatingBeanDefinition();
}

通过上面BeanDefinition的类描述信息看到,其继承了

AttributeAccessor



BeanMetadataElement

,我们也来看一下这两个接口吧。

public interface AttributeAccessor {
	//设置附加属性
	void setAttribute(String name, @Nullable Object value);
   //获取附加属性
	@Nullable
	Object getAttribute(String name);
   //移除附加属性
	@Nullable
	Object removeAttribute(String name);

	boolean hasAttribute(String name);

	String[] attributeNames();

}

对于

AttributeAccessor

接口,我理解为它是为BeanDefinition设置附加属性的,有没有都行。也不会影响到Bean的属性赋值等操作

public interface BeanMetadataElement {

	//获取BeanDefinition的来源,可以理解为创建BeanDefinition的那个类,setSource会在AbstractBeanDefinition中进行
	@Nullable
	default Object getSource() {
		return null;
	}

}



BeanDefinition的分类

在这里插入图片描述


AbstractBeanDefinition

是 BeanDefinition 的子抽象类,它实现了接口中定义的一系列操作方法,并定义了一系列的常量属性,这些常量会影响到 Spring 实例化 Bean过程 。


  • RootBeanDefinition

    代表一个被合并的BeanDefinition。可以由多个具有继承关系的

    GenericBeanDefinition

    创建而来。RootBeanDefinition也可被当做普通的BeanDefinition注册到容器中。

  • ChildBeanDefinition

    不可以单独存在,必须依赖一个父 BeanDefinition(通过观察

    ChildBeanDefinition

    构造方法可知)。

构造 ChildBeanDefinition 时,通过构造方法传入父 BeanDetintion 的名称或通过 setParentName 设置父名称。它可以从父类继承方法参数、属性值,并可以重写父类的方法,同时也可以增加新的属性或者方法。


  • GenericBeanDefinition

    是 当前BeanDefinition的首选实现类。它允许指定一个类以及可选的构造函数参数值和属性值。此外,可以通过“parentName”属性动态定义父BeanDefinition。


AnnotatedBeanDefinition

主要是用来操作注解元信息的,与注解Bean有关


  • ConfigurationClassBeanDefinition

    描述 标注 @Configuration 注解的类中,通过 @Bean 注解实例化的 Bean(后面分析@Bean的解析时会说到)

  • AnnotatedGenericBeanDefinition

    描述标注了@Configuration注解的Bean

  • ScannedGenericBeanDefinition

    描述标注了@Component注解的Bean,也包括由@Component派生出来的@Service、@Controller 等

实际上

RootBeanDefinition



ChildBeanDefinition

组成的这种层次结构的BeanDefinition,用

GenericBeanDefinition

也可以实现。当下更愿意将

RootBeanDefinition

当做一个被合并的

BeanDefinition

,而一般情况下 主要使用

GenericBeanDefinition

下面就演示下几种BeanDefinition的使用



BeanDefinition的使用

先来看一下实体类

@Data
public class SuperUser implements Serializable {

    private String address;

    public SuperUser(String address) {
        this.address = address;
    }

    public SuperUser() {
    }
}


@Data
@ToString(callSuper = true)
public class User extends SuperUser {

    private String name;

    private Integer age;


    public User() {
    }

    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

}

基于RootBeanDefinition/ChildBeanDefinition 注册有层次的Bean

public class ChildBeanDefinitionDemo {
    public static void main(String[] args) {

        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
		//父BeanDefinition
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
        rootBeanDefinition.setBeanClass(SuperUser.class);
        MutablePropertyValues propertyValues = new MutablePropertyValues();
        propertyValues.addPropertyValue("address", "地址");

        rootBeanDefinition.setPropertyValues(propertyValues);
        //设置附加属性
		rootBeanDefinition.setAttribute("a","b");
		//设置bd来源
        rootBeanDefinition.setSource(ChildBeanDefinitionDemo.class);
	      //子BeanDefinition
        //superUser是parentName
        ChildBeanDefinition childBeanDefinition = new ChildBeanDefinition("superUser");
        childBeanDefinition.setBeanClass(User.class);
		//设置构造函数参数
        ConstructorArgumentValues argumentValues = new ConstructorArgumentValues();
        argumentValues.addIndexedArgumentValue(0, "我就是我");
        argumentValues.addIndexedArgumentValue(1, 18);
        childBeanDefinition.setConstructorArgumentValues(argumentValues);
				childBeanDefinition.setPrimary(true);
		//注册BeanDefinition
        context.registerBeanDefinition("superUser", rootBeanDefinition);
        context.registerBeanDefinition("user", childBeanDefinition);

        context.refresh();

        User user = context.getBean("user",User.class);
        System.out.println(user);

        SuperUser superUser = context.getBean("superUser",SuperUser.class);
        System.out.println(superUser);
        context.close();
    }

}

基于GenericBeanDefinition 注册有层次的Bean

public class GenericBeanDefinitionDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

        //父BeanDefinition
        GenericBeanDefinition rootBeanDefinition = new GenericBeanDefinition();
        rootBeanDefinition.setBeanClass(SuperUser.class);
        //设置参数
        MutablePropertyValues propertyValues = new MutablePropertyValues();
        propertyValues.addPropertyValue("address", "地址");

        rootBeanDefinition.setPropertyValues(propertyValues);

        //子BeanDefinition
        GenericBeanDefinition childBeanDefinition = new GenericBeanDefinition();
        childBeanDefinition.setBeanClass(User.class);
        //设置构造参数
        ConstructorArgumentValues argumentValues = new ConstructorArgumentValues();
        argumentValues.addIndexedArgumentValue(0, "我就是我");
        argumentValues.addIndexedArgumentValue(1, 18);

        childBeanDefinition.setConstructorArgumentValues(argumentValues);
        childBeanDefinition.setParentName("superUser");
        //类型相同时 以子类为主
        childBeanDefinition.setPrimary(true);

        context.registerBeanDefinition("superUser", rootBeanDefinition);
        context.registerBeanDefinition("user", childBeanDefinition);

        context.refresh();

        User user = context.getBean("user", User.class);
        System.out.println(user);

        SuperUser superUser = context.getBean("superUser", SuperUser.class);
        System.out.println(superUser);
        context.close();
    }

}

可以看出两者的实现基本上是相同的。

上面我们介绍了通过API的方式注册有层次的BeanDefinition,下面也给出通过XML配置的方式

<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="user" class="com.wojiushiwo.dto.User" parent="superUser"  primary="true">
        <property name="name" value="wojiushiwo"/>
        <property name="age" value="18"/>
    </bean>

    <bean id="superUser" class="com.wojiushiwo.dto.SuperUser">
        <property name="address" value="地址"/>
    </bean>

</beans>

如上 通过bean标签中parent属性为user指定了父BeanDefinition,也可实现与API相同的效果。


加载xml资源至Spring容器的代码这里就不再赘述了,可参见其他文章

关于

AnnotatedBeanDefinition

接口相关的BeanDefinition实例使用这里先不介绍了,等在分析常用注解的解析过程时再聊。

以上就是本章讨论的主要内容了,如您在阅读过程中发现有错误,还望指出,感谢!



版权声明:本文为zyxwvuuvwxyz原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。