网站首页 > java教程 正文
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 课件,
源码,安装包,还有最新大厂面试资料等等等
- 上一篇: 云原生的基于容器的开发:Quarkus改进了Java
- 下一篇: 为什么建议一个容器中只运行一个进程
猜你喜欢
- 2025-01-04 详解docker容器的资源监控方案
- 2025-01-04 为什么建议一个容器中只运行一个进程
- 2025-01-04 云原生的基于容器的开发:Quarkus改进了Java
- 2025-01-04 Java容器化参数配置最佳实践
- 2025-01-04 为啥Java应用迁移到容器后会出现OOM?
- 2025-01-04 Java并发容器及使用场景
- 2025-01-04 知识整理——Java 并发容器
- 2025-01-04 用项目案例彻底理解Spring IOC容器
- 2025-01-04 Java面试题:Spring容器启动流程是怎样的?
- 2025-01-04 Java 服务 Docker 容器化最佳实践
你 发表评论:
欢迎- 04-24Java Collections 工具类集合框架中常用算法解析
- 04-24桶排序的简单理解
- 04-24Java集合框架底层实现原理大揭秘
- 04-24Java 集合框架全面解析:选对数据结构,提升开发效率
- 04-24c#集合排序
- 04-24Java面试中常被问到的集合类深度解读
- 04-24VBA技术资料MF278:对集合进行排序
- 04-24Spring 最常用的 7 大类注解,史上最强整理
- 最近发表
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)