servlet 学习总结

写作目的:自学了 servlet 相关知识后,对其有了更多的认识,将这些认识记录下来并分享一些心得。

1. 背景

1.1 什么是 servlet?

servlet 是基于 java 技术的 web 组件,被 servlet 容器所管理,用来生成动态的 web 内容。

1.2 什么是 servlet 容器?

servlet 容器也称 servlet 引擎,常见的有 tomcat 等。容器主要提供如下的功能:

  1. 网络服务,必须支持 http1.0 和 http1.1 协议
  2. 管理 servlet 的生命周期

总得来说:

servlet 的作用在于,动太生成 web 内容。而 servlet 要发挥它的作用必须有 servlet 容器的支持。

2. Servlet

在学完 servlet 后,我认为应该从三个方面去认识学习 servlet。这三个方面是:

  1. servlet 的生命周期
  2. servlet 的相关组件
  3. servlet 在 web 项目中的运作流程

下面就依次从这三个方面来记录一下 servlet 的相关知识。

2.1 生命周期

servlet 的生命周期是在 javax.servlet.Servlet 这个接口中定义好的。

 public interface Servlet {
    void init(ServletConfig var1) throws ServletException;

    ServletConfig getServletConfig();

    void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

    String getServletInfo();

    void destroy();
}

可以看到整个 servlet 的生命周期被定义为

  1. 初始化
  2. 服务(处理客户端请求)
  3. 销毁
    三个阶段依次对应的接口方法:
  4. init(ServletConfig var1) throws ServletException;
  5. service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
  6. destroy();

问题:
1. servlet 的生命阶段是如何完成的?
答: servlet 的生命周期是由 servlet 容器所管理的,所以它的初始化阶段是容器调用它的 init 方法来完成的,服务阶段是调用它的 service 方法,而销毁阶段自然是调用它的 destroy 方法。

2. servlet 什么时候初始化?
答: servlet 的 class 文件被加载并实例化以后,处理客户端请求之前。

3. servlet 什么时候加载?
答: servlet 容器启动的过程中。

4. servlet 什么时候实例化?
答: 可以在容器启动之后立即实例化,也可以在 servlet 将要处理客户端请求之前。

5. servlet 什么时候被销毁?
答: 1. 容器需要释放一些内存空间时 2. 容器关闭时

2.2 相关组件

servlet 有一些对象组件在 servlet 的各生命阶段提供一些职责服务,而从简化并方便开发者的使用。

2.2.1 ServletContext

ServletContext 又称上下文,它由容器所创建,并且是一个全局唯一的对象,可以被容器中所有的 servlet 所共享。

ServletContext 的主要作用有两个:

  1. 实现 servlet 之间数据共享
  2. 读取资源文件

一些代码示例:
通过ServletContext来获取全局的、整个Web应用的初始化参数:


    name
    gavin

利用ServletContext对象读取资源文件(比如properties文件)

// 这种方法的默认读取路径就是Web应用的根目录
InputStream stream = this.getServletContext().getResourceAsStream("dbinfo.properties");
// 创建属性对象
Properties properties = new Properties();
properties.load(stream);
String name = properties.getProperty("name");
String password = properties.getProperty("password");
out.println("name="+name+";password="+password);

注意:如果这个文件放在了src目录下,这时就不能用ServletContext来读取了,必须要使用类加载器去读取。

// 类加载器的默认读取路径是src根目录
InputStream stream = MyServlet.class.getClassLoader().getResourceAsStream("dbinfo.properties")

2.2.2 ServletConfig

ServletConfig 对于这个对象,在 Servlet 接口中有这样一个相关方法:

ServletConfig getServletConfig();

而在 Servlet 的子类 GenericServlet 有这样的定义:

private transient ServletConfig config;

public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
}

通过代码的分析,我们可以知道这个对象与 servlet 的关系是 1:1 的。

从配置文件的角度来说:web.xml 中的 的子标签 里的 key-value 式的参数就被保存在这个对象中。

另外:servlet 有一个 getInitParameter 方法, 这个方法的底层也是通过 ServletConfig 这个对象来实现的,代码如下:

