Spring源码之BeanDefinition篇章

Spring之BeanDefinition

1、对象和bean的区别

Spring的任何源码都绕不开bean的生命周期,首先来看一下bean和对象的区别:

作为一个普通对象来说,一个类编译生成xx.class之后,通过构造方法(具体类,非抽象和接口)创建出来一个对象;

作为Spring的bean来说,是经历过完成的bean的生命周期的,其中包含了上面创建对象这一步,但是Spring中bean的形成比创建对象更为苛刻。也就是说一个bean肯定是一个对象,但是一个对象并非是一个bean。

2、BeanDefinition作用

在Java中,对象是怎么来进行描述的呢?对象首先是通过类来进行描述,描述类的属性和方法。

而在Spring中,BeanDefinition的作用就是用来描述一个bean的。

首先来看下BeanDefinitio接口的注释:

BeanDefinition 描述了一个 bean 实例,它具有属性值、构造函数参数值以及具体实现提供的更多信息。
这只是一个最小的接口:主要目的是允许 BeanFactoryPostProcessor 内省和修改属性值和其他 bean 元数据。

直接看下源码中的说明:

// AttributeAccessor:额外属性说明
// BeanMetadataElement:BeanDefinition有关的额外说明
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
  // 单例bean		
  String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
  // 原型bean
  String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;

  int ROLE_APPLICATION = 0;
  int ROLE_SUPPORT = 1;
  int ROLE_INFRASTRUCTURE = 2;
  
  // 设置当前BeanDefinition的父BeanDefinition,这个下面介绍
  void setParentName(@Nullable String parentName);

  // 获取当前BeanDefinition的父BeanDefinition
  @Nullable
  String getParentName();

  // 设置当前bean的class对应的全限定类名
  void setBeanClassName(@Nullable String beanClassName);

  // 获取当前bean的class对应的全限定类名
  String getBeanClassName();

  // 设置当前bean作用域
  void setScope(@Nullable String scope);


  // 获取得到当前bean的作用域
  String getScope();

  // 设置当前bean是否是懒加载的bean
  void setLazyInit(boolean lazyInit);

  // 获取
  boolean isLazyInit();

  // 当前bean是否依赖了其他bean,如果依赖了,优先加载其他bean
  void setDependsOn(@Nullable String... dependsOn);

  // 获取得到当前bean所依赖其他bean的名称
  String[] getDependsOn();

  // 设置是否作为bean的一个候选者。也就是说是否是其他bean的所依赖的bean
  void setAutowireCandidate(boolean autowireCandidate);

  // 表示的当前bean参不参与自动装配
  boolean isAutowireCandidate();

  // 表示的当前bean是否优先参与自动装配
  void setPrimary(boolean primary);


  boolean isPrimary();

  // 设置FactoryBean名称
  void setFactoryBeanName(@Nullable String factoryBeanName);

  String getFactoryBeanName();

  // 利用哪个FactoryMethod创建出来的
  void setFactoryMethodName(@Nullable String factoryMethodName);

  String getFactoryMethodName();

  // 构造参数中的值的设置
  ConstructorArgumentValues getConstructorArgumentValues();

  // 是否在构造函数中被设置值
  default boolean hasConstructorArgumentValues() {
    return !getConstructorArgumentValues().isEmpty();
  }

  // 给bean属性设置的值
  MutablePropertyValues getPropertyValues();

  // 当前bean是否有被设置的值
  default boolean hasPropertyValues() {
    return !getPropertyValues().isEmpty();
  }

  // 设置当前bean的初始化方法
  void setInitMethodName(@Nullable String initMethodName);

  String getInitMethodName();

  // 给当前bean设置销毁方法
  void setDestroyMethodName(@Nullable String destroyMethodName);

  String getDestroyMethodName();

  // 没啥用
  void setRole(int role);
  int getRole();
  // 对当前bean的描述
  void setDescription(@Nullable String description);
  String getDescription();

  ResolvableType getResolvableType();

  // 当前bean是否是单例
  boolean isSingleton();

  // 当前bean是否是原型
  boolean isPrototype();

  // 当前bean是否是抽象beandefinition,并不是说beandefinition对应的类是个抽象类
  boolean isAbstract();

  // 对当前类文件资源的描述
  String getResourceDescription();

  // 得到原始的BeanDefinition
  BeanDefinition getOriginatingBeanDefinition();

}

在Spring中,随着Spring不断发展,Spring定义了越来越多的BeanDefinition用来说明不同类型的BeanDefinition。下面将会说明:

AutowireCandidate说明

表示的当前bean参不参与自动装配

这里来写一个例子说明:

public interface D { }

public class C implements D{ }

public class B implements D{ }

public class A {

    private D d;

    public D getD() {
        return d;
    }

    public void setD(D d) {
        this.d = d;
    }
}

对应的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 http://www.Springframework.org/schema/beans/Spring-beans.xsd"

	
    default-autowire="byType"
>

    <bean id="a" class="com.guang.beandefinitaiontest.A"  >
    </bean>

    <bean id="b" class="com.guang.beandefinitaiontest.B">

    </bean>

    <bean id="c" class="com.guang.beandefinitaiontest.C">

    </bean>

</beans>

对应的测试类:

public class Test {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("classpath:mySpring.xml");
        A a = classPathXmlApplicationContext.getBean(A.class);
        System.out.println(a.getD());
    }
}

如果要是按照上面的配置,那么A依赖的C,但是C有两个子类,都是bean,那么在注入的时候找到了两个bean,会报错!!!

但是如果只要注入其中的B注入,不要让C注入,那么这个时候就来使用AutowireCandidate,对应如下所示:

<?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 http://www.Springframework.org/schema/beans/Spring-beans.xsd"


    default-autowire="byType"
>

    <bean id="a" class="com.guang.beandefinitaiontest.A"  >
    </bean>

    <bean id="b" class="com.guang.beandefinitaiontest.B" autowire-candidate="false">

    </bean>

    <bean id="c" class="com.guang.beandefinitaiontest.C">

    </bean>

