Servlet

基于动态Web资源开发,Sun公司提供了Servlet和JSP两种技术。

针对Servlet技术的开发,Sun公司提供了一系列接口和类,其中最重要的是javax.servlet.Servlet接口。Servlet就是一种实现了Servlet接口的类,它由Web容器负责创建并调用,用于接收和响应用户的请求。

Servlet接口

接口中的方法,其中提到的Servlet容器指的是Web服务器。

  • void init(ServletConfig config):负责Servlet初始化工作,容器在创建好Servlet对象后,就会调用此方法。该方法接收一个ServletConfig类型的参数,Servlet容器通过这个参数向Servlet传递初始化配置信息。
  • ServletConfig getServletConfig():返回容器调用init(ServletConfig config)方法时传递给Servlet的ServletConfig对象。
  • String getServletInfo():返回一个字符串,其中包含关于Servlet的信息,例如,作者、版本和版权等信息。
  • void service(ServletRequest req, ServletResponse res):负责响应用户的请求,当容器接受到客户端访问Servlet对象的请求时,就会调用此方法。容器会构造一个表示客户端请求信息的ServletRequest对象和一个用于响应客户端ServletResponse对象作为参数传递给service()方法。在service()方法中,可以通过ServletRequest对象得到客户端的相关信息和请求信息,在对请求信息进行处理后,调用ServletResponse对象的方法设置响应信息。
  • void destroy():负责释放Servlet对象占用的资源。当Servlet对象被销毁时,容器会调用此方法。

第一个Servlet程序

创建一个Web项目HelloWeb,创建包com.eaglezsx.servlet,在里边创建类ServletDemo1,让其实现Servlet接口,然后重写里边的方法,找到service这个方法,输出一句话hello servlet。

package com.eaglezsx.servlet;

import java.io.IOException;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class ServletDemo1 implements Servlet{

    public void destroy() {
        // TODO Auto-generated method stub

    }

    public ServletConfig getServletConfig() {
        // TODO Auto-generated method stub
        return null;
    }

    public String getServletInfo() {
        // TODO Auto-generated method stub
        return null;
    }

    public void init(ServletConfig config) throws ServletException {
        // TODO Auto-generated method stub

    }

    public void service(ServletRequest req, ServletResponse res)
            throws ServletException, IOException {
        System.out.println("hello servlet");

    }

}

现在编写了.java,其.class会编译到WEB-INF的classes目录下,但WEB-INF这个目录下的文件不能被外部直接访问,如何让外界可以访问?

在web.xml文件的<web-app>元素下填写

<!-- 创建一个Servlet实例 -->
  <servlet>
    <servlet-name>servletDemo1</servlet-name>
    <servlet-class>com.eaglezsx.servlet.ServletDemo1</servlet-class>
  </servlet>
  <!-- 给Servlet提供(映射)一个可供客户端访问的URI -->
  <servlet-mapping>
    <servlet-name>servletDemo1</servlet-name>
    <url-pattern>/demo1</url-pattern>
  </servlet-mapping>

启动服务器,在浏览器中访问http://localhost:8080/HelloWeb/demo1,之后会在MyEclipse的控制台中会打印出hello servlet。

这里写图片描述
元素<servlet>用于注册Servlet,它的两个子元素<servlet-name><servlet-class>分别用来指定Servlet名称及其完整类名(找到那个类,右键–>Copy QualifiedName就可以获得全类名)。元素<servlet-mapping>用于映射Servlet对外访问的虚拟路径,它的子元素<servlet-name>的值必须和<servlet>元素中<servlet-name>相同,子元素<url-pattern>则是用于指定访问该Servlet的虚拟路径,该路径以正斜线(/)开头,代表当前Web应用程序的根目录。

Servlet的生命周期

在Java中,任何对象都有声明周期,Servlet也不例外。创建了当前应用的类后,会找lib下的jar,然后找Tomcat中lib目录的所有jar。

这里写图片描述
按照功能的不同,大致可以将Servlet的生命周期分为三个阶段,分别是初始化阶段、运行阶段和销毁阶段。

1.初始化阶段

当客户端向Servlet容器发出HTTP请求要求访问Servlet时,Servlet容器首先会解析请求,检查内存中是否已经有了该Serlvet对象,如果有直接使用该Serlvet对象,如果没有就创建Servlet实例对象,然后通过调用init()方法实现Servlet的初始化工作。在Servlet的整个生命周期内,它的init()方法只被调用一次。

