servlet简介

Servlet(Server Applet)是 Java Servlet 的简称,是使用 Java 语言编写的运行在服务器端的程序。具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容。通常来说,Servlet 是指所有实现了 Servlet 接口的类。Servlet的主要工作流程如下图:

Servlet 的请求首先会被 HTTP 服务器(如 Apache)接收,HTTP 服务器只负责静态 HTML 页面的解析,而 Servlet 的请求会转交给 Servlet 容器,Servlet 容器会根据 web.xml 文件中的映射关系,调用相应的 Servlet,Servlet 再将处理的结果返回给 Servlet 容器,并通过 HTTP 服务器将响应传输给客户端。

Servlet 主要用于处理客户端传来的 HTTP 请求,并返回一个响应,它能够处理的请求有 doGet() 和 doPost() 等。

Servlet 由 Servlet 容器提供,Servlet 容器是指提供了 Servlet 功能的服务器(如 Tomcat)。

Servlet 容器会将 Servlet 动态加载到服务器上,然后通过 HTTP 请求和 HTTP 应与客户端进行交互。

总结

  • servlet 是 JavaEE 规范之一,规范就是接口
  • servlet 是 JavaWeb三大组件之一。其余两个组件是Filter过滤器,Listener监听器
  • Servlet 是运行在服务器上的一个Java小程序。
  • 介于浏览器和web服务器之间的中间层,servlet可以获取浏览器的数据将其交给服务器,也可以将服务器端的数据库中信息交给浏览器。
  • Servlet 可以使用 javax.servlet 和 javax.servlet.http 包创建

Tomcat与Servlet的关系

Tomcat 是Web应用服务器,是一个Servlet/JSP容器. Tomcat 作为Servlet容器,负责处理客户请求,把请求传送给Servlet,并将Servlet的响应传送回给客户.而Servlet是一种运行在支持Java语言的服务器上的组件。

Servlet最常见的用途是扩展Java Web服务器功能,提供非常安全的,可移植的,易于使用的CGI替代品。

从http协议中的请求和响应可以得知,浏览器发出的请求是一个请求文本,而浏览器接收到的也应该是一个响应文本。

tomcat 服务器文件的含义

  • bin:二进制执行文件。里面最常用的文件是startup.bat,如果是 Linux 或 Mac 系统启动文件为 startup.sh。
  • conf:配置目录。里面最核心的文件是server.xml。可以在里面改端口号等。默认端口号是8080,也就是说,此端口号不能被其他应用程序占用。
  • lib:库文件。tomcat运行时需要的jar包所在的目录
  • logs:日志
  • temp:临时产生的文件,即缓存
  • webapps:web的应用程序。web应用放置到此目录下浏览器可以直接访问
  • work:编译以后的class文件。

Servlet 涉及的类和接口

在了解Servlet 涉及的类和接口之前,我们先对servlet规范核心类有个大致的映像:

接下来我们要说的是Servlet的四个类:

ServletConfig对象,ServletContext对象。以及request对象,response对象(另一个专题再讲)

Servlet类的继承体系

Servlet 接口----->  GenericServlet 类 ------> HttpServlet 类 -----> HttpServlet的实现类
  • Servlet 接口 定义了Servlet的行为规范
  • GenericServlet 类声明了 Servlet接口,做了很多空实现,并持有一个ServletConfig类的引用
  • HttpServlet 类实现了Service方法,对浏览器的GET和POST请求做了分发处理
  • HttpServlet的实现类 根据业务需求重写doGet和doPost方法即可

ServletConfig类

在使用 声明Servlet接口 这个方法创建Servlet实例时会出现ServletConfig类。这个类定义了一些可以返回关于Servlet信息的方法。但是在除init方法外也可以调用 getServletConfig() 来获取该Servlet的ServletConfig类对象。

一个Servlet对象有对应的一个ServletConfig对象。

参数:

  • getServletName() :获取servlet的名称,也就是我们在web.xml中配置的servlet-name
  • getServletContext():获取ServletContext对象,该对象的作用看下面讲解
  • getInitParameter(String):获取在servlet中初始化参数的值。这里注意与全局初始化参数的区分。这个获取的只是在该servlet下的初始化参数
  • getInitParameterNames():获取在Servlet中所有初始化参数的名字,也就是key值,可以通过key值,来找到各个初始化参数的value值。注意返回的是枚举类型

获取ServletConfig对象:通过继承父类(HttpServlet或GenericServlet)的方法得到一个ServletConfig对象

ServletConfig config = this.getServletConfig();

