*【Web第十二天】Servlet

Servlet

1.    servlet概述

1.1.servlet是什么

Servlet是sun公司提供的一门用于开发动态web资源的技术。

按照这套规范写出来的Servlet可以放置到web应用中在Servlet容器中运行。

1.2.开发Servlet步骤

(1)写一个类, 实现Servlet接口, 并实现其中的方法

(2)在web.xml中为servlet配置对外访问路径。

 

1.3.案例: 手写一个servlet

详细步骤参见: /FristServlet/手写一个servlet.txt

 

2.使用myeclipse开发servlet

使用myeclipse开发Servlet程序时, 可以新建一个Servlet, 默认继承HttpServlet, 在Servlet内部会覆盖doGet和doPost方法, 分别来处理GET和POST请求。

 

2.1.新建项目

新建一个Web Project,给一个项目名称,其他保持默认即可,然后finish,弹出的对话框选择yes即可。

 

 

2.2.新建一个servlet

新建一个servlet,如果new中找不到servlet,注意视图是否切换到MyEclipse中,给一个包名和类名,下面对勾只留doGet()和doPost(),其他去掉,下一步

 

这里将最后两行删掉,是一些xml中的提示,其他地方一般保持默认即可,也可以修改对外访问路径,其中name和URL会自动配置到xml中。最后finash。

 

将servlet中的注释以及方法中的默认实现全部删掉。

 

为什么要继承HttpServlet?

因为这是一个继承了GenericServlet的类,已经提供doGet()和doPost()方法,可以方便我们开发web项目。

而GenericServlet是一个基础的实现,如果要用此类,需要自己写doGet()和doPost()方法,并且需要在service方法中对请求进行判断,会比较麻烦。

 

HttpServlet底层又是如何实现的?

可以打开HttpServlet源码,发现HttpServlet也是继承自GenericServlet,同时,HttpServlet会写各种方法,比如doGet()和doPost(),然后在service方法中进行判断,不同的请求调用不同的方法。

 

注意:复制一个servletweb.xml中是不会自动生成配置信息的,所以一般不要复制,而是要新建!

 

 

 

2.3.tomcat配置到myeclipse

详细步骤见:/resource/MyEclipse中配置自己安装的tomcat

 

如果不配置,则每次都要手动将程序发布到tomcat中。

2.3.web应用发布到自己的tomcat

这里注意项目名称是否是自己要发布的项目,然后finish,最后ok。

 

 

也可以修改发布的项目名称,参见/resource/在myeclipse中修改web应用发布到Tomcat中的应用的名称.pdf

 

在MyEclipse中启动tomcat

 

启动后,可以通过浏览器访问servlet,比如:

http://localhost/day09/servlet/SecondServlet

 

可以查看servers中tomcat如果是debug模式,则可以修改方法中的代码而无需重启服务器,但是若要新建一个servlet,则需要重启服务器。

 

  1. 1.    Servlet的继承结构

Servlet接口 – 提供了一个Servlet应该具有的最基本的功能

           |

           |-- GenericServlet类, 实现了Servlet接口, 并实现了其中大部分的方法, 但是service方法没有实现, 这个方法需要开发人员自己去实现

                                |

                                |-- HttpServlet类, 继承了GenericServlet, 并实现了service方法,            在service方法中是根据不同的请求方式, 调用不同的doXxx方法, 因               此     我们在开发中, 只需要写一个类, 继承HttpServlet, 并覆盖                                doGet()和         doPost()方法分别来处理Get请求和POST请求即可!!

 

 

 

4.修改servlet模版

方式一:

将\resource\修改Servlet模板\com.genuitec.eclipse.wizards_9.0.0.me201108091322.jar文件拷贝到:

[Myeclipse安装目录]\Common\plugins目录下,会提示是否替换文件,确定替换即可,如果未出现提示,则看是否进对目录,或者是其他版本的MyEclipse

 

方式二:

  1. 在[Myeclipse安装目录]\Common\plugins目录下找到文件:com.genuitec.eclipse.wizards.xxx.jar,在此文件中的Templates目录下可以看到Servlet.java源代码。
  2. 打开源代码,将doGet()和doPost()两个方法的注释和方法中的内容删掉,在doPost()中调用doGet()方法即可。
  3. 修改之后保存
  4. 重新启动Myeclipse即可以使用新的模板代码了

 

 

 

 

5.Servlet调用过程

 

5.1.调用过程

参照<<servlet调用图解>>

 

 

 

 

5.2.Servlet生命周期

Servlet实例在第一次被访问时创建, 创建之后服务器会立即调用init方法进行初始化的操作, 从此以后该实例会一直驻留在服务器的内存中, 为后续的请求服务, 只要有请求访问servlet, 服务器就会调用service方法来处理这个请求, 直到服务器关闭或者是web应用被移出容器时为止, 随着web应用的销毁, servlet实例也会跟着销毁, 在销毁之前, 服务器就调用destroy方法进行善后的处理.

 

 

 

6.servlet虚拟路径的配置

在web.xml中的servlet对外访问的虚拟路径的配置, 可以直接写一个路径, 或者通过 * 号匹配符写一个路径.

 

方式一:直接写一个路径: /servlet/SecondServlet

方式二:通过*号匹配符写一个路径:

(1)以 / 开头, 以 /* 结尾, 如: /servlet/*         /a/*  /*

(2)以 *.后缀 的形式, 如: *.html       *.servlet  *.do *.action

使用*号匹配符写路径, 路径的配置变得更加灵活, 但是也可能会造成, 一个url会被多个servlet Mapping所匹配

 

Url:http://localhost/day09/servlet/SecondServlet.do

 

Servlet1:Test1: /servlet/*

Servlet2:Test2: *.do

 

匹配规则:

           *.后缀的优先级永远最低!!

           哪一个更接近哪一个起作用!!

 

示例:

Servlet1 映射到 /abc/*

Servlet2 映射到 /*

Servlet3 映射到 /abc

Servlet4 映射到 *.do

 

当请求URL为“/abc/a.html”,“/abc/*”和“/*”都匹配,哪个servlet响应

Servlet引擎将调用Servlet1。

当请求URL为“/abc”时,“/abc/*”和“/abc”都匹配,哪个servlet响应

Servlet引擎将调用Servlet3。

当请求URL为“/abc/a.do”时,“/abc/*”和“*.do”都匹配,哪个servlet响应

Servlet引擎将调用Servlet1。

当请求URL为“/a.do”时,“/*”和“*.do”都匹配,哪个servlet响应

Servlet引擎将调用Servlet2。

 

7.Request

代表http请求的对象

7.1.继承结构 (!!重要)

ServletRequest – 提供一个request对象最基本的功能

           |

           |-- HttpServletRequest – 继承了ServletRequest接口, 并在其基础上添加了很多和Http协议相关的方法

 

7.2.request的功能 (!!!重要)

  • 7.2.1.获取客户端相关的信息

getRequestURL方法 -- 返回客户端发出请求完整URL

           如: http://localhost/day09/servlet/SecondServlet

getRequestURI方法 -- 返回请求行中的资源名部分

           如: /day09/servlet/SecondServlet

getQueryString方法 -- 返回请求行中的参数部分

           如: username=zhangfei&password=123

getRemoteAddr方法 -- 返回发出请求的客户机的IP地址

           如: 127.0.0.1  //可能会出现0:0:0:0:0:0:0:1形式,是ipv6的表现形式。

getMethod -- 得到客户机请求方式

           如: GET或POST

!!getContextPath -- 获得当前web应用虚拟目录名称

           如: /day09

注意:在写路径时不要将web应用的虚拟路径的名称写死, 应该在需要写web应用的名称的地方通过getContextPath方法动态获取

  • 7.2.2.获取请求头信息

getHeader(name)方法 --- String

getHeaders(String name)方法 --- Enumeration<String>

可以通过遍历枚举遍历每一个信息

例如:while (values.hasMoreElements()) {

                                String value = (String) values.nextElement();

                                System.out.println(value);

                     }

getHeaderNames方法 --- Enumeration<String>

getIntHeader(name)方法  --- int

getDateHeader(name)方法 --- long(日期对应毫秒)

  • 7.2.3.获取请求参数(!!!重要)

getParameter(String name) --- String 通过name获得值

getParameterValues(String name)  --- String[ ] 通过name获得多值 checkbox

getParameterMap()  --- Map<String,String[ ]> key :name value: 多值

getParameterNames()  --- Enumeration<String> 获得所有name

 

package cn.sgy.web;

import java.io.IOException;
import java.util.Arrays;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class RequestDemo2 extends HttpServlet {

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

        // 获取请求参数
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String nickname = request.getParameter("nickname");
        
        System.out.println("用户名为:"+username);
        System.out.println("密码为:"+password);
        System.out.println("昵称为:"+nickname);
        
        //根据key获取value组成的数组
        String[] values = request.getParameterValues("hobby");
        System.out.println("爱好:"+Arrays.toString(values));
        
        //获取参数组成的map
        Map<String,String[]> map = request.getParameterMap();
        for(Map.Entry<String, String[]> entry : map.entrySet()){
            String key = entry.getKey();
            String[] vs = entry.getValue();
            System.out.println(key+":"+Arrays.toString(vs));
        }
    }

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

        doGet(request, response);

    }

}

 

 

请求参数中的乱码问题

 

乱码分析: 编码时和解码时使用的码表不一致造成的!!

           编码: 是在浏览器进行的, 浏览器发送数据使用的是什么编码? 

                     浏览器在打开当前页面时使用的是什么码表,也会使用相同的码表来发送数据.

                                打开页面使用utf-8, 所以发送数据也是用utf-8码表

 

           解码: 是在服务器端进行的, 服务器端接收数据使用的又是什么编码?

如果不指定, 服务器会使用默认的码表来接收浏览器发送过来的数据, 默认的码表是iso8859-1.

           解决方案:

乱码造成的原因是编码不一致, 所以应该让两端的编码保持一致!, 应该通知服务器使用utf-8来接受客户端发送过来的数据!!

 

request.setCharacterEncoding(“utf-8”);//用来通知服务器使用什么编码来接受请求实体内容中的数据, 如果使用的是POST提交, POST提交的请求参数就是在请求实体内容中!, 所有这个方法可以解决POST提交的乱码问题!!!

 

GET提交的请求参数由于不在请求实体内容中,而是在请求行中的请求资源路径后面拼接着, 所以这行代码对GET提交的参数乱码不起作用!!!

 

           GET提交的参数乱码问题该如何解决???

                     GET提交的乱码问题可以通过手动编解码来解决!!

                     //>>username为乱码, 通过乱码反向编码得回二进制数组

                     byte[] bytes = username.getBytes("iso8859-1");

                     //>>通过二进制数组查询正确的码表, 得出正确的数据

                     username = new String(bytes, "utf-8");

 

 

  • 7.2.4.实现请求转发(!!!重要)

请求重定向: 302状态码+location响应头

请求转发: 和请求重定向都可以实现资源的跳转, 但是区别是请求转发是服务器内部的并且是同一个WEB应用内部的资源跳转

请求转发的特点:

           一次请求对应一次响应

           地址栏地址不会发生变化

请求转发只能在同一个WEB应用内部资源之间进行跳转! 不能是不同的WEB应用或者不同的主机!

          

request开发细节:

           在转发之前, 如果response缓冲区被写入了数据但是还没有打给浏览器, 在转发时response缓冲区(数据)将会被清空!

例如:在RequestDemo3中添加代码:

response.getWriter().write("no money!");

发现浏览器中并未得到"no money!"这段字符串的响应。

 

           在转发之前, 如果response缓冲区被写入了数据并且已经打给了浏览器, 转发将会失败!!

例如:在RequestDemo3中添加代码:

response.getWriter().write("no money!");

       response.flushBuffer();

发现浏览器可以显示"no money!",因为强制刷新了,但是转发就会报错,因为已经响应过了,一次请求对应一次响应。

 

           在同一个Servlet中转发不能进行多次!!(A既转发B, 又转发给C)

           但是可以进行多重转发(比如A转发给B, B再转发给C)

 

 

 

 

  • 7.2.5.作为域对象来使用(!!!重要)

  域对象:如果一个对象具有一个可以被看见的范围, 利用该对象上的map可以在整个范围内实现资源的共享!

  域对象提供的方法(可以操作map中的数据):

           setAttribute(String name, Object value);  用来存储一个对象,也可以称之为存储一个域属性

           getAttribute(String name);   用来获取request中的数据

           removeAttribute(String name);   用来移除request中的域属性

           getAttributeNames();   获取所有域属性的名称

 

生命周期:

           一次请求开始时创建request对象, 一次请求结束时销毁request对象

                    

作用范围:

           整个请求链

主要功能:

           在整个范围内共享数据

           带数据到目的地

 

例如:在RequestDemo3中可以设置一些属性,比如是从数据库查询出来的数据,转发到RequestDemo4后,在RequestDemo4中从request域中获取数据,并打印到页面中。

 

 

  • 7.2.6.实现请求包含(用得少)

请求包含是服务器内部资源合并的现象

 

 

如果浏览器访问Servlet A, 但是A不能独立的处理这次请求, 需要另外一个Servlet B帮忙, 于是在A中可以将B包含进来, 包含的代码如下:

request.getRequestDispatcher(“B的路径”).include(request, response);

 

将B包含进来后, 将会由A和B共同来处理这次请求, 处理的结果也会合并在一起, 一起打给浏览器!

 

posted @ 2018-03-15 21:02  songyao  阅读(177)  评论(0编辑  收藏  举报