如何给Spring Boot 的嵌入式 Tomcat 部署多个应用?
题图:春节期间,北京植物园南门
Spring Boot 的应用,大都有这样的特别,你在添加了依赖之后,即使是 Web 应用,最终也可以通过 JAR 的形式运行,具体依赖的容器环境,则通过嵌入式的形式隐式的使用。
而像这些环境,Spring 的配置等,更多的隐藏在 Spring Boot 的内部,开发者可以更多的专注于「业务逻辑」的开发。
「解放了双手」的时候,话说回来,某些时候,也是有一些弊端的。比如像之前通过 WAR 文件的形式独立部署时,可以在容器内再额外部署一些「监控」应用,来观察容器的情况,应用的请求情况等,这些内容在嵌入式的时候,就有些办不从心了。
那对于 习惯了 Spring Boot 的 JAR 文件便捷运行的用户,有没有办法,能在保留 JAR 使用习惯的前提下,又能部署其他应用,来满足独立容器部署的形式和使用习惯呢?
答案是有的。鱼和熊掌,也可得兼。 后面我们会以嵌入式的 Tomcat 为例,来说明具体的实现方式。
首先,我们需要认识这一点,对于嵌入式的容器,他本质上依然还是容器,保留了容器的绝大数内容。所以,一些独立部署时的风格,接口也依然可以使用。
不熟悉 Spring Boot 内 Tomcat 工作原理的读者,可以参考这几篇旧文:
我们前面说,嵌入式容器,也还是容器,所以我们只要「拿到」这个容器,就可以对其进行操作了。
旧文里我们提过, Spring Boot 内的嵌入式 Tomcat,是自己 new 了一个Tomcat 实例出来,再把应用做为 Context 部署进去。我们要想部署其他的应用,也照着「葫芦」拿到 这个实例,部署应用。
Spring Boot 内,由于要支持各种 Servlet 容器,所以统一进行了抽象了创建容器的Factory,在 Spring Boot 1.x 和 2.x分别由
EmbeddedServletContainerFactory 和 ServletWebServerFactory 这两个接口表示。 而对应的工厂里创建出来的容器对象,在 1.x 和 2.x 中,分别由TomcatEmbeddedServletContainer 和 TomcatWebServer 这两个类来表示。
这个 Factory,也是做为一个 Bean 参与到Spring Boot 的启动流程中。我们需要做的,就是在启动的时候,定义这样一个Bean,并「重写」Factory 中可以拿到 Tomcat 实例的方法,拿到前面创建出来的 Tomcat 实例,即可完成应用的部署。
1.x 的方式如下:
@Bean
public EmbeddedServletContainerFactory servletContainerFactory() {
return new TomcatEmbeddedServletContainerFactory() {
protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
Tomcat tomcat) {
new File(tomcat.getServer().getCatalinaBase(), "webapps").mkdirs();
try {
Context context = tomcat.addWebapp("/test", "/home/test/sample.war"); // 这里是要部署的应用名称和路径
context.setParentClassLoader(getClass().getClassLoader());
} catch (Exception ex) {
throw new IllegalStateException("Failed to add webapp", ex);
}
return super.getTomcatEmbeddedServletContainer(tomcat);
}
};
}
2.x
@Bean
public ServletWebServerFactory servletContainerFactory() {
return new TomcatServletWebServerFactory() {
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
new File(tomcat.getServer().getCatalinaBase(), "hello").mkdirs();
try {
Context context =
tomcat.addWebapp("/foo", "/home/test/sample.war");
context.setParentClassLoader(getClass().getClassLoader());
} catch (Exception ex) {
throw new IllegalStateException("Failed to add webapp", ex);
}
return super.getTomcatWebServer(tomcat);
};
};
}
当然,还有其它的方法也可以实现类似的目的。
比如,几年前的一篇旧文,在分析 IDE里 Tomcat 的工作原理的时候,分析过 IDEA 里, Tomcat 是怎样部署应用的。那个实现思路,是通过 Tomcat 注册的 MBean,其中包含对于应用管理的MBean,对于嵌入式的 Tomcat,也依然放开了 MBean Server, 连接到上面就可以部署应用了。需要注意的一点,是嵌入式的 Tomcat,Host 的ObjectName,和独立运行的并不一样,需要注意,否则会导致部署失败。
如有需要,可以参考这两篇:
总结一下,嵌入式容器,也保留了独立部署容器的管理和使用习惯,在启动创建的过程中,可以获取其容器实例进行操作。也可以通过对外暴露的 MBean Server 进行操作。 条条大路通广州!
如果可以,请转发或者点个「好看」支持。感谢!
其他阅读(点击下方图片查看)
(SpringBoot DevTools 是怎样完成应用热部署的)
(你真的会高效的在GitHub搜索开源项目吗?)
(怎样才能Java Champion)
(如何开发自己的Spring Boot Starter)
(Tomcat是如何处理SpringBoot应用的?)
如果你喜欢本文
请长按二维码关注
转发朋友圈,是对我最大的支持。
更多精彩内容:
一台机器上安装多个Tomcat 的原理(回复001)
监控Tomcat中的各种数据 (回复002)
启动Tomcat的安全机制(回复003)
乱码问题的原理及解决方式(回复007)
Tomcat 日志工作原理及配置(回复011)
web.xml 解析实现(回复 012)
线程池的原理( 回复 014)
Tomcat 的集群搭建原理与实现 (回复 015)
类加载器的原理 (回复 016)
类找不到等问题 (回复 017)
代码的热替换实现(回复 018)
Tomcat 进程自动退出问题 (回复 019)
为什么总是返回404? (回复 020)
...
PS: 对于一些 Tomcat常见问题,在公众号的【常见问题】菜单中,有需要的朋友欢迎关注查看
如有帮助请点好看☟