Spring之BeanDefinition(二)

Spring之BeanDefinition

文章目录

  • Spring之BeanDefinition
    • 1、对象和bean的区别
    • 2、BeanDefinition作用
      • AutowireCandidate说明
      • Primary说明
      • ConstructorArgumentValues说明
        • 第一种使用方式
        • 第二种使用方式
      • MutablePropertyValues
      • abstract
      • 小结
    • 3、BeanDefinition的发展历程
    • 3、BeanDefinition继承体系
      • AttributeAccessor
      • BeanMetadataElement
      • AbstractBeanDefinition
        • 小结
      • 利用GenericBeanDefinition创建BeanDefinition
        • 问题一:BeanDefinition属性冗余
        • 问题二:子类BeanDefinition自定义属性
        • 问题三:GenericBeanDefinition作为父BeanDefinition合适吗
        • 问题四:BeanDefinition对应的class中都要有相同的属性值
        • 问题五:父BeanDefinition可以成为一个Bean吗
        • 问题六:RootBeanDefinition可以设置父parentname吗
        • 问题七:GenericBeanDefinition可以取代RootBeanDefinition吗
        • 问题八:GenericBeanDefinition怎么合并成RootBeanDefinition
        • 问题九:BeanDefinition中的class可以为接口吗
    • 5、不同方式解析得到不同的BeanDefinition
        • 5.1、xml解析BeanDefinition
        • 5.2、注解解析BeanDefinition
          • 5.2.1、AnnotatedGenericBeanDefinition
            • 特殊含义
          • 区别
          • 作用
    • 7、启动类的代码再次说明
      • 配置类的特殊性
    • 8、总结

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;// 原型beanString 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@NullableString getParentName();// 设置当前bean的class对应的全限定类名void setBeanClassName(@Nullable String beanClassName);// 获取当前bean的class对应的全限定类名String getBeanClassName();// 设置当前bean作用域void setScope(@Nullable String scope);// 获取得到当前bean的作用域String getScope();// 设置当前bean是否是懒加载的beanvoid setLazyInit(boolean lazyInit);// 获取boolean isLazyInit();// 当前bean是否依赖了其他bean,如果依赖了,优先加载其他beanvoid setDependsOn(@Nullable String... dependsOn);// 获取得到当前bean所依赖其他bean的名称String[] getDependsOn();// 设置是否作为bean的一个候选者。也就是说是否是其他bean的所依赖的beanvoid 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();// 得到原始的BeanDefinitionBeanDefinition 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 {@Nullableprivate 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@Nullablepublic 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*/@Nullablepublic BeanMetadataAttribute getMetadataAttribute(String name) {return (BeanMetadataAttribute) super.getAttribute(name);}@Overridepublic void setAttribute(String name, @Nullable Object value) {super.setAttribute(name, new BeanMetadataAttribute(name, value));}@Override@Nullablepublic Object getAttribute(String name) {BeanMetadataAttribute attribute = (BeanMetadataAttribute) super.getAttribute(name);return (attribute != null ? attribute.getValue() : null);}@Override@Nullablepublic 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。

5.1、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

5.2、注解解析BeanDefinition

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

