Servlet开发概述及ServletContext的使用

Servlet概述

什么是Servlet

Servlet是Sun公司提供的动态web资源开发技术。
本质上是一段Java程序,要求这个程序必须实现Servlet接口,以便服务器调用。

怎样写一个Servlet

两个步骤:

  1. 第一步:
    写一个java程序实现Servlet接口(此处直接继承了默认实现类GenericServlet)
package me.zipstream;
import java.io.*;
import javax.servlet.*;

public class FirstServlet extends GenericServlet{
    public void service(ServletRequest req, ServletResponse res) throws ServletException, java.io.IOException{
        res.getOutputStream().write("My FirstServlet!".getBytes());
    }
}
  1. 第二步:
    将编译好的带包的.class文件放到WEB-INF/classes下,然后再配置web应用的 web.xml注册Servlet。

此外,在Eclipse等IDE下开发Servlet就方便的多。
研究一下HttpServlet源代码会更好的理解。

Servlet详述

Servlet运行过程

web服务器收到客户端的Servlet访问请求后:

  1. 首先检查是否已经装载并创建了该Servlet的实例对象。如果是,则直接执行第4步,否则,执行第2步。
  2. 装载并创建该Servlet的一个实例对象。
  3. 调用Servlet实例对象的init()方法。
  4. 创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletResponse对象,然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去。
  5. WEB应用程序被停止或重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy()方法。

Servlet的生命周期

通常情况下,服务器会在Servlet第一次被调用时创建该Servlet类的实例对象(servlet出生);一旦被创建出来,该Servlet实例就会驻留在内存中,为后续请求服务;直至web容器退出,servlet实例对象才会被销毁(servlet死亡)。
在Servlet的整个生命周期内,Servlet的init方法只有在servlet被创建时被调用一次。
而对一个Servlet的每次访问请求都导致Servlet引擎调用一次servlet的service方法。对于每次访问请求,Servlet引擎都会创建一个新的HttpServletRequest请求对象和一个新的HttpServletResponse响应对象,然后将这两个对象作为参数传递给它调用的Servlet的service()方法,service方法再根据请求方式分别调用doXXX方法。
servlet被销毁前,会调用destroy() 方法。

Servlet接口的继承结构

Servlet接口 -- 定义了Servlet应该具有的基本方法
    |--GenericServlet --通用基本Servlet的实现,对于不常用的方法在这个实现类中进行了基本的实现,对于Service设计为了抽象方法,需要子类去实现
        |--HttpServlet --在通用Servlet的基础上基于HTTP协议进行了进一步的强化:实现了GenericServlet中的Service方法,判断当前的请求方式,调用对应到doXXX方法,这样一来我们开发Servlet的过程中只需继承HttpServlet ,覆盖具体要处理的doXXX方法就可以根据不同的请求方式实现不同的处理.一般不要覆盖父类中的Service方法只要覆盖doGet/doPost就可以了

注册Servlet

利用<servlet><servlet-mapping>标签注册一个Servlet
<servlet>
    <servlet-name>FirstServlet</servlet-name>
    <servlet-class>me.zipstream.FirstServlet</servlet-class> 
    注意:此处要的是一个Servlet的完整类名,不是包含.java或.class扩展的文件路径
</servlet>
<servlet-mapping>
    <servlet-name>FirstServlet</servlet-name>
    <url-pattern>/FirstServlet</url-pattern>
</servlet-mapping>

