前言

这一章将记录使用NetBeans在Tomcat中创建servlet程序。

Servlet(Server Applet)是运行在WEB服务端的小程序,目的是和客户端交互并且生成动态的web内容,它可以接收来自客户端的请求并生成响应。

环境搭建

本篇记录将基于Java环境和Tomcat服务器创建 Servlet 应用程序,关于集成Java环境和NetBeans搭建Tomcat服务器可以看之前的记录:

【WEB】使用NetBeans配置Tomcat虚拟主机


创建Servlet程序

首先是一个我们必须要新建一个Web项目才能创建servlet应用,打开 Netbeans IDE,选择File -> New Project。

然后选择Java Web -> Web Application创建Java Web项目。

给你的Web项目起一个名字,例如hoyue,我们可以点击browse选择这个项目存储的位置:

然后选择我们之前创建过的Tomcat服务器,path处可以填写不同的映射地址。

对于Frameworks则不需要选择其他的框架,直接点击Finish。至此Java Web项目已经创建完成。

要创建 Servlet,请打开Source Package,右键单击<default packages> -> New -> Servlet

接下来为你的Servlet类文件命名,同时也建议自定义Package(包)名。

PS:Servlet类文件没有 main() 方法,它是一个Java类,通过覆盖doGet()、doPost()等方法来处理客户端请求。

接下来,如果在配置部署中勾选了“将信息添加到部署描述符(web.xml)它会自动将你的servlet应用注册到web.xml中,让web程序能自动识别到你的应用。

我们可以修改URL Pattern(URL模式),用于定义访问我们的servlet应用URL。

当然还可以通过@WebServlet注解来解决,之后也会讲解。如果选择添加@WebServlet注解这里请不要勾选。

@WebServlet注解用于将一个Java类声明为Servlet,并指定它所处理的URL模式。

至此就已经创建好了servlet应用。


运行Servlet应用

遇到错误问题可以到下面的Q&A下查找!

以一个ServletHello作为一个测试应用,让它输出hello world。例如下面的这个程序:

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

/**
 * Hello World! servlet
 */
public class ServletHello extends HttpServlet
{ 
    /**
     * Respond to any HTTP GET request with an 
     * HTML Hello World! page.
     * @param request
     * @param response
     * @throws javax.servlet.ServletException
     * @throws java.io.IOException
     */
    @Override
    public void doGet (HttpServletRequest request,
                       HttpServletResponse response) 
      throws ServletException, IOException
        {
          // Set the HTTP content type in response header
          response.setContentType("text/html; charset=\"UTF-8\"");
          
        // Create the body of the response
        try ( // Obtain a PrintWriter object for creating the body
        // of the response
                PrintWriter servletOut = response.getWriter()) {
            // Create the body of the response
            servletOut.println(
                    "<!DOCTYPE html \n" +
                    " PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \n" +
                    " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\"> \n" +
                    "<html xmlns='http://www.w3.org/1999/xhtml'> \n" +
                    " <head> \n" +
                    " <title> \n" +
                    " ServletHello.java \n" +
                    " </title> \n" +
                    " </head> \n" +
                    " <body> \n" +
                    " <p> \n" +
                    " Hello World! \n" +
                    " </p> \n" +
                    " </body> \n" +
                    "</html> ");
            servletOut.close();
            }
        }
}

我们可以将这些代码添加到刚才创建好的文件中,替换到Package之下(如果你设定了Package的名字):

注意:如果代码块区域出现了黄色三角形警告,可以点击自动修复让NetBeans帮你修改警告的部分即可。(上述提供的代码已经做出一部分修改)例如:

下面添加内容仅为没有勾选添加到web.xml的用户需要的操作

如果你之前没有勾选添加到web.xml的选项,请添加一些内容用于定义URL模式

我们需要引用一个javax.servlet.annotation.WebServlet库和@WebServlet 注解。

import javax.servlet.annotation.WebServlet;
@WebServlet(name = "ServletHello", urlPatterns = { "/ServletHello" })
//Class 定义之前

