写在前面

在前面的文章中,我们学习了如何使用@Import注解向Spring容器中导入bean,可以使用@Import注解快速向容器中导入bean,小伙伴们可以参见《【Spring注解驱动开发】使用@Import注解给容器中快速导入一个组件》。可以在@Import注解中使用ImportSelector接口导入bean,小伙伴们可以参见《【Spring注解驱动开发】在@Import注解中使用ImportSelector接口导入bean》一文。今天,我们就来说说,如何在@Import注解中使用ImportBeanDefinitionRegistrar向容器中注册bean。

项目工程源码已经提交到GitHub:https://github.com/sunshinelyz/spring-annotation

ImportBeanDefinitionRegistrar概述

概述

我们先来看看ImportBeanDefinitionRegistrar是个什么鬼,点击进入ImportBeanDefinitionRegistrar源码,如下所示。

package org.springframework.context.annotation;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.core.type.AnnotationMetadata; public interface ImportBeanDefinitionRegistrar { default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
BeanNameGenerator importBeanNameGenerator) { registerBeanDefinitions(importingClassMetadata, registry);
} default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
} }

由源码可以看出,ImportBeanDefinitionRegistrar本质上是一个接口。在ImportBeanDefinitionRegistrar接口中,有一个registerBeanDefinitions()方法,通过registerBeanDefinitions()方法,我们可以向Spring容器中注册bean实例。

Spring官方在动态注册bean时,大部分套路其实是使用ImportBeanDefinitionRegistrar接口。

所有实现了该接口的类都会被ConfigurationClassPostProcessor处理,ConfigurationClassPostProcessor实现了BeanFactoryPostProcessor接口,所以ImportBeanDefinitionRegistrar中动态注册的bean是优先于依赖其的bean初始化的,也能被aop、validator等机制处理。

使用方法

ImportBeanDefinitionRegistrar需要配合@Configuration和@Import注解,@Configuration定义Java格式的Spring配置文件,@Import注解导入实现了ImportBeanDefinitionRegistrar接口的类。

ImportBeanDefinitionRegistrar实例

既然ImportBeanDefinitionRegistrar是一个接口,那我们就创建一个MyImportBeanDefinitionRegistrar类,实现ImportBeanDefinitionRegistrar接口,如下所示。

package io.mykit.spring.plugins.register.condition;