</beans>

那么再次测试,就符合条件了。我们想要符合的来注入进来而已。

Primary说明

表示的当前bean是否优先参与自动装配

这个与上面的对应,如果我两个bean都想要,但是想根据条件来进行获取。但是如果不加条件,我想默认获取的是其中的一个,换个条件就获取得到另外一个。

那么再次来修改一下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 http://www.Springframework.org/schema/beans/Spring-beans.xsd"


    default-autowire="byType"
>

    <bean id="a" class="com.guang.beandefinitaiontest.A"  >
    </bean>

    <bean id="b" class="com.guang.beandefinitaiontest.B" primary="false">

    </bean>

    <bean id="c" class="com.guang.beandefinitaiontest.C" primary="true">

    </bean>

</beans>

那么再次来执行上面的test测试方法:

com.guang.beandefinitaiontest.C@eec5a4a

那么有什么用呢?我们在使用sqlSessionTemplate的时候,可以设置主要候选者是simplesqlSessionTemplate,作为simple的提交方法会;使用batchsqlSessionTemplate作为批量提交的方式。只需要在注入的时候做一个选择即可。

ConstructorArgumentValues说明

在Spring帮我们创建对象的时候,将构造函数中的值设置到属性中。

第一种使用方式

public class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }
	// 其实是使用反射来进行操作的,这里使用get是为了方法获取打印看看值而已
    public String getName() {
        return name;
    }
}
    <bean id="person" class="com.guang.beandefinitaiontest.service.Person">
        <constructor-arg name="name" value="lig"/>
    </bean>
public class Test {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("classpath:mySpring.xml");
        Person person = classPathXmlApplicationContext.getBean(Person.class);
        System.out.println(person.getName());
    }

控制台打印:

lig

第二种使用方式

<bean id="person" class="com.guang.beandefinitaiontest.service.Person">
    <constructor-arg index="0" ref="lig"/>
</bean>

原理如下:

public class ConstructorArgumentValues {
	// 维护构造函数中下标对应的值
	private final Map<Integer, ValueHolder> indexedArgumentValues = new LinkedHashMap<>();
    
    // name:ref的方式来进行存储
	// 维护按照配置的属性来设置的值,按照name:ref的方式
	private final List<ValueHolder> genericArgumentValues = new ArrayList<>();
}

那么这里又引发了一个疑问,如果是name:value的形式呢?那么就是下面这种方式

MutablePropertyValues

给构造函数中来设置name:value的形式

<bean id="person" class="com.guang.beandefinitaiontest.service.Person">
    <constructor-arg index="0" value="lig"/>
</bean>

abstract

这里是对BeanDefinition做的描述,而不是对BeanDefinition对应的bean的类做的描述。所以不能够理解成bean对应的class是抽象的

而是说abstract修饰的BeanDefinition是无法创建出来对应的bean。

小结

在BeanDefinition接口中的属性定义,都可以在xml找到与之对应的标签。

3、BeanDefinition的发展历程

BeanDefinition在早期版本中,不是采用注解,也不是采用xml,而是直接通过BeanDefinition来进行配置bean的。

但是如果让程序员手动的来通过BeanDefinition来进行配置的话,那么让程序员是奔溃的。

举个例子说明一下:

        GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
        genericBeanDefinition.setAbstract(false);
        genericBeanDefinition.setBeanClass(A.class);
        genericBeanDefinition.setAutowireMode(1);
        genericBeanDefinition.setInitMethodName("init");
        genericBeanDefinition.setDestroyMethodName("destroy");
        genericBeanDefinition.setLazyInit(false);

采用这种方式来注册一个bean的话,那么确实是操作起来极其麻烦。

所以程序员不愿意使用编程方式来进行BeanDefinition定义,所以就衍生出来xml以及后期的注解式配置。

而对于xml来说,为了满足BeanDefinition原始方式,那么开发出来了对于每一个BeanDefinition定义的标签方式来进行配置。

3、BeanDefinition继承体系

AttributeAccessor

这个类是为了针对BeanDefinition的描述来进行增加的。可以用来描述BeanDefinition中的类是哪个类、哪个接口等等一些信息描述。

因为有些信息对于BeanDefinition来说,BeanDefinition已经设置了足够描述bean的属性,但是有些附加属性也需要来进行描述。但是如果添加到BeanDefinition中来说,又是多余的,因为不是所有的BeanDefinition都需要的。所以单独抽取出来一个进行操作。

那么首先看一下AttributeAccessor中的信息

public interface AttributeAccessor {

	// 设置对当前BeanDefinition的描述信息
	void setAttribute(String name, @Nullable Object value);

	// 得到某个属性的值
	Object getAttribute(String name);

	
	@SuppressWarnings("unchecked")
	default <T> T computeAttribute(String name, Function<String, T> computeFunction) {
		Assert.notNull(name, "Name must not be null");
		Assert.notNull(computeFunction, "Compute function must not be null");
		Object value = getAttribute(name);
		if (value == null) {
			value = computeFunction.apply(name);
			Assert.state(value != null,
					() -> String.format("Compute function must not return null for attribute named '%s'", name));
			setAttribute(name, value);
		}
		return (T) value;
	}

	// 移除某个属性
	Object removeAttribute(String name);

	// 是否包含
	boolean hasAttribute(String name);

	// 得到所有描述BeanDefinition的信息
	String[] attributeNames();

}

BeanDefinition接口中没有来做实现,但是在BeanDefinition的子类AbstractBeanDefinition来做了对应的实现。

在AttributeAccessor的子类AttributeAccessorSupport中利用一个map做一个维护,在AbstractBeanDefinition来进行操作:

public abstract class AttributeAccessorSupport implements AttributeAccessor, Serializable {
	private final Map<String, Object> attributes = new LinkedHashMap<>();
}

BeanMetadataElement

这里是为了bean的元信息,类似注解。比如bean对应的类所在的路径信息

public interface BeanMetadataElement {
    