我们使用 @WebServlet 注解来定义一个 servlet,并使用 urlPatterns 属性将 HTTP GET 请求映射到 /ServletHello 路径上。当客户端访问这个路径时,将会执行 doGet 方法并将 "Hello, World!" 字符串写入 HTTP 响应中。

然后我们就可以通过这个地址运行访问我们的应用了。选择左侧该Servlet应用,点击Run。

接下来设置URL模式,这里因为我们这个例子中没有参数,那就直接点击OK即可。

然后等待NetBeans的部署,完成后就可以在浏览器中查看结果了。我这个例子的地址为:http://localhost:8084/hoyue/ServletHello

一般你的地址为:http://hostname:[port]/project_name/servlet_name

成功结果如图:


动态响应案例

上面的hello例子没有体现出servlet根据Get与Post动态反馈内容的能力,下面是一个简单的动态响应案例。

一个博客有一个登录页面和一个评论页面。如果我们正确填写了账号密码就会跳转到评论页面,反之则没有。

博客文件将会被放在项目的MOBFiles文件夹下,登录页面为login.html,addentry.html表示一个评论页面,servlet类文件为Login.java

我们先创建servlet类文件并命名为Login,在Package之后写下如下代码:

代码我已经修改过了,去除了暂时没用到的import和修改路径地址。

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class Login extends HttpServlet {
/**
* Processes requests for both HTTP <code>GET</code> and <code>POST</code>
* methods.
*
* @param request servlet request
* @param response servlet response
* @throws ServletException if a servlet-specific error occurs
* @throws IOException if an I/O error occurs
*/
@Override
protected void doGet (HttpServletRequest request,
HttpServletResponse response) 
throws ServletException, IOException
{
    response.sendRedirect("./MOBFiles/login.html");
}

@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
    String username = request.getParameter("username");
    String password = request.getParameter("pwd");
    if (username != null && password != null &&
    username.equals("nice") &&
    password.equals("try")) {
      HttpSession session = request.getSession();
      session.setAttribute("loggedIn", true);
      response.sendRedirect("./MOBFiles/addentry.html");
    }
    else {
      response.sendRedirect("./MOBFiles/login.html");
    }
}

代码的意思是:

  • doGet方法中,servlet发送一个重定向响应到URL“./MOBFiles/login.html”。这意味着当用户使用HTTP GET请求访问servlet时,他们将被重定向到登录页面。
  • doPost方法中,servlet处理POST请求,使用getParameter()方法从请求中检索“username”和“pwd”参数的值。然后,它检查用户名和密码是否都不为null,并匹配正确值“nice”和“try”,如果是这样,它将使用getSession()方法创建一个新会话,并将一个名为“loggedIn”的属性设置为true。最后,它发送一个重定向响应到URL“./MOBFiles/addentry.html”,这意味着用户将被重定向到“addentry”页面。
  • 如果用户名和密码不匹配或未提供,则servlet将发送一个重定向响应到原本登录页面。

接下来就是两个简单的html页面和它的一些CSS文件,我们把它们放到项目的MOBFiles文件夹下,即需要把文件夹移到web目录下,如图:

这些文件放到了网盘中可以直接下载:

Download

当然我们需要修改login.html文件中提交的action,以便于将post内容发送给servlet。只需要填写/project_name/Login即可,当然你需要根据你设置的名称改变这个目录。

最后尝试运行这个servlet程序,然后在浏览器中访问它,我设置的地址为http://localhost:8084/hoyue/Login,一般是http://hostname:[port]/project_name/Login_name

输入地址后会被重定向到登录页面login.html,如图:

我们在servlet程序中设置的账号为nice,密码为try,输入正确后会跳转到评论页面,如图:

如果密码错误则重新返回登录页面:

至此,这个就是一个简单的根据GET与POST做出响应的servlet程序案例了。


Q&A

