欢迎光临
我们一直在努力

曹工说Spring Boot源码(3)-- 手动注册Bean Definition不比游戏好玩吗,我们来试一下

写在前面的话

相关背景及资源:

曹工说Spring Boot源码系列开讲了(1)-- Bean Definition到底是什么,附spring思维导图分享

工程代码地址 思维导图地址

工程结构图:

曹工说Spring Boot源码(3)-- 手动注册Bean Definition不比游戏好玩吗,我们来试一下

大体思路

  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的实现类中选一个吧:曹工说Spring Boot源码(3)-- 手动注册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(); } } 

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

曹工说Spring Boot源码(3)-- 手动注册Bean Definition不比游戏好玩吗,我们来试一下

发现框架里,主要使用了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

  • 海报
海报图正在生成中...
赞(1) 打赏
声明:
1、本博客不从事任何主机及服务器租赁业务,不参与任何交易,也绝非中介。博客内容仅记录博主个人感兴趣的服务器测评结果及一些服务器相关的优惠活动,信息均摘自网络或来自服务商主动提供;所以对本博客提及的内容不作直接、间接、法定、约定的保证,博客内容也不具备任何参考价值及引导作用,访问者需自行甄别。
2、访问本博客请务必遵守有关互联网的相关法律、规定与规则;不能利用本博客所提及的内容从事任何违法、违规操作;否则造成的一切后果由访问者自行承担。
3、未成年人及不能独立承担法律责任的个人及群体请勿访问本博客。
4、一旦您访问本博客,即表示您已经知晓并接受了以上声明通告。
文章名称:《曹工说Spring Boot源码(3)-- 手动注册Bean Definition不比游戏好玩吗,我们来试一下》
文章链接:https://www.456zj.com/40966.html
本站资源仅供个人学习交流,请于下载后24小时内删除,不允许用于商业用途,否则法律问题自行承担。

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址