作用:

  • 可以获取 Servlet程序的别名,即web.xml中 Servlet-name 标签中的值。
  • 获取初始化参数 init-param。
  • 获取 ServletContext 对象。

示例:

public class HelloServlet implements Servlet {

    @Override
public void init(ServletConfig servletConfig) throws ServletException {
String initParameter = servletConfig.getInitParameter("abc"); //1234
String servletName = servletConfig.getServletName(); //HelloServlet
ServletContext servletContext = servletConfig.getServletContext();
//org.apache.catalina.core.ApplicationContextFacade@515de15
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
ServletConfig servletConfig = getServletConfig();
}
...
}

web.xml中内容

<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.hsh.test.HelloServlet</servlet-class>
<init-param> <!--初始化参数必须定义在servlet标签内,且只有当前servlet可以使用该参数 -->
<param-name>abc</param-name>
<param-value>1234</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>

ServletContext 接口

tomcat为每个web项目都创建一个ServletContext实例,tomcat在启动时创建,服务器关闭时销毁,在一个web项目中共享数据,管理web项目资源,为整个web配置公共信息等,通俗点讲,就是一个web项目,就存在一个ServletContext实例,每个Servlet读可以访问到它。

概述:

  • 一个接口,表示Servlet的上下文对象。
  • 一个Web工程对应一个ServletContext对象。
  • ServletContext是一个域对象,这个域就是整个web工程。
  • ServletContext对象在web工程启动时创建,关闭时销毁。

域对象:像map一样存取数据的对象,这里的域指的是存取数据的操作范围。域对象存取数据一般为setAttribute(),getAttribute()。

参数:

  • setAttribute(String name, Object obj) 在web项目范围内存放内容,以便让在web项目中所有的servlet读能访问到
  • getAttribute(String name) 通过指定名称获得内容
  • removeAttribute(String name) 通过指定名称移除内容
  • getInitPatameter(String name)通过指定名称获取初始化值
  • getInitParameterNames()  获得枚举类型
  • String getRealPath(String path)根据资源名称得到资源的绝对路径

代码如下:

    ServletContext servletContext = this.getServletContext();
String path = servletContext.getRealPath("/WEB-INF/web.xml");
System.out.println(path); F:\myworkspace\ServletTest\out\artifacts\ServletTest_war_exploded\WEB-INF\web.xml
  • getResourceAsStream(java.lang.String path)获取web项目下指定资源的内容,返回的是字节输入流。InputStream

代码如下:

InputStream in  = servletContext.getResourceAsStream("/WEB-INF/web.xml");
InputStreamReader isr = new InputStreamReader(in,"UTF-8");
BufferedReader br = new BufferedReader(isr);
String s = null;
while((s =br.readLine())!=null){
System.out.println(s);
}
  • getResourcePaths(java.lang.String path) 指定路径下的所有内容。

代码如下:

Set set = servletContext.getResourcePaths("/WEB-INF");
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
  • RequestDispatcher getRequestDispatcher(String path) 参数表示要跳转到哪去

代码如下:

RequestDispatcher rd = servletContext.getRequestDispatcher("/Servlet1");
rd.forward(req,resp);

作用:

  • 获取web.xml中配置的上下文参数 context-param
  • 获取当前工程路径,格式: /工程路径
  • 获取工程部署到服务器硬盘上的绝对路径
  • 向map一样存取数据

示例:

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = getServletContext();
String a = context.getInitParameter("a");
String b = context.getInitParameter("b");
String contextPath = context.getContextPath();
String realPath = context.getRealPath("/resources");
System.out.println("初始化参数:"+a+","+b);
System.out.println("当前工程路径:"+contextPath); //输出的是当前module中web目录的地址
System.out.println("工程部署路径:"+realPath); //输出的是web目录下resources被部署在服务器端磁盘上的位置
/**
初始化参数:apple,banana
当前工程路径:/servletTest_war_exploded
工程部署路径:H:\ideaworkpath\workspace01\out\artifacts\servletTest_war_exploded\resources
*/
context.setAttribute("key1","value1");
Object key1 = context.getAttribute("key1");
System.out.println(key1); //输出value1
}

web.xml中内容

<!--ServletContext 对象只能获取到context-param标签下的参数,不能获取某个Servlet标签下的init-param标签的内容 -->
<context-param>
<param-name>a</param-name>
<param-value>apple</param-value>
</context-param>
<context-param>
<param-name>b</param-name>
<param-value>banana</param-value>
</context-param>

工程结构图

HttpServletRequest类

每次有http请求发送给tomcat,tomcat就会把http协议信息解析好封装在Request对象中,然后传递到Service方法中供我们使用,我们用HttpServletRequest可以获取到所有的请求的信息。

常用方法

  • getRequestURI():获取请求资源路径
  • getRequestURL():获取请求的统一资源路径(绝对路径)
  • getRemoteHost():获取客户端IP地址
  • getHeader(paraName):获取请求头中某个参数的值
  • getParameter(paraName):获取参数的值
  • getParameterValue():获取多个参数的值
  • getMethod():获取请求的方式(GET/POST)
  • setAttribute(key,value):设置域数据。 这个域数据的生命周期取决于当前的request
  • getAttribute(key):返回域数据
  • getRequestDispatcher():获取请求转发对象
  • req.setCharacterEncoding("UTF-8") : 解决中文乱码问题,要放在获取参数之前

HttpServletResponse类

每次有http请求发送给tomcat,tomcat都会创建一个response对象传递给Servlet使用,然后传递到Service方法中供我们使用,我们用HttpServletRequest可以获取到所有的请求的信息。

getWriter() 和 getOutputStream() 两种输出字符流只能同时使用一个。

  • response.setCharacterEncoding("UTF-8") 设置服务器的字符集为UTF-8
  • response.setHeader("Content-Type","text/html;charset=UTF-8") 通过响应头设置浏览器也使用UTF-8字符集
  • response.setContentType("text/html;charset=UTF-8") 同时设置服务器,浏览器,响应头都使用UTF-8

Servlet的生命周期

Servlet生命周期可分为5个步骤

  • 加载Servlet。当Tomcat第一次访问Servlet的时候,Tomcat会负责创建Servlet的实例
  • 初始化。当Servlet被实例化后,Tomcat会调用init()方法初始化这个对象
    • 补充:当客户端向 Servlet 容器发出 HTTP 请求要求访问 Servlet 时,Servlet 容器首先会解析请求,检查内存中是否已经有了该 Servlet 对象,如果有,则直接使用该 Servlet 对象,如果没有,则创建 Servlet 实例对象,然后通过调用 init() 方法实现 Servlet 的初始化工作。需要注意的是,在 Servlet 的整个生命周期内,它的 init() 方法只能被调用一次。
  • 处理服务。当浏览器访问Servlet的时候,Servlet 会调用service()方法处理请求
    • 补充:这是 Servlet 生命周期中最重要的阶段,在这个阶段中,Servlet 容器会为这个请求创建代表 HTTP 请求的 ServletRequest 对象和代表 HTTP 响应的 ServletResponse 对象,然后将它们作为参数传递给 Servlet 的 service() 方法。
    • service() 方法从 ServletRequest 对象中获得客户请求信息并处理该请求,通过 ServletResponse 对象生成响应结果。
    • 在 Servlet 的整个生命周期内,对于 Servlet 的每一次访问请求,Servlet 容器都会调用一次 Servlet 的 service() 方法,并且创建新的 ServletRequest 和 ServletResponse 对象,也就是说,service() 方法在 Servlet 的整个生命周期中会被调用多次。
  • 销毁。当Tomcat关闭时或者检测到Servlet要从Tomcat删除的时候会自动调用destroy()方法,让该实例释放掉所占的资源。一个Servlet如果长时间不被使用的话,也会被Tomcat自动销毁
  • 卸载。当Servlet调用完destroy()方法后,等待垃圾回收。如果有需要再次使用这个Servlet,会重新调用init()方法进行初始化操作。
    • 补充:当服务器关闭或 Web 应用被移除出容器时,Servlet 随着 Web 应用的关闭而销毁。在销毁 Servlet 之前,Servlet 容器会调用 Servlet 的 destroy() 方法,以便让 Servlet 对象释放它所占用的资源。在 Servlet 的整个生命周期中,destroy() 方法也只能被调用一次。
    • 在这里,Servlet 对象一旦创建就会驻留在内存中等待客户端的访问,直到服务器关闭或 Web 应用被移除出容器时,Servlet 对象才会销毁。

简单总结:只要访问Servlet,service()就会被调用。init()只有第一次访问Servlet的时候才会被调用。destroy()只有在Tomcat关闭的时候才会被调用。

Servlet工作流程

Servlet 的每次请求,Web 服务器在调用 service() 方法之前,都会创建 HttpServletRequest 和 HttpServletResponse 对象。其中,HttpServletRequest 对象用于封装 HTTP 请求消息,简称 request 对象。HttpServletResponse 对象用于封装 HTTP 响应消息,简称 response 对象

在浏览器的地址栏输入:http://ip:port/appNames/servlet

1)通过浏览器和ip:port和这个服务器建立连接。

2) 浏览器会生成一个请求数据包(路径appNames/servlet)向服务器发送请求。

