写点什么

SpringBoot 应用使用自定义的 ApplicationContext 实现类

作者:程序员欣宸
  • 2022 年 6 月 10 日
  • 本文字数:5911 字

    阅读完需:约 19 分钟

SpringBoot应用使用自定义的ApplicationContext实现类

欢迎访问我的 GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos

为什么要自定义 application context

  • 在学习 spring 容器初始化的过程中,发现 spring 容器预留了一些扩展点,我们可以写子类来做功能扩展,例如 AbstractApplicationContext 类的 initPropertySources、postProcessBeanFactory、onRefresh 等方法都是空方法,留给子类来扩展用;

在传统 spring 框架下的扩展方式

  • 传统的 spring 框架下使用哪个 ApplicationContext 实现类,是自己写代码来指定的,下面是基于 spring 框架的应用的启动代码:


public static void main(String[] args) {        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");        Simple bean = context.getBean(Simple.class);        bean.execute();        context.close();    }
复制代码


  • 如果我们要用自定的 ApplicationContext 实现类,只要将上面的 ClassPathXmlApplicationContext 改成我们做的类即可;

探寻 SpringBoot 框架下的扩展方式

  • 先看一段代码,以下是一个 SpringBoot 应用启动的代码:


@SpringBootApplicationpublic class CustomizeapplicationcontextApplication {
public static void main(String[] args) { SpringApplication springApplication = new SpringApplication(CustomizeapplicationcontextApplication.class); springApplication.run(args); }}
复制代码


  • 从上述代码可知,我们需要去查看 SpingApplication 的 run 方法源码,那里应该有 ApplicationContext 初始化相关的信息,在 SpingApplication 的 run 方法中,果然找到了有用信息,如下图红框所示,是 createApplicationContext 方法创建了 ApplicationContext 实现类:



  • 去看看 createApplicationContext 方法,如下图:



  • 上图中有两点需要注意:


  1. 红框中显示,ApplicationContext 实现类来自成员变量 applicationContextClass,只要我们能设置成员变量 applicationContextClass,就达到了目的:在应用中使用自定义的 ApplicationContext 实现类;

  2. 绿框中显示,如果没有设置成员变量 applicationContextClass,就把 AnnotationConfigServletWebServerApplicationContext 作为 ApplicationContext 实现类,用在 spring 环境中,所以,我们在自定义 ApplicationContext 实现类时,用 AnnotationConfigServletWebServerApplicationContext 作为父类最合适(除了我们自己扩展的东西,其他部分都和默认的保持一致);

如何设置自定义的 ApplicationContext 实现类

  • 从 createApplicationContext 方法可以看出,设置了成员变量 applicationContextClass,就达到了使用自定义 ApplicationContext 实现类的目的,那么如何设置成员变量 applicationContextClass 呢?就在下面这个 setApplicationContextClass 方法:


public void setApplicationContextClass(      Class<? extends ConfigurableApplicationContext> applicationContextClass) {    this.applicationContextClass = applicationContextClass;    if (!isWebApplicationContext(applicationContextClass)) {      this.webApplicationType = WebApplicationType.NONE;    }  }
复制代码


  • 我们只要在启动应用的时候,在调用 springApplication 的 run 方法之前先调用 setApplicationContextClass 方法就能指定 ApplicationContext 实现类了;

  • 分析到此,您是否和我一样有疑虑:setApplicationContextClass 方法会不会被在其他地方被调用,导致我们的设置无效呢?通过以下两种方法来检查:


  1. 在 IntelliJ IDEA 中,通过"Find Usages"的方式查找该方法被调用的地方,结果只发现了一处 SpringBootServletInitializer 类的 onStartup 方法,这是 war 包形式的应用在配置 Initializer 的时候才会用到,main 方法启动的方式中不会用到;

  2. 在 setApplicationContextClass 方法中打断点,debug 启动应用,确认不会走进来;

实战使用自定义的 ApplicationContext 实现类

  • 理论分析完毕,可以实战验证了:

  • 基于 maven 创建一个 SpringBoot 的 web 应用,pom.xml 如下:


<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  <modelVersion>4.0.0</modelVersion>
<groupId>com.bolingcavalry</groupId> <artifactId>customizeapplicationcontext</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging>
<name>customizeapplicationcontext</name> <description>Demo project for Spring Boot</description>
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.4.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent>
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>
复制代码


  • 创建类 CustomizeApplicationContext,继承自 AnnotationConfigServletWebServerApplicationContext,重写了父类的几个方法,如下:


package com.bolingcavalry.customizeapplicationcontext;
import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
/** * @Description : 自定义的ApplicationContext实现类 * @Author : zq2599@gmail.com * @Date : 2018-08-11 17:12 */public class CustomizeApplicationContext extends AnnotationConfigServletWebServerApplicationContext {
Log logger = LogFactory.getLog(CustomizeApplicationContext.class);
@Override protected void initPropertySources() { super.initPropertySources(); logger.info("execute override initPropertySources"); }
@Override protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { super.postProcessBeanFactory(beanFactory); logger.info("execute override postProcessBeanFactory"); }
@Override protected void onRefresh() { super.onRefresh(); logger.info("execute override onRefresh"); }}
复制代码


  • 启动类 CustomizeapplicationcontextApplication 的 main 方法中,调用 setApplicationContextClass 方法,将 ApplicationContext 实现类设置为 CustomizeApplicationContext:


package com.bolingcavalry.customizeapplicationcontext;
import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplicationpublic class CustomizeapplicationcontextApplication {
public static void main(String[] args) { SpringApplication springApplication = new SpringApplication(CustomizeapplicationcontextApplication.class); springApplication.setApplicationContextClass(CustomizeApplicationContext.class); springApplication.run(args); }}
复制代码


  • 启动应用,查看日志如下,CustomizeApplicationContext 中重写的方法都被执行了,并且 initPropertySources 被执行了两次,那是因为除了 AbstractApplicationContext 类中有调用,在 ServletWebServerApplicationContext 类的 onRefresh 中会执行 createWebServer 方法,而 createWebServer 方法中也会调用一次 initPropertySources 方法:


2018-08-11 17:21:18.833  INFO 5132 --- [           main] c.CustomizeapplicationcontextApplication : Starting CustomizeapplicationcontextApplication on zhaoqinwin10 with PID 5132 (D:\temp\201808\09\customizeapplicationcontext\target\classes started by 12167 in D:\temp\201808\09\customizeapplicationcontext)2018-08-11 17:21:18.840  INFO 5132 --- [           main] c.CustomizeapplicationcontextApplication : No active profile set, falling back to default profiles: default2018-08-11 17:21:18.957  INFO 5132 --- [           main] c.b.c.CustomizeApplicationContext        : Refreshing com.bolingcavalry.customizeapplicationcontext.CustomizeApplicationContext@5906ebcb: startup date [Sat Aug 11 17:21:18 CST 2018]; root of context hierarchy2018-08-11 17:21:18.958  INFO 5132 --- [           main] c.b.c.CustomizeApplicationContext        : execute override initPropertySources2018-08-11 17:21:18.967  INFO 5132 --- [           main] c.b.c.CustomizeApplicationContext        : execute override postProcessBeanFactory2018-08-11 17:21:20.085  INFO 5132 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)2018-08-11 17:21:20.102  INFO 5132 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]2018-08-11 17:21:20.103  INFO 5132 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.322018-08-11 17:21:20.106  INFO 5132 --- [ost-startStop-1] o.a.catalina.core.AprLifecycleListener   : The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [C:\jdk\bin;C:\WINDOWS\Sun\Java\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\ProgramData\Oracle\Java\javapath;C:\Program Files (x86)\Intel\iCLS Client\;C:\Program Files\Intel\iCLS Client\;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files (x86)\Intel\Intel(R) Management Engine Components\DAL;C:\Program Files\Intel\Intel(R) Management Engine Components\DAL;C:\Program Files (x86)\Intel\Intel(R) Management Engine Components\IPT;C:\Program Files\Intel\Intel(R) Management Engine Components\IPT;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\Program Files\Intel\WiFi\bin\;C:\Program Files\Common Files\Intel\WirelessCommon\;C:\software\apache-maven-3.3.3\bin\;C:\software\Git\bin;E:\work\dubbo\zookeeper-3.3.6/bin;E:\work\dubbo\zookeeper-3.3.6/conf;D:\soft\gradle-1.1\bin;C:\jdk\bin;C:\software\svn\bin;D:\software\AndroidSdk\platform-tools;D:\software\calibre\;D:\software\gradle-2.14.1\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\Program Files\Git\cmd;C:\Users\12167\AppData\Local\Microsoft\WindowsApps;C:\Users\12167\AppData\Local\Programs\Fiddler;%USERPROFILE%\AppData\Local\Microsoft\WindowsApps;.]2018-08-11 17:21:20.226  INFO 5132 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext2018-08-11 17:21:20.227  INFO 5132 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1273 ms2018-08-11 17:21:20.305  INFO 5132 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Servlet dispatcherServlet mapped to [/]2018-08-11 17:21:20.310  INFO 5132 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'characterEncodingFilter' to: [/*]2018-08-11 17:21:20.310  INFO 5132 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]2018-08-11 17:21:20.310  INFO 5132 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpPutFormContentFilter' to: [/*]2018-08-11 17:21:20.310  INFO 5132 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'requestContextFilter' to: [/*]2018-08-11 17:21:20.330  INFO 5132 --- [           main] c.b.c.CustomizeApplicationContext        : execute override initPropertySources2018-08-11 17:21:20.330  INFO 5132 --- [           main] c.b.c.CustomizeApplicationContext        : execute override onRefresh
复制代码

实战源码下载

  • 本章实战的源码可以在 github 下载,地址和链接信息如下表所示:


  • 这个 git 项目中有多个文件夹,本章源码在文件夹 customizeapplicationcontext 下,如下图红框所示:


  • 至此,我们通过查看 SpringApplication 的源码,搞清楚了 spring 环境的 ApplicationContext 实现类在 SpringBoot 框架下如何指定,也做了一次简单的自定义实战,今后在研究和学习 spring 过程中,如果需要扩展 spring 容器就能在 SpringBoot 环境下进行了,相比传统的 war 包部署、以及指定多个 jar 包的 classpath 等操作,springboot 应用的单个 jar 包更方便省事。

欢迎关注 InfoQ:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...

发布于: 刚刚阅读数: 4
用户头像

搜索"程序员欣宸",一起畅游Java宇宙 2018.04.19 加入

前腾讯、前阿里员工,从事Java后台工作,对Docker和Kubernetes充满热爱,所有文章均为作者原创,个人Github:https://github.com/zq2599/blog_demos

评论

发布
暂无评论
SpringBoot应用使用自定义的ApplicationContext实现类_Java_程序员欣宸_InfoQ写作社区