	default Object getSource() {
		return null;
	}

}

对应的实现是在BeanMetadataElement的子类BeanMetadataAttributeAccessor中来进行实现的。

public class BeanMetadataAttributeAccessor extends AttributeAccessorSupport implements BeanMetadataElement {

	@Nullable
	private Object source;


	/**
	 * Set the configuration source {@code Object} for this metadata element.
	 * <p>The exact type of the object will depend on the configuration mechanism used.
	 */
	public void setSource(@Nullable Object source) {
		this.source = source;
	}

	@Override
	@Nullable
	public Object getSource() {
		return this.source;
	}


	/**
	 * Add the given BeanMetadataAttribute to this accessor's set of attributes.
	 * @param attribute the BeanMetadataAttribute object to register
	 */
	public void addMetadataAttribute(BeanMetadataAttribute attribute) {
		super.setAttribute(attribute.getName(), attribute);
	}

	/**
	 * Look up the given BeanMetadataAttribute in this accessor's set of attributes.
	 * @param name the name of the attribute
	 * @return the corresponding BeanMetadataAttribute object,
	 * or {@code null} if no such attribute defined
	 */
	@Nullable
	public BeanMetadataAttribute getMetadataAttribute(String name) {
		return (BeanMetadataAttribute) super.getAttribute(name);
	}

	@Override
	public void setAttribute(String name, @Nullable Object value) {
		super.setAttribute(name, new BeanMetadataAttribute(name, value));
	}

	@Override
	@Nullable
	public Object getAttribute(String name) {
		BeanMetadataAttribute attribute = (BeanMetadataAttribute) super.getAttribute(name);
		return (attribute != null ? attribute.getValue() : null);
	}

	@Override
	@Nullable
	public Object removeAttribute(String name) {
		BeanMetadataAttribute attribute = (BeanMetadataAttribute) super.removeAttribute(name);
		return (attribute != null ? attribute.getValue() : null);
	}

}

AbstractBeanDefinition

这个类是对BeanDefinition的实现,里面简单定义了一些内容,并做了一些默认的实现和扩展。

既然是抽象类,那么就无法直接创建一个BeanDefinition,而上面的继承体系中看到一些子类,那么接下来来对子类做说明。

那么再把图拿过来:

我们首先可以看到AbstractBeanDefinition的三大子类:GenericBeanDefinitionChildBeanDefinitionRootBeanDefinition

我们看ChildBeanDefinition类上都有一行注释:

Since Spring 2.5, the preferred way to register bean definitions programmatically is the GenericBeanDefinition class, which allows to dynamically define parent dependencies through the GenericBeanDefinition.setParentName method. This effectively supersedes the ChildBeanDefinition class for most use cases.

对应的中文翻译:

从 Spring 2.5 开始,以编程方式注册 bean 定义的首选方式是 GenericBeanDefinition 类,它允许通过 GenericBeanDefinition.setParentName 方法动态定义父依赖项。 对于大多数用例,这有效地取代了 ChildBeanDefinition 类。

说明了自从Spring2.5之后,常用的是GenericBeanDefinition来代替ChildBeanDefinition,因为GenericBeanDefinition可以通过GenericBeanDefinition.setParentName 方法动态定义父依赖项。

而在RootBeanDefinition上也有一行注释:

Root bean definitions may also be used for registering individual bean definitions in the configuration phase. However, since Spring 2.5, the preferred way to register bean definitions programmatically is the GenericBeanDefinition class. GenericBeanDefinition has the advantage that it allows to dynamically define parent dependencies, not 'hard-coding' the role as a root bean definition.

对应的中文翻译:

根 bean 定义也可用于在配置阶段注册单个 bean 定义。 但是,从 Spring 2.5 开始,以编程方式注册 bean 定义的首选方法是 GenericBeanDefinition 类。 GenericBeanDefinition 的优点是它允许动态定义父依赖项,而不是将角色“硬编码”为根 bean 定义。

那么也就是说Spring2.5之后,推荐使用的是GenericBeanDefinition来解决硬编码问题。但是没有说GenericBeanDefinition可以来替代RootBeanDefinition,所以RootBeanDefinition也有其特殊的作用。

那么综合起来:GenericBeanDefinition来代替ChildBeanDefinition,因为可以给其设置父依赖并且可以解决硬编码问题。

那么看一下ChildBeanDefinition的构造方法:

	public ChildBeanDefinition(String parentName) {
		super();
		this.parentName = parentName;
	}

.....

可是就是没有发现无参构造方法来进行设置父BeanDefinition的构造函数。

那么为什么要这样子来进行设置?????

Spring设计的初衷就是为了让ChildBeanDefinition永远只能够作为子BeanDefinition。

而GenericBeanDefinition则可以通过无参构造方法来设置父BeanDefinition的名称。所以GenericBeanDefinition有更大的灵活性。

小结

RootBeanDefinition不能够作为子BeanDefinition存在,而ChildBeanDefinition不能够作为父BeanDefinition出现。

只有GenericBeanDefinition既可以作为父BeanDefinition出现,也可以作为子BeanDefinition出现。

不懂的可以将下面的问题看完就可以

利用GenericBeanDefinition创建BeanDefinition

那么参考官方文档案例来进行实践:

https://docs.Spring.io/Spring-framework/docs/current/reference/html/core.html#beans-child-bean-definitions

下面不通过xml的方式来进行操作,而是使用注解方式来进行操作。

public class A {
    public void init(){
        System.out.println("Ainit");
    }

    public void destroy(){
        System.out.println("Adestroy");
    }
}

public class B {
    public void init(){
        System.out.println("Binit");
    }

