【Spring Boot】SpringBoot设计了哪些可拓展的机制?
文章目录
- 前言
- SpringBoot核心源码
- 拓展Initializer
- 拓展监听器ApplicationListener
- BeanFactory的后置处理器 & Bean的后置处理器
- AOP
- 其他的拓展点
前言
- 当我们引入注册中心的依赖,比如nacos的时候,当我们启动springboot,这个服务就会根据配置文件自动注册到注册中心中,这个动作是如何完成的?
- 注册中心使用了SpringBoot中的事件监听机制,在springboot初始化的时候完成服务注册
SpringBoot核心源码
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { ...this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));// Servletthis.webApplicationType = WebApplicationType.deduceFromClasspath(); this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class)); // 注意这里,Initializersthis.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 注意这里 Listenersthis.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = this.deduceMainApplicationClass();
}
我们可以看到空的SpringBoot项目有一些initializers以及一些listeners
注意这两行,换言之我们只要实现这两个类就可以自定义拓展SpringBoot了!
这里和手写Starter都是对SpringBoot的拓展,有兴趣的小伙伴可以看这篇文章
拓展Initializer
再看这张图
我们需要研究一下ApplicationContextInitializer这个类:
@FunctionalInterface
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> { /** * Initialize the given application context. * @param applicationContext the application to configure */ void initialize(C applicationContext);
}
这样就很清晰了,我们尝试手写一个继承类:
public class DemoInitializer implements ApplicationContextInitializer { @Override public void initialize(ConfigurableApplicationContext applicationContext) { System.out.println("自定义初始化器执行..."); ConfigurableEnvironment environment = applicationContext.getEnvironment(); Map<String, Object> map = new HashMap<>(1); map.put("name", "sccccc"); environment.getPropertySources().addLast(new MapPropertySource("DemoInitializer", map)); System.out.println("DemoInitializer execute, and add some property"); }
}
通过SPI机制将自定义初始化器交给list集合initializers
然后再debug,就会发现:
最后经过一次回调:
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ... applyInitializers(context); ...// Add boot specific singleton beans 下面是beanFactory的操作
遍历所有的初始化器,然后
/**
* Apply any {@link ApplicationContextInitializer}s to the context before it is
* refreshed.
* @param context the configured ApplicationContext (not refreshed yet)
* @see ConfigurableApplicationContext#refresh()
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
protected void applyInitializers(ConfigurableApplicationContext context) { for (ApplicationContextInitializer initializer : getInitializers()) { Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class); Assert.isInstanceOf(requiredType, context, "Unable to call initializer."); initializer.initialize(context); }
}
流程:
拓展监听器ApplicationListener
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener { /** * Handle an application event. */ void onApplicationEvent(E event); /** * Create a new {@code ApplicationListener} for the given payload consumer. */ static <T> ApplicationListener<PayloadApplicationEvent<T>> forPayload(Consumer<T> consumer) { return event -> consumer.accept(event.getPayload()); } }
这里和上面initializer一样,就不演示了
BeanFactory的后置处理器 & Bean的后置处理器
Spring Boot解析配置成BeanDefinition的操作在invokeBeanFactoryPostProcessors方法中
自定义BeanFactory的后置处理器:
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactorybeanFactory) throws BeansException {Arrays.asList(beanFactory.getBeanDefinitionNames()).forEach(beanDefinitionName ->System.out.println(beanDefinitionName));System.out.println("BeanFactoryPostProcessor...");}
}
自定义Bean的后置处理器:
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName)throws BeansException {if(beanName.equals("userController")){System.out.println("找到了userController: "+bean);}return null;}
}
AOP
这个相信大家用的比较多,可以自定义切面:
@Aspect
@Component
public class LogAspect {// 切入点 Pointcut 可以对Service服务做切面
@Pointcut("execution(* com.example.service.*.*(..))")
public void mypointcut(){}// 前置通知
@Before(value = "mypointcut()")
public void before(JoinPoint joinPoint){System.out.println("[前置通知] 准备开始记录日志...");System.out.println("[前置通知] 目标类是: "+joinPoint.getTarget());System.out.println("[前置通知] 目标方法是:"+joinPoint.getSignature().getName());
}// 后置通知
@AfterReturning(value = "mypointcut()")
public void afterReturning(JoinPoint joinPoint){System.out.println("[后置通知] 记录日志完成...");System.out.println("[后置通知] 目标类是: "+joinPoint.getTarget());System.out.println("[后置通知] 目标方法是:"+joinPoint.getSignature().getName());
}/*@Around(value = "mypointcut()")
public void around(ProceedingJoinPoint joinPoint){System.out.println("[环绕通知] 日志记录前的操作...");try {joinPoint.proceed();System.out.println("[环绕通知] 日志记录后的操作...");System.out.println("[环绕通知] "+joinPoint.getTarget());System.out.println("[环绕通知] "+joinPoint.getSignature().getName());} catch (Throwable throwable) {System.out.println("[环绕通知] 发生异常的操作...");throwable.printStackTrace();}finally {...}
}
其他的拓展点
- Banner
方法地址:
printBanner(env)->bannerPrinter.print->SpringBootBanner#printBanner
可以在resource目录下建立banner.txt文件夹实现自定义Banner
- Runners
流程:
自定义:
@Component
public class JackApplicationRunner implements ApplicationRunner {@Overridepublic void run(ApplicationArguments args) throws Exception {System.out.println("JackApplicationRunner...");}
}
相关文章:

【Spring Boot】SpringBoot设计了哪些可拓展的机制?
文章目录 前言SpringBoot核心源码拓展Initializer拓展监听器ApplicationListenerBeanFactory的后置处理器 & Bean的后置处理器AOP其他的拓展点 前言 当我们引入注册中心的依赖,比如nacos的时候,当我们启动springboot,这个服务就会根据配置…...

《程序员面试金典(第6版)》面试题 10.10. 数字流的秩
题目描述 假设你正在读取一串整数。每隔一段时间,你希望能找出数字 x 的秩(小于或等于 x 的值的个数)。请实现数据结构和算法来支持这些操作,也就是说: 实现 track(int x) 方法,每读入一个数字都会调用该方法; 实现 g…...

智能洗地机好用吗?值得入手的洗地机推荐
洗地机是一款高效的地面清洁设备,不仅可以很好清理地面不同形态的干湿垃圾,还减少了人工和水资源的浪费,是我们日常生活中必不可少的清洁工具。作为以一位评测博主,很多朋友咨询我在选购洗地机时应该注意哪些要点,有哪…...

Spring Security实战(一)——基于内存和数据库模型的认证与授权
目录 简介 一、初识Spring Security(入门案例) (1)新建project (2)选择依赖 (3)编写一个 HelloController (4)启动项目,访问localhost:8080…...
轻松掌握FFmpeg编程:从架构到实践
轻松掌握FFmpeg编程:从架构到实践 (Master FFmpeg Programming with Ease: From Architecture to Practice 引言 (Introduction)FFmpeg简介与应用场景 (Brief Introduction and Application Scenarios of FFmpeg)为什么选择FFmpeg进行音视频处理 (Why Choose FFmpeg…...
桌面应用程序开发攻略(初步了解)
什么是桌面应用程序? 桌面应用开发是指为桌面计算机或其他类似设备(如服务器)开发软件应用程序的过程。桌面应用通常是独立于浏览器运行的,并且可以在操作系统的桌面或应用程序菜单中找到。桌面应用可以使用各种编程语言开发&…...

【李老师云计算】HBase+Zookeeper部署及Maven访问(HBase集群实验)
索引 前言1. Zookeeper1.1 主机下载Zookeeper安装包1.2 主机解压Zookeeper1.3 ★解决解压后文件缺失1.4 主机配置Zookeeper文件1.4.1 配置zoo_sample.cfg文件1.4.2 配置/data/myid文件 1.5 主机传输Zookeeper文件到从机1.6 从机修改Zookeeper文件1.6.1 修改zoo.cfg文件1.6.2 修…...

第11章_常用类和基础API
第11章_常用类和基础API 讲师:尚硅谷-宋红康(江湖人称:康师傅) 官网:http://www.atguigu.com 本章专题与脉络 1. 字符串相关类之不可变字符序列:String 1.1 String的特性 java.lang.String 类代表字符串…...

Java语言数据类型与c语言数据类型的不同
目录 一、c语言数据类型 1.基本类型: 2.枚举类型: 3.空类型: 4.派生类型: 二、C语言编程需要注意的64位和32机器的区别 三、 不同之处 一、c语言数据类型 首先,先来整体介绍一下C语言的数据类型分类。 1.基…...
C# Replace()、Trim()、Split()、Substring()、IndexOf() 、 LastIndexOf()函数
目录 一、Replace() 二、Trim() 三、Split() 四、Substring() 五、IndexOf() 六、LastIndexOf() 一、Replace() 在C#中,Replace()是一个字符串方法,用于将指定的字符或子字符串替换为另一个字符或字符串。下面是一些Replace()方法的常见用法和示例…...

C++类的理解与类型名,类的成员,两种定义方式,类的访问限定符,成员访问,作用域与实例化对象
面向过程和面向对象初步认识 C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题 C是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成 面向…...
【华为OD机试真题 C++】1051 - 处理器问题 | 机试题+算法思路+考点+代码解析
文章目录 一、题目🔸题目描述🔸输入输出🔸样例1🔸样例2 二、题目解析三、代码参考 作者:KJ.JK 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 &…...
Linux 常用操作命令大全
一、基础知识 1.1 Linux系统的文件结构 /bin 二进制文件,系统常规命令 /boot 系统启动分区,系统启动时读取的文件 /dev 设备文件 /etc 大多数配置文件 /home 普通用户的家目录 /lib 32位函数库 /lib64 64位库 /media 手动临时挂载点 /mnt 手动临时挂载点…...

Git使用教程
Git 目标 Git简介【了解】 使用Git管理文件版本【重点】 远程仓库使用【掌握】 分支管理【重点】 远程仓库【掌握】 一、Git简介 1、版本控制系统简介 1.1、版本控制前生今世 版本控制系统Version Control Systems,简称 VCS是将『什么时候、谁、对什么文件…...

substrate中打印调试信息的多种方式详解
目录 1. 获取substrate-node-template代码2. 添加一个用于测试的pallet至依赖到pallets目录3. log方式来输出信息3.1 将log依赖添到cargo.toml文件3.2 log-test/src/lib.rs修改call方法 3.3 polkadot.js.调用测试函数do_something_log_test4. printable trait方式来输出信息4.1…...

Disentangled Graph Collaborative Filtering
代码地址:https://github.com/ xiangwang1223/disentangled_graph_collaborative_filtering Background: 现有模型在很大程度上以统一的方式对用户-物品关系进行建模(将模型看做黑盒,历史交互作为输入,Embedding作为输出。)&…...

Nginx快速上手
Nginx快速上手 OVERVIEW Nginx快速上手一、基本概念1.Nginx初步认识2.正向/反向代理(1)正向代理(2)反向代理 二、Nginx 安装和配置1.安装2.Nginx指令3.Nginx配置 三、Nginx的使用1.Web服务器(1)静态网页存储…...
【设计模式】实际场景解释策略模式与工厂模式的应用
文章目录 前言策略模式概念场景示例 工厂模式概念场景示例 策略模式与工厂模式的比较相同点不同点 总结 前言 策略模式和工厂模式是常见的设计模式,它们可以帮助我们更好地组织和管理代码,提高代码的可维护性和可扩展性。 在本篇博客中,我将…...

外包干了三年,算是废了...
先说一下自己的情况。大专生,19年通过校招进入湖南某软件公司,干了接近3年的测试,今年年上旬,感觉自己不能够在这样下去了,长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了三年,…...

九龙证券|光模块概念股封单资金超3亿元,传媒板块涨停潮来袭
今天A股三大股指低开低走。沪深两市收盘共37股涨停。剔除4只ST股,合计33股涨停。另外,10股封板未遂,整体封板率为78.72%。 涨停战场: 华工科技封单资金超3亿元 从收盘涨停板封单量来看,同方股份封单量最高࿰…...
conda相比python好处
Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理:…...

循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...

CentOS下的分布式内存计算Spark环境部署
一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架,相比 MapReduce 具有以下核心优势: 内存计算:数据可常驻内存,迭代计算性能提升 10-100 倍(文档段落:3-79…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...

tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...
C++.OpenGL (14/64)多光源(Multiple Lights)
多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...

视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...

R语言速释制剂QBD解决方案之三
本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...

面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...