Tomcat Embed启动后localhost无法访问的解决方法及原因分析

tomcat-embed是开源Web服务器Tomcat在java中的内嵌版本,均由 Apache Tomcat官方发布。但网络上关于tomcat-embed的文章并不多,而且大多是基于8.x的版本,与最新版本在部分用法上有所不同。例如较新的tomcat-embed 10.x如果沿用8.x的代码,我们是无法在启动后正常的通过localhost进行访问的,在这里与大家分享一下lz的经验。

旧版的配置

我们先使用一个非常简单的例子来演示8.x版本的tomcat-embed配置启动流程:

public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().print("hello");
    }
}

上面是用来测试的servlet,接着我们初始化tomcat:

// class TomcatInit

// path是为了方便演示,个人不推荐这种写法
private static String path=TomcatInit.class.getProtectionDomain().getCodeSource().getLocation().getPath();

public static void main(String[] args) throws LifecycleException {
    Tomcat tomcat = new Tomcat();
    // 配置tomcat基本信息
    tomcat.setBaseDir(path+"static");
    Context context=tomcat.addContext("",path+"static");
    // 添加servlet
    Tomcat.addServlet(context,"MyServlet",new MyServlet());
    context.addServletMappingDecoded("/hello","MyServlet");
    // 启动tomcat并保持状态,否则程序运行完将直接关闭
    tomcat.start();
    tomcat.getServer().await();
}

关于这部分的配置在这里不再多说,网上相关教程还是不少的。但是当我们在9.x、10.x或更高版本中使用这段代码运行时,在浏览器中输入localhost:8080/hello会提示无法访问。

解决方法

此时,我们需要在tomcat.start()前面加入一段代码,Tomcat才会监听端口:

...省略其它代码
// tomcat9.0以上必须调用getConnector()方法之后才会监听端口
tomcat.getConnector();
// 启动tomcat并保持状态,否则程序运行完将直接关闭
tomcat.start();
tomcat.getServer().await();

原因分析

但通过查阅getConnector的源码注释,我们发现以下说明:

If there's no connector defined, it will create and add a default connector using the port and address
specified in this Tomcat instance, and return it for further customization.

如果没有定义连接器,它将使用此Tomcat实例中指定的端口和地址创建并添加一个默认连接器,并返回它以进行进一步定制。

根据上面的说法,Tomcat应该会自己创建一个默认的连接器,并没有解释为什么需要调用getConnector()才会激活端口监听。通过源码,我们猜测出了一种可能:

public Connector getConnector() {
    Service service = getService();
    if (service.findConnectors().length > 0) {
        return service.findConnectors()[0];
    }
    // The same as in standard Tomcat configuration.
    // This creates an APR HTTP connector if AprLifecycleListener has been
    // configured (created) and Tomcat Native library is available.
    // Otherwise it creates a NIO HTTP connector.
    Connector connector = new Connector("HTTP/1.1");
    connector.setPort(port);
    service.addConnector(connector);
    return connector;
}

其中service.addConnector(connector)是一个值得注意的关键点,这里方法创建了一个connector,并将其加入了当前tomcat实例的service中。那么我们猜测,导致需要额外调用getConnetor()的原因,是因为注释中所说的默认connetor连接器创建其实是在getConnetor()中创建的,而外部并不会主动去调用它。因此想创建默认的连接器,需要手动调用getConnetor(),否则将不会自动创建。

但是为什么Tomcat 8.x版本不需要呢?对比两个版本的源码,我们得出了答案:

// 8.5.32
public void start() throws LifecycleException {
    getServer();
    getConnector();
    server.start();
}

// 10.0.0
public void start() throws LifecycleException {
    getServer();
    server.start();
}

我们发现,旧版tomcat-embed在启动时自动调用了getConnetor(),而新版是没有的,这也解释了为什么在高版本中需要额外调用getConnetor的原因。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值