zoukankan      html  css  js  c++  java
  • 曹工说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

  • 相关阅读:
    Spark小课堂Week3 FirstSparkApp(RDD开发)
    Catalyst揭秘 Day5 optimizer解析
    Spark小课堂Week2 Hello Streaming
    Spark小课堂Week1 Hello Spark
    Catalyst揭秘 Day4 analyzer解析
    Catalyst揭秘 Day3 sqlParser解析
    Catalyst揭秘 Day2 Catalyst源码初探
    Catalyst揭秘 Day1 Catalyst本地解析
    java泛型
    java中数组以及集合
  • 原文地址:https://www.cnblogs.com/grey-wolf/p/12070377.html
Copyright © 2011-2022 走看看