当前位置: 首页 > news >正文

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 {...}
}
复制代码

其他的拓展点

  1. Banner

方法地址: printBanner(env)->bannerPrinter.print->SpringBootBanner#printBanner

可以在resource目录下建立banner.txt文件夹实现自定义Banner

  1. 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官网地址&#xff1a;http://layer.layui.com/ 1、多个iframe弹出层&#xff08;非嵌套&#xff09; 1.打开iframe弹出层js代码 &#xff08;1&#xff09;示例一&#xff1a; content参数可传入要打开的页面&#xff0c;type参数传2&#xff0c;即可打开iframe类型的弹层…...

BearPi环境搭建及基本使用

这是一篇总结&#xff0c;一些坑的记录 具体教程请访问&#xff1a; BearPi-HM_Nano: 小熊派BearPi-HM Nano开发板基于HarmonyOS的源码 - Gitee.com 第一步&#xff1a;安装虚拟机 不做赘述 第二步&#xff1a;下载资源 这里要用到ubuntu的一些基础知识&#xff0c;不会的…...

算法笔记-换根DP

换根DP 一般是给定一棵不定根树&#xff0c;求以每个节点为根的一些信息。可以通过二次扫描&#xff1a; 第一次扫描&#xff0c;任选一点为根&#xff0c;在有根树上&#xff0c;自底向上转移第二次扫描&#xff0c;从上一次扫描的根开始&#xff0c;自顶向下计算 P3478 [P…...

LeetCode 785. Is Graph Bipartite【DFS,二分图】中等

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…...

【微信小程序】-- 分包 - 独立分包 分包预下载(四十五)

&#x1f48c; 所属专栏&#xff1a;【微信小程序开发教程】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &…...

2.3 连续性随机变量

思维导图&#xff1a; 学习目标&#xff1a; 我会按照以下步骤学习连续型随机变量&#xff1a; 复习概率论的基础知识&#xff0c;包括概率、期望、方差等概念和公式&#xff0c;以及离散型随机变量的概率分布函数和概率质量函数的概念和性质。 学习连续型随机变量的概念和性…...

【DES详解】(一)处理input block(64 bits)

一、DES 加密算法总览 0-1、初识置换 IP&#xff08;Initial Permutation&#xff09; 输入&#xff1a;明文&#xff08;64 bits&#xff09; 过程&#xff1a;初识置换 输出&#xff1a;处理后的明文permuted input&#xff08;64 bits&#xff09; 首先&#xff0c;对需要解…...

redis笔记——三种特殊的数据结构

三种特殊数据类型 geospatial&#xff08;地理位置&#xff09; 用于定位&#xff0c;附近的人&#xff0c;距离计算 添加元素 geoadd key 经度 纬度 描述名称&#xff0c;可一次添加多个元素 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 编码是一种简单的码元位置顺序替换暗码&#xff0c;属于凯撒密码的一种。此类编码具有可逆性&#xff0c;可以自我解密&#xff0c;主要用于应对快速浏览&…...

mp4视频无法播放的解决方法

mp4视频是我们日常工作生活中经常会遇到的视频格式&#xff0c;但如果遇到重要的mp4视频无法播放了&#xff0c;该怎么办呢?有mp4视频无法播放的解决方法吗?下面小编为大家整理了这个问题产生的原因以及相应的解决方法&#xff0c;让我们看一看。 什么情况下会导致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…...

气传导和骨传导耳机哪个好?简单科普这两种蓝牙耳机

在生活中&#xff0c;我们经常会用到耳机&#xff0c;特别是在日常娱乐听歌、运动休闲、户外通勤的时候&#xff0c;一款舒适的耳机是必不可少的。 而最近几年&#xff0c;随着科技的发展&#xff0c;各大品牌也相继推出了各种类型的耳机&#xff0c;其中比较热门的就有气传导…...

浅尝GoWeb开发之Gin框架

一、框架简介 gin 目前应用最广泛的golang框架&#xff0c;甚至已经变成了golang的官方框架&#xff0c;但它主要是一个RESTFul的框架。封装比较优雅&#xff0c;API友好&#xff0c;源码注释比较明确。个人比较推荐。 beego 国内最早的golang框架&#xff0c;也是最全的MV…...

工程行业管理系统-专业的工程管理软件-提供一站式服务

Java版工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离 功能清单如下&#xff1a; 首页 工作台&#xff1a;待办工作、消息通知、预警信息&#xff0c;点击可进入相应的列表 项目进度图表&#xff1a;选择&#xff08;总体或单个&#xff09;项目显示1…...

目标检测YOLO系列-YOLOV7运行步骤(推理、训练全过程)

下载源代码&#xff1a;点击下载 进入项目根目录并执行以下命令安装requirements.txt中的相关依赖 pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple官网下载权重yolov7.pt&#xff08;测试使用&#xff09;、yolov7-tiny.pt&#xff08;训练使用…...

Spring Boot + Spring Security基础入门教程

Spring Security简介 Spring Security 是一个功能强大且高度可定制的身份验证和访问控制框架。Spring Security 致力于为 Java 应用程序提供身份验证和授权的能力。 Spring Security 两大重要核心功能&#xff1a;用户认证&#xff08;Authentication&#xff09;和用户授权&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实现网关