    public void destroy(){
        System.out.println("Bdestroy");
    }
}

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.register(AppConfig.class);

        GenericBeanDefinition aGenericBeanDefinition  = new GenericBeanDefinition();
        aGenericBeanDefinition.setInitMethodName("init");
        aGenericBeanDefinition.setLazyInit(false);
        aGenericBeanDefinition.setPrimary(true);
        aGenericBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
        aGenericBeanDefinition.setDestroyMethodName("destroy");
        aGenericBeanDefinition.setBeanClass(A.class);
        applicationContext.registerBeanDefinition("a",aGenericBeanDefinition);

        GenericBeanDefinition bGenericBeanDefinition  = new GenericBeanDefinition();
        bGenericBeanDefinition.setInitMethodName("init");
        bGenericBeanDefinition.setLazyInit(false);
        bGenericBeanDefinition.setPrimary(true);
        bGenericBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
        bGenericBeanDefinition.setDestroyMethodName("destroy");
        bGenericBeanDefinition.setBeanClass(B.class);
        applicationContext.registerBeanDefinition("b",bGenericBeanDefinition);

        applicationContext.refresh();

        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println("bean的名称是:"+beanDefinitionName);
        }
    }
}

那么通过控制台打印:

Ainit
Binit
bean的名称是:org.Springframework.context.annotation.internalConfigurationAnnotationProcessor
bean的名称是:org.Springframework.context.annotation.internalAutowiredAnnotationProcessor
bean的名称是:org.Springframework.context.annotation.internalCommonAnnotationProcessor
bean的名称是:org.Springframework.context.event.internalEventListenerProcessor
bean的名称是:org.Springframework.context.event.internalEventListenerFactory
bean的名称是:appConfig
bean的名称是:a
bean的名称是:b

可以看到这样子来进行操作的时候是可以利用GenericBeanDefinition来进行注册BeanDefinition的。

问题一:BeanDefinition属性冗余

但是同时我们发现了一个问题:我们发现在定义aGenericBeanDefinition和bGenericBeanDefinition的时候,发现二者属性有大量相同的内容,那么这种肯定是存在着冗余的。那么有没有java中的继承的方式来抽取出来冗余的BeanDefinition的属性呢?不然让程序员写大量相同的方法无异于是在重复造轮子。

Spring当然也考虑到了这种情况。下面请看解决方式:

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.register(AppConfig.class);

        GenericBeanDefinition parentGenericBeanDefinition  = new GenericBeanDefinition();
        parentGenericBeanDefinition.setInitMethodName("init");
        parentGenericBeanDefinition.setLazyInit(false);
        parentGenericBeanDefinition.setPrimary(true);
        parentGenericBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
        parentGenericBeanDefinition.setDestroyMethodName("destroy");

        parentGenericBeanDefinition.setAbstract(true);

        applicationContext.registerBeanDefinition("parent",parentGenericBeanDefinition);


        GenericBeanDefinition aGenericBeanDefinition  = new GenericBeanDefinition();
//        aGenericBeanDefinition.setInitMethodName("init");
//        aGenericBeanDefinition.setLazyInit(false);
//        aGenericBeanDefinition.setPrimary(true);
//        aGenericBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
//        aGenericBeanDefinition.setDestroyMethodName("destroy");
        aGenericBeanDefinition.setBeanClass(A.class);
        aGenericBeanDefinition.setParentName("parent");
        applicationContext.registerBeanDefinition("a",aGenericBeanDefinition);

        GenericBeanDefinition bGenericBeanDefinition  = new GenericBeanDefinition();
//        bGenericBeanDefinition.setInitMethodName("init");
//        bGenericBeanDefinition.setLazyInit(false);
//        bGenericBeanDefinition.setPrimary(true);
//        bGenericBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
//        bGenericBeanDefinition.setDestroyMethodName("destroy");
        bGenericBeanDefinition.setBeanClass(B.class);
        bGenericBeanDefinition.setParentName("parent");
        applicationContext.registerBeanDefinition("b",bGenericBeanDefinition);

        applicationContext.refresh();

        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println("bean的名称是:"+beanDefinitionName);
        }
    }
}

控制台打印:

Ainit
Binit
bean的名称是:org.Springframework.context.annotation.internalConfigurationAnnotationProcessor
bean的名称是:org.Springframework.context.annotation.internalAutowiredAnnotationProcessor
bean的名称是:org.Springframework.context.annotation.internalCommonAnnotationProcessor
bean的名称是:org.Springframework.context.event.internalEventListenerProcessor
bean的名称是:org.Springframework.context.event.internalEventListenerFactory
bean的名称是:appConfig
bean的名称是:parent
bean的名称是:a
bean的名称是:b

但是这里我们可以看到,在parentGenericBeanDefinition中我们是没有设置对应的Bean的class属性的。这里和java中的继承还是有点区别的。

因为我们没有必要为了一些共同的属性而来单独设置一个类来放这些属性。所以这里没有必要来创建一个BeanDefinition来放置对应的Class

问题二:子类BeanDefinition自定义属性

对于上面的抽象出来的公共的BeanDefinition来说,对于大部分来说,都是适用的。但是对于某些BeanDefinition来说,可能就不想使用功能来进行使用这些公共的属性。难道说我们又得去写一个BeanDefinition来适应么?不需要!!!!

而且子类不需要做任何操作!直接子当前的BeanDefinition定义中自己来写即可,如下所示,想让bGenericBeanDefinition是原型的并且实例化方法不是Init,而是create方法:

public class B {
    public void init(){
        System.out.println("Binit");
    }

    public void create(){
        System.out.println("Bcreate");
    }

    public void destroy(){
        System.out.println("Bdestroy");
    }
}



public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.register(AppConfig.class);

        GenericBeanDefinition parentGenericBeanDefinition  = new GenericBeanDefinition();
        parentGenericBeanDefinition.setInitMethodName("init");
        parentGenericBeanDefinition.setLazyInit(false);
        parentGenericBeanDefinition.setPrimary(true);
        parentGenericBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
        parentGenericBeanDefinition.setDestroyMethodName("destroy");

        parentGenericBeanDefinition.setAbstract(true);

        applicationContext.registerBeanDefinition("parent",parentGenericBeanDefinition);


        GenericBeanDefinition aGenericBeanDefinition  = new GenericBeanDefinition();