2.运行阶段

在这个阶段,Servlet容器会为这个请求创建代表HTTP请求的ServletRequest对象和代表HTTP响应的ServletResponse对象,然后将它们作为参数传递给Servlet的service()方法。service()从ServletRequest对象中获得客户请求信息并处理该请求,通过ServletResponse对象生成响应结果。在Servlet的整个生命周期内,对于Servlet的每一次访问请求,Servlet容器都会调用一次Servlet的service()方法,并且创建新的ServletRequest和ServletResponse对象,也就是说,service()方法在Servlet的整个生命周期中会被调用多次。

3.销毁阶段

当服务器关闭或Web应用被移除容器时,Serlvet随着Web应用的销毁而销毁。在销毁Servlet之前,Servlet容器会调用Servlet的destroy()方法,以便让Servlet对象释放它所占用的资源。在Servlet的整个生命周期中,destroy()方法也只被调用一次。Servlet对象一旦创建就会驻留在内存中等待客户端的访问,直到服务器关闭,或Web应用被移除容器时Servlet对象才会销毁。

自动加载Servlet程序

有时,我们希望某些Servlet程序可以在Tomcat启动时随即启动(其构造方法和init方法会被调用 )。例如,当启动一个Web项目时,首先需要对数据库信息进行初始化。

  <servlet>
    <servlet-name>servletDemo1</servlet-name>
    <servlet-class>com.eaglezsx.servlet.ServletDemo1</servlet-class>
    <!-- 设置Servlet在Web应用启动时初始化 -->
    <load-on-startup>1</load-on-startup>
  </servlet>

<servlet>元素中添加元素<load-on-startup>,用于指定Servlet被加载的时机和顺序,其值必须是一个整数。如果这个值是一个负数,或者没有设定这个元素,Servlet容器将在客户端首次请求这个Servlet时加载它;如果这个值为正整数或0,Servlet容器将在Web应用启动时加载并初始化Servlet,并且<load-on-startup>的值越小,它对应的Servlet就越先被加载。这个元素应该放在最后一个。

可以在其构造方法和init方法中分别输出一句话,在Tomcat启动的时候,观察控制台,会发现这两句话被打印出来了。

Servlet的三种创建方式

1.实现javax.servlet.Servlet接口

2.继承javax.servet.GenericServlet类(适配器模式 )

实现Servlet接口的话,需要重写里边的所有方法。现在继承GenericServlet类(这个类实现了Servlet接口,除了 service方法为抽象的别的方法都已实现),只需要重写service方法就可以了。

查看GenericServlet的源码,这时候会提示没有找到文件。因为服务器已经实现了JavEE规范,这是由服务器创建的,服务器源码中会有,找到之前下载的apache-tomcat-7.0.75-src.zip,添加进来,就可以看到起源码了。

GenericServlet中实现了Servlet和ServletConfig接口,里边的方法基本都实现了,现在GenericServlet中有一堆方法,用的时候时候直接用就行了。

3.继承javax.servlet.http.HttpServlet类(模板方法)

因为大多数Web应用都是通过HTTP和客户端进行交互,因此,在Servlet接口中,提供了一个抽象类HttpServlet,它是GenericServlet的子类,专门用于创建应用于HTTP的Servlet。

查看HttpServlet中的源码

public abstract class HttpServlet extends GenericServlet {

    ...

    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(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
        } else {
            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, 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(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
        } else {
            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
        }
    }

    ...

    protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {

        String method = req.getMethod();

        if (method.equals(METHOD_GET)) {
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
                doGet(req, resp);
            } else {
                long ifModifiedSince;
                try {
                    ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                } catch (IllegalArgumentException iae) {
                    ifModifiedSince = -1;
                }
                if (ifModifiedSince < (lastModified / 1000 * 1000)) {
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else {
                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }

        } else if (method.equals(METHOD_HEAD)) {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);

        } else if (method.equals(METHOD_POST)) {
            doPost(req, resp);

        } else if (method.equals(METHOD_PUT)) {
            doPut(req, resp);

        } else if (method.equals(METHOD_DELETE)) {
            doDelete(req, resp);

        } else if (method.equals(METHOD_OPTIONS)) {
            doOptions(req,resp);

        } else if (method.equals(METHOD_TRACE)) {
            doTrace(req,resp);

        } else {
            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[1];
            errArgs[0] = method;
            errMsg = MessageFormat.format(errMsg, errArgs);

            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
        }
    }

    ...

    @Override
    public void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException {

        HttpServletRequest  request;
        HttpServletResponse response;

        try {
            request = (HttpServletRequest) req;
            response = (HttpServletResponse) res;
        } catch (ClassCastException e) {
            throw new ServletException("non-HTTP request or response");
        }
        service(request, response);
    }
}

最主要看这四个方法

  • doPost
  • doGet
  • service(ServletRequest req, ServletResponse res)
  • service(HttpServletRequest req, HttpServletResponse resp)

HttpServlet主要有两大功能,第一是根据用户请求方式的不同,定义相应的doXxx()方法处理用户请求。例如,与Get请求方式对应的doGet()方法。第二是通过service()方法将HTTP请求和响应分别转为HttpServletRequest和HttpServletResponse。

由于HttpServlet类在重写的service()方法中,为每一种HTTP请求方式都定义了对应的doXxx方法,因此,当定义的类继承HttpServlet后,只需要根据请求方式,重写对应的doXxx方法即可,而不需要重写service()方法。

通过get请求或post请求提交出现乱码情况时的解决方法是不同的,现在将这两种请求方式分开,到时候分别编写对应的解决方法。

自动创建Servlet

以前是New–>Class,现在改成New–>Servlet

然后填写类名ServletDemo2,选择自动创建的方法(只选doGet和doPost),然后下一步,Display Name和Description里面的内容都删了(只是一些说明,没有必要显示),Servlet的名字和Mapping URL自己看情况设置。

然后点击完成,那些配置信息会自动填写在web.xml中。但通过默认Servlet模板生成的很乱,一堆注释和out.println。

这里写图片描述
在MyEclipse的安装目录\Common\plugins中找到com.genuitec.eclipse.wizards这个jar文件,用压缩工具打开,注意是打开不是解压这个jar包 ,进入templates文件夹,可以看到里面有一个Servlet.java文件,把相关内容改为

<aw:method name="doGet">

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


    }

</aw:method>

<aw:method name="doPost">

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

</aw:method>

然后保存,重新启动MyEclipse,再生成的Servlet就没有那么多没用的东西。更详细的可以查看修改MyEclipse默认生成的Servlet以及JSP页面

Servlet虚拟路径的映射

1.Servlet的多重映射

Servlet的多重映射指的是同一个Servlet可以被映射成多个虚拟路径。也就是说,客户端可以通过多个路径实现对同一个Servlet的访问。

  <servlet-mapping>
    <servlet-name>servletDemo1</servlet-name>
    <url-pattern>/demo1</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>servletDemo1</servlet-name>
    <url-pattern>/demo2</url-pattern>
  </servlet-mapping>

这样访问demo2或demo1的时候都会映射到servletDemo1

或者写成

  <servlet-mapping>
    <servlet-name>servletDemo1</servlet-name>
    <url-pattern>/demo1</url-pattern>
    <url-pattern>/demo2</url-pattern>
  </servlet-mapping>

2.Servlet映射路径中使用通配符

(1)格式为“*.扩展名”,例如“*.do”匹配以“*.do”结尾的所有URL地址

(2)格式“/*”,例如“/abc/*”匹配以“/abc”开始的所有URL地址。

这两种通配符的格式不能混合使用,例如,/abc/*.do就是不合法的映射路径。另外,当客户端访问一个Servlet时,如果请求的URL地址能够匹配多个虚拟路径,那么Tomcat将采取最具体匹配原则查找与请求URL最接近的虚拟映射路径。

3.默认Servlet

如果某个Servlet的映射路径仅仅是一个正斜线(/),那么这个Servlet就是当前Web应用的默认Servlet。

Servlet服务器在接收访问请求时,如果在web.xml文件中找不到匹配的<servlet-mapping>元素的URL,就会将访问请求交给默认Servlet处理,也就是说,默认Servlet用于处理其他Servlet都不处理的访问请求。

在Tomcat安装目录下的web.xml文件中也配置了一个默认的Servlet


    <servlet>
        <servlet-name>default</servlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
        ...
    </servlet>

    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

当Tomcat服务器中的某个Web应用没有默认Servlet时,都会将DefaultServlet作为默认的Servlet。当客户端访问Tomcat服务器中的某个静态HTML文件时,DefaultServlet会判断HTML是否存在,如果存在,就会将数据以流的形式会送给客户端,否则会报告404错误。

Servlet的线程安全

单实例:每次访问多线程

解决线程安全的最佳办法,不要写全局变量,而写局部变量。

ServletConfig接口

这个接口用来获取配置信息,在web.xml中的servlet元素中填写,现在我要在程序中获取到这些信息。有三种方式

    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>

第一种

public class ServletDemo1 extends HttpServlet{
    private ServletConfig config;
    @Override
    public void init(ServletConfig config) throws ServletException {
        this.config=config;
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        String value=config.getInitParameter("encoding");
        System.out.println(value);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        System.out.println("Post");
    }

}

重写其父类的父类GenericServlet中的init(ServletConfig config)方法,得到ServletConfig,通过其getInitParameter方法获得。

第二种

String value=this.getServletConfig().getInitParameter("encoding");
System.out.println(value);

通过其父类GenericServlet中的getServletConfig()得到ServletConfig,然后通过其getInitParameter方法获得。

第三种

String value=this.getInitParameter("encoding");
System.out.println(value);

直接通过GenericServlet中的getInitParameter方法获得,因为GenericServlet实现了ServletConfig接口。

ServletContext接口

当Servlet容器启动时,会为每个Web应用创建一个唯一的ServletContext对象代表当前Web应用,该对象不仅封装了当前Web应用的所有信息,而且实现了多个Servlet之间数据的共享。

1.获取Web应用程序的初始化参数

在web.xml文件中,不仅可以配置Servlet的初始化信息,还可以配置整个Web应用的初始化信息。

在根元素<web-app>中填写

    <context-param>
        <param-name>address</param-name>
        <param-value>beijing</param-value>
    </context-param>

在Servlet中填写代码

String value=this.getServletContext().getInitParameter("address");
System.out.println(value);

2.实现多个Servlet对象共享数据

由于一个Web应用中的所有Servlet共享一个ServletContext对象,因此Servlet-Context对象的域属性可以被该Web应用中的所有Servlet访问。在ServletContext中维护了一个Map集合。

  • Enumeration getAttributeNames():返回一个Enumeration对象,该对象包含所有存放在ServletContext中的所有域属性名。
  • Object getAttibute(String name):根据参数指定的域属性名返回一个与之匹配的域属性值。
  • void removeAttribute(String name):根据参数指定的域属性名,从ServletContext中删除匹配的域属性。
  • void setAttribute(String name,Object obj):设置ServletContext的域属性,其中name是域属性名,obj是域属性值。

在第一个Servlet的doGet方法中填写代码

ServletContext application=this.getServletContext();
application.setAttribute("name", "Tom");

在第二个Servlet的doGet中填写

String name=(String)this.getServletContext().getAttribute("name");
System.out.println(name);

先放我第一个,再访问第二个,就会在控制台打印出Tom。

3.读取Web应用下的资源文件

  • String getRealPath(String path):返回资源文件在服务器文件系统上的真实路径(文件的绝对路径)。参数path代表资源文件的虚拟路径,它应该以正斜线(/)开始,“/”表示当前Web应用的根目录,如果Servlet容器不能将虚拟路径转换为文件系统的真实路径,则返回null。

1.在MyEclipse的src目录下,创建一个文件test.properties,里边填写Address=Beijing

MyEclipse中src目录下创建的资源文件在Tomcat服务器启动时会被复制到WEB-INF/classes目录下

2.在Servlet中的doGet中填写代码

ServletContext sc=this.getServletContext();
String path=sc.getRealPath("/WEB-INF/classes/test.properties");
Properties prop=new Properties();
prop.load(new FileInputStream(path));
System.out.println(prop.getProperty("Address"));

Servlet的转发

在ServletDemo1中填写代码

protected void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {

        ServletContext sc=this.getServletContext();
        RequestDispatcher rd=sc.getRequestDispatcher("/ServletDemo2");
        rd.forward(req, resp);
        System.out.println("转发回来了");
}

在ServletDemo2中填写代码

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

        System.out.println("转发过来了");
}

在浏览器访问ServletDemo1的时候,控制台显示为

转发过来了
转发回来了

这里写图片描述

评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值