3) 服务器收到请求数据包,分析请求资源路径做精准定位,通过请求的appName查找webapps文件下面的appName做匹配,匹配上了需要获取web.xml中的servlet(mapping)。 

4) 服务器创建两个对象:

  • 第一个对象:请求对象,该对象实现了HttpServletRequest接口,服务器会将请求数据包中的数据解析出来,存储在该对象里。这样做的好处是没有必要理解http协议,只需要读取request。
  • 第二个对象:响应对象,实现了HttpServletResponse接口,作用是servlet处理完成后的结果可以存放到该对象上,然后服务器依据该对象的数据生成响应数据包。

5) servlet在执行servlet()方法时,可以通过request获取请求数据,也可以将处理结果存放到response上。然后服务器与响应对象直接形成一个默契,生成一个响应数据包给浏览器。

6)浏览器解析服务器返回的响应数据包,生成响应的结果。

简单总结:Servlet访问的过程:

Http请求---->web.xml--------> url -pattern----->servlet-name----->servlet-class-----> QuickStratServlet(对应的Class文件)

下图有助于理解工作流程

根据图中可见,当浏览器中发生了请求事件,

  • 会向 Web 服务器发送了一个 HTTP 请求;
  • Web 服务器根据收到的请求,会先创建一个 HttpServletRequest 和 HttpServletResponse 对象,然后再调用相应的 Servlet 程序。
  • 在 Servlet 程序运行时,它首先会从 HttpServletRequest 对象中读取数据信息,然后通过 service() 方法处理请求消息,并将处理后的响应数据写入到 HttpServletResponse 对象中。最后,Web 服务器会从 HttpServletResponse 对象中读取到响应数据,并发送给浏览器。

注:在 Web 服务器运行阶段,每个 Servlet 都只会创建一个实例对象,针对每次 HTTP 请求,Web 服务器都会调用所请求 Servlet 实例的 service(HttpServletRequest request,HttpServletResponse response)方法,并重新创建一个 request 对象和一个 response 对象。

servlet 表单数据

浏览器使用两种方法可将信息传递到 Web 服务器,分别为 GET 方法和 POST 方法。

前端的HTML页面中只有form表单用POST方式发送请求时使用的是http协议的POST方式,其余都为GET方式

GET方法

  • 浏览器使用的默认方法,该方法会生成一个很长的字符串来包含浏览器中的信息,所以不适用于传输密码等重要信息。而且,GET方法有大小限制,最多传输1024个字符
  • 服务端用doGet() 方法来处理 GET请求

POST方法

-浏览器向后台传输数据的可靠方法,它会将信息包装成一个单独的消息,以标准的形式发送到服务端。服务端用doPost() 方法来处理POST请求

使用servlet 读取表单数据

Servlet 处理表单数据,这些数据会根据不同的情况使用不同的方法自动解析:

  • getParameter(): 调用 request.getParameter() 方法来获取表单参数的值。
  • getParameterValues():如果参数出现一次以上,则调用该方法,并返回多个值,例如复选框。
  • getParameterNames():如果想要得到当前请求中的所有参数的完整列表,则调用该方法。

以GET方法为例:

@WebServlet("/HelloForm")
public class HelloForm extends HttpServlet {
... //省略构造方法
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置响应内容类型
response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter();
String title = "使用 GET 方法读取表单数据";
// 处理中文
String name =new String(request.getParameter("name").getBytes("ISO-8859-1"),"UTF-8");
String docType = "<!DOCTYPE html> \n";
out.println(docType +
"<html>\n" +
"<head><title>" + title + "</title></head>\n" +
"<body bgcolor=\"#f0f0f0\">\n" +
"<h1 align=\"center\">" + title + "</h1>\n" +
"<ul>\n" +
" <li><b>站点名</b>:"
+ name + "\n" +
" <li><b>网址</b>:"
+ request.getParameter("url") + "\n" +
"</ul>\n" +
"</body></html>");
} // 处理 POST 方法请求的方法
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}

在浏览器中输入URL:http://localhost:8080/TomcatTest/HelloForm?name=嘻嘻哈哈&url=www.xxhh.com,就能在页面中获取到name和 url 的显示

这里不多介绍servlet相关参数和例子了,本篇主要还是介绍什么是servlet,让大家有个入门概念。之后在request、response的专题中再详细介绍,也会在javaweb例子中复习servlet的相关方法和知识。

Servlet的url匹配顺序