//        aGenericBeanDefinition.setInitMethodName("init");
//        aGenericBeanDefinition.setLazyInit(false);
//        aGenericBeanDefinition.setPrimary(true);
//        aGenericBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
//        aGenericBeanDefinition.setDestroyMethodName("destroy");
        aGenericBeanDefinition.setBeanClass(A.class);
        aGenericBeanDefinition.setParentName("parent");
        applicationContext.registerBeanDefinition("a",aGenericBeanDefinition);

        GenericBeanDefinition bGenericBeanDefinition  = new GenericBeanDefinition();
        bGenericBeanDefinition.setInitMethodName("create");
//        bGenericBeanDefinition.setLazyInit(false);
//        bGenericBeanDefinition.setPrimary(true);
        bGenericBeanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
//        bGenericBeanDefinition.setDestroyMethodName("destroy");
        bGenericBeanDefinition.setBeanClass(B.class);
        bGenericBeanDefinition.setParentName("parent");
        applicationContext.registerBeanDefinition("b",bGenericBeanDefinition);

        applicationContext.refresh();

        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
//        for (String beanDefinitionName : beanDefinitionNames) {
//            System.out.println("bean的名称是:"+beanDefinitionName);
//        }
    }
}

通过控制台显示:

Ainit

这个时候没有打印出来Binit方法,因为B是原型的了。所以没有在一开始就来打印。

但是发现,尽管B是原型的,但是B仍然是一个BeanDefinition。只需要打印一下当前的beanDefinitionName即可。

问题三:GenericBeanDefinition作为父BeanDefinition合适吗

是可以的,但是并不合适。因为在Spring源码中,是利用RootBeanDefinition来做为父BeanDefinition的。而子BeanDefinition在Spring中不推荐使用ChildBeanDefinition,而是推荐使用GenericBeanDefinition。

所以为了遵循Spring中的规范,我们也应该这样子来进行操作。

所以最终代码如下所示:

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.register(AppConfig.class);

        RootBeanDefinition parentGenericBeanDefinition  = new RootBeanDefinition();
        parentGenericBeanDefinition.setInitMethodName("init");
        parentGenericBeanDefinition.setLazyInit(false);
        parentGenericBeanDefinition.setPrimary(true);
        parentGenericBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
        parentGenericBeanDefinition.setDestroyMethodName("destroy");

        parentGenericBeanDefinition.setAbstract(true);

        applicationContext.registerBeanDefinition("parent",parentGenericBeanDefinition);


        GenericBeanDefinition aGenericBeanDefinition  = new GenericBeanDefinition();
//        aGenericBeanDefinition.setInitMethodName("init");
//        aGenericBeanDefinition.setLazyInit(false);
//        aGenericBeanDefinition.setPrimary(true);
//        aGenericBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
//        aGenericBeanDefinition.setDestroyMethodName("destroy");
        aGenericBeanDefinition.setBeanClass(A.class);
        aGenericBeanDefinition.setParentName("parent");
        applicationContext.registerBeanDefinition("a",aGenericBeanDefinition);

        GenericBeanDefinition bGenericBeanDefinition  = new GenericBeanDefinition();
        bGenericBeanDefinition.setInitMethodName("create");
//        bGenericBeanDefinition.setLazyInit(false);
//        bGenericBeanDefinition.setPrimary(true);
        bGenericBeanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
//        bGenericBeanDefinition.setDestroyMethodName("destroy");
        bGenericBeanDefinition.setBeanClass(B.class);
        bGenericBeanDefinition.setParentName("parent");
        applicationContext.registerBeanDefinition("b",bGenericBeanDefinition);

        applicationContext.refresh();

        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
//        for (String beanDefinitionName : beanDefinitionNames) {
//            System.out.println("bean的名称是:"+beanDefinitionName);
//        }
    }
}

问题四:BeanDefinition对应的class中都要有相同的属性值

比如说A、B中都有一个属性name,那么为了来给name进行赋值的时候,也可以在父BeanDefinition来设置值

那么这里可以使用到上面的MutablePropertyValues来进行设置

对应如下所示:

public class A {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void init(){
        System.out.println("Ainit");
    }

    public void destroy(){
        System.out.println("Adestroy");
    }
}

public class B {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void init(){
        System.out.println("Binit");
    }

    public void create(){
        System.out.println("Bcreate");
    }

    public void destroy(){
        System.out.println("Bdestroy");
    }
}

对应的测试类:

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.register(AppConfig.class);

        RootBeanDefinition parentGenericBeanDefinition  = new RootBeanDefinition();
        parentGenericBeanDefinition.setInitMethodName("init");
        parentGenericBeanDefinition.setLazyInit(false);
        parentGenericBeanDefinition.setPrimary(true);
        parentGenericBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
        parentGenericBeanDefinition.getPropertyValues().add("name","lig");
        parentGenericBeanDefinition.setDestroyMethodName("destroy");

        parentGenericBeanDefinition.setAbstract(true);

        applicationContext.registerBeanDefinition("parent",parentGenericBeanDefinition);


        GenericBeanDefinition aGenericBeanDefinition  = new GenericBeanDefinition();
//        aGenericBeanDefinition.setInitMethodName("init");
//        aGenericBeanDefinition.setLazyInit(false);
//        aGenericBeanDefinition.setPrimary(true);
//        aGenericBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
//        aGenericBeanDefinition.setDestroyMethodName("destroy");
        aGenericBeanDefinition.setBeanClass(A.class);
        aGenericBeanDefinition.setParentName("parent");
        applicationContext.registerBeanDefinition("a",aGenericBeanDefinition);

        GenericBeanDefinition bGenericBeanDefinition  = new GenericBeanDefinition();
        bGenericBeanDefinition.setInitMethodName("create");
//        bGenericBeanDefinition.setLazyInit(false);
//        bGenericBeanDefinition.setPrimary(true);
        bGenericBeanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
