Servlet知识学习和总结

概念辨析

首先来看看,最初碰到这个概念时的困惑:

  1. 什么是Servlet? 什么是Servlet容器?
  2. Servlet可以做什么?
  3. Servlet有什么应用?

学SpringBoot的时候看到说它默认使用嵌入式Servlet 容器Tomcat, 然后看到有人问到Servlet 的原理是什么,以及有大佬的知乎回答说建议要**精通Servlet **,看来这两个名称相似的概念是有所区别的:

Servlet 与 Servlet 容器的关系有点像枪和子弹的关系,枪是为子弹而生,而子弹又让枪有了杀伤力。虽然它们是彼此依存的,但是又相互独立发展,这一切都是为了适应工业化生产的结果。从技术角度来说是为了解耦,通过标准化接口来相互协作。
来自: 许令波:《Servlet 工作原理解析》

我们常见的Servlet 容器便有:Tomcat,Jetty等。

再来看Servlet 原理:
Servlet 教程|菜鸟教程左边的目录可以看到一系列可以实现的功能:实现客户端和服务器的HTTP请求和响应,过滤器,异常处理,Cookit和Session, 包括数据库访问,文件上传,重定向,发送电子邮件等等。

Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层

可见,Servlet 在客户端和服务端间的交互通讯中起了重要作用,虽然现在基本上不会有人使用存粹的Servlet 来实现整个Web应用,不过它却是Java web开发技术的基础,所以很有必要了解,理解,掌握,乃至精通。

学习过程

热身

  1. 迅速浏览完了Servlet 教程|菜鸟教程的所有实例,整体上大致看一下都做了什么事情,花费时间1~2h
  2. 印象最深刻的学习方式,自然是通过编程实现来体会,所以对几个重要的Servlet 的功能进行实践,代码主要也是参考菜鸟教程的内容。
  3. 在功能上掌握后,再回来看 许令波:《Servlet 工作原理解析》和JavaWeb——Servlet

编程实践

在实现中Servlet 主要使用了javax.Servlet javax,Servlet .http两个包,写好的程序需要运行在带有支持Java Servlet 规范的解释器的Web服务器,即我们开头所说的Servlet 容器

准备
平时用的是Jetty,所以在IDEA上重新装了Tomcat来体验:IntelliJ IDEA配置Tomcat(完整版教程)

第一个Servlet 程序

  • 和教程有所差别的是,IDEA使得我们只需要把HelloWorld.java放在src下就行;
  • 对于web.xml里的配置其实实现的就是注入(存疑?)和映射功能;对比spring里面的知识,其实只需要在HelloWorld上增加注解就可以不用管web.xml了
  • 注意:之后代码通常的做法是,写出执行逻辑,然后写入response的输出流PrintWriter writer = response.getWriter();中返回。
	<servlet>
        <servlet-name>HelloWorldservlet-name>
        <servlet-class>HelloWorldservlet-class>
    servlet>
    
    <servlet-mapping>
        <servlet-name>HelloWorldservlet-name>
        <url-pattern>/HelloWorldurl-pattern>
    servlet-mapping>
@WebServlet("/HelloWorld")
public class HelloWorld extends HttpServlet {
    private String message;
    ......
   }

获取表单数据
此处已经使用了HTTP请求,主要为了展示三个函数的用法,用来获取请求request中的参数:

  • getParameter()
  • getParameterValues()
  • getParameterNames()

HTTP请求和HTTP响应消息
先要知道请求的结构组成,这两种请求的结构相似:

状态行 HTTP/1.1 200 OK
头部字段 Content-Type: text/html
Header2: …

HeaderN: …
空格
内容实体
...



编码功能实现包括:

1. 获取HTTP请求头部消息
2. 设置HTTP响应的头部消息
3. 设置HTTP响应的状态码

Servlet 过滤器
有两个问题可以提出来:

  • 过滤器有什么作用?
  • 常见的应用有哪些?

过滤器的作用可以总结为以下两点,分别针对请求响应

  • 在客户端的请求访问后端资源之前,拦截这些请求。
  • 在服务器的响应发送回客户端之前,处理这些响应。

用图来简单表示:
Servlet知识学习和总结_第1张图片
过滤器的作用可就广泛了:最常见的身份验证,消息加密,消息审核,数据压缩,图像转换等等。

1. 实现过滤器功能
2. 多个过滤器

.xml里的配置也可以用注解实现(其实看起来也不是很简洁…):

@WebFilter(filterName = "LogFilter", urlPatterns = "/*", initParams = {
        @WebInitParam(name = "Site", value = "菜鸟教程")
})
public class LogFilter implements Filter {
......
}

多个过滤器时,需要注意过滤器的执行顺序

Filter的执行顺序与在web.xml配置文件中的配置顺序一致,一般把Filter配置在所有的Servlet之前。

不过如果是注解怎么确定顺序呢?

Servlet3.0之前使用web.xml配置按照mapping的顺序即先映射的先过滤;
Servlet3.0后使用注解则按照类名的自然顺序,即类名的字母顺序来排因为容器加载时按此顺序加载

异常处理
那么,Servlet 是怎么处理异常的呢?
比如如果搜索了不存在的页面,如果不进行异常处理,出现的是默认的404丑丑的界面,而通过异常处理,可以转向我们自己定制的404页面

本质上还是进行一个ErrorHandler的Servlet 的类定义,添加映射。然后再配置一下进行错误页面的定义,其中可以包含具体的状态码的处理( 如404 Not Found 未找到,或 403 Forbidden 禁止),或者具体的异常的处理(如ServletException 或 IOException),或者索性所有错误统一处理(java.lang.Throwable异常处理)

Cookie
Cookie的存放位置:客户端计算机的文本文件上。

一般使用Cookie的流程如下:

  1. 客户端发送HTTP请求(访问浏览器)
  2. 服务器发送一个HTTP响应,其中头部信息包含了Set-Cookie,这里面的信息可以是:性别,年龄,识别号等
  3. 客户端发送HTTP请求,其中头部保护了Cookie,此时服务器就能唯一识别客户端
  4. 服务器HTTP响应

(存疑:如何防止使用Cookie伪造HTTP请求呢?)

编码功能实现包括:

1. 对表单提交后的请求,返回一个带cookies的响应,并在页面显示cookies消息
2. 发出一个页面请求,返回一个读取cookies的响应,并显示在页面中
3. 删除Cookie可以通过设置过期时间为0的方法实现: setMaxAge()

Session跟踪
Servlet Session跟踪可以使得服务器识别客户端,来保持一段会话。

个人理解就是用户在某网站里面有一系列操作(如点击各个链接或者做一系列功能/点击按钮)时,因为每次都要往服务器发送请求,有一些需要根据个人信息来判别,这里就需要服务器有能力唯一识别出这个用户。
听起来和Cookie有点像?使用Cookie也能实现,不过可能浏览器会禁止使用Cookie,所以这种方法不是很靠谱。

实际上有三种方式能可以让 Session 正常工作:

  1. 基于 URL Path Parameter,默认就支持
  2. 基于 Cookie,如果你没有修改 Context 容器个 cookies 标识的话,默认也是支持的
  3. 基于 SSL,默认不支持,只有 connector.getAttribute(“SSLEnabled”) 为 TRUE 时才支持

通过实现HttpServlet 接口,使得在用户跨多个页面发出请求时可以识别用户,也能存储有关的用户信息。

服务器需要在向客户端发送响应之前,调用 request.getSession()方法。

编码功能实现包括:

1. 实现一个记录本页面访问次数的计时器,通过获取request里的httpsession对象,设施它的属性来改变

(存疑:实现里面貌似只是单页面的操作,怎么跨页面呢?)1.设置全局变量,可以控制Serlvet内的操作,init()里面初始化,用isNew()来判断是否为新用户 2.跨页面如何处理,毕竟此时访问的是不同的Serlvet?

数据库访问
此处就是使用了JDBC访问数据库,然后用doGet()返回了

重定向
两种方法:

  1. 使用response 对象的 sendRedirect() 方法来实现
  2. setStatus() 和 setHeader()方法,记得之前有个状态码302就是重定向状态码

总结

需要了解的是:

Servlet的生命周期:

  1. init()方法进行初始化(只在用户第一次创建时调用)
  2. service()方法处理客户端的请求(我们代码实现用的doGet()或者doPost()都是由service()调用的处理函数)
  3. destory()方法结束生命周期
  4. 由JVM垃圾回收器进行回收

一些重要的类

  • ServletConfig
  • ServletContext
  • ServletRequest
  • ServletResponse
    Servlet知识学习和总结_第2张图片

和Servlet直接相关的类有三个:ServletConfig,ServletRequest,ServletResponse。
这三个类都是通过容器传递给Servlet的。

ServletConfig: 获取Servlet运行时需要的配置属性; Servlet在init的时候由容器传递过来。
ServletContext: 表示应用程序,Tomcat为每1个Web应用程序创建1个该实例,即启动时创建,服务器关闭时销毁。有什么作用呢? 可以在一个web项目中共享数据,配置web公共信息等,每个Servlet可以访问到它。

  • GenericServlet

我们一般使用的HttpServlet是继承的GenericServlet类(该类实现了Servlet, ServletConfig接口)
值得一提的是该类有2个init()函数:第1个带参数的,是为了在类中全局设置ServletConfig的实例;第二是没有参数的,是为了继承该类后,用来重写初始化,实现我们自己的功能的;

  • ServletContextListener 全局监听器

ServletContextListener是一个接口,继承了EventListener类。
里面只有两个方法:

  1. void contextInitialized(ServletContextEvent var1); 应用启动时,ServletContext进行初始化,然后调用该方法(web.xml/注解里需要指定listener标签/属性)
  2. void contextDestroyed(ServletContextEvent var1); 应用停时,ServletContext被销毁,调用该方法

ServletContextListener在Spring中如何应用呢?
《JavaWeb——Servlet》中末尾有解析:

一般我们用这句代码可以显示地实例化一个Spring IOC容器

ApplicationContext applicationContext =new ClassPathXmlApplicationContext("***.xml";

ContextLoaderListener类实现了ServletContextListener接口。

  1. 当Servlet容器启动时,ServletContext对象被初始化.
  2. 然后Servlet容器调用web.xml中注册的监听器的public void contextInitialized(ServletContextEvent event)方法.
  3. 该方法中,调用了this.initWebApplicationContext(event.getServletContext())方法,在这个方法中实例化了Spring IOC容器。即ApplicationContext对象。
  4. 因此,当ServletContext创建时我们可以创建applicationContext对象,当ServletContext销毁时,我们可以销毁applicationContext对象。这样applicationContext就和ServletContext“共生死了”。

涉及Tomcat的部分
(这部分看了很多遍,暂时先粗略总结一下,参考: 许令波:《Servlet 工作原理解析》)
我们的Servlet是在Tomcat(Servlet容器)中运行的,所以不得不谈一下请求是如何由Tomcat接收,然后到达我们的程序,以及响应如何从Tomcat发出去的。

Tomcat中的容器:

  1. Tomcat有一系列的容器等级,其中Context容器直接管理Serlvet中的包装类Wrapper
  2. 一个Context容器对应一个Web项目

启动过程:

  1. Tomcat添加一个Web项目,创建一个StandardContext容器(即Context容器)
  2. 给这个容器设置设置必要参数,如url和path代表Web项目在Tomcat中的访问路径和实际物理路径,以及ContextConfig配置,ContextConfig负责整个Web项目配置的解析工作
  3. 将这个Context容器添加到父容器Host中
  4. 调用Tomcat的start方法启动Tomcat,在Tomcat中,所有容器都继承Lifecycle接口,它管理容器的整个生命周期,当容器发生修改和状态变化时(比如启动),它会去通知已经注册的观察者(Listener)
  5. Context容器初始化状态init: ContextConfig的init方法会解析一系列配置文件
  6. Context容器执行startInternal方法: 一系列工作(省略)…获取ServletContext并设置必要参数; 初始化Servlet!
  7. ContextConfig的configureStart方法执行来进行初始化Web项目,解析web.xml文件(保存在WebXml对象中),其中包括关键信息和Web项目的入口。目的是将web.xml中的属性设置到Context容器中,同时创建Servlet对象,filter, listener等
    这里会将Servlet包装成Context容器中的StandardWrapper,为什么要这样做呢?

什么要将 Servlet 包装成 StandardWrapper 而不直接是 Servlet 对象。这里 StandardWrapper 是 Tomcat 容器中的一部分,它具有容器的特征,而 Servlet 为了一个独立的 web 开发标准,不应该强耦合在 Tomcat 中。

到这里的时候,Servlet被包装成StandardWrapper并且作为子容器被添加到了Context中,相关的属性也被解析到了Context中,所以之前说过,Context容器直接管理运行Serlvet。

创建Servlet对象:(这里实在有点晕…)

  1. 如果 Servlet 的 load-on-startup 配置项大于 0,那么在 Context 容器启动的时候就会被实例化
  2. Wrapper. loadServlet开始创建Servlet 实例, 该方法获取servletClass ,然后把它交给InstanceManager 去创建一个基于 servletClass.class 的对象

初始化Servlet:

  1. StandardWrapper 的 initServlet方法会初始化Servlet,通过调用Servlet的init方法。

到这里就完成了Servlet运行之前的准备工作了,其中的步骤简化了很多细节的地方,主要梳理了一下主线。然后简要说明一下Servlet是如何被调用的:

Servlet工作过程:

  1. 用户在浏览器输入一个URL,如http://hostname:port/servletpath, 其中hostname:port用来建立TCP连接,后面的字符串用来选择服务器中的子容器处理用户请求
  2. Tomcat根据映射来匹配对应的容器,这个映射关系存在mapper类中
  3. 执行Filter链,通知web.xml中配置了的Listener
  4. 执行Servlet中的service方法

这就是我们熟悉的处理部分了。

Servlet 的确已经能够帮我们完成所有的工作了,但是现在的 web 应用很少有直接将交互全部页面都用 servlet 来实现,而是采用更加高效的 MVC 框架来实现。这些 MVC 框架基本的原理都是将所有的请求都映射到一个 Servlet,然后去实现 service 方法,这个方法也就是 MVC 框架的入口。

整体将相关内容过了一遍,如果有新的体会,再继续更新。

你可能感兴趣的:(Java知识点,网络编程)