写在前面的话

相关背景及资源:

曹工说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

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

  1. 曹工说Spring Boot源码(2)-- Bean Definition到底是什么,咱们对着接口,逐个方法讲解

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码系列开讲了(1)-- Bean Definition到底是什么,附spring思维导图分享 工程代码地址 思维导图地址 工程结构图: 正 ...

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

    写在前面的话&&About me 网上写spring的文章多如牛毛,为什么还要写呢,因为,很简单,那是人家写的:网上都鼓励你不要造轮子,为什么你还要造呢,因为,那不是你造的. 我不是要 ...

  3. 曹工说Spring Boot源码(5)-- 怎么从properties文件读取bean

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  4. 曹工说Spring Boot源码(6)-- Spring怎么从xml文件里解析bean的

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  5. 曹工说Spring Boot源码(7)-- Spring解析xml文件,到底从中得到了什么(上)

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  6. 曹工说Spring Boot源码(8)-- Spring解析xml文件,到底从中得到了什么(util命名空间)

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  7. 曹工说Spring Boot源码(9)-- Spring解析xml文件,到底从中得到了什么(context命名空间上)

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  8. # 曹工说Spring Boot源码(10)-- Spring解析xml文件,到底从中得到了什么(context:annotation-config 解析)

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  9. 曹工说Spring Boot源码(11)-- context:component-scan,你真的会用吗(这次来说说它的奇技淫巧)

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  10. 曹工说Spring Boot源码(12)-- Spring解析xml文件,到底从中得到了什么(context:component-scan完整解析)

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

随机推荐

  1. HTML基础学习心得分享

    开始学些Html的时候主要进行一些简单的静态网页的处理: 1.HTML 标题 HTML 标题(Heading)是通过 h1-h6 加中括号<>等标签进行定义的. 2.HTML 段落 HTM ...

  2. web前端开发面试题(Vue.js)

    1.active-class是哪个组件的属性?嵌套路由怎么定义? 答:vue-router模块的router-link组件. 2.怎么定义vue-router的动态路由?怎么获取传过来的动态参数?  ...

  3. Java基础知识总结之垃圾回收机制

    垃圾回收机制 Java垃圾回收机制是Java语言的重要功能之一.当程序创建对象,数组等引用类型对象时,系统会自动在内存区为之分配一块内存,对象就保存在这块内存区内,当这块内存不再被任何变量引用时,这块 ...

  4. es6 filter方法应用

    let arr =[ {title:'aaaa',read:100,hot:true}, {title:'bbbb',read:50,hot:false}, {title:'ccc',read:100 ...

  5. Java方法的可变参数

    class Demo { public static int sum(int ... data) { //此处可以传递一个数组,也可以是多个参数 int sum = 0; for (int i : d ...

  6. easywechat微信开发SDK之小微商户进件(一)

    微信本身不提供小微商户进件的SDK,偶然发现easywechat这么个东西,官网地址是https://www.easywechat.com/  整合了微信开发中常用的接口,包括微信公众号相关接口,微信 ...

  7. flask实现验证码并验证

    效果图: 点击图片.刷新页面.输入错误点击登录时都刷新验证码 实现步骤: 第一步:先定义获取验证码的接口 verificationCode.py #验证码 @api.route('/imgCode') ...

  8. P1055 ISBN号码

    题目描述 每一本正式出版的图书都有一个ISBN号码与之对应,ISBN码包括99位数字.11位识别码和33位分隔符,其规定格式如x-xxx-xxxxx-x,其中符号-就是分隔符(键盘上的减号),最后一位 ...

  9. 如何看一款app里面所包含的图片

    在开发制作App的过程中,有时候会想看看一些精美的App里面所设计的素材.这个时候就需要用到我给大家展现的方法了.下面就看看该如何操作能让一个App呈现出它原始的一面,这次我以Any.Do为例给大家演 ...

  10. MySQL必知必会(组合Where子句,Not和In操作符)

    SELECT prod_id, prod_price, prod_name FROM products ; SELECT prod_id, prod_price, prod_name FROM pro ...