//        bGenericBeanDefinition.setDestroyMethodName("destroy");
        bGenericBeanDefinition.setBeanClass(B.class);
        bGenericBeanDefinition.setParentName("parent");
        applicationContext.registerBeanDefinition("b",bGenericBeanDefinition);

        applicationContext.refresh();

        A a = applicationContext.getBean(A.class);
        System.out.println(a.getName());

        B b = applicationContext.getBean(B.class);
        System.out.println(b.getName());

//        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
//        for (String beanDefinitionName : beanDefinitionNames) {
//            System.out.println("bean的名称是:"+beanDefinitionName);
//        }
    }
}

控制台打印:

Ainit
lig
Bcreate
lig

可以看到原型的bean(我理解的原型bean是从容器中的BeanDefinition来进行理解的)在获取得到的时候,也是有初始值的。

完美解决我们的问题。这里对应的是name:value的方式,那么name:ref的方式来进行解决也是同样可以来进行注入的。

问题五:父BeanDefinition可以成为一个Bean吗

可以的。这里需要考虑一下原因:如果仅仅只是为了来做属性抽取,不需要来进行实例化操作,那么可以不设置Class并设置抽象即可;但是如果当前父BeanDefinition需要作为一个模板并且也需要使用,那么将其设置为非抽象且设置class。

只要不把父BeanDefinition设置成抽象的,父BeanDefinition也是可以称为bean的。

如下所示:

parentGenericBeanDefinition.setAbstract(false);

但是需要给父BeanDefinition设置一个对应的class,让其能够实例化。

问题六:RootBeanDefinition可以设置父parentname吗

是不可以的。因为在RootBeanDefinition中的setparentname会抛出异常。

	public void setParentName(@Nullable String parentName) {
		if (parentName != null) {
			throw new IllegalArgumentException("Root bean cannot be changed into a child bean with parent reference");
		}
	}

为什么要这样子来进行设计?

我的想法是:Spring设置RootBeanDefinition就是想让其来作为一个模板,而不是想让其再做为一个子BeanDefinition。

如果想让其作为子BeanDefinition,那么还不如直接来使用GenericBeanDefinition来作为其子BeanDefinition。

所以使用RootBeanDefinition作为子BeanDefinition这样子来设置没有任何意义。

所以RootBeanDefinition可以作为父BeanDefinition且可以称为bean,或者是父BeanDefinition直接充当模板BeanDefinition。

但是唯一不可以作为子BeanDefinition。

问题七:GenericBeanDefinition可以取代RootBeanDefinition吗

因为通过上面的结论可以看到:RootBeanDefinition不能够作为子BeanDefinition存在,而ChildBeanDefinition不能够作为父BeanDefinition出现。只有GenericBeanDefinition既可以作为父BeanDefinition出现,也可以作为子BeanDefinition出现。

那么从这里看到GenericBeanDefinition是可以取代RootBeanDefinition的。但是可以从Spring源码中看到Spring最早期(Spring2.5之前)的时候是没有GenericBeanDefinition的,在Spring2.5之后才出现的。而最早期的设计中,最终父子BeanDefinition合并的时候都是需要合并成一个新的RootBeanDefinition来进行操作的。这种代码存在于Spring中太过于悠久了,如果修改,那么就会造成大量的修改。

但是设计出来GenericBeanDefinition就是处于一种折中考虑的。替换RootBeanDefinition和ChildBeanDefinition的缺陷的,但是却不能够来进行替代RootBeanDefinition。

问题八:GenericBeanDefinition怎么合并成RootBeanDefinition

问题七的衍生。通过继承体系可以看到RootBeanDefinition和GenericBeanDefinition是平级的,都是继承AbstractBeanDefinition的,那么合并的时候是怎么来进行合并的?

下面来写伪代码来进行解释:

RootBeanDefinition rootBeanDefinition = merge(GenericBeanDefinition){
    
    RootBeanDefinition rootBeanDefinition = new RootBeanDefinition()
    rootBeanDefinition.setXxx(GenericBeanDefinition.getXXX())
    return rootBeanDefinition;
}

这里也解释了上面为什么合并成一个新的RootBeanDefinition了。

当然,我想还有一个原因,就是不能够破坏原有的BeanDefinition,因为原有的BeanDefinition可能也被其他的BeanDefinition来进行合并

所以处于这点考虑,原来的BeanDefinition保留,直接生成一个新的RootBeanDefinition是最为合适的。

问题九:BeanDefinition中的class可以为接口吗

可以的,因为BeanDefinition是用来描述bean的,但是不是所有的BeanDefinition都可以作为模板来生成bean。

如果说BeanDefinition添加了abstract为true的BeanDefinition,就是无法来创建出来bean的。

所以说对于接口或者还是具体类来说,没有特别的地方,关键是看具体的实现。

虽然class是接口的时候不能够创建对象,但是可以通过动态代理来为其设置一个对象。这也是mybatis能够来进行实现的原因。

5、不同方式解析得到不同的BeanDefinition

首先通过xml方式来解析bean看看对应的BeanDefinition是哪种BeanDefinition;然后看看注解解析的是哪种BeanDefinition。

xml解析BeanDefinition

测试:

public class A {
}

public class B {
}

public class C {
}

对应的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 http://www.Springframework.org/schema/beans/Spring-beans.xsd"


    default-autowire="byType"
>

    <bean id="a" class="com.guang.beandefinitaiontest.demo3.service.A" abstract="true">

    </bean>

    <bean id="b" class="com.guang.beandefinitaiontest.demo3.service.B" parent="a">

    </bean>

    <bean id="c" class="com.guang.beandefinitaiontest.demo3.service.C" >

    </bean>

</beans>

测试:

public class Test {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("mySpring.xml");
        ConfigurableListableBeanFactory beanFactory = classPathXmlApplicationContext.getBeanFactory();
        BeanDefinition a = beanFactory.getBeanDefinition("a");
        System.out.println("a="+a.getClass().getSimpleName());
        BeanDefinition b = beanFactory.getBeanDefinition("b");
        System.out.println("b="+b.getClass().getSimpleName());
        BeanDefinition c = beanFactory.getBeanDefinition("c");
        System.out.println("c="+c.getClass().getSimpleName());
    }
}

