baseservlet怎么写_Servlet功能模块化——封装BaseServlet

1、问题引入

在我们刚接触servlet的时候,我们开发是这个样子的:

为了写一个用户登录功能,我们新建一个LoginServlet。

为了写一个用户注册功能,我们新建一个SignUpServlet。

为了写一个用户更新功能,我们新建一个UserUpdateServlet。

为了写一个用户删除功能,我们新建一个UserDeleteServlet。

为了……

哈哈,这还只是一个简单的User,但是很明显,正常的项目上两位数的实体类是很正常的。

难道我们对每个实体类的操作都要像上面那样建立 N 个 Servlet才行吗?那岂不是得好几十个Servlet才能满足功能的开发。。。

接下来,这篇文章将为你们解开这个疑惑OwO

2、Servlet是如何响应我们发出的请求?

在讨论如何解决上述问题前,我们先来一起看一下Servlet是如何响应我们发出的请求的。

下图是一个标准的Servlet,我们通过发起get或者post请求访问servlet

5dcbb8c31e92fd5f6c82458a00ca03a3.png

但是,大家有没有想过为什么我们的get/post请求,就可以调用相应的doGet/doPost方法呢?

那么,我们就来一探究竟吧!

首先我们来看下我们刚刚建立的LoginServlet的结构图

4f4aeb53b8d4d93e813567663f5fd876.png

从图中可以看见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 

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是指自己编写的方法名称。

779499b4a8cfde8c9be04df89e8921d2.png

哈哈,是不是使用起来非常简单快捷!

而且,最重要的是,我们由原本每个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

1542bed880d9d8effe0a2acc7b32239b.png

这个UserServlet里面有两个方法,一个是 login 登录,一个是signUp注册

4.3 启动Tomcat,访问UserServlet

我们启动tomcat,并在地址栏输入http://localhost/lemonfish/UserServlet/login(这里根据自己的端口和路径 进行调整,我这里端口是80,且applicationContext是lemonfish),按下回车键。

我们来看下控制台的输出结果是什么呢?

cf18a63b7efd92518ea1af5ecd1f3f50.gif

当当当!!!

这里成功输出了Log In,说明我们成功一半了。

不过别急,我们再试试signUp方法

487d1c4e950b3f6b4df9a770e6c6f75e.gif

哈哈,这下我们可以放心啦!

我们成功通过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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值