Cao worker said Spring Boot source (3) - Manual registration Bean Definition gaming fun than you, let's try

EDITORIAL words

Background and resources:

Cao worker said the lecture series Spring Boot source (1) - Bean Definition what in the end is attached spring Mind Map Share

Project code address Mind Mapping address

Engineering Schematic:

General idea

  1. Select bean definition implementation class and instantiate bean definition

  2. Sign bean definition
  3. get bean to see if work

Select bean definition implementation class

This time, saying the purpose: we want to generate bean definition by way of code manually and registered with the bean factory.

My thinking is that, since the previous two, analyzed the various methods bean definition interface, it can be considered to have a basic understanding of it. but

org.springframework.beans.factory.config.BeanDefinitionJust an interface, the interface can not be instantiated, but also out of the question registered.

We realized from the bean definition of a class selected it:

Non-abstract implementation class mainly in the following three:

  1. org.springframework.beans.factory.support.GenericBeanDefinition: Winners, selected by us, but also the official recommendation, comments can be set dynamically mentioned GenericBeanDefinitionthe name of the parent bean definition;

    This does, org.springframework.beans.factory.support.RootBeanDefinitionand org.springframework.beans.factory.support.ChildBeanDefinitioncan also be achieved inheritance bean, but it is likely that this bean as a pre-defined child/parentway, too dead.

    Official yourself in ChildBeanDefinitionthe comments writes that:

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

    注意最后那句话,supresede这个单词我还他么不太认识,专门查了下词典,意思是取代、代替,那这句话意

    思就是,大部分时候,GenericBeanDefinition取代了ChildBeanDefinition的作用。

    这个下面有两个子类,之前也提过,主要是供那种通过注解方式,比如@controller这种扫描进来的bean definition。

  2. org.springframework.beans.factory.support.ChildBeanDefinition,官方都不建议用了,直接跳过吧;

  3. org.springframework.beans.factory.support.RootBeanDefinition,在@configuration中有用,后面再讲

基于上面的思路,我们选了GenericBeanDefinition,这个类可以直接new,new了之后再通过set方法设置beanClassName等。

public class GenericBeanDefinition extends AbstractBeanDefinition {

    private String parentName;


    /**
     * 无参构造函数,但是你看到下面那一堆set方法了吧,就是让你自己设
     * Create a new GenericBeanDefinition, to be configured through its bean
     * properties and configuration methods.
     * @see #setBeanClass
     * @see #setBeanClassName
     * @see #setScope
     * @see #setAutowireMode
     * @see #setDependencyCheck
     * @see #setConstructorArgumentValues
     * @see #setPropertyValues
     */
    public GenericBeanDefinition() {
        super();
    }
}

还有一个方式是,我们看看框架里怎么用的,经过我一番搜索,

发现框架里,主要使用了org.springframework.beans.factory.support.BeanDefinitionBuilder

org.springframework.beans.factory.support.BeanDefinitionReaderUtils,而且,框架里,还是前者用的多,也比较方便(后面有示例代码)。

注册bean definition

然后,知道怎么构造GenericBeanDefinition了,那么要怎么注册呢,这个也简单,我们看看beanFactory

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
        implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable 

不只实现了ConfigurableListableBeanFactory,还实现了BeanDefinitionRegistry

public interface BeanDefinitionRegistry extends AliasRegistry {

    /**
     * 注册beanDefinition,要自己指定beanName
     * Register a new bean definition with this registry.
     * Must support RootBeanDefinition and ChildBeanDefinition.
     * @param beanName the name of the bean instance to register
     * @param beanDefinition definition of the bean instance to register
     * @throws BeanDefinitionStoreException if the BeanDefinition is invalid
     * or if there is already a BeanDefinition for the specified bean name
     * (and we are not allowed to override it)
     * @see RootBeanDefinition
     * @see ChildBeanDefinition
     */
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException;
    ...
}

所以,我们只要调用org.springframework.beans.factory.support.DefaultListableBeanFactory的注册方法即可。

这里说下beanNameGenerator,一开始我用的org.springframework.beans.factory.support.DefaultBeanNameGenerator,结果生成的bean的名称是这样的:

org.springframework.simple.TestService#0,这和我们平时使用autowired方式,生成的beanName不一样啊,不习惯。于是改成了org.springframework.context.annotation.AnnotationBeanNameGenerator,就对了!