控制台打印:

a=GenericBeanDefinition
b=GenericBeanDefinition
c=GenericBeanDefinition

可以看到通过xml解析的都是GenericBeanDefinition

注解解析BeanDefinition

首先得明确一点,注解解析的是有两种:@Configuration和@Component以及@Bean这三种常见方式。

public class MyService {

}

@Configuration
@ComponentScan("com.guang.beandefinitaiontest.demo4")
public class AppConfig {


    @Bean
    public MyService myService(){
        return new MyService();
    }
}


@Component
public class UserService {

}

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        ConfigurableListableBeanFactory beanFactory = annotationConfigApplicationContext.getBeanFactory();
        BeanDefinition appConfig = beanFactory.getBeanDefinition("appConfig");
        System.out.println("配置类对应的BeanDefinition是:"+appConfig.getClass().getSimpleName());
        BeanDefinition userService = beanFactory.getBeanDefinition("userService");
        System.out.println("@Componnent对应的BeanDefinition是:"+userService.getClass().getSimpleName());

        BeanDefinition myService = beanFactory.getBeanDefinition("myService");
        System.out.println("@Bean对应的BeanDefinition是:"+myService.getClass().getSimpleName());
    }
}

控制台:

配置类对应的BeanDefinition是:AnnotatedGenericBeanDefinition
@Componnent对应的BeanDefinition是:ScannedGenericBeanDefinition
@Bean对应的BeanDefinition是:ConfigurationClassBeanDefinition

可以发现@Bean对应的是ConfigurationClassBeanDefinition,@Componnent的为ScannedGenericBeanDefinition,而配置类的为AnnotatedGenericBeanDefinition。

所以下面就是来分为三个常用的BeanDefinition的区别

AnnotatedGenericBeanDefinition

这个要做一个额外说明:这个类是继承了GenericBeanDefinition、AnnotatedBeanDefinition两个接口

AnnotatedGenericBeanDefinition这个类只是针对特殊的注解(@Configuration或者是@ComponentScan)来进行生成当前对象。

特殊含义

这个类在spring体系中承担了一个特殊的角色,对于spring来说,扫描的类默认是ScannedGenericBeanDefinition。

而通过下面的代码将其注册成为一个特殊的BeanDefinition。这个类spring设计的初衷是:在使用注解情况下,不通过扫描,也能够得到一个BeanDefinition。但是程序员也可以手动的来注册一个这样的一个BeanDefinition,但是查找spring的API发现,要想实现这样的一个BeanDefinition,要继承接口,实现大量的方法,所以spring直接通过一个API接口,开放给程序员来进行使用,从而减少了冗余代码,而让代码看起来更加的简洁。

那么看一下我们的注解启动类方法中的AnnotationConfigApplicationContext的无参构造方法:

public AnnotationConfigApplicationContext() {

    StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("Spring.context.annotated-bean-reader.create");
    // 在这里直接创建了一个AnnotatedBeanDefinitionReader对象的的解析器来解析
    // @Configuration
    this.reader = new AnnotatedBeanDefinitionReader(this);

    createAnnotatedBeanDefReader.end();

    this.scanner = new ClassPathBeanDefinitionScanner(this);
}

然后再看一下register方法

	public void register(Class<?>... componentClasses) {
		Assert.notEmpty(componentClasses, "At least one component class must be specified");
		StartupStep registerComponentClass = this.getApplicationStartup().start("Spring.context.component-classes.register").tag("classes", () -> Arrays.toString(componentClasses));
        // 直接利用reader来进行注册
		this.reader.register(componentClasses);
		registerComponentClass.end();
	}

原因是在register方法中直接创建了一个AnnotatedGenericBeanDefinition来盛放这里的内容。

// 关键代码所在位置
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);

所以使用register方法可以让一个类创建得到一个AnnotatedGenericBeanDefinition。

这就是spring提供的一个API操作,简单方便。

但是从这里也可以看到,获取得到容器有两种写法

第一种:

AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(AppConfig.class);

第二种:

        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(AppConfig.class);
        context.refresh();

AnnotatedBeanDefinitionReader不光可以解析AnnotatedBeanDefinition,还可以用来解析GenericBeanDefinition。

因为AnnotatedGenericBeanDefinition是GenericBeanDefinition的子类,那么子类中就有父类中所有的解析方式。

所以要是想要快速注册一个bean,那么也可以直接使用register来注册一个BeanDefinition,让AnnotatedBeanDefinitionReader来对其做解析。

测试一下:

@Configuration
@ComponentScan("com.guang.beandefinitaiontest.demo5")
public class AppConfig {

}

public class LgService {

}

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(AppConfig.class);
        context.registerBean(LgService.class);
        context.refresh();

        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        String lgService = beanFactory.getBeanDefinition("lgService").getClass().getSimpleName();
        System.out.println(lgService);
    }
}

控制台打印结果:

AnnotatedGenericBeanDefinition

虽然说我们可以通过context来注册一个AnnotatedGenericBeanDefinition,但是这是Spring不推荐的使用方式。

因为Spring针对AnnotatedGenericBeanDefinition对应的类是想让其成为一个配置类的,但是不是配置类的类让我们给改装成成了一个AnnotatedGenericBeanDefinition。不合理

那么来看一下二者区别,严格来说基本上是没有区别的。

public class ScannedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition {

	private final AnnotationMetadata metadata;


	/**
	 * Create a new ScannedGenericBeanDefinition for the class that the
	 * given MetadataReader describes.
	 * @param metadataReader the MetadataReader for the scanned target class
	 */
	public ScannedGenericBeanDefinition(MetadataReader metadataReader) {
		Assert.notNull(metadataReader, "MetadataReader must not be null");
		this.metadata = metadataReader.getAnnotationMetadata();
		setBeanClassName(this.metadata.getClassName());
		setResource(metadataReader.getResource());
	}

