JavaWeb-03-Servlet-02-Servlet接口实现类
JavaWeb-03-Servlet-02-Servlet接口实现类
1.什么是Servlet接口,有什么用?
Servlet接口来自于Servlet规范中的一个接口,这个接口存在于Http服务器所提供的jar包中。
Servlet接口的具体位置:位于Tomcat服务器下的lib文件中的servlet-api.jar,在jar包中的位置:【javax.servlet.Servlet】。
在Servlet规范中,Http服务器能调用的【动态资源文件】必须是一个实现了Servlet接口的java类。
如:
class MyClass01 {
//非Servlet接口实现类,不是动态资源文件,Tomcat服务器无权调用
}
class MyClass02 implements Servlet{
//是Servlet接口实现类,是合法的动态资源文件,Tomcat服务器有权调用
}
2.Servlet接口实现类开发步骤(如何创建一个动态资源文件)
2.1第一步:创建一个java类继承HttpServlet
当一个java类继承抽象类HttpServlet后,这个java类就成为了一个Servlet接口实现类。
HttpServlet抽象类是GenericServlet抽象类的子类,而GenericServlet实现了Servlet接口,所以GenericServlet的子类HttpServlet以及HttpServlet的子类都是Servlet接口实现类。
(抽象类) (抽象类) (接口)
自己写的类----【继承】---->HttpServlet----【继承】---->GenericServlet----【实现】------>Servlet
[Servlet接口实现类] [Servlet接口实现类] [Servlet接口实现类]
- service()【实现】 - init()【实现】 - init()【抽象方法】
- getServletConfig()【实现】 - getServletConfig()【抽象方法】
- getServletInfo()【实现】 - getServletInfo()【抽象方法】
- destroy()【实现】 - destroy()【抽象方法】
- service()【仍是抽象方法】 - service()【抽象方法】
为什么不直接让自己写的类直接实现Servlet接口呢?
理由是:
- 当实现一个接口后必须实现该接口中所有的方法。
- 而在Servlet接口的所有方法中,我们只需要使用其中一个方法service,其他方法不需要实现。
- 为了降低接口实现类实现接口过程的难度,就将接口中实现类所不需要的抽象方法交给抽象类来完成。这样接口实现类只需要对接口中需要的方法进行重写。
Servlet接口:
接口实现类不需要的抽象方法:
- init()
- getServletConfig()
- getServletInfo()
- destroy()
接口实现类需要的抽象方法:
- service()
Tomcat根据规范调用Servlet接口实现类规则:
-
Tomcat有权创建Servlet接口实现类实例对象
-
Servlet sltDemo = new ServletDemo01();
-
-
Tomcat根据实例对象调用service方法处理当前请求:
-
sltDemo.service();
-
2.2第二步:重写HttpServlet中的两个方法【doGet、doPost】
package com.tsccg.controller;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Author: TSCCG
* @Date: 2021/08/12 11:35
*/
public class ServletDemo01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("ServletDemo01类对浏览器发送的GET请求作出处理");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("ServletDemo01类对浏览器发送的POST请求作出处理");
}
}
浏览器发送请求的方式有两种:GET和POST
在浏览器发送请求时,将请求方式信息放入Http请求协议包里的【请求行】中。
请求行:[
URL:请求地址
Method:【GET/POST】
]
请求头:[...]
空白行:[...]
请求体:[...]
- 当为GET时,Tomcat服务器通过动态资源文件实例对象调用doGet()方法对请求进行处理
- 当为POST时,Tomcat服务器通过动态资源文件实例对象调用doPost()方法对请求进行处理
GET
浏览器--------->sltDemo.doGet()//1.第一种:GET
POST
浏览器--------->sltDemo.doPost()//2.第二种:POST
但在前面不是说过,处理当前请求是通过service方法吗?
-
Servlet sltDemo = new ServletDemo01(); sltDemo.service();
底层抽象类HttpServlet实现了service方法,我们可以看看是如何实现的:
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();//获取请求方式【GET/POST】
long lastModified;
if (method.equals("GET")) {//如果请求方式是GET,那么就执行this.doGet(req, resp);
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")) {//如果请求方式是POST,那么就执行this.doPost(req, resp);
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抽象类实现的service()方法中,
根据请求方式信息执行【this.doGet(req, resp)】或【this.doPost(req, resp)】
其中,this指的是当前实例对象。
当子类ServletDemo继承父类HttpServlet后,子类也继承了父类实现的service()方法,
在子类重写父类中的doGet()和doPost()方法后,如果用子类的实例对象去调用service()方法:
Servlet sltDemo = new ServletDemo01();
sltDemo.service();
那么在service()方法中执行的【this.doGet(req, resp)】或【this.doPost(req, resp)】中的this,就是当前子类的实例对象【sltDemo】,调用的doGet()或doPost()方法就是子类重写的方法。
这种通过父类来决定在什么情况下调用子类中方法的模式,在设计模式中被称为【模板设计模式】。
2.3将Servlet接口实现类信息"注册"到Tomcat服务器
"注册",在这里是通知的意思。
在一个网站中,会有很多的java类,其中有普通java类和动态资源文件,而Tomcat服务器无法区分哪些是动态资源文件。
所以,我们就需要通知Tomcat服务器哪些是可以操作的动态资源文件。
如何通知呢?我们可以通过配置web目录下的WEB-INF文件夹中存储的web.xml文件来实现。
[网站]--->[web]--->[WEB-INF]--->[web.xml]
在web-app标签栏内写入如下配置信息:
<!-- 将Servlet接口实现类的类路径地址交给Tomcat服务器 -->
<servlet>
<servlet-name>demo01Path</servlet-name><!-- 声明一个变量,用于存储Servlet接口实现类的类路径地址 -->
<servlet-class>com.tsccg.controller.ServletDemo01</servlet-class><!-- Servlet接口实现类的类路径 -->
</servlet>
<!-- 以上语句作用:Tomcat:String demo01 = "com.tsccg.controller.ServletDemo01"; -->
<!-- 由于java类的类路径一般都很长,用户在浏览器中请求时非常不便。
【http://localhost:8080/MyWeb/com/tsccg/controller/ServletDemo01】
为了降低用户访问Sevlet接口实现类的难度,我们需要设置一个简短的别名 -->
<servlet-mapping>
<servlet-name>demo01Path</servlet-name><!-- 上面的变量名 -->
<url-pattern>/demo01</url-pattern><!-- 别名【一定要在开头加一个斜杠/】 -->
</servlet-mapping>
<!--
现在通过浏览器索要ServletDemo01时需要写的地址:
【http://localhost:8080/MyWeb/demo01】
-->
然后发布网站:
开启服务器并通过浏览器访问ServletDemo01:
我们通过浏览器地址栏发送的请求,都是【GET】方式。
当Tomcat服务器接收到【GET】请求后,创建ServletDemo01的实例对象,并调用service方法处理当前请求。
在service()方法里,根据【GET】请求方式,执行【this.doGet(req, resp);】。即用当前实例对象调用实例对象中重写的doGet方法。