曹工說Spring Boot源碼(3)-- 手動注冊Bean Definition不比游戲好玩嗎,我們來試一下


寫在前面的話

相關背景及資源:

曹工說Spring Boot源碼系列開講了(1)-- Bean Definition到底是什么,附spring思維導圖分享

工程代碼地址 思維導圖地址

工程結構圖:

大體思路

  1. 選擇bean definition實現類,並實例化bean definition

  2. 注冊bean definition

  3. get bean查看是否work

選擇bean definition實現類

這次,先說目的:我們要通過代碼方式手動生成bean definition並注冊到bean factory。

我的思路是這樣的,既然前面兩節,分析了bean definition接口中的各個方法,也算對其有了基本的了解了。但

org.springframework.beans.factory.config.BeanDefinition只是一個接口,接口是不能實例化的,也無從談起注冊了。

我們從bean definition的實現類中選一個吧:

非抽象的實現類主要有以下三個:

  1. org.springframework.beans.factory.support.GenericBeanDefinition:幸運兒,被我們選中的,也是官方推薦的,注釋里提到可以動態設置GenericBeanDefinition的parent bean definition的名稱;

    這個呢,org.springframework.beans.factory.support.RootBeanDefinitionorg.springframework.beans.factory.support.ChildBeanDefinition也能實現bean的繼承關系,但是可能這種預先定義一個bean為child/parent的方式,太死了。

    官方自己在ChildBeanDefinition的注釋里寫到:

    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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM