Servlet是什么
Servlet本质就是一个接口,在servlet-api.jar这个jar包中就定义了Servlet接口。我们知道接口的作用其实就是定义一种规范,Servlet接口定义的则是关于Servlet生命周期以及处理http请求的一种规范。Servlet中定义了5个方法,Servlet容器会通过调用这几个方法与具体的Servlet来交互,包括管理创建、销毁Servlet实例,处理Http请求等。
Servlet接口中的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13
| public interface Servlet {
public void init(ServletConfig config) throws ServletException;
public ServletConfig getServletConfig();
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
public String getServletInfo();
public void destroy(); }
|
Servlet容器
首先,我们从服务端编程说起,如下是使用Socket编写服务端的一个示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| public class ServerSocketHandler implements Runnable {
private Socket socket;
public ServerSocketHandler(Socket socket) { this.socket = socket; }
@Override public void run() { BufferedReader bufferedReader = null; PrintWriter printWriter = null; try { bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); printWriter = new PrintWriter(socket.getOutputStream(), true); String line; StringBuilder info = new StringBuilder(); while ((line = bufferedReader.readLine()) != null) { info.append(line).append("\n"); } System.out.println("Server接收到了客户端发送的信息:\n" + info); printWriter.println("我是服务端\n我已接收到了请求..."); } catch (Exception e) { e.printStackTrace(); } finally { if (bufferedReader != null) { try { bufferedReader.close(); } catch (IOException e) { e.printStackTrace(); } } if (printWriter != null) { printWriter.close(); } if (socket != null) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } }
} } }
public class Server {
private static final int PORT = 9000; private static final ExecutorService threadPool = Executors.newFixedThreadPool(5);
public static void main(String[] args) { ServerSocket serverSocket = null; try { serverSocket = new ServerSocket(PORT); System.out.println("服务器启动了..."); while (true) { Socket socket = serverSocket.accept(); threadPool.execute(new ServerSocketHandler(socket)); } } catch (Exception e) { e.printStackTrace(); } finally { if (serverSocket != null) { try { serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } }
} }
|
从这个简单的示例可以看出,服务端在处理客户端的网络请求时,主要做了3件事情:
Servlet一整套体系(即Java Servlet API)的引入,就是为了简化这个服务端编程的过程。Servlet容器,顾名思义里面存放着Servlet对象,当客户端发送Http请求时容器会将接收到的请求封装为ServletRequest,并且将响应封装为ServletResponse,然后找到对应的Servlet实例,调用Servlet的service方法处理请求。如下图:

上图中的Tomcat就是最常用的Servlet容器。
web.xml
为了理解地更深入一点,我们需要了解一下web.xml文件。
web.xml文件是Tomcat中Web项目中的一个配置文件,主要用于配置Filter、Listener、Servlet等,但并不是必须的,一个Java Web项目没有web.xml文件也是照样能跑起来的。Tomcat启动的时候,会根据web.xml文件中配置的Servlet、Filter等创建对应的实例,当客户端发送一个请求过来,再根据配置的请求路径找到对应的Servlet实例,将请求交给它去处理。
如下就是一个名为HelloServlet的 Servlet 在web.xml文件中的配置示例:
1 2 3 4 5 6 7 8
| <servlet> <servlet-name>HelloServlet</servlet-name> <servlet-class>com.lzumetal.web.servlet.HelloServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>HelloServlet</servlet-name> <url-pattern>/HelloServlet</url-pattern> </servlet-mapping>
|
@WebServlet注解
在 Servlet3.0 以后,我们也可以不用再web.xml里面配置Servlet,只需要在Servlet类上加上@WebServlet注解就可以配置该Servlet的属性了。
1 2 3 4 5 6 7 8 9
| @WebServlet(name = "annoationServlet", urlPatterns = "/test/testAnnotationServlet") public class AnnotationServlet extends HttpServlet {
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().print("from the Servlet by annotation"); } }
|
比如基于SpringBoot的项目就没有web.xml文件,想要配置Servlet的话就可以通过注解的方式配置。
Servlet相关的接口和类
前面说到Servlet是接口,具体的实现类就可以处理网络请求,我们编写Servlet时一般不会直接去实现Servlet,而是去继承HttpServlet。HttpServlet则继承自GenericServlet这个抽象类。下面分析一下这三者。
Servlet接口的方法
| 方法声明 |
功能描述 |
| void init(ServletConfig config) |
容器在创建好 Servlet 对象后,就会调用此方法。该方法接收一个 ServletConfig 类型的参数,Servlet 容器通过该参数向 Servlet 传递初始化配置信息 |
| ServletConfig getServletConfig() |
用于获取 Servlet 对象的配置信息,返回 Servlet 的 ServletConfig 对象 |
| String getServletInfo() |
返回一个字符串,其中包含关于 Servlet 的信息,如作者、版本和版权等信息 |
| void service (ServletRequest request,ServletResponse response) |
负责响应用户的请求,当容器接收到客户端访问 Servlet 对象的请求时,就会调用此方法。 容器会构造一个表示客户端请求信息的 ServletRequest 对象和一个用于响应客户端的 ServletResponse 对象作为参数传递给 service() 方法。 在 service() 方法中,可以通过 ServletRequest 对象得到客户端的相关信息和请求信息,在对请求进行处理后,调用 ServletResponse 对象的方法设置响应信息 |
| void destroy() |
负责释放 Servlet 对象占用的资源。当服务器关闭或者 Servlet 对象被移除时,Servlet 对象会被销毁,容器会调用此方法 |
GenericServlet

在GenericServlet一个重要点是增加了一个成员变量ServletConfig,并且在init(ServletConfig config)方法中对它赋值,再重新定义了一个抽象的init()方法。
1 2 3 4 5
| @Override public void init(ServletConfig config) throws ServletException { this.config = config; this.init(); }
|
但GenericServlet并没有实现service (ServletRequest request,ServletResponse response)方法,需要子类去实现。
HttpServlet
HttpServlet继承了GenericServlet,本身也是个抽象类,不能直接进行实例化。这个类包含了对http协议请求的通用处理,
HttpServlet主要有两大功能,具体如下。
- 根据用户请求方式的不同,定义相应的
doXxx()方法处理用户请求。例如,与 GET 请求方式对应的 doGet() 方法,与 POST 方式对应的 doPost() 方法。当定义的类继承 HttpServlet 后,只需要根据请求方式重写对应的 doXxx() 方法即可,而不需要重写 service() 方法。
- 通过
service() 方法将 HTTP 请求和响应分别强转为 HttpServletRequest 和 HttpServletResponse 类型的对象。

BaseHttpServlet
如果在项目中使用要写Servlet,通常会写一个抽象类继承自HttpServlet,比如类名为BaseHttpServlet,并且把get、post等方法都统一用一个方法去处理。例如:
1 2 3 4 5 6 7 8 9 10
| protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doService(request, response); }
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doService(request, response); }
protected abstract void doService(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
|