Servlet是我开始接触javaweb后的第一个难点,当时课程设置在大二,在Java基础匆忙结课后就开始学习Javaweb,Servlet所涉及的一些方法以及原理完全没有仔细了解过。写这篇笔记的目的就是用现有的知识对servlet加深理解,也是对知识对复习笔记
首先我在IDEA中新建了一个web项目导入了Servlet的jar包,通过 Ctrl+H 可以得到下图的目录结构:
可以看到,最外层的目录项为Interface,即Servlet接口。然后二级目录中,GenericServlet实现了Interfa Servlet,这就是我们常在编写类写的implement Servlet。 在日常使用中,我们常用到getMethod()方法,而在继承类中调用该方法是会报错的,因为servlet实现的方法的父类是图中的三级目录:HttpServlet实现的,你无法通过调用抽象类的父类来实现抽象类中的方法。所以就出现了我们日常使用的:
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
不止如此,GenericService在实现interface时定义了很多空实现,我们可以通过httpServlet继承来实现其中service(),即请求分发。
而通过上述方式来继承HttpServlet就可以来实现我们自己的方法。
所以概括来说,Servlet的继承体系大概如下图所示:
我们首先来看Servlet接口:
package javax.servlet; import java.io.IOException; public interface Servlet { void init(ServletConfig var1) throws ServletException; ServletConfig getServletConfig(); void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException; String getServletInfo(); void destroy(); }
它定义了Servlet所能实现的操作。
然后再来看GenericServlet,可以看到都是Servlet接口的实现,返回值都是一些空值或者参数,并且引入了ServletConfig来实现一些方法:
package javax.servlet; import java.io.IOException; import java.io.Serializable; import java.util.Enumeration; public abstract class GenericServlet implements Servlet, ServletConfig, Serializable { private static final long serialVersionUID = 1L; private transient ServletConfig config; public GenericServlet() { } public void destroy() { } public String getInitParameter(String name) { return this.getServletConfig().getInitParameter(name); } public Enumeration<string> getInitParameterNames() { return this.getServletConfig().getInitParameterNames(); } public ServletConfig getServletConfig() { return this.config; } public ServletContext getServletContext() { return this.getServletConfig().getServletContext(); } public String getServletInfo() { return ""; } public void init(ServletConfig config) throws ServletException { this.config = config; this.init(); } public void init() throws ServletException { } public void log(String message) { this.getServletContext().log(this.getServletName() + ": " + message); } public void log(String message, Throwable t) { this.getServletContext().log(this.getServletName() + ": " + message, t); } public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException; public String getServletName() { return this.config.getServletName(); } }
其中,可以看到service()方法被定义为抽象方法,作为唯一,且最重要的抽象方法,其存在的意义就是:谁来继承该类,谁就要来实现父类的方法,即实现接口的功能。那么谁来继承GenericServlet?就是刚才结构中所提到的HttpServlet:
我们来看继承类后对service()方法的实现:
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); long lastModified; if (method.equals("GET")) { lastModified = this.getLastModified(req); if (lastModified == -1L) { this.doGet(req, resp); } else {
它所实现的,就是对请求方式的获取和判断,即根据请求方法来分发。在上面的代码中可以看到在获取到请求后通过字符串匹配来判断,并且通过if语句执行对应的方法,这就是请求分发。
这里我们就以上面的GET和POST请求为例,当判断到请求方式为GET或POST后,执行doGet(),doPost()方法:
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String msg = lStrings.getString("http.method_get_not_supported"); this.sendMethodNotAllowed(req, resp, msg); } <!-------------------------------> protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String msg = lStrings.getString("http.method_post_not_supported"); this.sendMethodNotAllowed(req, resp, msg); }
他们都执行了sendMethodNotAllowed()
private void sendMethodNotAllowed(HttpServletRequest req, HttpServletResponse resp, String msg) throws IOException { String protocol = req.getProtocol(); if (protocol.length() != 0 && !protocol.endsWith("0.9") && !protocol.endsWith("1.0")) { resp.sendError(405, msg); } else { resp.sendError(400, msg); } }
而这个方法,只是抛出了异常来指明是否支持该类型请求。
在我们实际生产中,我们接收请求后需要发给对应的方法来执行特定的业务逻辑,那么我们在自定义类中继承HttpServlet后,只需要通过重写doGet和doPost方法即可。
通过对继承体系以及代码实现的分析,我们可以概括为如下的逻辑图: