SpringBoot设计了哪些可拓展的机制?

news2024/11/15 19:34:15

SpringBoot核心源码

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {  
    ...
    this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
    // Servlet
    this.webApplicationType = WebApplicationType.deduceFromClasspath();  
    this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));  
   
    // 注意这里,Initializers
    this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));  
    // 注意这里 Listeners
    this.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 {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory
    beanFactory) throws BeansException {
        Arrays.asList(beanFactory.getBeanDefinitionNames())
        .forEach(beanDefinitionName ->
        System.out.println(beanDefinitionName));
        System.out.println("BeanFactoryPostProcessor...");
    }
}
复制代码

自定义Bean的后置处理器:

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public 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 {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("JackApplicationRunner...");
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/439408.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

sentinel配置文件

转载自 https://blog.csdn.net/u012441222/article/details/80751390 Redis的哨兵机制是官方推荐的一种高可用&#xff08;HA&#xff09;方案&#xff0c;我们在使用Redis的主从结构时&#xff0c;如果主节点挂掉&#xff0c;这时是不能自动进行主备切换和通知客户端主节点下…

涨点神器:基于Yolov8小目标遮挡物性能提升(SEAM、MultiSEAM)

1.遮挡物检测简介 不同的目标检测应用场景有不同的检测难点,小目标、多尺度以及背景复杂等问题,被遮挡的物体仍然是最先进的物体检测器面临的挑战。本文尝试解决待测目标相互遮挡带来的检测困难,对于人脸遮挡提出了一个名为 SEAM 的注意力模块并引入了排斥损失来解决它,引…

机器学习入门实例-加州房价预测-3(选择与训练模型+调参)

选择与训练模型 使用线性回归 from sklearn.linear_model import LinearRegressionfrom sklearn.metrics import mean_squared_errorlin_reg LinearRegression()lin_reg.fit(housing_prepared, housing_labels)housing_predictions lin_reg.predict(housing_prepared)lin_ms…

直播预告 | TDengine Apache SeaTunnel 联合应用最佳实践

TDengine 自诞生之日起&#xff0c;除产品层面的技术创新和实力提升外&#xff0c;也在大力完善自身产品生态&#xff0c;以此进一步满足用户的业务需求、提升使用体验。 近日&#xff0c;TDengine 与 Apache SeaTunnel 展开集成合作&#xff0c;双方将于 4 月 18 日 19:00 联…

二、Java 并发编程(5)

本章概要 线程上下文切换 线程上下文切换的流程导致线程上下文切换的原因 Java中的阻塞队列 阻塞队列的主要操作Java中阻塞队列的实现 2.7 线程上下文切换 CPU 利用时间片轮询来为每个任务都服务一定的时间&#xff0c;然后把当前任务的状态保存下来&#xff0c;继续服务下…

IO多路转接—select,poll,epoll

目录 select 函数介绍 select基本工作流程 select的优缺点及适用场景 poll poll的优缺点 epoll epoll的相关系统调用 epoll_create epoll_ctl epoll_wait epoll工作原理 epoll服务器编写 epoll的优点 epoll工作方式 select 函数介绍 系统提供select函数来实现多路复…

Spring核心设计思想

目录 前言&#xff1a; Spring是什么 什么是IoC 传统开发思想 IoC开发思想 Spring IoC 什么是DI 小结&#xff1a; 前言&#xff1a; 官网中提出&#xff1a;Spring makes programming Java quicker, easier, and safer for everybody. Spring’s focus on speed, simp…

YOLOv7+单目测距(python)

YOLOv7单目测距&#xff08;python&#xff09; 1. 相关配置2. 测距原理3. 相机标定3.1&#xff1a;标定方法13.2&#xff1a;标定方法2 4. 相机测距4.1 测距添加4.2 主代码 5. 实验效果 相关链接 1. YOLOV5 单目测距&#xff08;python&#xff09; 2. YOLOV5 双目测距&…

基于springboot的招聘信息管理系统源码数据库论文

目 录 1 绪 论 1.1 课题背景与意义 1.2 系统实现的功能 1.3 课题研究现状 2系统相关技术 2.1 Java语言介绍 2.2 B/S架构 2.3 MySQL 数据库介绍 2.4 MySQL环境配置 2.5 SpringBoot框架 3系统需求分析 3.1系统功能 3.2可行性研究 3.2.1 经济可行性 …

力扣sql中等篇练习(六)

力扣sql中等篇练习(六) 1 购买了产品A和产品B却没有购买产品C的顾客 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 1.2 示例sql语句 # 先求出既有的,然后再去筛选掉没有的 # 去重用不了内连接 SELECT t1.customer_id,c.customer_name FROM ( SELECT distinct cust…

《Spring MVC》 第二章 第一个程序

前言 Spring MVC 是 Spring 框架提供的一款基于 MVC 模式的轻量级 Web 开发框架。 Spring MVC 本质是对 Servlet 的进一步封装&#xff0c;其最核心的组件是DispatcherServlet&#xff0c;它是 Spring MVC 的前端控制器&#xff0c;主要负责对请求和响应的统一地处理和分发。C…

C++ auto 内联函数 指针空值

本博客基于 上一篇博客的 序章&#xff0c;主要对 C 当中对C语言的缺陷 做的优化处理。 上一篇博客&#xff1a;C 命名空间 输入输出 缺省参数 引用 函数重载_chihiro1122的博客-CSDN博客 auto关键字 auto作为一个新的类型指示符来指示编译器&#xff0c;auto声明的变量必须由…

uni-app使用时遇到的坑

一.uni-app开发规范 1.微信小程序request请求需要https 小程序端&#xff1a; 在本地运行时&#xff0c;可以使用http 但是预览或者上传时&#xff0c;使用http无法请求 APP端&#xff1a; 一般APP可以使用http访问 高版本的APP可能需要用https访问 二. uni-app项目 配置App升…

Java语言请求示例,电商商品详情接口,接口封装

Java具有大部分编程语言所共有的一些特征&#xff0c;被特意设计用于互联网的分布式环境。Java具有类似于C语言的形式和感觉&#xff0c;但它要比C语言更易于使用&#xff0c;而且在编程时彻底采用了一种以对象为导向的方式。 使用Java编写的应用程序&#xff0c;既可以在一台…

如何更好的进行数据管理?10 条建议给到你

这个时代数据量的快速增长和数据复杂性的大幅度提高&#xff0c;让企业迫切的寻找更加智能的方式管理数据&#xff0c;从而有效提高 IT 效率。 管理数据库不是单一的目标&#xff0c;而是多个目标并行&#xff0c;如数据存储优化、效率、性能、安全。只有管理好数据从创建到删除…

newman结合jenkins实现自动化测试

一、背景 为了更好的保障产品质量和提升工作效率&#xff0c;使用自动化技术来执行测试用例。 二、技术实现 三、工具安装 3.1 安装newman npm install -g newman查看newman版本安装是否成功&#xff0c;打开命令行&#xff0c;输入newman -v&#xff0c;出现 版本信息即安…

浅述 国产仪器 6362D光谱分析仪

6362D光谱分析仪&#xff08;简称&#xff1a;光谱仪&#xff09;是一款高分辨、大动态高速高性能光谱分析仪&#xff0c;适用于600&#xff5e;1700nm光谱范围的DWDM、光放大器等光系统测试&#xff1b; LED、FP-LD、DFB-LD、光收发器等光有源器件测试&#xff1b;光纤、光纤光…

C语言基础应用(五)循环结构

引言 如果要求123…100&#xff0c;你会怎么求解呢&#xff1f; 如果按照常规代码 int main() {int sum 0;sum 1;sum 2;sum 3;...sum 100;printf("The value of sum is %d\n",sum);return 0; }就会特别麻烦&#xff0c;并且代码过于冗长。下面将引入循环的概念…

硬件知识的基础学习

GPIO、继电器、三极管、PWM、MOS管 的 输入与输出。 本人没有系统的学习过专业的硬件知识&#xff0c;只有在实践过程中向前辈简单的学习&#xff0c;若有问题&#xff0c;还请大佬指正。 目录 一、GPIO 1.1 输入与输出的区别 1.2 输入 1.2.1 电流流向和电阻区分上拉输入…

动力节点老杜Vue笔记——Vue程序初体验

目录 一、Vue程序初体验 1.1 下载并安装vue.js 1.2 第一个Vue程序 1.3 Vue的data配置项 1.4 Vue的template配置项 一、Vue程序初体验 可以先不去了解Vue框架的发展历史、Vue框架有什么特点、Vue是谁开发的&#xff0c;对我们编写Vue程序起不到太大的作用&#xff0c;…