您好,欢迎访问一九零五行业门户网

SpringBoot集成tomcat的方法是什么

spring boot 支持目前主流的 servlet 容器,包括 tomcat、jetty、undertow,可以在我们的项目中方便地集成这些 servlet 容器,减少了开发、运维的工作量。而传统的应用开发,需要经过繁锁的操作步骤:安装 tomcat –> 修改 tomcat 配置 –> 部署 war 包 –> 启动 tomcat –> 运维……,这个工作量不小,尤其是集群部署、应用迁移的时候。而采用 spring boot 之后,一切变得如此简单,打包 –> java -jar –> 运维,只需要一个 jar 包便可以随意部署安装。
spi在分析源码前,我们先来了解下 spring 的 spi 机制。我们知道,jdk 为了方便应用程序进行扩展,提供了默认的 spi 实现(serviceloader),dubbo 也有自己的 spi。spring 也是如此,他为我们提供了springfactoriesloader,允许开发人员通过meta-inf/spring.factories文件进行扩展,下面举一个例子方便理解
假如,我想要往 spring 容器中添加一个applicationcontextinitializer做一些初始化工作,我们可以借助 spring 提供的这个 spi 功能完成这个需求。
首先,在项目中创建meta-inf/spring.factories文件,文件内容如下所示:
org.springframework.context.applicationcontextinitializer=\
我们再写个 test case,便可以通过 spi 的方式获取我们定义的applicationcontextinitializer。看似很简单的一个功能,但是 spring boot 正是利用这个强大的扩展点,在 spring framework 的基础上为我们集成了常用的开源框架
@testpublic void testspringspi() { list<applicationlistener> listeners = springfactoriesloader.loadfactories( applicationlistener.class, classutils.getdefaultclassloader() ); system.out.println( listeners );
我们再来看看这个springfactoriesloader,关键代码如下所示,它通过读取meta-inf/spring.factories文件,并且查找方法参数指定的 class,然后创建对应的实例对象,并且返回。此外,还支持排序,可以使用以下几种方式进行排序
org.springframework.core.ordered:实现该接口
org.springframework.core.annotation.order:注解
javax.annotation.priority:注解
public static <t> list<t> loadfactories(class<t> factoryclass, classloader classloader) { list<string> factorynames = loadfactorynames(factoryclass, classloadertouse); list<t> result = new arraylist<t>(factorynames.size()); for (string factoryname : factorynames) { result.add(instantiatefactory(factoryname, factoryclass, classloadertouse)); } annotationawareordercomparator.sort(result); return result;
接下来,我们来分析下 spring boot 是如何利用 spi 机制集成 tomcat
springboot for tomcat在分析 tomcat 集成的源码之前,我们先来了解下 embeddedservletcontainer
embeddedservletcontainer:
spring 用embeddedservletcontainer封装了内嵌的 servlet 容器,提供了start、stop等接口用于控制容器的生命周期,并且 spring 内置了 tomcat、jetty、undertow 容器的实现,类图所下所示
我们再来看看 spring boot 中最常用的springbootapplication注解,原来是多个注解的综合体,而这个enableautoconfiguration便是 spring boot 用做自动化配置的注解
@springbootconfiguration@enableautoconfiguration@componentscan(excludefilters = { @filter(type = filtertype.custom, classes = typeexcludefilter.class), @filter(type = filtertype.custom, classes = autoconfigurationexcludefilter.class) })public @interface springbootapplication { // code......
我们在spring-boot-autoconfigure模块可以看到大量的 spi 配置,部分如下所示
# auto configure
org.springframework.boot.autoconfigure.enableautoconfiguration=\
org.springframework.boot.autoconfigure.web.embeddedservletcontainerautoconfiguration,\
原来enableautoconfiguration注解引入了embeddedservletcontainerautoconfiguration,而这个便是内嵌 servlet 容器的配置类,tomcat、jetty、undertow 都在这个类上面,通过@conditionalonclass注解加载不同的 servlet 容器。但是,这个类仅仅是注册了tomcatembeddedservletcontainerfactory,不足以帮助我们解除所有的困惑。不要急,我们先来看看tomcatembeddedservletcontainerfactory的类图。
由上面的类图可知,它实现了以下接口:
embeddedservletcontainerfactory:它是一个工厂模式,用于创建embeddedservletcontainer,即用于创建一个内嵌的 servlet 容器,这个接口里面只有一个getembeddedservletcontainer方法
configurableembeddedservletcontainer:用于配置embeddedservletcontainer,比如说端口、上下文路径等
分析了上面两个接口,原来创建 servlet 容器的工作是由embeddedservletcontainerfactory完成的,看下getembeddedservletcontainer方法的调用栈。在embeddedwebapplicationcontext中重写了genericwebapplicationcontext#onrefresh()方法,并且调用getembeddedservletcontainer方法创建 servlet 容器,我们接下来分析这个创建过程。
关键代码如下(省略异常处理):
embeddedwebapplicationcontext.java@overrideprotected void onrefresh() { super.onrefresh(); createembeddedservletcontainer();}private void createembeddedservletcontainer() { embeddedservletcontainer localcontainer = this.embeddedservletcontainer; servletcontext localservletcontext = getservletcontext(); if (localcontainer == null && localservletcontext == null) { // 从容器中获取bean,如果使用tomcat则返回tomcatembeddedservletcontainerfactory embeddedservletcontainerfactory containerfactory = getembeddedservletcontainerfactory(); this.embeddedservletcontainer = containerfactory.getembeddedservletcontainer(getselfinitializer()); } else if (localservletcontext != null) { getselfinitializer().onstartup(localservletcontext); } initpropertysources();
我们先画出主要的流程图
由上图可知,embeddedwebapplicationcontext在执行onrefresh方法的时候,首先调用父类的onrefresh,然后从容器中获取embeddedservletcontainerfactory的实现类。由于我们在 classpath 下面可以获取 tomcat 的 jar 包,因此embeddedservletcontainerautoconfiguration会在 spring 容器中注册tomcatembeddedservletcontainerfactory这个 bean。然后,由它创建tomcatembeddedservletcontainer,我们来看看具体的创建过程,代码如下所示:
tomcatembeddedservletcontainerfactory.java@overridepublic embeddedservletcontainer getembeddedservletcontainer( servletcontextinitializer... initializers) { tomcat tomcat = new tomcat(); // 实例化 apache tomcat file basedir = (this.basedirectory != null ? this.basedirectory : createtempdir("tomcat")); tomcat.setbasedir(basedir.getabsolutepath()); // 创建 connector 组件,默认使用org.apache.coyote.http11.http11nioprotocol connector connector = new connector(this.protocol); tomcat.getservice().addconnector(connector); // 支持对 connector 进行自定义设置,比如设置线程池、最大连接数等 customizeconnector(connector); tomcat.setconnector(connector); tomcat.gethost().setautodeploy(false); configureengine(tomcat.getengine()); for (connector additionalconnector : this.additionaltomcatconnectors) { tomcat.getservice().addconnector(additionalconnector); } preparecontext(tomcat.gethost(), initializers); return gettomcatembeddedservletcontainer(tomcat);
首先是实例化tomcat对象,然后创建connector组件,并且对connector进行相关的参数设置,同时也允许我们通过tomcatconnectorcustomizer接口进行自定义的设置。ok,创建了tomcat实例之后,需要创建tomcatembeddedservletcontainer,它依赖tomcat对象,在构造方法中便会启动 tomcat 容器,从而完成各个组件的启动流程
public tomcatembeddedservletcontainer(tomcat tomcat, boolean autostart) { assert.notnull(tomcat, "tomcat server must not be null"); this.tomcat = tomcat; this.autostart = autostart; initialize();}private void initialize() throws embeddedservletcontainerexception { synchronized (this.monitor) { addinstanceidtoenginename(); // remove service connectors to that protocol binding doesn't happen yet removeserviceconnectors(); // start the server to trigger initialization listeners this.tomcat.start(); // we can re-throw failure exception directly in the main thread rethrowdeferredstartupexceptions(); context context = findcontext(); contextbindings.bindclassloader(context, getnamingtoken(context), getclass().getclassloader()); // unlike jetty, all tomcat threads are daemon threads. we create a // blocking non-daemon to stop immediate shutdown startdaemonawaitthread(); }
以上就是springboot集成tomcat的方法是什么的详细内容。
其它类似信息

推荐信息