这里是普通文章模块栏目内容页
如何给Spring Boot 的嵌入式 Tomcat 部署多个应用?

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

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

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

「解放了双手」的时候,话说回来,某些时候,也是有一些弊端的。比如像之前通过 WAR 文件的形式独立部署时,可以在容器内再额外部署一些「监控」应用,来观察容器的情况,应用的请求情况等,这些内容在嵌入式的时候,就有些办不从心了。

那对于 习惯了 Spring Boot 的 JAR 文件便捷运行的用户,有没有办法,能在保留 JAR 使用习惯的前提下,又能部署其他应用,来满足独立容器部署的形式和使用习惯呢?

答案是有的。鱼和熊掌,也可得兼。 后面我们会以嵌入式的 Tomcat 为例,来说明具体的实现方式。

首先,我们需要认识这一点,对于嵌入式的容器,他本质上依然还是容器,保留了容器的绝大数内容。所以,一些独立部署时的风格,接口也依然可以使用。

不熟悉 Spring Boot 内 Tomcat 工作原理的读者,可以参考这几篇旧文:

Tomcat 是怎样处理 SpringBoot应用的?

Tomcat 中 的可插拔以及 SCI 的实现原理

如何开发自己的 Spring Boot Starter

我们前面说,嵌入式容器,也还是容器,所以我们只要「拿到」这个容器,就可以对其进行操作了。

旧文里我们提过, 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) { 

#p#分页标题#e#

                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 进行操作。

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

收藏
0
有帮助
0
没帮助
0