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...");}
}
相关文章:
SpringBoot设计了哪些可拓展的机制?
SpringBoot核心源码 public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { ...this.primarySources new LinkedHashSet(Arrays.asList(primarySources));// Servletthis.webApplicationType WebApplicationType.deduceFromClass…...
Layer组件多个iframe弹出层打开与关闭及参数传递
Layer官网地址:http://layer.layui.com/ 1、多个iframe弹出层(非嵌套) 1.打开iframe弹出层js代码 (1)示例一: content参数可传入要打开的页面,type参数传2,即可打开iframe类型的弹层…...
BearPi环境搭建及基本使用
这是一篇总结,一些坑的记录 具体教程请访问: BearPi-HM_Nano: 小熊派BearPi-HM Nano开发板基于HarmonyOS的源码 - Gitee.com 第一步:安装虚拟机 不做赘述 第二步:下载资源 这里要用到ubuntu的一些基础知识,不会的…...
算法笔记-换根DP
换根DP 一般是给定一棵不定根树,求以每个节点为根的一些信息。可以通过二次扫描: 第一次扫描,任选一点为根,在有根树上,自底向上转移第二次扫描,从上一次扫描的根开始,自顶向下计算 P3478 [P…...
LeetCode 785. Is Graph Bipartite【DFS,二分图】中等
本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章…...
【微信小程序】-- 分包 - 独立分包 分包预下载(四十五)
💌 所属专栏:【微信小程序开发教程】 😀 作 者:我是夜阑的狗🐶 🚀 个人简介:一个正在努力学技术的CV工程师,专注基础和实战分享 ,欢迎咨询! &…...
2.3 连续性随机变量
思维导图: 学习目标: 我会按照以下步骤学习连续型随机变量: 复习概率论的基础知识,包括概率、期望、方差等概念和公式,以及离散型随机变量的概率分布函数和概率质量函数的概念和性质。 学习连续型随机变量的概念和性…...
【DES详解】(一)处理input block(64 bits)
一、DES 加密算法总览 0-1、初识置换 IP(Initial Permutation) 输入:明文(64 bits) 过程:初识置换 输出:处理后的明文permuted input(64 bits) 首先,对需要解…...
redis笔记——三种特殊的数据结构
三种特殊数据类型 geospatial(地理位置) 用于定位,附近的人,距离计算 添加元素 geoadd key 经度 纬度 描述名称,可一次添加多个元素 127.0.0.1:6379> geoadd china:city 113.28 23.12 guangzhou (integer) 1 1…...
网络安全之编码加密算法
网络安全之编码加密算法 一、ROT5/13/18/47编码转换二、MD5加密 一、ROT5/13/18/47编码转换 ROT5、ROT13、ROT18、ROT47 编码是一种简单的码元位置顺序替换暗码,属于凯撒密码的一种。此类编码具有可逆性,可以自我解密,主要用于应对快速浏览&…...
mp4视频无法播放的解决方法
mp4视频是我们日常工作生活中经常会遇到的视频格式,但如果遇到重要的mp4视频无法播放了,该怎么办呢?有mp4视频无法播放的解决方法吗?下面小编为大家整理了这个问题产生的原因以及相应的解决方法,让我们看一看。 什么情况下会导致mp4视频无法…...
搭建Mysql
登录root账号 su root #上传 mysql-advanced-5.7.17-linux-glibc2.5-x86_64.tar.gz #创建mysql的用户组/用户, data目录及其用户目录 groupadd mysql useradd -g mysql -d /home/mysql mysql mv mysql-advanced-5.7.17-linux-glibc2.5-x86_64 mysql mkdir /home/mysql/data…...
气传导和骨传导耳机哪个好?简单科普这两种蓝牙耳机
在生活中,我们经常会用到耳机,特别是在日常娱乐听歌、运动休闲、户外通勤的时候,一款舒适的耳机是必不可少的。 而最近几年,随着科技的发展,各大品牌也相继推出了各种类型的耳机,其中比较热门的就有气传导…...
浅尝GoWeb开发之Gin框架
一、框架简介 gin 目前应用最广泛的golang框架,甚至已经变成了golang的官方框架,但它主要是一个RESTFul的框架。封装比较优雅,API友好,源码注释比较明确。个人比较推荐。 beego 国内最早的golang框架,也是最全的MV…...
工程行业管理系统-专业的工程管理软件-提供一站式服务
Java版工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离 功能清单如下: 首页 工作台:待办工作、消息通知、预警信息,点击可进入相应的列表 项目进度图表:选择(总体或单个)项目显示1…...
目标检测YOLO系列-YOLOV7运行步骤(推理、训练全过程)
下载源代码:点击下载 进入项目根目录并执行以下命令安装requirements.txt中的相关依赖 pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple官网下载权重yolov7.pt(测试使用)、yolov7-tiny.pt(训练使用…...
Spring Boot + Spring Security基础入门教程
Spring Security简介 Spring Security 是一个功能强大且高度可定制的身份验证和访问控制框架。Spring Security 致力于为 Java 应用程序提供身份验证和授权的能力。 Spring Security 两大重要核心功能:用户认证(Authentication)和用户授权&am…...
MySQL数据库,表的增删改查详细讲解
目录 1.CRUD 2.增加数据 2.1创建数据 2.2插入数据 2.2.1单行插入 2.2.2多行插入 3.查找数据 3.1全列查询 3.2指定列查询 3.3查询字段为表达式 3.3.1表达式不包含字段 3.3.2表达式包含一个字段 3.3.3表达式包含多个字段 3.4起别名 3.5distinct(去重) 3.6order …...
SpringCloud-Gateway实现网关
网关作为流量的入口,常用的功能包括路由转发、权限校验、限流等 Spring Cloud 是Spring官方推出的第二代网关框架,由WebFluxNettyReactor实现的响应式的API网关,它不能在传统的servlet容器工作,也不能构建war包。基于Filter的方式…...
Redis 如何配置读写分离架构(主从复制)?
文章目录 Redis 如何配置读写分离架构(主从复制)?什么是 Redis 主从复制?如何配置主从复制架构?配置环境安装 Redis 步骤 通过命令行配置从节点通过配置文件配置从节点Redis 主从复制优点Redis 主从复制缺点 Redis 如何…...
C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!
一、引言 在数据驱动的背景下,知识图谱凭借其高效的信息组织能力,正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合,探讨知识图谱开发的实现细节,帮助读者掌握该技术栈在实际项目中的落地方法。 …...
Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)
目录 一、👋🏻前言 二、😈sinx波动的基本原理 三、😈波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、🌊波动优化…...
python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...
Windows安装Miniconda
一、下载 https://www.anaconda.com/download/success 二、安装 三、配置镜像源 Anaconda/Miniconda pip 配置清华镜像源_anaconda配置清华源-CSDN博客 四、常用操作命令 Anaconda/Miniconda 基本操作命令_miniconda创建环境命令-CSDN博客...
Qt的学习(二)
1. 创建Hello Word 两种方式,实现helloworld: 1.通过图形化的方式,在界面上创建出一个控件,显示helloworld 2.通过纯代码的方式,通过编写代码,在界面上创建控件, 显示hello world; …...