如何表达bean间依赖

这里先介绍两种方式,分别是构造器注入和property注入。对了,先不要和我提什么autowired哈,那个是自动,这个呢,手动。也许,后面你会更懂autowired,也更懂自动。

构造器注入

核心代码:


@ToString
public class TestControllerByConstructor {

    TestService testService;

    /**
     * 基本类型依赖
     */
    private String name;


    public TestControllerByConstructor(TestService testService, String name) {
        this.testService = testService;
        this.name = name;
    }

    public TestService getTestService() {
        return testService;
    }

    public String getName() {
        return name;
    }
}
package org.springframework.simple;

import lombok.ToString;

@ToString
public class TestService implements ITestService{
}
/**
 * 2. 构造bean definition,并在bean definition中表达bean之间的依赖关系
 */
GenericBeanDefinition iTestServiceBeanDefinition = (GenericBeanDefinition) BeanDefinitionBuilder
                .genericBeanDefinition(TestService.class).getBeanDefinition();
log.info("iTestServiceBeanDefinition:{}",iTestServiceBeanDefinition);

GenericBeanDefinition iTestControllerBeanDefinition = (GenericBeanDefinition) BeanDefinitionBuilder
    .genericBeanDefinition(TestControllerByConstructor.class)
    // 这里,看这里,这里在表达依赖了
    .addConstructorArgReference("testService")
    .addConstructorArgValue("wire by constructor")
    .getBeanDefinition();

完整代码:

package org.springframework.simple.byconstructor;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.support.*;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.simple.ITestService;
import org.springframework.simple.TestService;
import org.springframework.util.Assert;

@Slf4j
public class ManualRegisterBeanDefinitionDemoByConstructor {
    public static void main(String[] args) {
        wireDependencyByConstructor();
    }


    /**
     * 通过构造器的方式来注入依赖
     */
    private static void wireDependencyByConstructor() {
        /**
         * 1:生成bean factory
         */
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        /**
         * 2. 构造bean definition,并在bean definition中表达bean之间的依赖关系
         */
        GenericBeanDefinition iTestServiceBeanDefinition = (GenericBeanDefinition) BeanDefinitionBuilder
                .genericBeanDefinition(TestService.class).getBeanDefinition();
        log.info("iTestServiceBeanDefinition:{}",iTestServiceBeanDefinition);

        GenericBeanDefinition iTestControllerBeanDefinition = (GenericBeanDefinition) BeanDefinitionBuilder
                .genericBeanDefinition(TestControllerByConstructor.class)
                .addConstructorArgReference("testService")
                .addConstructorArgValue("wire by constructor")
                .getBeanDefinition();


        /**
         * 3. 注册bean definition
         */
//        DefaultBeanNameGenerator generator = new DefaultBeanNameGenerator();
        AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
        String beanNameForTestService = generator.generateBeanName(iTestServiceBeanDefinition, factory);
        factory.registerBeanDefinition(beanNameForTestService, iTestServiceBeanDefinition);

        String beanNameForTestController = generator.generateBeanName(iTestControllerBeanDefinition, factory);
        factory.registerBeanDefinition(beanNameForTestController, TestControllerBeanDefinition);

        /**
         * 4. 获取bean
         */
        TestControllerByConstructor bean = factory.getBean(TestControllerByConstructor.class);
        log.info("TestControllerByConstructor:{}",bean);

        ITestService testService = factory.getBean(ITestService.class);
        log.info("testService bean:{}",testService);

        Assert.isTrue(bean.getTestService() == testService);
    }
}

property注入

原理类似,核心代码不同之处如下:

        GenericBeanDefinition iTestControllerBeanDefinition = (GenericBeanDefinition) BeanDefinitionBuilder
                .genericBeanDefinition(TestControllerWireByProperty.class)
                // 这里是调用的property相关方法
                .addPropertyReference("t","testService")
                .addPropertyValue("name","just test")
                .getBeanDefinition();

总结

今天就到这里,有问题请指出哈,欢迎大家和我一起阅读spring boot源码。

源码地址:

https://gitee.com/ckl111/spring-boot-first-version-learn/tree/master/all-demo-in-spring-learning/spring-manual-register-bean-definition

Guess you like

Origin www.cnblogs.com/grey-wolf/p/12070377.html