public class MyService {}@Configuration
@ComponentScan("com.guang.beandefinitaiontest.demo4")
public class AppConfig {@Beanpublic 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,而**@Configuration注解对应的为AnnotatedGenericBeanDefinition**。

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

5.2.1、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对象的的解析器来解析// @Configurationthis.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应该也有@Overridepublic final AnnotationMetadata getMetadata() {return this.metadata;}// 注解类的扫描是没有FactoryMethod的,只有xml方式才有// 所以GenericBeanDefinition中才有这个的实现(GenericBeanDefinition父类中有)@Override@Nullablepublic MethodMetadata getFactoryMethodMetadata() {return null;}}
区别

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

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

再比如:AnnotationMetadata元信息的区别

1ScannedGenericBeanDefinition是将metadata存在AnnotationMetadata中;
2AnnotatedGenericBeanDefinition是将元信息存在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;****

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/19067.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

pve安装ikuai并设置,同时把pve的网络连接到ikuai虚拟机

目录 前因 前置条件 安装ikuai 进入ikuai的后台 配置lan口&#xff0c;以及wan口 配置lan口桥接 按实际情况来设置了 单拨&#xff08;PPOE拨号&#xff09; 多拨(内外网设置点击基于物理网卡的混合模式) 后续步骤 pve连接虚拟机ikuai的网络以及其他虚拟机连接ikuai的网…

Arcgis地图实战一:单个图层中设施的隐藏及显示

文章目录 1.效果图预览2.弹框的实现3.显示及隐藏的实现 1.效果图预览 2.弹框的实现 let alert this.alertCtrl.create();alert.setTitle(请选择设施);for (let item of this.ctralllayers) {alert.addInput({type: checkbox,label: item.name,value: item.id,checked: item.vi…

什么是线程?为什么需要线程?和进程的区别?

目录 前言 一.线程是什么&#xff1f; 1.1.为什么需要线程 1.2线程的概念 1.3线程和进程的区别 二.线程的生命周期 三.认识多线程 总结 &#x1f381;个人主页&#xff1a;tq02的博客_CSDN博客-C语言,Java,Java数据结构领域博主 &#x1f3a5; 本文由 tq02 原创&#xf…

ChatGPT能否撰写科研论文?

ChatGPT&#xff0c;这款被许多人誉为语言处理领域的“黑马”&#xff0c;究竟能否应用于撰写科研论文&#xff1f;近期&#xff0c;以色列理工学院生物学家兼数据科学家Roy Kishony带领的团队&#xff0c;针对这一问题进行了系列研究&#xff0c;其结果已在《Nature》杂志上发…

Andorid解析XML格式数据遇到的坑

以下是《第一行代码 第三版》解析XML格式数据部分遇到的坑 一、首先是安装Apache遇到的坑 具体参考文章Apache服务器下载安装及使用&#xff08;更新&#xff09;_apache下载_★邱↓邱★的博客-CSDN博客&#xff08;可以不看文中的安装部分了&#xff09; 启动服务那块儿建议…

面试总结-Redis篇章(十一)——分片集群、数据读写规则

分片集群、数据读写规则 主从&#xff08;解决高并发&#xff09;和哨兵&#xff08;解决高可用&#xff09;分别解决了高并发读、高可用的问题。但是依然有两个问题没有解决&#xff1a;解决办法&#xff1a;使用分片集群可以解决上述问题。 特征&#xff1a;客户端请求可以访…

echars力引导关系图

效果图 力引导关系图 力引导布局是模拟弹簧电荷模型在每两个节点之间添加一个斥力&#xff0c;每条边的两个节点之间添加一个引力&#xff0c;每次迭代节点会在各个斥力和引力的作用下移动位置&#xff0c;多次迭代后节点会静止在一个受力平衡的位置&#xff0c;达到整个模型…

AD21 PCB设计的高级应用(九)3D PDF的输出

&#xff08;九&#xff09;3D PDF的输出 1.3D PDF的输出2.制作PCB 3D视频 1.3D PDF的输出 Altium Designer 19 带有 3D输出功能,能够直接将 PCB 的 3D效果输出到 PDF 中。 ’(1)打开带有 3D 模型的 PCB 文件,执行菜单栏中“文件”→“导出”→“PDF3D”命令&#xff0c;选择…

IDEA中Git面板操作介绍 变基、合并、提取、拉取、签出

IDEA中Git面板操作介绍 变基、合并、提取、拉取、签出 面板介绍 变基、合并 提取、拉取 签出、Checkout 面板介绍 如图&#xff0c;在IDEA的Git面板中&#xff0c;仓库会分为本地仓库和远程仓库&#xff0c;代码仓库里面放的是各个分支。 分支前面的书签&#x1f516;标志…

Python:列表(list)与元组(tuple)

列表与元组 列表&#xff1a;list元组&#xff1a;tuple 比较直观的区分&#xff1a;列表是中括号"[ ]“&#xff0c;元组是小括号”( )"元组可以看成列表的只读形式 # 列表 list1 [hello, world] list2 [1, 2, 3, 4, 5] list3 ["a", "b", &…

【Redis】内存数据库Redis进阶(Redis哨兵集群)

目录 分布式缓存 Redis 四大问题搭建Redis哨兵集群哨兵原理Redis哨兵集群小结RedisTemplate集成哨兵机制 分布式缓存 Redis 四大问题 基于 Redis 集群解决单机 Redis 存在的四大问题&#xff1a; 搭建Redis哨兵集群 搭建一个三节点形成的 Sentinel 集群&#xff0c;来监管 R…

git使用(由浅到深)

目录流程图 1. 分布式版本控制与集中式版本控制 1.1 集中式版本控制 集中式版本控制系统有:CVS和SVN它们的主要特点是单一的集中管理的服务器&#xff0c;保存所有文件的修订版本&#xff1b;协同开发人员通过客户端连接到这台服务器&#xff0c;取出最新的文件或者提交更新…

Java小型操作系统模拟(采用策略模式结合反射进行搭建,支持一些简单的命令)

Java小型操作系统模拟 项目说明第一阶段&#xff1a;反射结合策略模式搭建基本的命令结构第二阶段&#xff1a;注解结合反射与策略模式&#xff0c;将结构进一步规范第三阶段&#xff1a;开启新的窗口&#xff0c;将控制台输入切换到新窗口中&#xff0c;同时创建右键菜单&…

【雕爷学编程】MicroPython动手做(27)——物联网之掌控板小程序3

知识点&#xff1a;什么是掌控板&#xff1f; 掌控板是一块普及STEAM创客教育、人工智能教育、机器人编程教育的开源智能硬件。它集成ESP-32高性能双核芯片&#xff0c;支持WiFi和蓝牙双模通信&#xff0c;可作为物联网节点&#xff0c;实现物联网应用。同时掌控板上集成了OLED…

Stable Diffusion AI绘画学习指南【本地环境搭建win+mac】

一、硬件配配置要求 系统&#xff1a;windows 10 / Mac os 硬盘&#xff1a;C 盘预留 15GB 以上&#xff0c;其他盘 50GB 以上,Stable Ddiffusion的很多大模型都是以 GB 起步。 显卡&#xff1a;4GB 以上&#xff0c;建议 8GB, 效率高&#xff0c;能玩大尺寸的图 CPU&…

微信小程序(van-tabs) 去除横向滚动条样式(附加源码解决方案+报错图)

问题描述 今天第一次接触vant组件库。 ant官网地址适用于Vue3 支持Vue2、Vue3、微信小程序等 我在使用van-tabs组件时遇到了一个问题&#xff0c;如下图所示&#xff1a; 从图片上可以看到有个灰色的横向滚动条&#xff0c;一开始领导给我说这个问题&#xff0c;我反反复复都…

C++ ——stack、queue容器模拟实现及deque容器底层介绍

deque文档 stack文档 deque文档 文章目录 &#x1f345;1. deque容器&#x1f352;deque底层&#x1f352;deque的优势&#x1f352;deque的劣势 &#x1fad0;2. stack模拟实现&#x1f95d;3. queue模拟实现 &#x1f345;1. deque容器 查看文档可发现&#xff0c;栈和队列都…

Netty自定义消息协议的实现逻辑处理粘包拆包、心跳机制

Netty 自定义消息协议的实现逻辑自定义编码器 心跳机制实现客户端发送心跳包 自定义消息协议的实现逻辑 消息协议&#xff1a;这一次消息需要包含两个部分&#xff0c;即消息长度和消息内容本身。 自定义消息编码器︰消息编码器将客户端发送的消息转换成遵守消息协议的消息&…

Linux第一个小程序-进度条(缓冲区概念)

1.\r和\n C语言中有很多字符 a.可显字符 b.控制字符 对于回车其实有两个动作&#xff0c;首先换行&#xff0c;在将光标指向最左侧 \r &#xff1a;回车 \n&#xff1a;换行 下面举个例子&#xff1a; 把\n去掉会怎样 什么都没输出。为什么&#xff1f; 2.缓冲区概念 观察下两个…