使用 Maven [Servlet app] 从 cmd 启动 Tomcat 嵌入式服务器?

Posted

技术标签:

【中文标题】使用 Maven [Servlet app] 从 cmd 启动 Tomcat 嵌入式服务器?【英文标题】:Start Tomcat embedded server using Maven [Servlet app] from cmd? 【发布时间】:2021-06-29 15:10:15 【问题描述】:

我的实习任务是查询一些 API。我必须通过不使用任何应用程序框架来做到这一点,即 Spring 或 Spring Boot。

两个学期前,我将 Servlet 编程作为一门课程。但我忘记了大部分内容。

其中一个要求是能够从 cmd 启动应用程序。所以我决定从 Eclipse(文件 -> 新建 -> Maven 项目)创建简单的 Maven 项目。另外,我在嵌入式 Tomcat 中添加了一个依赖项,因此应用程序可以通过使用 Maven 命令从 cmd 启动。

我的pom.xml 中有这个:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.company</groupId>
  <artifactId>AssignmentAppWeb</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>AssignmentApp</name>
  <description>Assignment App</description>
  <properties>
    <tomcat.version>8.0.48</tomcat.version>
    </properties>
  <dependencies>
  
    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-core</artifactId>
        <version>$tomcat.version</version>
    </dependency>
    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-jasper</artifactId>
        <version>$tomcat.version</version>
    </dependency>
    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-logging-juli</artifactId>
        <version>$tomcat.version</version>
    </dependency>
  </dependencies>
  
  <build>
    <plugins>
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>appassembler-maven-plugin</artifactId>
            <version>2.0.0</version>
            <configuration>
                <assembleDirectory>target</assembleDirectory>
                <programs>
                    <program>
                        <mainClass>p.Main</mainClass>
                    </program>
                </programs>
                
 
            </configuration>
            
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>assemble</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
         <plugin>  
            <!-- Build an executable JAR -->  
            <groupId>org.apache.maven.plugins</groupId>  
            <artifactId>maven-jar-plugin</artifactId>  
            <version>3.1.0</version>  
            <configuration>  
                <archive>  
                    <manifest>  
                        <mainClass>p.Main</mainClass>
                    </manifest>  
                </archive>  
            </configuration>  
        </plugin>  
    </plugins>
  </build>
</project>

这是我的主课:

package p;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.startup.Tomcat;

public class Main 
    
    public static void main(String[] args) throws LifecycleException 
        Tomcat tomcat = new Tomcat();
        tomcat.setBaseDir("temp");
        tomcat.setPort(8080);
         
        String contextPath = "/";
        String docBase = new File(".").getAbsolutePath();
         
        Context context = tomcat.addContext(contextPath, docBase);
         
        HttpServlet servlet = new HttpServlet() 
            @Override
            protected void doGet(HttpServletRequest req, HttpServletResponse resp)
                    throws ServletException, IOException 
                PrintWriter writer = resp.getWriter();
                 
                writer.println("<html><title>Welcome</title><body>");
                writer.println("<h1>Have a Great Day!</h1>");
                writer.println("</body></html>");
            
        ;
         
        String servletName = "Servlet1";
        String urlPattern = "/go";
         
        tomcat.addServlet(contextPath, servletName, servlet);      
        context.addServletMappingDecoded(urlPattern, servletName);
         
        tomcat.start();
        tomcat.getServer().await();
    


这是一般项目结构:

如果我将cd 转换为target,在执行mvn clean instal,然后java -jar AssignmentAppWeb-0.0.1-SNAPSHOT.jar 之后,我会收到此错误:

C:\Users\Miljan\Desktop\FevoWS1\AssignmentAppWeb\target>java -jar AssignmentAppWeb-0.0.1-SNAPSHOT.jar
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.NoClassDefFoundError: javax/servlet/Servlet
        at java.lang.Class.getDeclaredMethods0(Native Method)
        at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
        at java.lang.Class.privateGetMethodRecursive(Class.java:3048)
        at java.lang.Class.getMethod0(Class.java:3018)
        at java.lang.Class.getMethod(Class.java:1784)
        at sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:650)
        at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:632)
Caused by: java.lang.ClassNotFoundException: javax.servlet.Servlet
        at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
        ... 7 more

我做错了什么?我只需要通过 Servlet 添加几个控制器就可以了。不确定通过web.xml 注册servlet 是否比通过Main 类中的代码更正确。那么,如何启动嵌入式 Tomact?

【问题讨论】:

您需要将所有(运行时)依赖项添加到您的执行中。 Maven 有很多机制,参见例如程序集插件和依赖项:复制。我认为这里的 exec 插件就足够了:mvn exec:java -Dexec.mainClass=p.Main 这是正确的。 localhost:8080/go 确实显示消息。如果你愿意,写一个答案而不是这个评论,我会投票并设置为正确。谢谢尼科斯。 【参考方案1】:

本题问题的原因是Java要运行这个程序,需要Maven定义的所有运行时依赖。在大多数项目中,这些都太多且太复杂而无法手动指定,主要是 IMO 因为传递依赖。 Maven 作为我们的依赖管理器,提供了辅助工具。以下是我所知道的一些:

    最简单的例子,Maven Exec 插件。如果您不介意通过 Maven 项目运行您的程序。对于这种情况:

    mvn exec:java -Dexec.mainClass=p.Main
    

    Maven 组装插件。该站点的描述非常重要:“使开发人员能够将项目输出组合到一个可分发的存档中,该存档还包含依赖项、模块、站点文档和其他文件”。这并不简单,但仍然非常简单。它由一个称为程序集描述符的文件配置,该文件精确地定义了最终程序集中要包含的内容。

    带有dependency:copydependency:copy-dependencies 的Maven 依赖插件会将依赖jar 复制到文件系统中的某个文件夹中。它当然可以包括传递依赖关系并应用简单的转换,例如从 jar 文件中剥离版本号。从那里您可以手动将它们包含在您的类路径中,或者让脚本为您完成。

    Maven Shade 插件向前迈进了一步,将所有依赖项和应用程序代码重新打包到一个 jar 中,可以选择重命名其中的一些。

【讨论】:

以上是关于使用 Maven [Servlet app] 从 cmd 启动 Tomcat 嵌入式服务器?的主要内容,如果未能解决你的问题,请参考以下文章

将 App Engine servlet-api-2.5 升级到 servlet-api-3.1?

如何配置 maven 以使用 servlet 3

maven中servlet报错:不识别此servlet问题的解决办法

IDEA使用maven建web项目示例

使用Eclipse + Maven 构建Java Web 项目

Tomcat 10中Servlet无法正常使用的解决办法