下面是一些常见问题与解答,如果遇到了其他问题,欢迎在评论区留言,或通过邮箱等方式联系我

  1. Q:Class "[classname]" neither has a main method nor is it a servlet specified in web.xml
    首先,请注意在左侧文件中右键运行文件,而不是点击上方的运行键。上方的运行键在部署后将需要main()方法,而servlet没有main()方法。
    排除上述,这个问题还说明你的Servlet类没有在web.xml文件中被定义为servlet:如果你想将你的类作为 servlet 运行,那么你需要在 web.xml文件中定义它。

    • 如果你选择在创建servlet的时候就勾选了自动添加,请在Files-web-WEB-INF,找到web.xml文件,检查<servlet-name>和<servlet-class>是否填写正确,如果你修改了文件名,则也需要同步修改这里的配置。它的格式形如:
      <servlet>
          <servlet-name>Servletname</servlet-name>
          <servlet-class>package_name.Servletname</servlet-class>
      </servlet>
      
      <servlet-mapping>
          <servlet-name>Servletname</servlet-name&gt;
          <url-pattern>/url</url-pattern>
      </servlet-mapping>

      其中,Servletname为你设置的名字,package_name为你设置的包名,url是你设置的映射URL。

    • 如果你没有勾选添加到web.xml,那么请检查@WebServlet注解,例如在ServletHello类上添加@WebServlet注解,指定名称为"MyServlet",URL模式为"/hello",则它形如:
      @WebServlet(name = "MyServlet", urlPatterns = { "/hello" })
  2. Q:"HTTP Status 404 – Not Found"错误
    这通常是由于servlet的URL映射不正确导致的。确保在web.xml文件中或者在@WebServlet注解里正确地映射了servlet的URL,并且在浏览器中输入的URL与映射匹配。例如我的项目名字为hoyue,映射的URL模式为/ServletHello,那么我应该在浏览器中输入的是http://hostname:[port]/hoyue/ServletHello.
  3. Q:javax.servlet 不存在
    可能是因为没有将 Servlet API 添加到项目的依赖项中。
    在 NetBeans 中打开项目,右键单击项目,选择 "Properties",在 "Properties" 窗口中,选择 "Libraries" 选项卡。点击 "Add Library" 按钮,选择 "Java Platform", 选择任何一个新版的 "Java EE API"。确定即可,应该能够在项目中引用 javax.servlet 类。
    或者用其他头文件来代替它,把javax改为jakarta即可。
  4. Q:Tomcat部署时报错,出现无法找到类名称:org.apache.catalina.core.StandardWrapperValve.invoke 分配异常的servlet [classname]
    java.lang.ClassNotFoundException: [classname]
    这通常是由于Tomcat找不到你的servlet类文件导致的。通常情况下,Tomcat的类加载器会根据web.xml定位你的servlet类,你需要确保你的java文件名与web.xml中描述的一致。如果还是不行,你可以将Java文件打成一个 JAR 包,并将该JAR包放置到 Tomcat的lib目录中。这样,Tomcat 的类加载器就可以从 lib 目录加载你的类了。
  5. Q:Can't import javax.servlet.annotation.WebServlet
    请注意当前Tomcat版本后重试几次,可以尝试在web.xml头部添加下面代码声明它的规则:

    <web-app xmlns="http://java.sun.com/xml/ns/javaee" 
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
             http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
  6. Q:没用到的import头
    可以删除没有影响
  7. Q: 头部报错incorrect package
    如果你在创建的时候没有给包一个名字,它会默认为一个空的名字(default package),此时不需要package语句声明,但如果你自定义了,你就需要package语句在头部声明位于某个包中,例如:package servlet;
  8. Q:类名与文件名不匹配
    Java编译器会在编译源代码文件时,根据文件名来确定编译后的类的名称。如果文件名与public类的名称不匹配,编译器将无法正确地创建该类。请检查你的public类名是否与文件名一致。

后记

这一章记录了使用NetBeans在Tomcat中创建servlet程序的一些简单细节。

这里的一切都有始有终,却能容纳所有的不期而遇和久别重逢。
最后更新于 2023-05-17