网关作为流量的入口&#xff0c;常用的功能包括路由转发、权限校验、限流等 Spring Cloud 是Spring官方推出的第二代网关框架&#xff0c;由WebFluxNettyReactor实现的响应式的API网关&#xff0c;它不能在传统的servlet容器工作&#xff0c;也不能构建war包。基于Filter的方式…...

Redis 如何配置读写分离架构(主从复制)?

文章目录 Redis 如何配置读写分离架构&#xff08;主从复制&#xff09;&#xff1f;什么是 Redis 主从复制&#xff1f;如何配置主从复制架构&#xff1f;配置环境安装 Redis 步骤 通过命令行配置从节点通过配置文件配置从节点Redis 主从复制优点Redis 主从复制缺点 Redis 如何…...

RTX3070 + CUDA 11.0 实战:手把手教你从零搭建 PointNet.pytorch 环境(附常见报错解决)

RTX3070 CUDA 11.0 实战&#xff1a;手把手教你从零搭建 PointNet.pytorch 环境&#xff08;附常见报错解决&#xff09; 当你手握一块RTX3070显卡&#xff0c;想要复现PointNet这一经典点云处理网络时&#xff0c;是否曾被环境配置的各种坑绊住脚步&#xff1f;本文将带你避开…...

如何将微信聊天记忆转化为数字珍藏:WeChatMsg的数据主权革命

如何将微信聊天记忆转化为数字珍藏&#xff1a;WeChatMsg的数据主权革命 【免费下载链接】WeChatMsg 提取微信聊天记录&#xff0c;将其导出成HTML、Word、CSV文档永久保存&#xff0c;对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/we…...

告别外挂EEPROM:手把手教你用AUTOSAR Fee模块在MCU内部Flash存数据(附Vector DaVinci配置)

告别外挂EEPROM&#xff1a;用AUTOSAR Fee模块实现MCU内部Flash数据存储实战指南 在汽车电子控制单元&#xff08;ECU&#xff09;开发中&#xff0c;非易失性数据存储一直是硬件选型的重要考量点。传统方案往往需要外挂一颗EEPROM芯片来存储参数、标定值和故障码等关键数据&am…...

5分钟搞定OpenCV摄像头实时监控(附Jupyter避坑指南)

5分钟搞定OpenCV摄像头实时监控&#xff08;附Jupyter避坑指南&#xff09; 在计算机视觉领域&#xff0c;实时摄像头监控是最基础也最实用的功能之一。无论是安防监控、人脸识别还是简单的视频采集&#xff0c;OpenCV都提供了简洁高效的接口。但对于Python初学者和Jupyter Not…...

5大核心价值重构云游戏体验:Sunshine让你的游戏突破硬件与空间限制

5大核心价值重构云游戏体验&#xff1a;Sunshine让你的游戏突破硬件与空间限制 【免费下载链接】Sunshine Self-hosted game stream host for Moonlight. 项目地址: https://gitcode.com/GitHub_Trending/su/Sunshine 在数字娱乐日益碎片化的今天&#xff0c;玩家们面临…...

DeerFlow免费开源:字节跳动出品,个人研究者的强大AI工具

DeerFlow免费开源&#xff1a;字节跳动出品&#xff0c;个人研究者的强大AI工具 1. 项目概述 DeerFlow是由字节跳动公司开源的一款深度研究辅助工具&#xff0c;基于LangStack技术框架开发。这个项目通过整合语言模型、网络搜索和Python代码执行等能力&#xff0c;为个人研究…...

商业应用(12)电影院零售票务系统开发—东方仙盟练气期

未来之窗开源收银台生态未来之窗开源收银台生态&#xff1a;让中小微企业告别重复开发&#xff0c;普惠式接入多场景收银能力 在数字化转型的浪潮中&#xff0c;中小微企业的痛点往往藏在 “重复造轮子” 里 —— 便利店需要收银台、餐饮店需要收银台、游乐场需要带押金管理的收…...

AI头像生成器实战:用Qwen3-32B为你的社交头像设计专属描述文案

AI头像生成器实战&#xff1a;用Qwen3-32B为你的社交头像设计专属描述文案 1. 为什么你需要一个AI头像生成器 在社交媒体时代&#xff0c;一个独特的头像已经成为个人品牌的重要组成部分。无论是LinkedIn上的专业形象&#xff0c;还是Instagram上的创意展示&#xff0c;头像都…...

技术赋能B端拓客:号码核验行业的迭代升级与价值深耕,

在数字经济持续深耕的当下&#xff0c;B端市场的竞争逻辑已发生根本性转变&#xff0c;“粗放拓客”逐渐被“精准高效”取代&#xff0c;企业对拓客全流程的效率与成本管控提出了更高要求。号码核验作为B端拓客的前置核心环节&#xff0c;其作用远不止于简单的空号筛查&#xf…...

如何快速突破iOS限制:终极降级完全手册

如何快速突破iOS限制&#xff1a;终极降级完全手册 【免费下载链接】downr1n downgrade tethered checkm8 idevices ios 14, 15. 项目地址: https://gitcode.com/gh_mirrors/do/downr1n 你是否曾想过让旧款iPhone重获新生&#xff1f;是否对苹果系统的版本限制感到困扰&…...