Java Web学习系列(二) Servlet基础

简介

Servlet是使用Java语言编写的运行在服务端的程序。Servlet主要用于处理客户端传来的HTTP请求,并返回响应

Servlet由Servlet容器提供,Servlet容器就是指提供了Servlet功能的服务器(如Tomcat)。Servlet工作流程:

客户端 -> HTTP服务器 -> Servlet容器 -> Servlet(解析并响应)

对Servlet的请求会先被HTTP服务器接收,HTTP服务器负责解析静态资源,对于Servlet的请求交给Servlet容器,Servlet容器根据web.xml配置去调用响应的Servlet,最后再倒着将处理结果返回给客户端

实现Servlet程序

Servlet接口中定义了5个抽象方法:

  • void init(ServletConfig config):容器在创建好Servlet对象后,就会调用此方法。该方法接收一个ServletConfig类型的参数,Servlet容器通过这个参数向Servlet传递初始化配置信息
  • ServletConfig getServletConfig():用于获取Servlet对象的配置信息
  • String getServletInfo():返回包含关于Servlet的信息,如作者、版本、版权等
  • void service(ServletRequest request, ServletResponse response):负责响应用户的请求
  • void destroy():负责释放Servlet对象占用的资源。当服务器关闭或者Servlet对象被移除时,Servlet对象会被销毁,容器会调用此方法

有两个默认的Servlet接口实现类:

  • GenericServlet:是一个抽象类,为Servlet接口提供了部分实现,它并没有实现HTTP请求处理
  • HttpServlet:是GenericServlet的子类,它继承了GenericServlet的所有方法,并且为HTTP请求中的POST、GET等类型提供了具体操作方法

HelloWorldServlet.java中写入:

1
2
3
4
5
6
7
8
9
10
import java.io.*;
import javax.servlet.*;

public class HelloWorldServlet extends GenericServlet {
public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException{
PrintWriter out = response.getWriter();
out.println("Hello World");
}
}

throws主要是声明这个方法会抛出这种类型的异常,使它的调用者知道要捕获这个异常

编译后将生成的HelloWorldServlet.class移动到webapps/Test/WEB-INF/classes,其中classes目录需要自己创建;之后在webapps/Test/WEB-INF/web.xml配置:

1
2
3
4
5
6
7
8
9
10
<servlet>
<servlet-name>HelloWorldServlet</servlet-name>
<servlet-class>
HelloWorldServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloWorldServlet</servlet-name>
<url-pattern>/HelloWorldServlet</url-pattern>
</servlet-mapping>

元素<servlet>用于注册Servlet,它的子元素<servlet-name>用来指定Servlet名称,<servlet-class>用于指定完整类名;元素<servlet-mapping>用于映射Servlet对外访问的虚拟路径,它的子元素<servlet-name>的值必须和<servlet>的子元素<servlet-name>的值相同;<url-pattern>用于指定访问该Servlet的虚拟路径,路径以/开头,代表当前web应用程序的根目录

配置成功后访问127.0.0.1/HelloWorldServlet查看效果

配置过程中需要注意以下问题

问题及解决方法

1.环境变量要配置servlet-api,如果不配置而去直接编译则会报错,因为Servlet程序是一个JavaEE程序而不是JavaSE程序,Tomcat下lib文件包含了很多与Tomcat服务器相关的JAR文件,servlet-api.jar文件就是与Servlet相关的JAR文件

配置环境变量:

sudo vi /etc/profile

添加:

CLASSPATH=/opt/tomcat8/lib/servlet-api.jar:$CLASSPATH

然后:

source /etc/profile

2.java.lang.UnsupportedClassVersionError

高版本的JDK能向下兼容以前版本的class文件,但不能运行以后更高版本的class文件;因此更改一下javac版本即可:

3.java.lang.NoClassDefFoundError

这个问题好像是web.xml配置servlet路径出了问题,把servlet的class文件直接放到classes里面就正常,但放到classes里面的子文件夹中再配置web.xml就会出错,暂时不明白.有明白的大佬可以联系我,本菜鸡不胜感激

Servlet的生命周期

Servlet的生命周期分为三个阶段:

  • 初始阶段:客户端发出请求访问Servlet,Servlet容器会解析请求,检查内存中是否已经有了该Servlet对象,如果有则直接使用该Servlet对象,没有就创建Servlet对象,然后通过init()方法实现Servlet初始化工作。在Servlet生命周期中,init()只会被调用一次
  • 运行阶段:每次请求都会调用service()方法去处理。在Servlet生命周期中,service()方法会被多次调用
  • 销毁阶段:当服务器关闭或Web应用被移出容器时,在Servlet销毁之前,Servlet容器会调用Servlet的destory()方法。在Servlet生命周期中,destory()方法只会被调用一次

现在修改HelloWorldServlet源码,重新编译进行验证:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.io.*;
import javax.servlet.*;

public class HelloWorldServlet extends GenericServlet {

public void init(ServletConfig config) throws ServletException {
System.out.println("init method is called");
}

public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException{
PrintWriter out = response.getWriter();
out.println("Hello World");
System.out.println("service method is called");
}

public void destory(){
System.out.println("destroy method is called");
}

}

编译好后请求,然后切换到logs/目录,输入:

sudo tail -f catalina.out

tail -f会把指定文件的最尾部的内容显示在屏幕上,并且不断刷新,常用于查看正在改变的日志文件

可以看到,首次请求时会调用init()方法,之后的每次请求都会调用service()方法,可是我怎么弄都发现不能调用destory()…无论是执行./shutdown.sh还是在管理servlet的页面停止servlet或是卸载servlet,都不能调用:)可能被覆盖了

自动加载Servlet程序:在web.xml中的<servlet>标签中添加:

<load-on-startup>1</load-on-startup>

这样便会随着tomcat的启动自动启动servlet

使用HttpServlet类处理GET和POST请求

HttpServlet主要有两大功能:

  • 根据请求方式不同,定义doGetdoPost方法分别处理GET和POST请求
  • 通过service方法将HTTP请求和响应转为HttpServletRequestHttpServletResponse对象

下面这个例子,实现了对不同请求方式的处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class HelloWorldServlet extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException{
PrintWriter out = response.getWriter();
out.write("this is doGet method");
}

public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException{
PrintWriter out = response.getWriter();
out.write("this is doPost method");
}

}

Servlet虚拟路径映射

多重映射

既可以通过配置多个<servlet-mapping>元素来实现,也可以配置多个<url-pattern>来实现

1
2
3
4
5
6
7
8
9
10
11
<servlet-mapping>
<servlet-name>HelloWorldServlet</servlet-name>
<url-pattern>/HelloWorldServlet</url-pattern>
<url-pattern>/test1</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>HelloWorldServlet</servlet-name>
<url-pattern>/test</url-pattern>
<url-pattern>/test2</url-pattern>
</servlet-mapping>

上面的配置可以通过访问:/HelloWorldServlet/test/test1/test2这几个路径来访问HelloWorldServlet应用

通配符

有时希望访问根目录下的任何路径都能访问到目标Servlet应用,或者指定正确后缀名才能访问,则用到了通配符

通配符格式有以下两种:

  • *.扩展名:例如*.do,匹配以.do结尾的URL
  • /*:匹配任何字符串

这两种格式不能混用,例如/*.do

缺省Servlet

url-pattern仅仅是一个/时,这个Servlet就是当前Web应用的缺省Servlet

1
2
3
4
<servlet-mapping>
<servlet-name>HelloWorldServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

缺省Servlet用于处理其他Servlet都不处理的访问请求。对于上述配置,访问/gtfly也能正常访问到HelloWorldServlet