public String getInitParameter(String name) {
    //就是这里了(1)
    ServletConfig sc = this.getServletConfig();
    if (sc == null) {
        throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
    } else {
        //还有这(2)
        return sc.getInitParameter(name);
    }
}

2.2.3 Request

请求,不用多说。更多的详情可以参考 w3c 的教程:
Servlet 客户端 HTTP 请求

2.2.4 Response

响应,不用多说。更多的详情可以参考 w3c 的教程:
Servlet 服务器 HTTP 响应

2.2.5 Session

Session 与 Cookie 从来都是一起“出生入死”。由于 http 是无状态协议,seesion 和 cookies 的出现就是为了弥补这一点的。不同的是:cookie 是客户端保存状态,session 是服务端保存状态。
更多主情参考:
理解HTTP session原理及应用

2.2.6 Filter

filter 是一段可以重复使的代码,它的作用是在拦截 request 和 response 并调整它们。在 web 容器启动之后,处理客户端请求之前,容器必须为过滤器列表中每个过滤器实例化一个对象并初始化,然后调用映射到的过滤器的 doFilter 方法。

2.2.7 Listener

监听器监听的是事件,servlet 提供的事件类型如下:

  1. servlet上下文事件
    1. ServletContextListener Servlet上下刚刚创建,接收第一个请求,上下文关闭
    2. ServletContextAttributeListener 监听Servlet 上下文的属性已添加、删除、或替换
  2. http 会话事件
    1. HttpSessionListener 会话已创建、销毁或超时
    2. HttpSessionAttributeListener 已经在 HttpSession 上添加、移除、或替换属性
    3. httpHttpSessionIdListener HttpSession 的 ID 将被改变
    4. HttpSessionActivationListener HttpSession 已被激活或钝化
    5. HttpSessionBindingListener 对象已经从 HttpSession 绑定或解除绑定
  3. servlet请求事件
    1. ServletRequestListener 一个 servlet 请求已经开始由 Web 组件处理
    2. ServletRequestAttributeListener 已经在 ServletRequest 上添加、移除、或替换属
      性。
    3. AsyncListener 超时、连接终止或完成异步处理

2.3 运作流程

2.3.1 web.xml 启动

  1. 容器启动的过程中读取 web.xml 的配置
  2. 创建一个全局唯的 ServletContext 对象,并将 标签中的参数初始化到这个对象中
  3. 定位(加载)各 servlet,filter, listener 的 class 文件
  4. 如果配置了 ServletContextListener,容器启动之后会运行这个监听器中的代码来为容器做一些初始化动作
  5. 容器启动完成
  6. 当第一个请求过来时,容器通过 url 映射找到对应的 servlet 的 class 文件,实例化一个 servlet 对象,然后调用这个对象的 init 方法初始化它, 最后调用它的 service 方法来处理请求。(这里用到了模板方法模式)
  7. 最后当容器想要关闭时,会先调用每个 servlet 的 destroy 方法,以释放一些资源。

2.3.2 非 web.xml 启动

不是说项目中没有 web.xml 文件,而是这个文件中什么初始化配置都没有只有最外层的 标签。

  1. 类路径下添加目录:META-INF/services
  2. 自定义一个类实现 ServletContainerInitializer 接口
  3. 在类上以 @HandlesTypes(value = {xxx.class, xxx.class}) 这样的 annotation 标注感兴趣的类
  4. 在 onStartup(Set> set, ServletContext servletContext) 方法中,感兴趣的类会被放到第一个参数 set 中传过来。可以存这个方法中对 servletContext 做一些处理。如:添加 servlet , listener 或者 filter。

心得

新技术的正确的学习方法:

  1. 先了解这个新技术的背景,了解它所要解决的问题
  2. 参照一些教程或官网做出一些能正常运行的 demo
  3. 了解它的构成(最要的组件)
  4. 了解它的运作流程
  5. 将组件与运作流程结合深入了解其运行原理

经过以上几步,才能算是大体撑握一个新技术,从对于那么特别细的技术点,则应该在真正用时再去查相关资料再运用。这既保证了对新东西的撑握,又不至于浪费过多的时间。

你可能感兴趣的:(java)