servlet接口实现类的开发步骤:
- 创建一个类,让其继承HttpServlet类,使之成为servlet接口实现类
- 重写HttpServlet中的doPost和doGet方法,以编写处理这两种请求的逻辑
- 将servlet接口实现类的信息注册到Tomcat服务器
我们先来看第一步,创建一个类,让其继承HttpServlet类,使之成为servlet接口实现类:
一个类需要实现servlet的接口才能被服务器操作,如创建这个servlet实现类的对象,之后根据此实例对象调用service方法处理当前请求(如post或get)。
但直接继承servlet不太好,因为你需要实现这个接口的所有方法,事实上你要用的只是接口中的service方法,因此让你这个类继承HttpServlet更好,因为后者实现了那些你不需要的方法,所以继承了HttpServlet之后你可以专心的操作service方法。
HttpServlet是一个抽象类,其作用就是:降低接口实现类对接口实现过程的难度。https://www.bilibili.com/video/BV1y5411p7kb?p=2,16min左右。
下图是servlet接口中定义的方法,我们用不到前四个,只能用到service:
追根溯源,查看我们继承的HttpServlet类,发现里面并没有实现servlet中我们用不到的方法,于是继续查看GenericServlet类,发现其实现了Servlet接口:
在GenericServlet类中,destroy方法的实现:
getServletInfo的实现:
init实现:
getServletConfig实现:
可以看到,即便是直接实现Servlet接口的类,对这几个用不到的方法也没做啥事,然后:
我们发现在GenericServlet类中,把service方法给留出来了,他没有对其进行实现,而是在HttpServlet中对其进行了实现:
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod(); // 获取请求的方法,即浏览器是以何种方式进行请求的:get或post
long lastModified;
if (method.equals("GET")) {
lastModified = this.getLastModified(req);
if (lastModified == -1L) {
this.doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader("If-Modified-Since");
if (ifModifiedSince < lastModified) {
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才方便做一些开发, 下面说一下继承此类之后要做啥
第二步,重写HttpServlet中的doPost和doGet方法,以编写处理这两种请求的逻辑:
问题来了,为啥要重写这俩方法呢?你刚刚不是说一个类实现了Servlet接口,然后Tomcat就能创建这个类的对象,在然后通过这个对象调用service方法吗?你现在又重写了这俩方法,那他俩是咋被调用的?我们先查看一下HttpServlet类可重写的方法:
发现有7个带do的方法,浏览器之前发送请求的时候有7种方式,但目前只考虑get和post两种了。
我们现在来看post和get方法是咋被调用的,继续看上面的service代码,解释在注释中:
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod(); // 获取请求的方法,即浏览器是以何种方式进行请求的:get或post
long lastModified;
if (method.equals("GET")) { // 如果是get方法
lastModified = this.getLastModified(req);
if (lastModified == -1L) {
this.doGet(req, resp); // 如果是子类调用service,那么这个this就指向子类对象,此时如果重写doGet方法,即可编写我们想要的逻辑,doGet方法在此时即被调用,下方的doPost同理
} else {
long ifModifiedSince = req.getDateHeader("If-Modified-Since");
if (ifModifiedSince < lastModified) {
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")) { // 和doGet同理
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);
}
}
我们不必调用service、doGet和doPost方法,因为父类已经为我们做好了,这种由父类决定在何种情况下调用子类中的方法,在设计模式中称为–模板设计模式
上述逻辑为:
我们已经说了两步:1. 创建一个类,让其继承HttpServlet,以便Tomcat能创建这个类的对象;2. 实现doPost和doGet方法,以便我们写的类的对象能调用父类service方法进而处理post或get请求,下面是第三步:
第三步,将servlet接口实现类的信息注册到Tomcat服务器:
上图的全路径如图所示,如果想访问OneServlet.class,这么访问就太麻烦了:
因此用/one和全路径做映射,访问时更方便:
Servlet对象的生命周期
我们用代码来验证一下:
第一个servlet实现类,OneServlet:
public class OneServlet extends HttpServlet {
public OneServlet() {
System.out.println("OneServlet 实例对象被创建!");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
第二个servlet实现类,TwoServlet:
public class TwoServlet extends HttpServlet {
public TwoServlet() {
System.out.println("TwoServlet 实例对象被创建!");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
xml配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>OneServlet</servlet-name>
<servlet-class>com.atjx.controller.OneServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>TwoServlet</servlet-name>
<servlet-class>com.atjx.controller.TwoServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>OneServlet</servlet-name>
<url-pattern>/one</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>TwoServlet</servlet-name>
<url-pattern>/two</url-pattern>
</servlet-mapping>
</web-app>
可以看到,我们对TwoServlet配置了:<load-on-startup>1</load-on-startup>,这意味着TwoServlet的对象在Tomcat启动时就被创建,而OneServlet的对象会在第一次被访问时被创建。
现在运行上面程序:
我们发现TwoServlet实例被创建了,这是因为我们配置了相应设置,现在我们访问OneServlet:
OneServlet被创建,因为默认设置就是第一次访问servlet实现类的时候创建其对象。
现在我们再次访问:
发现doGet又执行了一次,但构造函数并没有执行第二次,这也是前面提到的,一个servlet实现类的对象只会被创建一次。