关于Servlet的几个细节

  1. 一个<servlet>可以对应多个<serlvet-mapping>,从而一个Servlet可以有多个路径来访问。
  2. url-partten中的路径可以使用*匹配符号进行配置,但只能是/开头/*结尾或*.后缀这两种方式
    由于*的引入,有可能一个路径被多个url-partten匹配,这时优先级判断条件如下:
    (1) 哪个最确切就匹配哪个
    (2) *.后缀永远匹配级最低
  3. <serlvet>可以配置<load-on-startup>来指定启动顺序
  4. 缺省Servlet
    如果有一个Servlet的url-partten被配置为了一根正斜杠/,这个Servlet就变成了缺省Serlvet.其他Servlet 都不处理的请求,由缺省Servlet来处理。
    其实对于静态资源的访问就是由缺省Servlet来执行。
    设置404页面500页面等提示页面也是由缺省Servlet来执行。
    通常我们不会自己去配置缺省Servlet。
  5. 线程安全问题
    由于默认情况下Servlet在内存中只有一个对象,当多个浏览器并发访问Servlet时就有可能产生线程安全问题。
    解决方案:
    (1) 加锁–效率降低。
    (2) SingleThreadModel接口 – 不能真的防止线程安全问题。
    (3) 最终解决方案:在Servlet中尽量少用类变量,如果一定要用类变量则用锁来防止线程安全问题,但是要注意锁住的内容应该是造成线程安全问题的核心代码,尽量的少锁住内容,减少等待时间来提高servlet的响应速度。

ServletConfig

ServletConfig代表当前Servlet在web.xml中的配置信息。
String getServletName() – 获取当前Servlet在web.xml中配置的名字
String getInitParameter(String name) –获取当前Servlet指定名称的初始化参数的值
Enumeration getInitParameterNames() –获取当前Servlet所有初始化参数的名字组成的枚举
ServletContext getServletContext() –获取代表当前web应用的ServletContext对象

初始化参数究竟有什么用呢?
很多不想在程序中写死的东西都可以定义在xml配置文件中的初始化参数里。
比如整个工程的编码方式,数据库中的用户信息等。

ServletContext

ServletContext代表当前web应用。很重要。它可以用来实现很多功能。

做为域对象可以在整个web应用范围内共享数据

package me.zipstream;

import java.io.IOException;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
//ServletContext作为域对象在整个web应用范围内共享数据
public class Demo1Servlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        ServletContext context = this.getServletContext();
        context.setAttribute("apple", "red apple");
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

}

在Demo1Servlet中设置的数据信息在Demo2Servlet中可以通过ServletContext共享。

package me.zipstream;

import java.io.IOException;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
//ServletContext作为域对象在整个web应用范围内共享数据
public class Demo2Servlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        ServletContext context = this.getServletContext();
        String v = (String) context.getAttribute("apple");
        System.out.println(v);
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

}

用来获取web应用的初始化参数

实现Servlet的请求转发

请求转发和请求重定向:
请求转发 : 服务器内部进行资源流转
重定向 : 302+Location

请求转发是一次请求一次响应实现资源流转.请求重定向两次请求两次响应。

/*
 * ServletContext实现请求转发
 */
public class Demo4Servlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        RequestDispatcher dispatcher = context.getRequestDispatcher("/servlet/Demo5Servlet");
        dispatcher.forward(request, response);
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

}
/*
 * ServletContext实现请求转发
 */
public class Demo5Servlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.getWriter().write("See it! ");
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

}

读取资源文件

在Servlet中读取资源文件时:
如果写相对路径和绝对路径,由于路径将会相对于程序启动的目录,在web环境下,就是tomcat启动的目录即tomcat/bin目录,所以会找不到资源;
如果写硬盘路径,可以找到资源,但是只要一换发布环境,这个硬盘路径很可能是错误的,同样不行;
为了解决这样的问题ServletContext提供了getRealPath方法,在这个方法中传入一个路径,这个方法的底层会在传入的路径前拼接当前web应用的硬盘路径从而得到当前资源的硬盘路径,这种方式即使换了发布环境,方法的底层也能得到正确的web应用的路径从而永远都是正确的资源的路径

*
 * ServletContext读取资源文件
 */
public class Demo6Servlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        ServletContext context = this.getServletContext();
        String realPath = context.getRealPath("config.properties");

        Properties prop = new Properties();
        prop.load(new FileReader(realPath));

        System.out.println(prop.getProperty("username"));
        System.out.println(prop.getProperty("password"));
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

}
/*
 * 在非Servlet环境下用类加载器方式读取资源文件
 */
public class Service {
    public void method1() throws FileNotFoundException, IOException{
        Properties prop = new Properties();
        prop.load(new FileReader(Service.class.getClassLoader().getResource("../../config.properties").getPath()));

        System.out.println(prop.getProperty("username"));
        System.out.println(prop.getProperty("password"));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值