网站首页 > 开源技术 正文
Servlet与Servlet容器关系
Servlet
比较这两个的区别, 就得先搞清楚Servlet 的含义, Servlet (/?s?rvlit/ ) 翻译成中文就是小型应用程序或者小服务程序, 与之相类似的是Server (/?s??rv?r/), 翻译过来是服务器的意思, 可见这二者承担类似的功能,但是Servlet更轻量,
web开发的本质就一句话:客户端和服务器交换数据。于是使用 Java 的 Socket 套接字进行编程,去处理客户端来的 tcp 请求,经过编解码处理读取请求体,获取请求行,然后找到请求行对应的处理逻辑步入服务器的处理中,处理完毕把对应的结果返回给当前的 Socket 链接,响应完毕,关闭 Socket。
上述过程中, 建立连接、传输数据、关闭连接等过程是tomcat容器帮你做了这些事情, 而拿到请求行之后去找对应的 url 路由,这一部分是谁做的呢?是Servlet ! 简单来说Servlet就是一段处理 web 请求的逻辑。
具体来说Servlet具有以下几个特点:
- Servlet是用Java编写的Server端程序,它与协议和平台无关。
- Servlet运行于Java-enabled Web Server中。
- Java Servlet可以动态地扩展Server的能力,并采用请求-响应模式提供Web服务。
- 最早支持Servlet技术的是JavaSoft的Java Web Server。
- 此后,一些其它的基于Java的Web Server开始支持标准的Servlet API。
- Servlet的主要功能在于交互式地浏览和修改数据,生成动态Web内容。
上面六点中,最需要被记住的是Servlet可以动态地扩展Server的能力,并采用请求-响应模式提供Web服务。
JDK中的Servlet是一个接口:
public interface Servlet {
public void init(ServletConfig config) throws ServletException;
public ServletConfig getServletConfig();
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
public String getServletInfo();
public void destroy();
}
可以看到Servlet 是一个接口, 规定了请求从容器到达 web 服务端的规范,详细内容在后面的Servlet生命周期中详细梳理,这儿简单概括三个重要步骤是:
- init():初始化请求的时候要做什么;
- service():拿到请求的时候要做什么;
- destory():处理完请求销毁的时候要做什么。
所有实现 Servlet 的实现方都是在这个规范的基础上进行开发。那么 Servlet 中的数据是从哪里来的呢?答案就是 Servlet 容器。容器才是真正与客户端打交道的那一方。一个容器中 Servlet 可以有多个, 常见的Servlet容器Tomcat,它监听了客户端的请求端口,根据请求行信息确定将请求交给哪个Servlet 处理,找到处理的Servlet之后,调用该Servlet的 service() 方法,处理完毕将对应的处理结果包装成ServletResponse 对象返回给客户端。
Servlet容器
现在讲讲Servlet容器, 前面说过看Servlet只是一个接口或者说是规范, 那么就势必有具体实现, 而Servlet具体实现或者说包装器是Wrapper, 直接管理Wrapper的容器就是Context, 一个 Context 对应一个 Web 工程, 也就是说Context 容器如何运行将直接影响 Servlet 的工作。
?
由图可以知道, Tomcat底层是Context, Context负责管理Servlet包装类Wrapper。
下面创建一个实例对象并调用 start 方法就可以很容易启动 Tomcat,我们还可以通过这个对象来增加和修改 Tomcat 的配置参数,如可以动态增加 Context、Servlet 等。我们就选择 Tomcat7 自带的 examples Web 工程,并看看它是如何加到这个 Context 容器中的。
//给 Tomcat 增加一个 Web 工程:
Tomcat tomcat = getTomcatInstance();
File appDir = new File(getBuildDirectory(), "webapps/examples");
tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath());
tomcat.start();
ByteChunk res = getUrl("http://localhost:" + getPort() +
"/examples/servlets/servlet/HelloWorldExample");
assertTrue(res.toString().indexOf("<h1>Hello World!</h1>") > 0);
上述代码是创建一个 Tomcat 实例并新增一个 Web 应用,然后启动 Tomcat 并调用其中的一个 HelloWorldExample Servlet,看有没有正确返回预期的数据。
//Tomcat 的 addWebapp 方法的代码如下:
public Context addWebapp(Host host, String url, String path) {
silence(url);
Context ctx = new StandardContext();
ctx.setPath( url );
ctx.setDocBase(path);
if (defaultRealm == null) {
initSimpleAuth();
}
ctx.setRealm(defaultRealm);
ctx.addLifecycleListener(new DefaultWebXmlListener());
ContextConfig ctxCfg = new ContextConfig();
ctx.addLifecycleListener(ctxCfg);
ctxCfg.setDefaultWebXml("org/apache/catalin/startup/NO_DEFAULT_XML");
if (host == null) {
getHost().addChild(ctx);
} else {
host.addChild(ctx);
}
return ctx;
}
添加一个 Web 应用时将会创建一个 StandardContext 容器,并且给这个 Context 容器设置必要的参数(
url 代表这个应用在 Tomcat 中的访问路径; path 代表这个应用实际的物理路径) 其中最重要的一个配置是 ContextConfig,【ContextConfig监听器】继承了 【LifecycleListener 监听器接口】,它是在调用清单 2 时被加入到 StandardContext 容器中。 当 Context 容器初始化状态设为 init 时,添加在 Context 容器的 Listener 将会被调用。【ContextConfig监听器】将会负责整个 Web 应用配置文件的解析工作。最后将这个 Context 容器加到父容器 Host 中。
Servlet生命周期
Servlet生命周期分为四个部分: 实例化==>初始化==>执行处理==>销毁。
实例化
new , 服务器第一次被访问时,加载一个Servlet容器,只会被加载一次。
初始化
init:创建完Servlet容器后,会调用仅执行一次的init()初始化方法,用于初始化Servlet对象,无论多少台客户端在服务器运行期间访问都不会再执行init()方法。
可以在继承的GenericServlet这个抽象类中看到初始化方法:
public void init() throws ServletException {
}
而在我们的Servlet类中应继承调用该方法:
public void init() throws ServletException {
super.init();
}
创建Servlet对象的时机:
- Servlet容器启动时:读取web.xml配置文件中的信息,构造指定的Servlet对象,创建ServletConfig对象,同时将ServletConfig对象作为参数来调用Servlet对象的init方法。
- 在Servlet容器启动后:客户首次向Servlet发出请求,Servlet容器会判断内存中是否存在指定的Servlet对象,如果没有则创建它,然后根据客户的请求创建HttpRequest、HttpResponse对象,从而调用Servlet 对象的service方法。
- Servlet Servlet容器在启动时自动创建Servlet,这是由在web.xml文件中为Servlet设置的<load-on-startup>属性决定的。从中我们也能看到同一个类型的Servlet对象在Servlet容器中以单例的形式存在。
执行处理
执行处理——service()方法
它是Servlet的核心,负责响应客户的请求。每当一个客户请求一个HttpServlet对象,该对象的Service()方法就要调用,而且传递给这个方法一个“请求”(ServletRequest)对象和一个“响应”(ServletResponse)对象作为参数。在HttpServlet中已存在Service()方法。默认的服务功能是调用与HTTP请求的方法相应的do功能。
HttpServlet的抽象类提供了doGet()、doPost()……等方法。对应了request请求的发送方法,与之相匹配:
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_get_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(405, msg);
} else {
resp.sendError(400, msg);
}
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_post_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(405, msg);
} else {
resp.sendError(400, msg);
}
}
上面是操作性最高的部分。
销毁
销毁——destroy:在服务器关闭或重启时,Servlet会调用destroy方法来销毁,将Servlet容器标记为垃圾文件,让GC做回收处理。我们编写的Servlet是调用了GenericServlet抽象类的destroy方法:
@Override
public void destroy() {
super.destroy();
}
Servlet工作原理
1、首先简单解释一下Servlet接收和响应客户请求的过程:
客户发送一个请求,Servlet是调用service()方法对请求进行响应,service()方法中对请求的方式进行了匹配。选择调用doGet,doPost等这些方法,然后再进入对应的方法中调用逻辑层的方法,实现对客户的响应。在Servlet接口和GenericServlet中是没有doGet()、doPost()等等这些方法的,HttpServlet中定义了这些方法,但是都是返回error信息,所以,我们每次定义一个Servlet的时候,都必须实现doGet或doPost等这些方法。
2、每一个自定义的Servlet都必须实现Servlet的接口,Servlet接口中定义了五个方法,其中比较重要的三个方法涉及到Servlet的生命周期,分别是上文提到的init(),service(),destroy()方法。GenericServlet是一个通用的,不特定于任何协议的Servlet,它实现了Servlet接口。而HttpServlet继承于GenericServlet,因此HttpServlet也实现了Servlet接口。所以我们定义Servlet的时候只需要继承HttpServlet即可。
3、Servlet接口和GenericServlet是不特定于任何协议的,而HttpServlet是特定于HTTP协议的类,所以HttpServlet中实现了service()方法,并将请求ServletRequest、ServletResponse 强转为HttpRequest 和 HttpResponse。
4、另外,Servlet是单例模式,线程是不安全的,因此在service()方法中尽量不要操作全局变量。但实际上,可以通过使用session和application来代替全局变量,只是会加大服务器负载。
Servlet处理请求的过程
- 客户端发送请求给服务器。
- 容器根据请求及web.xml判断对应的Servlet是否存在,如果不存在则返回404
- 容器根据请求及web.xml判断对应的Servlet是否已经被实例化,若是相应的Servlet没有被实例化,则容器将会加载相应的Servlet到Java虚拟机并实例化
- 调用实例对象的service()方法,并开启一个新的线程去执行相关处理。调用servce方法,判断是调用doGet方法还是doPost方法
- 业务完成后响应相关的页面发送给客户端。
学习更多JAVA知识与技巧,关注与私信博主(学习)学习JAVA 课件,
源码,安装包,还有最新大厂面试资料等等等
猜你喜欢
- 2025-01-05 传统手动实现 UserService 和 基于 ServiceImpl 的简化写法的区别
- 2025-01-05 版本不兼容Jar包冲突该如何是好?
- 2025-01-05 如何将你开发的Java程序注册为Windows系统服务
- 2024-08-06 Dubbo3.0动态配置中心源码分析(dubbo常用配置)
- 2024-08-06 基于动态代理 Mock Dubbo 服务的实现方案
- 2024-08-06 windows中nginx注册服务的最简单方法
- 2024-08-06 微服务架构之服务冶理Dubbo-Netty流程
- 2024-08-06 开发速度快 10 倍!Airbnb 用 GraphQL+Apollo 做到了
- 2024-08-06 服务间歇性停顿问题优化|得物技术
- 2024-08-06 Dubbo3.0中zookeeper配置中心构建源码分析以及其他配置中心简介
你 发表评论:
欢迎- 05-16东契奇:DFS训练时喷了我很多垃圾话 我不懂他为什么比赛不这么干
- 05-16这两球很伤!詹姆斯空篮拉杆不中 DFS接里夫斯传球空接也没放进
- 05-16湖人自媒体调查:89%球迷希望DFS回归79%希望詹姆斯回归
- 05-16Shams:湖人得到全能球员DFS 节省了1500万奢侈税&薪金空间更灵活
- 05-16G5湖人胜率更高!詹姆斯不满判罚,DFS谈5人打满下半场:这很艰难
- 05-16DFS:当东契奇进入状态 所有防守者在他面前都像个圆锥桶
- 05-16上一场9中6!DFS:不能让纳兹-里德这样的球员那么轻松地投三分
- 05-16WIDER FACE评测结果出炉:滴滴人脸检测DFS算法获世界第一
- 最近发表
-
- 东契奇:DFS训练时喷了我很多垃圾话 我不懂他为什么比赛不这么干
- 这两球很伤!詹姆斯空篮拉杆不中 DFS接里夫斯传球空接也没放进
- 湖人自媒体调查:89%球迷希望DFS回归79%希望詹姆斯回归
- Shams:湖人得到全能球员DFS 节省了1500万奢侈税&薪金空间更灵活
- G5湖人胜率更高!詹姆斯不满判罚,DFS谈5人打满下半场:这很艰难
- DFS:当东契奇进入状态 所有防守者在他面前都像个圆锥桶
- 上一场9中6!DFS:不能让纳兹-里德这样的球员那么轻松地投三分
- WIDER FACE评测结果出炉:滴滴人脸检测DFS算法获世界第一
- 湖人自媒体调查:89%球迷希望DFS回归 79%希望詹姆斯回归
- 一觉醒来湖人苦盼的纯3D终于到位 DFS能带给紫金军多少帮助
- 标签列表
-
- jdk (81)
- putty (66)
- rufus (78)
- 内网穿透 (89)
- okhttp (70)
- powertoys (74)
- windowsterminal (81)
- netcat (65)
- ghostscript (65)
- veracrypt (65)
- asp.netcore (70)
- wrk (67)
- aspose.words (80)
- itk (80)
- ajaxfileupload.js (66)
- sqlhelper (67)
- express.js (67)
- phpmailer (67)
- xjar (70)
- redisclient (78)
- wakeonlan (66)
- tinygo (85)
- startbbs (72)
- webftp (82)
- vsvim (79)
本文暂时没有评论,来添加一个吧(●'◡'●)