springboot源码解析-自定义系统初始化器

猿起缘灭      2022-04-05     338

关键词:

开篇之前先把祖师爷搬出来
  费玉清:问大家一个脑筋急转弯,说西方人在浴缸中洗澡,打一种小吃,小吃街里很常见的那种
      思考。。。
      思考。。。
      思考。。。
  揭晓谜底:涮羊肉
  反正谜底我已经揭晓了,至于大家能不能看到,我就不管了,哈哈

概述

  本系列主要分析springboot启动过程中干了什么事情,尽量以白话的形式写出来,因为本人也很小白,望包涵。

  系统初始化器是springboot在容器刷新之前执行的一个回调函数,其主要的作用就是向容器中注册属性,平时我们可能不会用到吧,但是在spring框架内部这个系统初始化器使用非常多,大家就当看看大佬是如何做的初始化,以及可以想一下他们为什么这样做,或许以后自己写程序也可以学习这种思想。

 

实现方式

  通过实现ApplicationContextInitializer接口来定义系统初始化器,这个接口是一个函数式接口,就是接口中只有一个方法,是java8的新特性。下面来看一下这个接口的定义。

/**
 * Callback interface for initializing a Spring {@link ConfigurableApplicationContext}
 * prior to being {@linkplain ConfigurableApplicationContext#refresh() refreshed}.
 *
 * <p>Typically used within web applications that require some programmatic initialization
 * of the application context. For example, registering property sources or activating
 * profiles against the {@linkplain ConfigurableApplicationContext#getEnvironment()
 * context's environment}. See {@code ContextLoader} and {@code FrameworkServlet} support
 * for declaring a "contextInitializerClasses" context-param and init-param, respectively.
 *
 * <p>{@code ApplicationContextInitializer} processors are encouraged to detect
 * whether Spring's {@link org.springframework.core.Ordered Ordered} interface has been
 * implemented or if the @{@link org.springframework.core.annotation.Order Order}
 * annotation is present and to sort instances accordingly if so prior to invocation.
 *
 * @author Chris Beams
 * @since 3.1
 * @param <C> the application context type
 * @see org.springframework.web.context.ContextLoader#customizeContext
 * @see org.springframework.web.context.ContextLoader#CONTEXT_INITIALIZER_CLASSES_PARAM
 * @see org.springframework.web.servlet.FrameworkServlet#setContextInitializerClasses
 * @see org.springframework.web.servlet.FrameworkServlet#applyInitializers
 */
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {

    /**
     * Initialize the given application context.
     * @param applicationContext the application to configure
     */
    void initialize(C applicationContext);

}

大家可以看一下上面的解释,主要分为三段,总结下来就是:

  1. 这个接口的方法是在ConfigurableApplicationContext上下文中refresh()方法执行之前执行的一个回调
  2. 通常用在一些需要为应用上下文初始化的web应用中,比如,向容器中注册属性和激活配置
  3. 实现类可以使用@Order注解修饰,在调用之前可以对实例进行排序(注:@Order可以决定bean的执行顺序,值越小优先级越高)

上面对应用初始化器做了一个简单的介绍,并且看了应用初始化器的接口定义,下面就使用具体的例子来实战,自定义一些初始化,验证一下,这个先提前说一下,应用初始化有3种注入方式,具体看下面的例子。

第一种方式:使用META-INF/spring.factories

1.自定义系统初始化器

@Order(1)
public class FirstApplicationContextInitializer implements ApplicationContextInitializer<GenericApplicationContext> {

    @Override
    public void initialize(GenericApplicationContext context) {
        ConfigurableEnvironment environment = context.getEnvironment();
        Map<String,String> map = new HashMap<>();
        map.put("first","hello first");
        environment.getSystemProperties().putAll(map);
        System.out.println("firstApplicationContextInitializer is start");
    }
}

解释一下上面代码:这个代码很简单,就是向系统环境中添加一个新的属性,属性的key是first,value是hello first,然后当这个系统初始化器被执行的时候会打印firstApplicationContextInitializer is start

 

2.在resource目录下新建META-INF目录,之后在META-INF目录下新建文件spring.factories,在文件中添加如下代码

org.springframework.context.ApplicationContextInitializer=com.example.demo.initialize.FirstApplicationContextInitializer

