如何给Spring Boot 的嵌入式 Tomcat 部署多个应用?

开发 开发工具
嵌入式容器,也保留了独立部署容器的管理和使用习惯,在启动创建的过程中,可以获取其容器实例进行操作。也可以通过对外暴露的 MBean Server 进行操作。

Spring Boot 的应用,大都有这样的特别,你在添加了依赖之后,即使是 Web 应用,最终也可以通过 JAR 的形式运行,具体依赖的容器环境,则通过嵌入式的形式隐式的使用。

而像这些环境,Spring 的配置等,更多的隐藏在 Spring Boot 的内部,开发者可以更多的专注于「业务逻辑」的开发。

[[257217]]

「解放了双手」的时候,话说回来,某些时候,也是有一些弊端的。比如像之前通过 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 的方式如下:

  1. @Bean 
  2.     public EmbeddedServletContainerFactory servletContainerFactory() { 
  3.         return new TomcatEmbeddedServletContainerFactory() { 
  4.    protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer( 
  5.                     Tomcat tomcat) { 
  6.                 new File(tomcat.getServer().getCatalinaBase(), "webapps").mkdirs(); 
  7.                 try { 
  8.                     Context context = tomcat.addWebapp("/test", "/home/test/sample.war"); // 这里是要部署的应用名称和路径 
  9.                     context.setParentClassLoader(getClass().getClassLoader()); 
  10.                 } catch (Exception ex) { 
  11.                     throw new IllegalStateException("Failed to add webapp", ex); 
  12.                 } 
  13.                 return super.getTomcatEmbeddedServletContainer(tomcat); 
  14.             } 
  15.         }; 
  16.     } 

2.x

  1. @Bean 
  2.     public ServletWebServerFactory servletContainerFactory() { 
  3.         return new TomcatServletWebServerFactory() { 
  4.             protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) { 
  5.                 new File(tomcat.getServer().getCatalinaBase(), "hello").mkdirs(); 
  6.                 try { 
  7.                     Context context = 
  8.                             tomcat.addWebapp("/foo", "/home/test/sample.war"); 
  9.                     context.setParentClassLoader(getClass().getClassLoader()); 
  10.                 } catch (Exception ex) { 
  11.                     throw new IllegalStateException("Failed to add webapp", ex); 
  12.                 } 
  13.                 return super.getTomcatWebServer(tomcat); 
  14.             }; 
  15.         }; 
  16.     } 

当然,还有其它的方法也可以实现类似的目的。

比如,几年前的一篇旧文,在分析 IDE里 Tomcat 的工作原理的时候,分析过 IDEA 里, Tomcat 是怎样部署应用的。那个实现思路,是通过 Tomcat 注册的 MBean,其中包含对于应用管理的MBean,对于嵌入式的 Tomcat,也依然放开了 MBean Server, 连接到上面就可以部署应用了。需要注意的一点,是嵌入式的 Tomcat,Host 的ObjectName,和独立运行的并不一样,需要注意,否则会导致部署失败。

总结一下,嵌入式容器,也保留了独立部署容器的管理和使用习惯,在启动创建的过程中,可以获取其容器实例进行操作。也可以通过对外暴露的 MBean Server 进行操作。

【本文为51CTO专栏作者“侯树成”的原创稿件,转载请通过作者微信公众号『Tomcat那些事儿』获取授权】

戳这里,看该作者更多好文

责任编辑:赵宁宁 来源: 51CTO专栏
相关推荐

2018-05-02 16:34:56

EAF嵌入式框架

2012-07-30 14:13:11

Linux 2.6内核嵌入式

2009-04-11 15:22:24

Linux 2.6内核应用

2009-12-17 18:38:56

Fedora 7嵌入式

2009-04-11 15:12:24

2011-01-14 13:13:23

嵌入式Linux开发

2009-05-28 13:39:13

Windows CE

2010-06-09 19:56:04

2022-01-04 22:19:38

Linux开发嵌入式

2022-01-03 23:33:40

Linux组件系统

2009-12-24 17:21:38

嵌入式Linux

2023-01-04 09:37:16

2022-12-14 08:06:08

2020-04-22 11:51:41

物联网嵌入式编程IOT

2023-04-27 07:06:18

2022-03-25 20:00:40

人工智能机器人

2009-05-14 09:28:16

嵌入式面试求职

2014-05-26 15:14:04

DIGIA嵌入式Qt

2011-07-01 16:43:26

微软MVP微软嵌入式金融行业

2010-06-09 11:33:50

嵌入式开发微软Windows 7
点赞
收藏

51CTO技术栈公众号