大家好,我是考100分的小小码 ,祝大家学习进步,加薪顺利呀。今天说一说Servlet功能模块化——封装BaseServlet[通俗易懂],希望您对编程的造诣更进一步.
1、问题引入
在我们刚接触servlet
的时候,我们开发是这个样子的:
- 为了写一个用户登录功能,我们新建一个
LoginServlet
。 - 为了写一个用户注册功能,我们新建一个
SignUpServlet
。 - 为了写一个用户更新功能,我们新建一个
UserUpdateServlet
。 - 为了写一个用户删除功能,我们新建一个
UserDeleteServlet
。 - 为了……
哈哈,这还只是一个简单的User
,但是很明显,正常的项目上两位数的实体类是很正常的。
难道我们对每个实体类的操作都要像上面那样建立 N 个 Servlet才行吗?那岂不是得好几十个Servlet
才能满足功能的开发。。。
接下来,这篇文章将为你们解开这个疑惑OwO
2、Servlet是如何响应我们发出的请求?
在讨论如何解决上述问题前,我们先来一起看一下Servlet是如何响应我们发出的请求的。
下图是一个标准的Servlet
,我们通过发起get
或者post
请求访问servlet
但是,大家有没有想过为什么我们的get
/post
请求,就可以调用相应的doGet
/doPost
方法呢?
那么,我们就来一探究竟吧!
首先我们来看下我们刚刚建立的LoginServlet
的结构图
从图中可以看见servlet的继承关系,servlet
接口在最上面一层,而GenericServlet
实现了servlet
接口。
我们先来看下最上面的servlet
接口里面有什么,大家重点看service
方法就行
// servlet接口
public interface Servlet {
// servlet被创建时进行的初始化方法
void init(ServletConfig var1) throws ServletException;
ServletConfig getServletConfig();
// Called by the servlet container to allow the servlet to respond to a request.
// 翻译下就是:由Servlet容器调用,以允许Servlet响应请求。
void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
String getServletInfo();
void destroy();
}
因此,我们得知了servlet
接口中的service
方法是用来响应请求的。
不过,接口只是定义了一套规范,我们得看下,具体实现是什么。
因此,我们继续往下走,看一下实现了servlet
接口的GenericServlet
。
但是,我们点进GenericServlet
的源码时,发现该类是一个抽象类,在servlet
的基础上,丰富了一些方法。
同时,里面的service
方法是一个抽象方法,我们猜测可能是由它的子类HttpServlet
实现了service
方法
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
// 里面比较长,这里就列一个service方法.
public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
}
因此,我们继续往下走,看一下继承了GenericServlet
的HttpServlet
。
我们发现service
方法在这里被具体实现了!
哈哈,是不是有种离答案
越来越近的味道了。
那我们赶紧来看看吧。
我们可以看到里面有非常多的if
判断。
在if (method.equals("GET"))
的下面调用了doGet
方法,Post也是同理。
// HttpServlet
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求的类型,比如Post、Get等等
String method = req.getMethod();
long lastModified;
// 如果为 GET
if (method.equals("GET")) {
lastModified = this.getLastModified(req);
if (lastModified == -1L) {
// 调用doGet
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);
// 如果为 Post
} else if (method.equals("POST")) {
// 调用 doPost
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);
}
}
到这里,我们就大概明白了,Servlet
是如何响应我们发出的请求了。
总结:如果我们的servlet
没有覆写service
方法的话,那么servlet
就会调用httpServlet
实现的service
方法去响应请求。根据请求的类型,调用具体不同的方法。
3、 BaseServlet的编写
接下来,就是我们本篇文章的核心BaseServlet
要登场啦 !!!
思路:
- 编写一个
BaseServlet
继承HttpServlet
,并且覆写其中的service
方法。 - 利用反射机制,完成
单Servlet
的多用。 - 让我们正常的
业务Servlet
去继承BaseServlet
- 正常使用(下面会介绍)
接下来我们用代码说话,会简单理解
一些。
3.0 先介绍如何使用
这边先简单介绍如何使用
- 首先,将
@WebSerlvet
写成/xxxx/`的形式,这个`/
代表匹配所有 - 编写自己的方法
- 前端发送url写成类似
(省略)/UserServlet/xx
即可,这个xx
是指自己编写的方法名称。
哈哈,是不是使用起来非常简单快捷!
而且,最重要的是,我们由原本每个Servlet
只能写一个方法,变成了可以写多个
方法,这实现了我们功能模块化
的目的!
不过,不用急,接下来就进入最重要的BaseServlet
的编写
3.1 BaseServlet的编写
一句话:BaseServlet主要是利用了反射的机制
完成了上述功能。
我们想理下具体操作的思路,最好再上代码。
-
首先,我们需要知道前端发送过来的请求,具体希望我们调用什么方法,比如是 登录还是注册呢?
答:由
@WebServlet
注解中的/xx/*
,我们可以规定好,如果需要调用登录方法,就写成/xx/login
,这个login(也就是方法名称)就是我们用来识别调用什么方法的关键字。 -
得到调用的方法名称,那么我们如果调用这个方法呢?
答:通过
反射
去获取该方法,并调用。
完整代码及解析:
public class BaseServlet extends HttpServlet {
// 覆写service方法
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 这里获取URL或者URI都可以,URI: /lemonfish/UserServlet/login URL: http://localhost/lemonfish/UserServlet/login
String requestURI = req.getRequestURI();
// 2. 获取最后`/`的索引
int beginIndex = requestURI.lastIndexOf("/");
// 3. 使用substring,获取方法名称
String methodName = requestURI.substring(beginIndex + 1);
try {
/*** * 记住谁调用了“service”方法,this就是谁, * 因为我们自己编写的UserServlet继承了BaseServlet * 因此,这个service方法也是属于UserServlet的 * 而前端访问的是UserServlet * 因此 this 是 UserServlet的一个对象 * * 4. 这里 根据 方法名称 和 方法参数的class类型 ,利用反射获取UserServlet的该方法。 * * 如果大家这两行代码看不太懂,可以先去复习下 反射 的知识。 ***/
Method method = this.getClass().getDeclaredMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
// 5. 使用this调用该方法。
method.invoke(this, req, resp);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
4、BaseServlet的实例演示
4.1 写一个BaseServlet
如上
4.2 写一个UserServlet,并继承BaseServlet
这个UserServlet里面有两个方法,一个是 login
登录,一个是signUp
注册
4.3 启动Tomcat,访问UserServlet
我们启动tomcat,并在地址栏输入http://localhost/lemonfish/UserServlet/login
(这里根据自己的端口和路径 进行调整,我这里端口是80,且applicationContext
是lemonfish
),按下回车键。
我们来看下控制台的输出结果是什么呢?
当当当!!!
这里成功输出了Log In
,说明我们成功一半了。
不过别急,我们再试试signUp
方法
哈哈,这下我们可以放心啦!
我们成功通过BaseServlet
完成了访问一个Servlet
的同时,可以使用多个方法。
这样,我们在开发的时候就可以根据自己的业务需求编写对应的xxxServlet
即可。
4.4 处理转发的小坑
在写转发路径的时候要记住加/
喔,这样会直接追加在根路径下
如果写成hello.jsp
,那么就会追加到当前路径(也就是UserServlet
下面),
这样service
方法就会去UserServlet
下面找名字为hello.jsp
的方法,就会报错!
request.getRequestDispatcher("/hello.jsp").forward(request, response);
5. DEMO源码地址
为了大家方便,我决定把这个demo的源码直接上传到 Github
这样方便大家进行阅读学习,如果觉得不错,还请赏颗 Star
(●’◡’●)
虽然之前用码云用的比较多 – -,但是最近开始应该会主要用Github
了。
6. 写在最后
封装一个BaseServlet
对我们开发效率有很大的提高,特别是在还没有接触框架之前。
希望大家看完能有所收获~~(●’◡’●)
如果大家觉得有所帮助的话,能点个👍b( ̄▽ ̄)d 是再好不过的事情啦hhh
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/13816.html