解释:等号前面是系统初始化器接口的路径,这个不要改,等号后面是自定义的具体实现类,路径要写自己程序的这个类所在的路径

 

3.写一个controller,然后后去系统的环境,看看能不能后去到在系统初始化器中添加的first属性

@RestController
public class InitializeController {

    @Autowired
    ApplicationContext applicationContext;

    @GetMapping("/first")
    public String test(){
        String a = applicationContext.getEnvironment().getProperty("first");
        return a;
    }

    @GetMapping("/second")
    public String test1(){
        String a = applicationContext.getEnvironment().getProperty("second");
        return a;
    }

    @GetMapping("/third")
    public String test2(){
        String a = applicationContext.getEnvironment().getProperty("third");
        return a;
    }

}

ok,做完以上3部,就可以启动springboot程序了,下面是我的启动程序时的打印结果。

 .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.6.RELEASE)
firstApplicationContextInitializer is start
2020-05-29 10:20:52.989  INFO 1576 --- [           main] com.example.demo.DemoApplication         : Starting DemoApplication on ganxinledeMacBook-Pro.local with PID 1576 (/Users/ganxinle/workspace/demo/target/classes started by ganxinle in /Users/ganxinle/workspace/demo)
2020-05-29 10:20:52.991  INFO 1576 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
2020-05-29 10:20:53.686  INFO 1576 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2020-05-29 10:20:53.696  INFO 1576 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-05-29 10:20:53.696  INFO 1576 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.33]
2020-05-29 10:20:53.749  INFO 1576 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2020-05-29 10:20:53.749  INFO 1576 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 721 ms
2020-05-29 10:20:53.860  INFO 1576 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2020-05-29 10:20:53.979  INFO 1576 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2020-05-29 10:20:53.982  INFO 1576 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 1.258 seconds (JVM running for 1.577)
2020-05-29 10:21:12.351  INFO 1576 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2020-05-29 10:21:12.351  INFO 1576 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2020-05-29 10:21:12.354  INFO 1576 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 3 ms

大家可以看到我标红的地方,就是打印的结果,下面在浏览器中调用http://localhost:8080/first

可以看到已经获取到first属性的值。

总结:第一种实现方式已经介绍完毕,大家看过之后肯定有疑问,为什么要在resource目录下搞一个META-INF,还新建一个spring.factories,为什么在spring.factories中要那样配置,憋着机,等我介绍完3中方式之后,会把源码扒拉出来,让大家看一下具体的原因,没兴趣看源码的,到时候可以不用看那部分,哈哈

springmvc自定义类型转换器converter参数解析器handlermethodargumentresolver(代码片段)

...请求入参处理方式都在这了更多Spring系列源码分析文章见SpringBoot专栏:精通SpringBoot二、类型转换器ConverterSpring官方文档Spring3.0引入了一个core.convert,并提供通用类型转换系统的包。系统定义了一个SPI来实现类型转换逻辑... 查看详情

springboot系统初始化器

1、SpringBoot系统初始化器系统初始化器介绍SpringFactoriesLoader介绍系统初始化器原理总结 2、系统初始化器介绍类名:ApplicationContextinitializer介绍:Spring容量刷新之前执行的一个回调函数作用:向SpringBoot容器中注册属性使用:继... 查看详情

springboot自定义参数解析器(代码片段)

springboot自定义参数解析器1.前言2.springMVC参数解析器3.如何自定义参数解析器4测试1.前言1.springMVC是如何把参数解析完毕后注入到controller方法参数上的呢?在javaweb阶段,我们都学过使用HttpServletRequest这个对象获取参数࿰... 查看详情

springboot源码解析##如何自定义starter让springboot扫描到你的配置

  配置factories文件,然后让spring去扫描到  查看详情

6.通过demo分析自定义类加载器以及launcher源码分析(代码片段)

...lassLoader()呢3.3系统类加载器和扩展类加载器是如何创建和初始化的3.4如何将自定义类加载器作为默认的系统类加载器的3.4.1通过SystemClassLoaderAction构造方法将默认的系统类的类加载器,后续通过判断看是否返回3.4.2判断是否配置了... 查看详情

springboot3.0源码启动流程源码解析•下(代码片段)

...容器打印启动时间发布事件执行特定的run方法上一篇《【SpringBoot3.0源码】启动流程源码解析•上》,主要讲解了newSpringApplication()设置了一些初始化器和监听器,接下来我们讲解下run方法的调用。步入run方法:publicConf... 查看详情

spring源码解析-自定义标签解析和spi机制-3

...时加载到缓存,最初的版本是jdk中实现的,后来在spring、springboot、dubbo中都有相应的使用。3.JDK的SPI机制:META-INF下 查看详情

springboot自定义xml文件解析

...我们自定义的xml文件格式和xml文件解析处理器。新建一个Springboot工程,pom如下。SelfDefineXmlTrial/pom.xml:然后,新建一个用于测试controller。com.lfqy.springboot.selfdefxml.controller.SelfDefXmlController:最后,创建一个Springboot的启动类。com.lfqy... 查看详情

spring事件发布监听源码解析(代码片段)

文章目录初始化事件发布器流程注册事件监听器流程容器事件发布流程总结Spring事件监听机制离不开容器IOC特性提供的支持,比如容器会自动创建事件发布器,自动识别用户注册的监听器并进行管理,在特定的事件发... 查看详情

spring事件发布监听源码解析

文章目录初始化事件发布器流程注册事件监听器流程容器事件发布流程总结Spring事件监听机制离不开容器IOC特性提供的支持,比如容器会自动创建事件发布器,自动识别用户注册的监听器并进行管理,在特定的事件发... 查看详情

spring中自定义session管理,springsession源码解析(代码片段)

系列文章:SpringBoot学习大纲,可以留言自己想了解的技术点目录系列文章:SpringBoot学习大纲,可以留言自己想了解的技术点1、session是什么?1>session在哪里?2>服务器怎么知道每次说话的是哪个session3&... 查看详情

springboot源码解析-----springboot精髓:集成aop

本篇主要集成Sping一个重要功能AOP我们还是先回顾一下以前Spring中是如何使用AOP的,大家可以看看我这篇文章spring5源码深度解析-----AOP的使用及AOP自定义标签Spring中使用AOP引入Aspect<dependency><groupId>org.aspectj</groupId><... 查看详情

springboot一篇搞定(cookiesession跳转内容协商converter解析器thymeleaf)(代码片段)

文章目录Cookie处理设置cookie获取cookie删除Cookie小结Session处理作用域Request参数设置页面跳转通过Response通过Request内容协商Json返回XML返回基本原理说明自定义类型转换器通过特定参数获取返回类型参数解析原理解析示例自定义解析... 查看详情

springboot源码深度解析,方法解析,类加载解析,容器建立

springboot的启动都是从main方法开始的,如下:@SpringBootApplicationpublicclassApplication{publicstaticvoidmain(String[]args){SpringApplication.run(Application.class,args);}}后面会进入SpringApplication的初始化方法:publicstaticCo 查看详情

springboot3.0源码启动流程源码解析•上(代码片段)

文章目录初始化SpringBoot启动类:@SpringBootApplicationpublicclassAppRunpublicstaticvoidmain(String[]args)SpringApplication.run 查看详情

spring事件发布监听源码解析(代码片段)

文章目录初始化事件发布器流程注册事件监听器流程容器事件发布流程总结Spring事件监听机制离不开容器IOC特性提供的支持,比如容器会自动创建事件发布器,自动识别用户注册的监听器并进行管理,在特定的事件发... 查看详情

hession源码解析自定义序列化器(代码片段)

...的文件达到几百MB。无奈之下,仔细分析了下hessian的源码。介绍 先看一张hessian主要的几个概念图说明: Serializer 序列化的接口Deserializer反序列化的接口AbstractHessianInput hessian自定义的输入流,提供对应的read各种类... 查看详情

springboot攻略十一、自定义isqlinjector,添加通用方法updateallcolumnbyid

参考技术A参考【SpringBoot攻略七、集成mybatisplus实战】,做如下修改:1、自定义sql注入器GeneralMybatisPlusSqlInjector2、方法对应的实现类UpdateAllColumnById参考其他基本方法的实现类源码如:UpdateById等等3、MybatisPlus自定义SQL方法枚举类Ge... 查看详情