当一个请求发送到servlet容器的时候,容器先会将请求的url减去当前应用上下文的路径作为servlet的映射url,比如我访问的是http://localhost/atomy/user/aaa.html,我的应用上下文是atomy,容器会将http://localhost/atomy去掉,剩下的/user/aaa.html部分拿来做servlet的映射匹配。这个映射匹配过程是有顺序的,而且当有一个servlet匹配成功以后,就不会去理会剩下的servlet了。其匹配规则和顺序如下:

  • 精确路径匹配。例子:比如servletA 的url-pattern为 /test,servletB的url-pattern为 /* ,这个时候,如果我访问的url为http://localhost/test ,这个时候容器就会先进行精确路径匹配,发现/test正好被servletA精确匹配,那么就去调用servletA,也不会去理会其他的servlet了。
  • 最长路径匹配。例子:servletA的url-pattern为/test/,而servletB的url-pattern为/test/a/,此时访问http://localhost/test/a时,容器会选择路径最长的servlet来匹配,也就是这里的servletB。
  • 扩展匹配。 如果url最后一段包含扩展,容器将会根据扩展选择合适的servlet。例子:servletA的url-pattern:*.action
  • 最后, 如果前面三条规则都没有找到一个servlet,容器会根据url选择对应的请求资源。如果应用定义了一个default servlet,则容器会将请求丢给default servlet

Servlet是单例的吗

单例模式的定义:一个类有且仅有一个实例,并且自行实例化向整个系统提供。如果按照Java中单例的定义,那么当Servlet没有实现SingleThreadModel接口时,它确实是单例的。

其实不然!

Servlet并不是单例,只是容器让它只实例化一次,变现出来的是单例的效果而已!

init()->service()->destroy()

在Servlet规范中,对于Servlet单例与多例定义如下:

“Deployment Descriptor”, controls how the servlet container provides instances of the servlet.For a servlet not hosted in a distributed environment (the default), the servlet container must use only one instance per servlet declaration. However, for a servlet implementing the SingleThreadModel interface, the servlet container may instantiate multiple instances to handle a heavy request load and serialize requests to a particular instance.

上面规范提到: 如果一个Servlet没有被部署在分布式的环境中,一般web.xml中声明的一个Servlet只对应一个实例。 而如果一个Servlet实现了SingleThreadModel接口,就会被初始化多个实例。默认20个.

Servlet的load-on-startup标签

在servlet的配置当中,

<load-on-startup>1</load-on-startup>

含义是:标记容器是否在启动的时候就加载这个servlet。当值为0或者大于0时,表示容器在应用启动时就加载这个servlet;当是一个负数时或者没有指定时,则指示容器在该servlet被选择时才加载。正数的值越小,启动该servlet的优先级越高。

配置load-on-startup后,servlet在startup后立即加载,但只是调用servlet的init()方法,用以初始化该servlet相关的资源。初始化成功后,该servlet可响应web请求;如未配置load-on-startup,容器一般在第一次响应web请求时,会先检测该servlet是否初始化,如未初始化,则调用servlet的init()先初始化,初始化成功后,再响应请求。

最新文章

  1. CPP - sort
  2. Leetcode: Matchsticks to Square &amp;&amp; Grammar: reverse an primative array
  3. sql数据库常用语句总结
  4. hrbustoj 1429:凸多边形(计算几何,判断点是否在多边形内,二分法)
  5. r.js 前端项目打包
  6. java记录在线人数小案例
  7. java第五课:方法
  8. 如何使用 XSD
  9. spark1.1.0源码阅读-dagscheduler and stage
  10. 办理英属哥伦比亚大学(本科)学历认证『微信171922772』UBC学位证成绩单使馆认证University of British Columbia
  11. 遇到的[]bug
  12. 2018面向对象程序设计(Java)第4周学习指导及要求
  13. Cannot find module &#39;socket.io&#39;
  14. Java Callable接口——有返回值的线程
  15. python列表list
  16. Go 1 Release Notes
  17. MapReduce的原理及执行过程
  18. L200
  19. jq移除最后一个class的值
  20. 一张图看懂高通QC1.0-QC4.0快充进化之路!QC2.0跟QC3.0充电区别

热门文章

  1. Python脚本与Metasploit交互攻击
  2. POJ1324贪吃蛇(状态压缩广搜)
  3. Day003 彻底搞懂++、--
  4. java+selenium使用JS、键盘滑动滚动条
  5. locustfile中的User类和HttpUser类
  6. Blazor实现未登录重定向到登录页的方法
  7. Canal详细入门实战(使用总结)
  8. Promise解析(待完成)
  9. UI设计师、平面设计师常用的网站大全,初学者必备,大家都在用!
  10. 『政善治』Postman工具 — 13、Postman接口测试综合练习