import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata; /**
* @author binghe
* @version 1.0.0
* @description ImportBeanDefinitionRegistrar的实现类
*/
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { /**
* AnnotationMetadata: 当前类的注解信息
* BeanDefinitionRegistry:BeanDefinition注册类
* 通过调用BeanDefinitionRegistry接口的registerBeanDefinition()方法,可以将所有需要添加到容器中的bean注入到容器中。
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry){ }
}

可以看到,这里,我们先创建了MyImportBeanDefinitionRegistrar类的大体框架。接下来,我们在PersonConfig2类上的@Import注解中,添加MyImportBeanDefinitionRegistrar类,如下所示。

@Configuration
@Import({Department.class, Employee.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
public class PersonConfig2 {

接下来,创建一个Company类,作为测试测试ImportBeanDefinitionRegistrar接口的bean,如下所示。

package io.mykit.spring.plugins.register.bean;

/**
* @author binghe
* @version 1.0.0
* @description 测试ImportBeanDefinitionRegistrar接口的使用
*/
public class Company {
}

接下来,就要实现MyImportBeanDefinitionRegistrar类中的registerBeanDefinitions()方法的逻辑了,添加逻辑后的registerBeanDefinitions()方法如下所示。

    /**
* AnnotationMetadata: 当前类的注解信息
* BeanDefinitionRegistry:BeanDefinition注册类
* 通过调用BeanDefinitionRegistry接口的registerBeanDefinition()方法,可以将所有需要添加到容器中的bean注入到容器中。
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry){
boolean employee = registry.containsBeanDefinition("employee");
boolean department = registry.containsBeanDefinition("department");
if (employee && department){
BeanDefinition beanDefinition = new RootBeanDefinition(Company.class);
registry.registerBeanDefinition("company", beanDefinition);
}
}

registerBeanDefinitions()方法的实现逻辑很简单,就是判断Spring容器中是否同时存在以employee命名的bean和以department命名的bean,如果同时存在以employee命名的bean和以department命名的bean,则向Spring容器中注入一个以company命名的bean。

接下来,我们就运行SpringBeanTest类中的testAnnotationConfig7()方法来进行测试,输出结果信息如下所示。

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
personConfig2
io.mykit.spring.plugins.register.bean.Department
io.mykit.spring.plugins.register.bean.Employee
io.mykit.spring.plugins.register.bean.User
io.mykit.spring.plugins.register.bean.Role
person
binghe001

可以看到,在输出结果中,并没有看到“company”,这是因为输出结果中存在io.mykit.spring.plugins.register.bean.Department和io.mykit.spring.plugins.register.bean.Employee,并不存在我们代码逻辑中的department和employee。所以,我们将registerBeanDefinitions()方法的逻辑稍微修改下,修改后的代码如下所示。

/**
* AnnotationMetadata: 当前类的注解信息
* BeanDefinitionRegistry:BeanDefinition注册类
* 通过调用BeanDefinitionRegistry接口的registerBeanDefinition()方法,可以将所有需要添加到容器中的bean注入到容器中。
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry){
boolean employee = registry.containsBeanDefinition(Employee.class.getName());
boolean department = registry.containsBeanDefinition(Department.class.getName());
if (employee && department){
BeanDefinition beanDefinition = new RootBeanDefinition(Company.class);
registry.registerBeanDefinition("company", beanDefinition);
}
}

接下来,我们再次运行SpringBeanTest类中的testAnnotationConfig7()方法来进行测试,输出结果信息如下所示。

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
personConfig2
io.mykit.spring.plugins.register.bean.Department
io.mykit.spring.plugins.register.bean.Employee
io.mykit.spring.plugins.register.bean.User
io.mykit.spring.plugins.register.bean.Role
person
binghe001
company

可以看到,此时输出了company,说明Spring容器中已经成功注册了以company命名的bean。

好了,咱们今天就聊到这儿吧!别忘了给个在看和转发,让更多的人看到,一起学习一起进步!!

项目工程源码已经提交到GitHub:https://github.com/sunshinelyz/spring-annotation

写在最后

如果觉得文章对你有点帮助,请微信搜索并关注「 冰河技术 」微信公众号,跟冰河学习Spring注解驱动开发。公众号回复“spring注解”关键字,领取Spring注解驱动开发核心知识图,让Spring注解驱动开发不再迷茫。

【Spring注解驱动开发】在@Import注解中使用ImportBeanDefinitionRegistrar向容器中注册bean的更多相关文章

  1. 【String注解驱动开发】面试官让我说说:如何使用FactoryBean向Spring容器中注册bean?

    写在前面 在前面的文章中,我们知道可以通过多种方式向Spring容器中注册bean.可以使用@Configuration结合@Bean向Spring容器中注册bean:可以按照条件向Spring容器中 ...

  2. 【Spring注解驱动开发】使用@Import注解给容器中快速导入一个组件

    写在前面 我们可以将一些bean组件交由Spring管理,并且Spring支持单实例bean和多实例bean.我们自己写的类,可以通过包扫描+标注注解(@Controller.@Servcie.@Re ...

  3. 【Spring注解驱动开发】在@Import注解中使用ImportSelector接口导入bean

    写在前面 在上一篇关于Spring的@Import注解的文章<[Spring注解驱动开发]使用@Import注解给容器中快速导入一个组件>中,我们简单介绍了如何使用@Import注解给容器 ...

  4. 【String注解驱动开发】如何按照条件向Spring容器中注册bean?这次我懂了!!

    写在前面 当bean是单实例,并且没有设置懒加载时,Spring容器启动时,就会实例化bean,并将bean注册到IOC容器中,以后每次从IOC容器中获取bean时,直接返回IOC容器中的bean,不 ...

  5. 【Spring注解驱动开发】面试官:如何将Service注入到Servlet中?朋友又栽了!!

    写在前面 最近,一位读者出去面试前准备了很久,信心满满的去面试.没想到面试官的一个问题把他难住了.面试官的问题是这样的:如何使用Spring将Service注入到Servlet中呢?这位读者平时也是很 ...

  6. 0、Spring 注解驱动开发

    0.Spring注解驱动开发 0.1 简介 <Spring注解驱动开发>是一套帮助我们深入了解Spring原理机制的教程: 现今SpringBoot.SpringCloud技术非常火热,作 ...

  7. 【Spring注解驱动开发】组件注册-@ComponentScan-自动扫描组件&指定扫描规则

    写在前面 在实际项目中,我们更多的是使用Spring的包扫描功能对项目中的包进行扫描,凡是在指定的包或子包中的类上标注了@Repository.@Service.@Controller.@Compon ...

  8. 【Spring注解驱动开发】自定义TypeFilter指定@ComponentScan注解的过滤规则

    写在前面 Spring的强大之处不仅仅是提供了IOC容器,能够通过过滤规则指定排除和只包含哪些组件,它还能够通过自定义TypeFilter来指定过滤规则.如果Spring内置的过滤规则不能够满足我们的 ...

  9. 【Spring注解驱动开发】使用@Scope注解设置组件的作用域

    写在前面 Spring容器中的组件默认是单例的,在Spring启动时就会实例化并初始化这些对象,将其放到Spring容器中,之后,每次获取对象时,直接从Spring容器中获取,而不再创建对象.如果每次 ...

随机推荐

  1. (上)python3 selenium3 从框架实现学习selenium让你事半功倍

    本文感谢以下文档或说明提供的参考. Selenium-Python中文文档 Selenium Documentation Webdriver 参考 如有错误欢迎在评论区指出,作者将即时更改.文章从我的 ...

  2. Kubernetes as Database: 使用kubesql查询kubernetes资源

    写在前面 kubectl虽然查询单个的kubernetes资源或者列表都已经比较方便,但是进行更为多个资源的联合查询(比如pod和node),以及查询结果的二次处理方面却是kubectl无法胜任的.所 ...

  3. Python一键获取日漫Top100榜单电影信息

    最近看到一个 UP 主做的视频,使用可视化动态图,把目前播放量最多的 UP 主一一列出来,结果第一名是哔哩哔哩番剧,第一名的播放量是第二名近 10 倍. B站的番剧数量,也是相对其他平台比较多的,而且 ...

  4. mysql Invalid use of group function的解决办法

    错误语句:SELECT s.SID, s.Sname, AVG(a.score)FROM student sLEFT JOIN sc a ON s.SID = a.SID WHERE AVG(a.sc ...

  5. rancher证书过期

    背景 无法打开rancher服务,报错如下截图,可以看出是证书过期了无法连上k8s,注意这里的证书是rancher自身证书并非k8s证书. 解决方法 rancher升级:https://rancher ...

  6. ForkJoinPool分支合并框架-工作窃取

    Fork/Join 框架 Fork/Join 框架:就是在必要的情况下,将一个大任务,进行拆分(fork)成 若干个小任务(拆到不可再拆时), 再将一个个的小任务运算的结果进行 join 汇总 For ...

  7. 【JVM】如何理解强引用、软引用、弱引用、虚引用?

    整体架构 强引用 强引用是默认支持,当内存不足的时候,JVM开始垃圾回收,对于强引用的对象,就算是出现了OOM也不会回收对象. 强引用是最常见的普通对象引用,只要还有强引用指向对象,对象就存活,垃圾回 ...

  8. Beta冲刺 —— 6.1

    这个作业属于哪个课程 软件工程 这个作业要求在哪里 Beta冲刺 这个作业的目标 Beta冲刺 作业正文 正文 github链接 项目地址 其他参考文献 无 一.会议内容 1.讨论并解决每个人存在的问 ...

  9. Java并发编程 (四) 线程安全性

    个人博客网:https://wushaopei.github.io/    (你想要这里多有) 一.线程安全性-原子性-atomic-1 1.线程安全性 定义: 当某个线程访问某个类时,不管运行时环境 ...

  10. Java实现 LeetCode 677 键值映射(字典树)

    677. 键值映射 实现一个 MapSum 类里的两个方法,insert 和 sum. 对于方法 insert,你将得到一对(字符串,整数)的键值对.字符串表示键,整数表示值.如果键已经存在,那么原来 ...