什么是Servlet
Servlet是用Java编写的服务器端程序,大多情况下它被理解为任何实现Servlet接口的类。servlet接口定义的是一套处理网络请求的规范,所有实现servlet的类,都需要实现它的五个方法。
也就是说,所有想要处理网络请求的类,都需要思考三件事情:
(1)初始化时需要做什么?
(2)销毁时需要做什么
(3)接收到请求时需要做什么
不同于网络编程,我们不需要在servlet中编写 监听某端口的代码,也就是说,servlet不会直接和客户端打交道。那请求又是如何到达servlet的呢?答案是Tomcat。
什么是Tomcat
Tomcat是Web服务器和Servlet容器的结合体:Web服务器存放着网站文件等资源,可向Web客户端提供文档;而Servlet容器存放着Servlet对象。
当我们通过Web服务器映射的URL访问资源时,主要是三个过程:
- 接收请求
- 处理请求
- 响应请求
接收和响应的步骤交由Web服务器处理,由于处理请求的逻辑(方式)是不同的,因此便通过Servlet来交给程序员编写。
当我们使用到Tomcat后,就不再需要写main方法,也不再需要new一个类,Tomcat通过web.xml将这些事情做完,
Servlet的五个方法
public interface Servlet {
void init(ServletConfig var1) throws ServletException;
ServletConfig getServletConfig();
void service(ServletRequest request, ServletResponse reponse) throws ServletException, IOException;
String getServletInfo();
void destroy();
}
其中init、service、destroy是生命周期方法。init和destroy各自只执行一次,而service方法会在每次有新请求到来时被调用,我们的主要逻辑操作写在service中。
ServletConfig类
翻译的意思是Servlet配置,我们是在web.xml中配置的Servlet,因此ServletConfig对象包装了Servlet的一些参数信息。
Request/Response
当请求到达Tomcat后,Tomcat通过字符串解析,将各个请求头,请求地址,请求参数都封装进了Request对象中,我们通过此对象来获取发送的请求信息。
当Tomcat传送信息给Request时,Response对象是空的,我们可以逻辑处理后通过response.write()方法将结果写入response内部缓冲区。Tomcat拿到reponse里的信息后,组成响应发给客户端。
抽象类HttpServlet
浏览器发送请求有多种方式,最基本的是Get/Post,如果单单通过继承Servlet来编写逻辑代码,那么就需要根据不同的请求来编写代码。
为了简化这个过程,便有了HttpServlet抽象类:
public abstract class HttpServlet extends GenericServlet {
...
}
它继承自抽象类GenericServlet.
public abstract class GenericServlet implements Servlet,
ServletConfig, Serializable {
private static final long serialVersionUID = 1L;
private transient ServletConfig config;
//init 方法是随 Servlet 实例化而被调用的
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
public abstract void service(ServletRequest var1, ServletResponse var2)
throws ServletException, IOException;
public void init() throws ServletException {}
相比于原本的Servlet,GenericServlet类做了一部分改动:
(1)原本是形参的ServletConfig对象变成了成员变量,这方便其他方法使用。
(2)init方法还调用了一个init空参方法,若我们希望在Servlet创建时做一些其他操作,可以继承自GenericServlet,覆盖init空参方法。
GenericServlet类并没有重写service方法,它的子类HttpServlet实现了service方法:
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod(); //请求方式method
long lastModified;
if (method.equals("GET")) {
lastModified = this.getLastModified(req);
if (lastModified == -1L) {
this.doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader("If-Modified-Since");
} catch (IllegalArgumentException var9) {
ifModifiedSince = -1L;
}
if (ifModifiedSince < lastModified / 1000L * 1000L) {
this.maybeSetLastModified(resp, lastModified);
this.doGet(req, resp);
} else {
resp.setStatus(304);
}
}
} else if (method.equals("HEAD")) {
lastModified = this.getLastModified(req);
this.maybeSetLastModified(resp, lastModified);
this.doHead(req, resp);
} else if (method.equals("POST")) {
this.doPost(req, resp);
} else if (method.equals("PUT")) {
this.doPut(req, resp);
} else if (method.equals("DELETE")) {
this.doDelete(req, resp);
} else if (method.equals("OPTIONS")) {
this.doOptions(req, resp);
} else if (method.equals("TRACE")) {
this.doTrace(req, resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[]{method};
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(501, errMsg);
}
}
HttpServlet类要求其子类重写doGet,doPost等方法,若没有重写则会显示405错误。
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_get_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(405, msg);
} else {
resp.sendError(400, msg);
}
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_post_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(405, msg);
} else {
resp.sendError(400, msg);
}
}
Servlet的生命周期
(1)加载:当Tomcat第一次访问Servlet的时候,Tomcat会负责创建Servlet的实例;
(2)初始化:当Servlet被实例化后,Tomcat会调用init()方法初始化对象
(3)处理:当浏览器访问Servlet时,Servlet会调用service()方法处理请求。
(4)销毁:当Tomcat关闭或检测到Servlet要从Tomcat删除时,会自动调用destroy()方法。
(5)卸载:当Servlet调用完destroy()方法后,等待垃圾回收。如果有需要再次使用这个Servlet,会重新调用init()方法进行初始化操作。
第一个Servlet程序
以IDEA2019.3.1为例:
创建完后结构大致如下:
在WEB-INF下右键NEW->Directory,创建classes和lib两个目录,分别用于存放编译后的class文件和存放依赖的jar包。
点击右上角的倒数第三个图标,进入 Project Structure窗口。
点击Modules ->选定项目->切换到Path选项,将Output path” 和 “Test output path” 都改为之前创建的classes目录,这样后面编译的class文件默认生成到classes目录下
切换到Dependencies 选项,然后点击右边的+号,选择JARs or directories,然后选择Jar Directory,并选择之前创建的lib目录。
点击右上角的Tomcat图标,如下图中的Tomcat8.5.3.5,然后点击Edit Configurations。
然后按下图配置:
在编写Servlet程序前可能还还需要引入Servlet的jar包,在下图中点击+选择Java,找到Tomcat安装目录,在lib目录下选择servlet-api.jar包即可。
最后是程序部分:
public class MyServlet extends HttpServlet {
private String message;
@Override
public void init() throws ServletException {
message = "C2y";
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html");
PrintWriter out = resp.getWriter();
out.println("<h1>" + message + "</h1:>");
}
}
在web.xml中的< web-app>之间添加。你可以在MyServlet 类前添加@WebServlet("/MServlet"),这样就不必在web.xml中配置了。
<servlet>
<servlet-name>MServlet</servlet-name>
<servlet-class>MyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MServlet</servlet-name>
<url-pattern>/MServlet</url-pattern>
</servlet-mapping>
ServletContext
当Tomcat启动时,就会创建一个对应的ServletContext,它代表当前Web应用,所有Servlet共享一个ServletContext对象,因此Servlet之间可以通过ServletContext进行通讯。
ServletContext是一个域对象,它类似于HashMap,我们可以往里面存key-value,存入的方法是setAttribute(String name,Object obj)
ServletContext sc = this.getServletContext();
sc.setAttribute("name", "C2y")
取出数据:
ServletContext sc = this.getServletContext();
String val = (String)sc.getAttribute("name", "C2y")