	// 注解版的有元信息描述,xml和@Configuration应该也有
	@Override
	public final AnnotationMetadata getMetadata() {
		return this.metadata;
	}

    // 注解类的扫描是没有FactoryMethod的,只有xml方式才有
    // 所以GenericBeanDefinition中才有这个的实现(GenericBeanDefinition父类中有)
	@Override
	@Nullable
	public MethodMetadata getFactoryMethodMetadata() {
		return null;
	}

}
区别

其实本质上没有太大的区别,只有细微上的差距。

如果说上面的FactoryMethod就只有xml解析的才会有,而其他类型方式的BeanDefinition没有。

再比如:AnnotationMetadata元信息的区别

1、ScannedGenericBeanDefinition是将metadata存在AnnotationMetadata中;
2、AnnotatedGenericBeanDefinition是将元信息存在StandardAnnotationMetadata中

但是注解版的使用过程中,为什么要使用AnnotatedGenericBeanDefinition呢?

本质原因是要利用AnnotatedBeanDefinitionReader将@ComponentScan中的信息读取出来,要扫描哪些包下面的哪些类。

而对于ScannedGenericBeanDefinition来说,是在扫描阶段才需要使用到的,所以这也是一个区别。

只有先利用AnnotatedBeanDefinitionReader解析@ComponentScan中需要扫描的包之后,才可能知道要去扫描哪些类,将哪个bean扫描添加称为bean。

作用

如果对于个别类来说,不需要经过扫描,但是又想将其编程BeanDefinition,就可以利用AnnotatedBeanDefinitionReader来注册一个BeanDefinition。

所以在配置容器的第一行中有resgister方法,方便程序员更加方便的来添加一个BeanDefinition

但是如果用AnnotatedGenericBeanDefinition来做的话,看看代码量:

AnnotatedGenericBeanDefinition annotatedGenericBeanDefinition  = new AnnotatedGenericBeanDefinition(AppConfig.class);
annotatedGenericBeanDefinition.setBeanClass(LgService.class);

那么Spring也做了这个考虑,干脆直接提供AnnotatedBeanDefinitionReader来帮助我们来生成BeanDefinition,简化代码量和配置。

这里注意一下,对于AnnotatedBeanDefinition来说,这里设计的初衷就是为了仅仅针对配置类来进行设计的,而不是针对普通的类

最终生成AnnotatedBeanDefinition。

所以研究一下register方法之后,发现本身还是为我们提供了一个配置类的BeanDefinition。

7、启动类的代码再次说明

代码如下所示:

AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(AppConfig.class);

然后走到:

	public AnnotationConfigApplicationContext() {
        // 创建容器
        super();
		this.reader = new AnnotatedBeanDefinitionReader(this);
		createAnnotatedBeanDefReader.end();
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

那么看一下这两行new的代码:

this.reader = new AnnotatedBeanDefinitionReader(this);

让Spring内置的类称为BeanDefinition,开天辟地的五个BeanDefinition。

对于

this.scanner = new ClassPathBeanDefinitionScanner(this);

在创建的时候会向容器中来放入默认的过滤规则:

	public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
			Environment environment, @Nullable ResourceLoader resourceLoader) {

		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		this.registry = registry;
		// 使用默认规则
		if (useDefaultFilters) {
            // Mybatis可以利用这个
			registerDefaultFilters();
		}
		setEnvironment(environment);
		setResourceLoader(resourceLoader);
	}

接着来看:

public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
   this();
   register(componentClasses);
   refresh();
}

将配置类生成BeanDefinition,方面下面解析。

对于regiester方法来说:

	public void register(Class<?>... componentClasses) {
		for (Class<?> componentClass : componentClasses) {
			registerBean(componentClass);
		}
	}

继续看:

private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
                                @Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
                                @Nullable BeanDefinitionCustomizer[] customizers) {

  AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
  if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
    return;
  }

  abd.setInstanceSupplier(supplier);
  ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
  abd.setScope(scopeMetadata.getScopeName());
  String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));

  AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
  if (qualifiers != null) {
    for (Class<? extends Annotation> qualifier : qualifiers) {
      if (Primary.class == qualifier) {
        abd.setPrimary(true);
      }
      else if (Lazy.class == qualifier) {
        abd.setLazyInit(true);
      }
      else {
        abd.addQualifier(new AutowireCandidateQualifier(qualifier));
      }
    }
  }
  if (customizers != null) {
    for (BeanDefinitionCustomizer customizer : customizers) {
      customizer.customize(abd);
    }
  }

  BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
  definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
  BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}

最后一行代码就是来注册当前的类让其称为一个BeanDefinition到BeanDefinitionMap中来。

配置类的特殊性

@ComponentScan的功能可以理解成就是为了来进行扫描的,Spring不允许来扫描自己,不然就容易造成递归。

扫描--->解析--->扫描--->解析...........

那么这样子的存在是毫无意义的。

而@Configuration又是和@ComponentScan来组合使用的,因为在添加到容器中的时候,Spring就已经将自己注册进去了。

8、总结

1、BeanDefinition的作用、生成方式、每种BeanDefinition的特性和区别、提取公共、设置公共参数和构造值和注意事项;

2、AnnotatedBeanDefinitionReader的作用,添加配置类成为AnnotatedBeanDefinition,让Spring内置的类成为BeanDefinition并扫描当前包。因为本身作为了一个配置类,同时也是为了添加上一个扫描包的路径。那么说明本身是无法扫描到自身的(防止出现递归现象,扫描自己又解析自己,然后不断的重复);

3、ClassPathBeanDefinitionScanner设置扫描规则并让包下的有@Component注解的类在满足条件(看具体规则)后成为BeanDefinition

使用@Component注解标注的类解析出来的是ScannedGenericBeanDefinition;

使用@Configuration配置类+@Bean解析出来的是ScannedGenericBeanDefinition;

posted @ 2022-08-17 20:15  写的代码很烂  阅读(8)  评论(0编辑  收藏  举报