Fork me on GitHub

Servlet

注意:所有文章除特别说明外,转载请注明出处.

Servlet

开发步骤:

  • 编写一个java类,实现servlet接口
  • 把开发好的java类部署到web服务器 API文档

Servlet API Documentation

生命周期相关方法(life-cycle methods)

Servlet的生命周期通过java.servlet.Servlet接口中的init()service()、和destroy()方法表示。Servlet的生命周期有四个阶段:加载并实例化、初始化、请求处理、销毁。(见文末【参考链接】)

手动编写第一个servlet

1.在tomcat中新建一个web应用hello,在web应用中新建一个WEB-INF/classes目录

2.在classes目录新建一个FirstServlet

1
2
3
4
5
6
7
8
9
10
11
package org.iot;

import java.io.*;
import javax.servlet.*;

public class FirstServlet extends GenericServlet{
public void service(ServletRequest req,ServletResponse res)throws ServletException,java.io.IOException{
OutputStream out = res.getOutputStream();
out.write("hello servlet!!!".getBytes());
}
}

3.编译,javac -cp %CATALINA_HOME%/lib/servlet-api.jar -d . FirstServlet.java,命令行手动编译参考这里

4.在WEB-INF目录中新建一个web.xml文件,配置servlet的对外访问路径

5.启动tomcat访问

servlet的调用过程和生命周期

时序图 -> servlet的调用过程和生命周期

servlet的开发细节

  • 标签:<servlet>包含<servlet-name><servlet-class><servlet-mapping>包含<servlet-name><url-pattern>
  • 映射:web.xml中一个<servlet>可对应多个<servlet-mapping>
  • 通配符:<servlet-mapping><url-pattern>可以使用通配符,两种固定格式:*.扩展名;以/开头,以/*结尾

  • 对象:servlet由servlet引擎调用,不能独立运行。客户端多次请求,服务器只创建一个servlet实例,之后驻留内存中继续服务直至web容器退出才销毁它。

  • 请求:服务器针对客户端的每一次请求都会创建新的requestresponse对象(它们的生命周期很短),传给service方法。
  • 加载:servlet实例的创建和init方法的调用是在第一次请求时,而非服务器启动时,除非在<servlet>标签配置<load-on-start-up>,数字越小优先级越高
  • 缺省:映射路径为正斜杠/,则为当前web应用的缺省servlet,不匹配的都交给缺省
  • 线程安全:访问同一资源会引发线程安全问题; SingleThreadModel标记接口(已弃用)
  • ServletConfig:在<servlet>标签配置<init-param>,通过getServletConfig方法获得配置。可配置输出字符集,读哪个配置文件等等。
  • ServletContext:代表当前web应用,含有一些web应用全局性方法,实现web资源共享、servlet转发等。通过ServletConfig.getServletContext方法获得,在<context-param>标签配置。

ServletContext

API:Interface ServletContext

概念

一个web应用所有servlet共享同一个ServletContext对象,可实现数据共享。ServletContext被称为context对象。

域:作用范围 context域:整个应用程序范围。

ServletContext域:

  1. 这是一个容器
  2. 说明了这个容器的作用范围,也就是应用程序范围

转发:客户机一次请求;重定向:客户机两次请求

作用

  • 获取web应用的初始化参数
  • 实现servlet转发
  • 利用ServletContext对象读取资源文件
    • 获得文件路径
    • 读取资源文件的三种方式
    • .properties文件(属性文件)

配置文件:properties文件和xml文件;数据有关系使用xml文件,没有关系则使用properties文件。

1.通过ServletContextgetResourceAsStream方法,读取properties文件

模板代码(注意文件位置不同写路径会不同):

1
2
3
InputStream in = this.getServletContext().getResourceAsStream("/WEB-INF/classes/org/iot/servlet/db.properties");
Properties properties = new Properties();//map
properties.load(in);

2.通过servletContextgetRealPath方法得到资源的绝对路径,再通过传统方式(FileInputStream)读取

3.通过类装载器去读,ClassLoadergetResourceAsStream(如果读取资源文件的程序不是servlet),文件不能太大。只装载一次,所以如要读到更新后的数据,通过类装载的方式得到资源文件的位置,再通过传统方式读取资源文件的数据(用getResource得到path,再用FileInputStream

参考链接

servlet和Jsp生命周期解读


Servlet

1.Servlet程序编写  生命周期
2.ServletAPI Request Response 
3.Cookie 和 Session 

1.理论
Servlet 用来开发 动态web资源

静态web资源 : 固定数据文件
动态web资源 : 通过程序动态生成数据文件 

注意:Servlet技术基于Request-Response编程模型  HTTP协议也是基于请求响应 模型。Servlet技术 用来 开发基于HTTP web 应用程序 

接触 JavaEE API 程序接口和已经实现接口类的使用 JavaEE:Java Platform, Enterprise Edition 缩写 

2.Servlet快速入门
1.创建web project
2.编写 class 继承 HttpServlet
3.在web.xml 配置Servlet程序虚拟访问路径
用户在浏览器上通过这个路径访问编写Servlet程序

4.覆盖doGet或者doPost方法进行输出 (表示输出函数是doPost/doGet方法)

总结:Servlet 动态生成网页文件 

3.Servlet执行过程

1.用户在客户端发起url请求 : http://localhost/day05/hello   web.xml /hello 映射 HelloServlet程序

  在servlet写好之后,通过在web.xml文件中配置相应路径响应的servlet,然后将其映射过去。

2.用户提交请求时,get方式提交 执行 HelloServlet的 doGet 方法   post方式提交 执行 HelloServlet的 doPost 方法 

  用户在前台提交表单的方式 method="post/get" 对应在servlet中执行相应的doPost/doPost方法

4.Servlet程序在编写和运行时,需要 javaee 类库 (API支持)

* 在学习javase  List 需要 import java.util.List  需要 jre/lib/rt.jar 
* MyEclipse 自动导入 javaee5 liberary  存在 javaee.jar  提供 Servlet 需要类 API支持 (开发环境使Servlet程序正常编译)
* Serlvet程序运行tomcat环境中 没有javaee.jar , 在 tomcat/lib/servlet-api.jar 提供Servlet程序运行需要 类API 支持 (运行环境需要的)

5.手动编写Servlet运行

1.在webapps 新建 day05test目录   此为虚拟应用
2.在day05test 新建 WEB-INF/classes
3.将编写Servlet的java源码文件 放入 classes ,在 WEB-INF 配置web.xml 
4.编译Servlet的 java程序 

// 通过 -classpath 指定 Servlet需要jar 包
javac -classpath E:\apache-tomcat-6.0.14\lib\servlet-api.jar HelloServlet.java
//生成Servlet package结构    
javac -d . -classpath E:\apache-tomcat-6.0.14\lib\servlet-api.jar HelloServlet.java

6.Servlet运行原理分析

编写Servlet程序没有 main函数,tomcat调用Servlet程序执行

通过myeclipse向导创建 Servlet   在创建Servlet程序的同时,生成 web.xml 配置

* 生成Servlet信息非常复杂,想生成 Servlet 内容整洁一些,精简一些的话就修改Servlet模板 
    1、myeclipse工具   安装目录 common / plugins
    com.genuitec.eclipse.wizards_8.5.0.me201003052220.jar
    2、解压缩 templates/Servlet.java  这个就是Servlet模板 

7.通过API Servlet继承关系
Servlet接口
实现类 : GenericServlet
子类 HttpServlet 编写Servlet 继承HttpServlet

* 编写Servlet 间接 实现 Servlet 接口  (简化开发)

Servlet接口 提出,为了解决基于请求-响应模型数据处理 (并没有涉及与HTTP协议相关 API)
GenericServlet 实现接口 通用Servlet 也没有提供与 HTTP协议相关 API 
HttpServlet 引入与 协议相关 API 

8.Servlet生命周期

init(ServletConfig config)  初始化 
service(ServletRequest req, ServletResponse res)  提供服务方法
destroy()  销毁 

1、tomcat服务器启动时,没有创建Servlet对象

2、第一次访问时,tomcat构造Servlet对象,调用 init,执行service 

3、从第二次以后访问 tomcat 不会从新创建Servlet对象,也不会调用init   每一次访问都会调用service

4、当服务器重启或正常关闭时 调用destroy (正常关闭 shutdown.bat)

注意:Servlet对象是tomcat创建的,每次请求调用Servlet中service方法,tomcat服务器会在每次调用Servlet的service方法时,为该方法创建Request对象和Response对象

* 在 JavaEE API 中没有Request和Response实现类   实现类由Servlet服务器提供的,tomcat提供实现类 weblogic 提供实现类 


service方法 和 HttpServlet doGet/doPost 关系区别(必须阅读HttpServlet源代码) 

在HttpServlet代码实现中,根据请求方式不同 调用相应doXXX方法 get方式请求  doGet  post方式   doPost 

配置Servlet随tomcat服务器启动时 进行初始化   <load-on-startup >(web.xml中配置)  *<load-on-startup > 参数可以是一个数字 0-9 代表服务器加载优先级 0 最高 

例如:在tomcat启动时,想通过Servlet加载一些框架配置文件 配置随服务器启动 (struts1 )

结论:
    1、编写Servlet 继承HttpServlet
    2、编写Servlet 不需要覆盖service方法,只需要覆盖doGet和doPost 方法

    Servlet初始化时覆盖init() ,无需覆盖init(config) ?? 
    * init(Config) 调用 init() 

注意:当doGet和doPost代码逻辑相同时,可以相互调用,简化编程

9.一个Servlet可以配置多个 url-pattern (web.xml)

URL 配置格式 三种:
    1、完全路径匹配  (以/开始 ) 例如:/hello /init 
        * 当前工程没有被正确发布,访问该工程所有静态资源、动态资源而发生404错误  表示工程启动时出错了 
        * 查看错误时 分析错误
            1) 单一错误 : 从上到下 查看第一行你自己写的代码 (有的错误与代码无关,查看错误信息)
            2)复合错误 Caused by ---- 查看最后一个Caused by 
            * Invalid <url-pattern> init2 in servlet mapping 

    2、目录匹配 (以/开始) 例如:/*  /abc/* 
        / 代表网站根目录 

    3、扩展名 (不能以/开始) 例如:*.do *.action 
        典型错误 /*.do 

    优先级:完全匹配 > 目录匹配 > 扩展名匹配 

10.路径问题:编写九九乘法表

1、需要用户在客户端输入一个数字
2、Servlet接收客户输入数字 打印对应乘法表 

例如:在chengfabiao.html 通过 action 访问 ChengfabiaoServlet 路径可以用绝对路径和相对路径

1.相对路径:相对当前网页地址 路径  例如 chengfabiao  ./chengfabiao  ../chengfabiao
    例如: http://localhost/day05/chengfabiao.html  提交 action="chengfabiao" 

* 将url最后地址换成相对路径 
结果: http://localhost/day05/chengfabiao    服务器端 /chengfabiao

    例如: http://localhost/day05/aaa/chengfabiao.html 提交 action="chengfabiao"
    结果: http://localhost/day05/aaa/chengfabiao    服务器 /chengfabiao 
        * /aaa/chengfabiao 与服务器 /chengfabiao 不匹配 出现404 

    http://localhost/day05/aaa/chengfabiao.html 提供 action="../chengfabiao" 
    结果:http://localhost/day05/aaa/../chengfabiao   ..和/aaa抵消 http://localhost/day05/chengfabiao 可以匹配服务器 /chengfabiao

结论:如果用相对路径提交请求,考虑当前路径, 当前访问服务器资源路径不同   相对路径写法不同

绝对路径 解决相对路径,会根据当前地址改变问题。 
例如: /day05/chengfabiao 、http://localhost/day05/chengfabiao

绝对路径 以/开始 /访问服务器根目录 
例如: 客户端访问服务器,不管当前路径是什么   / 服务器根目录 http://localhost/day05   找到虚拟目录day05工程  /day05/chengfabiao  找到 day05工程下配置 虚拟路径/chengfabiao

结论: 客户端路径 /工程虚拟目录/servlet虚拟路径 例如:/day05/chengfabiao
服务器端 配置web.xml 不需要写工程虚拟目录  只要直接写/servlet虚拟路径  例如:/chengfabiao 

掌握Servlet程序编写

通过路径 访问Servlet 程序
    * Servlet 生命周期

    init 程序初始化
    service 程序业务层
    destroy 程序销毁    

1.学习init方法 init(ServletConfig) 通过ServletConfig 获得Servlet初始化参数

1、创建一个Servlet

2、在 web.xml 中 标签内通过 标签为Servlet配置初始化参数

itcast
传智播客

3、在Servlet程序中通过ServletConfig对象获得itcast对应数据
getInitParameter —— 通过name获得value
getInitParameterNames —– 获得所有name

  • 思考 :如何在 doGet 或 doPost 方法中获得 Servlet 初始化参数?

    1.将ServletConfig对象保存实例成员变量
    2.GenericServlet 已经将 ServletConfig 保存成员变量,在子类中通过 getServletConfig() 方法获得初始化参数

    结论:子类Servlet不需要覆盖 init(ServletConfig) 只需要通过GenericServlet中 getServletConfig() 获得ServletConfig对象
    应用:在init-param 指定配置文件位置和名称,配置Servlet随服务器启动创建 load-on-startup

  • ServletConfig 配置初始化数据,只能在配置Servlet获得,其它Servlet无法获得 每个Servlet程序都对应一个ServletConfig对象

  • ServletContext 是Servlet上下文对象,每一个工程都会创建单独ServletContext对象,这个对象代表当前web工程,操作ServletContext,必须通过 ServletConfig 获得对象

    应用:

    1、 获得整个web应用初始化参数
    2、 实现全局数据共享
    3、 实现服务器端转发功能
    4、 读取web工程资源文件
    

1.获取WEB应用的初始化参数 和 ServletConfig 对象不同

  • ServletConfig对象配置参数,只对配置Servlet有效,如果配置参数,所有Servlet都可以访问 通过ServletContext

2.通过 ServletContext 在多个Servlet间 共享数据
案例:在ServletContext中 保存站点访问次数 ,每当一个用户访问站点,将访问次数+1

    在CountServlet 初始化过程中,向ServletContext 保存访问次数 方法:ServletContext  setAttribute 

    每次访问次数 +1  数据存放ServletContext中 所有Servlet都可以获得该数据

* 在ServletContext中保存数据,所有Servlet都可以访问 

3.通过 ServletContext 完成服务器程序转发

什么是转发? 转发和重定向区别 ?
getRequestDispatcher(java.lang.String path)  完成转发

使用转发还是重定向? 转发性能好于重定向,请求次数好 

案例:统计字母次数
    request.getParameter("content") 获得form 提交内容 content 就是 textarea name属性

4.利用ServletContext对象读取资源文件

1.使用java application 读取文件,读取当前工程下所有文件  使用相对路径读取文件

2.使用Servlet读取文件 只能读取WebRoot下所有文件  必须使用绝对磁盘路径读取文件 

3.通过站点根目录绝对路径 获得磁盘绝对路径 getServletContext().getRealPath(“/WEB-INF/info.txt”)

注意:因为 WEB-INF/classes 非常特殊 (存放.class文件目录),被类加载器加载,通过Class类对象读取该目录下文件 
    String filename3 = c.getResource("/a1.txt").getFile();  表示: / 代表 /WEB-INF/classes 

结论:在WEB工程中,必须将文件路径转换绝对磁盘路径 c:\xxx e:\xxx\xxx  getServletContext().getRealPath("/xxx");   /代表WebRoot 
如果读取文件恰好位于 WEB-INF/classes 通过 类名.class.getResource("/文件名").getFile(); 获得绝对磁盘路径   / 代表 /WEB-INF/classes 

注意:
缺省Servlet 功能:处理其他Servlet都不处理请求
tomcat/conf/web.xml org.apache.catalina.servlets.DefaultServlet 作为缺省Servlet

总结:
    1、编写Servlet HelloServlet
    2、修改Servlet模板 
    3、Servlet生命周期 理论重点掌握
    4、Servlet url三种写法 完全、目录、扩展名
    5、路径问题:绝对路径   案例 九九乘法表
           将web.xml 配置路径复制到网页 在路径前 /工程名
    6、ServletConfig 和ServletContext 读取初始化参数区别 ?
    7、ServletContext数据共享案例   统计访问次数
    8、ServletContext转发案例   统计字母出现次数
    9、读取web工程中资源文件   绝对路径
        在web工程中,必须将 文件路径转换绝对磁盘路径 c:\xxx e:\xxx\xxx   getServletContext().getRealPath("/xxx");  /代表WebRoot 
        如果读取文件 恰好位于 WEB-INF/classes   通过 类名.class.getResource("/文件名").getFile(); 获得绝对磁盘路径  / 代表 /WEB-INF/classes 
    10、缺省Servlet 了解功能将静态资源数据内容读取写给客户端 

HttpServletResponse HttpServletRequest(关键:了解HTTP协议)

Web服务器(TOMCAT)收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象和代表响应的response对象。所以获取客户端提交的数据,只需要找到request对象即可,然后如果需要向容器输出数据,只需要找response对象即可。

1.HttpServletResponse

1.响应行 setStatus(int sc);//设置相应状态码 HTTP/1.1 200
2.响应头 setHeader(String name,String value);//设置响应头信息 
      请求重定向 sendRedirect(String location);

  浏览器使用的码表(告知)
    response.setHeader("content-type","text/html;charset=utf-8");

  告知客户端不缓存
    response.setHeader("pragma","no-cache");
    response.setHeader("cache-control","no-cache");
    response.setDataHeader("expires",0);
3.相应正文
    setCharacterEncoding(String charset);//告知服务器使用什么编码
    setContentType(String type);

2.HttpServletRequest

1.请求行
  Get方式 URL链接:http://localhost:8080/day09/servlet/req1?username=zs  协议:http/1.1

    getMethod(); 获得请求方式
    getRequestURL();返回客户端发出请求时的完整URL。
    getRequestURI(); 返回请求行中的资源名部分。
    getContextPath(); 当前应用的虚拟目录 /day09_01_request
    getQueryString() ; 返回请求行中的参数部分。

2.请求消息头

  String getHeader(String name)  根据头名称得到头信息值
  Enumeration   getHeaderNames()  得到所有头信息name
  Enumeration   getHeaders(String name)  根据头名称得到相同名称头信息值

3.请求正文

获取表单数据相关的方法
<input type="text" name="username" />
  getParameter(name) 根据表单中name属性的名,获取value属性的值方法 
  getParameterValues(String name)专为复选框提供的方法
  getParameterNames() 得到表单提交的所有name的方法 
  getParameterMap 到表单提交的所有值的方法   //做框架用,非常实用
  getInputStream  以字节流的方式得到所有表单数据

扩展:
    操作非表单数据相关的方法(request是一个域对象)
        void setAttribute(String name, Object value);
        Object getAttribute(String name);
        void removeAttribute(String name);

    请求转发相关方法

        //得到请求转发或请求包含的协助对象
        RequestDispatcher getRequestDispatcher(String path)
        forward(ServletRequest request, ServletResponse response) //转发的方法
        include(ServletRequest request, ServletResponse response) //请求包含

    请求编码相关方法

        //解决post方式编码
        request.setCharacterEncoding("UTF-8"); //告诉服务器客户端什么编码,只能处理post请求方式

        //解决get方式编码
        String name = new String(name.getBytes(“iso-8859-1”),”UTF-8”);

Servlet技术

两条主线
1、HTTP协议
2、Servlet生命周期

1.init()方法中参数 ServletConfig 对象的使用

通过 ServletConfig 获得 ServletContext 对象来使用

2.service()方法 含有两个参数对象 ServletRequest ServletResponse

在实际开发Servlet过程中不需要覆盖service,HttpServlet 根据请求方式自动调用 doGet 或者 doPost doGet和doPost参数 HttpServletRequest 和 HttpServletResponse


3.HttpServletRequest HttpServletResponse (学习重点)

客户端每次请求都会创建request对象和response对象 作用:被传递service / doGet / doPost

1.HttpServletRequest 封装客户端相关信息,服务器Servlet程序可以 通过request对象 操作客户端信息
2.HttpServletResponse 封装服务器向客户端发送响应数据信息,Servlet程序 通过response对象 向客户端发送响应

4.Response常用的API

1.setStatus 设置响应行 当中 状态码
2.setHeader 设置响应头信息
3.getOutputStream 获得字节流 ---- 输出响应体内容
4.getWriter 获得字符流 ---- 输出响应体内容

注意:

  • HttpServletResponse 继承 ServletResponse 接口 ,ServletResponse 并没有提供与HTTP协议相关API ,HttpServletResponse 添加了与协议相关 API
  • JavaEE API 中并没有提供 HttpServletResponse 实现类 —- 实现类由tomcat服务器提供的

5.服务器常用状态码 : 200 302 304 404 500

200 请求处理成功
302 客户端重定向
304 客户端访问资源没有被修改,客户端访问本地缓存
404 访问资源不存在
500 服务器内部出错

6.案例:

1.通过302 + Location 头信息实现页面重定向效果 
    response.setStatus(302);
    response.setHeader("Location", "/day06/welcome.html"); // 相对路径 和 绝对路径

注意:* / 由客户端定向服务器,代表客户端 / , 必须添加工程虚拟目录 

   头信息存在多个值

    Accept-Encoding: gzip, deflate --- key:value1,value2 
    response.addHeader 用于设置响应头有多个值 ------ 不常用 
重点:setHeader 

在Response API 中提供sendRedirect ---- 完成302+Location重定向效果 
例如: response.sendRedirect("/day06/welcome.html");

2.登陆重定向


3.自动刷新网页 

* 登陆成功,5秒后自动跳转XX页面 
原理:通过refresh 头信息 
格式 -----   refresh: 时间;url=跳转路径
例如: refresh:3;url=http://www.itcast.cn -------- 3秒后自动跳转http://www.itcast.cn 网站 

注意:* HTML 页面中存在一类非常特殊标签 <meta> ,<meta> 起到设置头信息作用 
<meta content="3;url=/day06/response/demo3/result.html" http-equiv="refresh"> ---- 完成自动跳转 

4.通过response头信息设置 浏览器禁止缓存

原理:和禁用缓存相关头信息 三个 
Cache-Control:no-cache
Expires:Thu, 01 Dec 1994 16:00:00 GMT  ----- setDateHeader("expires",-1);
Pragma : no-cache

IE 工具---Internet选项 ---常规 --- 设置 --- 查看文件
对于Servlet生成HTML页面,经常需要改变,禁止Servlet动态程序缓存 
注意:* 设置Expires时,通常 setDateHeader 为过期时间设置一个毫秒值,生成HTTP响应时,会自动转换日期字符串表示 

通过response 生成 客户端响应体,通过字节流和字符流两种输出方式

* 哪些情况用字节流? 哪些情况用字符流 ? 
    文件拷贝 ---- 字节流 
    分析文件内容 --- 字符流 (中文操作 字符流)

5.输出中文信息

对中文信息进行编码 
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8"); 

setCharacterEncodig 和 setContentType 区别 ?

结论:开发中只需要使用setContentType 就可以了

注意:
1、getOutputStream和getWriter 不能同时使用
2、必须在getOutputStream和getWriter 之前 设置响应 编码
3、getOutputStream和getWriter 输出内容 是 HTTP响应体
4、getOutputStream和getWriter 存在缓冲区的 ,在service方法结束时,自动关闭流,flush缓冲区内容

6.文件下载
第一种:通过超链接 完成文件下载 
* 如果浏览器可以识别该文件格式,直接打开,只有链接文件浏览器不识别文件格式,才会实现下载 

第二种:通过Servlet程序实现下载 
原理:通过Servlet读取目标程序,将资源返回客户端
通过程序下载文件 设置两个头信息 Content-Type Content-Disposition 
response.setContentType(getServletContext().getMimeType(filename));  ---- 设置文件类型 
response.setHeader("Content-Disposition", "attachment;filename="+ filename); ---- 设置文件以附件形式下载(对于浏览器识别格式文件)

7.验证码输出案例 
Java图形API 生成验证码图片 ----- 了解 
为什么需要验证码 ?防止有人通过程序恶意攻击网站 
验证码为什么是图片 ?为什么雪花或者干扰线?  
常用验证码 : 字母和数字 

验证码旋转效果 
rotate(double theta, double x, double y)  ----- 参数 theta 旋转弧度 
2PI 弧度 = 360 角度 

-30 ---- 30 角度 

验证码看不清楚,点击切换验证码 ---- 编写JavaScript程序 
方法一:设置验证码图片不缓存
方法二:每次访问使url 不同 ----- url?new Date().getTime() 当前时间 

HttpServletRequest 分为四个部分

HttpServletRequest 想比 ServletRequest 添加与协议相关 API

1、获取客户机信息
    URI和URL区别
    url:http://localhost/day06/request1  --- 完整
    uri:/day06/request1  ---- 部分

    URI 包含 URL的 ,URL 一定完成路径,URI可以相对路径 
    http://localhost/day06/request1 是一个URL 同时也是 URI
    ./hello  /day06/request1 ---- 都是URI 不是URL 

获得ip : request.getRemoteAddr();

获得当前访问资源路径 : request.getRequestURI().substring(request.getContextPath().length());


2、获取请求头信息
    getHeader 获得头信息的值,转换一个字符串
    getHeaders 获得头信息值 ,获得Enumeration
    getHeaderNames 获得所有头信息名称  返回 Enumeration

    * 掌握getHeader使用,遍历Enumeration 获得所有头信息

    编写防盗链程序,存在合法referer不是盗链,否则控制目标资源无法访问 !
    * 通过URL 绕过盗链判断

3、获取请求参数

    请求参数:指用户通过请求提交服务器一些数据

        <a href="url?xxx=xxx" >
        <form method="get">
        <form method="post">

    如:/day06/request4?name=zhangsan&city=beijing  这一路径中包括两个参数的 name 和 city 

request常用API四个

    getParameter
    getParameterValues
    getParameterNames
    getParameterMap 

非空校验
    if (username != null && username.trim().length() > 0) {} //短路 

乱码问题
    post方法:request.setCharacterEncoding("客户端编码集");

    get乱码手动解决
        username = URLEncoder.encode(username, "ISO-8859-1");// 用ISO编码
        username = URLDecoder.decode(username, "utf-8"); // 用utf-8解码
        简化上面写法 : username = new String(username.getBytes("ISO-8859-1"), "utf-8");

get乱码 配置tomcat默认解码字符集
    在tomcat/conf/server.xml 
    Connector中 添加一个属性 URIEncoding="utf-8"

结论:开发时,尽量不要修改tomcat默认解码集 ,提交请求请尽量使用post ,如果非要使用get ,手动编码

问题:http://localhost/day06/servlet?username=zhangsan+lisi
在服务器端 通过 request.getParameter("username") 结果是 ??? ----- zhangsan lisi 


4、利用请求域传递对象
    HttpServletRequest 和 ServletContext 类似 都是数据域对象 , 以Map方式保持数据

    区分:存活时间不同 

    ServletContext对象 服务器启动对象创建,服务器停止对象销毁
    ServletRequest对象 当产生一次请求时 创建,当响应结束后,对象销毁 

    通过request转发请求,request保存数据进行Servlet之间传递 ---- 应用?
    Servlet进行数据处理 --- 生成结果 --- 转发结果给JSP显示 

注意:

1、在使用forward之前 不能将响应内容传输到客户端
    情况一 response输出流执行flush 
    情况二 同一个Servlet不能连续使用forward 和 redirect
2、在执行forward 和 redirect时,清除之前写入响应流数据 
3、ServletContext进行转发 路径必须/开始,request进行转发路径可以使用相对路径 

转发和重定向区别
    1、转发一次请求、一次响应 重定向 两次请求 两次响应
    2、转发只能跳转站内程序,重定向定向任何站点 
    3、转发 URL地址不变 ,重定向URL地址改变 
    4、转发 对客户端不可见,重定向对客户端可见 
    5、转发共享同一个Request中数据,重定向两次请求,不同Request对象,不能共享Request数据 
  • request.setAttribute 必须和 request.getRequestDispatcher().forward 一起使用

RequestDispatcher 的 include 方法 用来做页面布局 —— <%@include%> jsp:include
将页面公共部分抽取出来,通过include 引用到页面中 —- 更加方便维护


总结:
1、Response 四个必须 API setStatus setHeader getOutputStream getWriter
setStatus 状态码
setHeader 头信息
getOutputStream getWriter 响应体

2、重定向 302 + Location ---- 简写 sendRedirect 
案例 用户登录重定向 

3、refresh自动刷新网页 
<meta> 标签使用 

4、禁用浏览器缓存 三个头字段 

5、响应中文乱码 ---- 都使用setContentType

6、文件下载 超链接和Servlet程序 
    Servlet程序 设置两个头字段 Content-Type Content-Disposition

7、验证码程序 (写完整存档)
    * 验证码点击切换 两种方法 

8、request请求行相关API getRequestURI getContextPath getRemoteAddr getMethod  
    思考:获得访问资源路径 

9、request头信息获得(不重要) ---- 掌握防盗链案例

10、获得请求参数 乱码解决 get 、post  (超级重要)

11、转发共享request数据 、include进行页面布局 ---- 了解即可

POST编码与GET编码

1.POST编码
<%@page pageEncoding=”UTF-8”%> 浏览器当前是什么编码就以什么编码提交
Servlet:因为客户端没有告诉服务器,请求正文的编码,于是服务器默认用ISO-8859-1进行编码,从而出现乱码
解决方法:告诉服务器请求正文的数据应该使用的编码是什么(request.setCharacterEncoding(“UTF-8”);)

2.GET编码
<%@page pageEncoding=”UTF-8”%> 浏览器当前是什么编码就以什么编码提交
Servlet:URL地址后的参数服务器默认用ISO-8859-1进行编码,这样会产生乱码
解决方法:如果依照POST的解决乱码的方法,结果是无效的。需要拿到原始的二进制数据,然后用UTF-8进行重新编码

byte b[] = name.getBytes(“ISO-8859-1”);//1010101
String name = new String(b,”UTF-8”);//解决


Request Response对象解决乱码的应用

1.服务器解决乱码
response.setContentType("text/html;charset=gbk");
2.浏览器解决乱码
request.setCharacterEncoding("utf-8");

Servlet技术

1、Servlet程序编写 ----- 生命周期
2、ServletAPI Request Response 
3、Cookie 和 Session 

注意:Servlet 用来 动态web资源 开发
静态web资源 : 固定数据文件
动态web资源 : 通过程序动态生成数据文件

Servlet技术基于Request-Response编程模型 ---- HTTP协议也是基于请求响应 模型 
* Servlet技术 用来 开发基于HTTP web 应用程序 

接触 JavaEE API ------ 程序 接口 和 已经实现接口 类的 使用 
JavaEE ---- Java Platform, Enterprise Edition  缩写 

Servlet快速入门
1、创建web project
2、编写 class 继承 HttpServlet
3、在web.xml 配置 Servlet程序 虚拟访问路径

    * 用户在浏览器上通过这个路径 访问编写Servlet程序 
4、覆盖doGet或者doPost方法 进行输出 

* Servlet 动态生成 网页文件 

执行过程
    1、用户在客户端发起url请求 : http://localhost/day05/hello ----- web.xml /hello 映射 HelloServlet程序
    2、用户提交请求时,get方式提交 执行 HelloServlet的 doGet方法 post方式提交 执行 HelloServlet的doPost 方法 

Servlet程序在编写和运行时,需要javaee 类库 (API支持)
    * 在学习javase  List 需要 import java.util.List  需要 jre/lib/rt.jar 
    * MyEclipse 自动导入 javaee5 liberary  存在 javaee.jar  提供 Servlet 需要类 API支持 (开发环境使Servlet程序正常编译)
    * Serlvet程序运行tomcat环境中 没有javaee.jar , 在 tomcat/lib/servlet-api.jar 提供Servlet程序运行需要 类API 支持 (运行环境需要的)

手动编写Servlet运行
    1、在webapps 新建 day05test目录 --- 虚拟应用
    2、在day05test 新建 WEB-INF/classes
    3、将编写Servlet的java源码文件 放入 classes ,在 WEB-INF 配置web.xml 
    4、编译Servlet的 java程序 

设置classpath路径
    javac -classpath E:\apache-tomcat-6.0.14\lib\servlet-api.jar HelloServlet.java  // 通过 -classpath 指定 Servlet需要jar 包

生成Servlet package结构 
    javac -d . -classpath E:\apache-tomcat-6.0.14\lib\servlet-api.jar HelloServlet.java


Servlet运行原理分析
    编写Servlet程序没有 main函数 ---- tomcat调用Servlet程序执行

    通过myeclipse向导 创建Servlet ---- 创建Servlet程序,生成web.xml 配置 
        * 生成Servlet信息非常复杂,想生成Servlet 内容整洁一些,精简一些 ------ 修改Servlet模板 
        1、myeclipse工具 ---- 安装目录 common / plugins
            com.genuitec.eclipse.wizards_8.5.0.me201003052220.jar

        2、解压缩 templates/Servlet.java --- 这个就是Servlet模板 

通过API Servlet继承关系 
    Servlet接口 ---- 实现类 : GenericServlet ------ 子类 HttpServlet  ------ 编写Servlet 继承HttpServlet 
        * 编写Servlet 间接 实现 Servlet 接口  (简化开发)
    Servlet接口 提出,为了解决基于请求-响应模型数据处理 (并没有涉及与HTTP协议相关 API)
    GenericServlet 实现接口 通用Servlet 也没有提供与 HTTP协议相关 API 
    HttpServlet 引入与 协议相关 API 

Servlet生命周期

1.init(ServletConfig config)  初始化 
2.service(ServletRequest req, ServletResponse res)  提供服务方法
3.destroy()  销毁 

1、tomcat服务器启动时,没有创建Servlet对象

//默认第一次请求时创建servlet实例,应用存在实例就存在,在实例被卸载,实例就销毁
2、第一次访问时,tomcat构造Servlet对象,调用 init,执行service 

3、从第二次以后访问 tomcat 不会从新创建Servlet对象,也不会调用init  每一次访问都会调用service 
4、当服务器重启或正常关闭时 调用destroy (正常关闭 shutdown.bat)

Servlet对象是tomcat创建的,每次请求调用Servlet中service方法,tomcat服务器会在每次调用Servlet的service方法时,为该方法创建Request对象和Response对象 
* 在 JavaEE API 中没有Request和Response实现类   实现类由Servlet服务器提供的,tomcat提供实现类 weblogic 提供实现类 

service方法 和 HttpServlet doGet/doPost 关系区别? 必须阅读HttpServlet源代码 
在HttpServlet代码实现中,根据请求方式不同 调用相应doXXX方法 get方式请求 --- doGet  post方式 --- doPost 

配置Servlet随tomcat服务器启动时 进行初始化   <load-on-startup >
*<load-on-startup > 参数可以是一个数字 0-9 代表服务器加载优先级 0 最高 
例如:在tomcat启动时,想通过Servlet加载一些框架配置文件 配置随服务器启动 (struts1 )

结论:
1、编写Servlet 继承HttpServlet
2、编写Servlet 不需要覆盖service方法,只需要覆盖doGet和doPost 方法

Servlet初始化时覆盖init() ,无需覆盖init(config) ?? 
* init(Config) 调用 init() 

当doGet和doPost代码逻辑相同时,可以相互调用,简化编程

Servlet的三种创建方式

1.实现javax.servlet.Servlet接口

2.实现javax.servlet.GenericServlet类(适配器模式)

3.继承javax.servlet.http.HttpServlet类(模板方法设计模式) 这种方式是开发中常用的方法

public class ServletDemo extends HttpServlet{
    protected void doGet(HttpServletRequest req,HttpServleResponse resp) throws ServletException,IOExcption{
        System.out.println("get方法实现");
        }
    protected void doPost(HttpServletRequest req,HttpServletResponse resp) throws ServletException,IOException{
        System.out.println("doPost方法实现");
        }
    }

web.xml映射细节

    1.配置多个映射路径(配置多个<servlet-mapping>):

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

    2.通配符*代表任意字符串

        1.url-pattern: *.do  以*.字符串的请求都可以访问   注:不要加/ (扩展名匹配)
        2.url-pattern: /*  任意字符串都可以访问 (绝对匹配)
        3.url-pattern:/action/* 以/action开头的请求都可以访问 (开头匹配)

    注意:如果url-pattern的值是/,表示执行默认映射。所有资源都是servlet

一个Servlet可以配置多个url-pattern

URL 配置格式 三种:
1、完全路径匹配 (以/开始 ) 例如:/hello /init

    * 当前工程没有被正确发布,访问该工程所有静态资源、动态资源 发生404 ----- 工程启动时出错了 
    * 查看错误时 分析错误
1) 单一错误 : 从上到下 查看第一行你自己写代码 (有的错误与代码无关,查看错误信息)
2)复合错误 Caused by ---- 查看最后一个Caused by 
* Invalid <url-pattern> init2 in servlet mapping 

2、目录匹配 (以/开始) 例如:/*  /abc/* 
/ 代表网站根目录 

3、扩展名 (不能以/开始) 例如:*.do *.action 
典型错误 /*.do 

优先级:完全匹配>目录匹配 > 扩展名匹配 

路径问题:编写九九乘法表
1、需要用户在客户端输入一个数字
2、Servlet接收客户输入数字 打印对应乘法表

在chengfabiao.html 通过 action 访问 ChengfabiaoServlet  路径可以用绝对路径和相对路径 

相对路径:相对当前网页地址 路径  例如 chengfabiao  ./chengfabiao ../chengfabiao
例如: http://localhost/day05/chengfabiao.html  提交 action="chengfabiao"

* 将url最后地址换成相对路径 
结果: http://localhost/day05/chengfabiao  ----- 服务器端 /chengfabiao

例如: http://localhost/day05/aaa/chengfabiao.html 提交 action="chengfabiao"
结果: http://localhost/day05/aaa/chengfabiao  ----- 服务器 /chengfabiao 
* /aaa/chengfabiao 与服务器 /chengfabiao 不匹配 出现404 

http://localhost/day05/aaa/chengfabiao.html 提供 action="../chengfabiao" 
结果:http://localhost/day05/aaa/../chengfabiao ---- > ..和/aaa抵消 http://localhost/day05/chengfabiao 可以匹配服务器 /chengfabiao

结论:如果用相对路径提交请求,考虑当前路径, 当前访问服务器资源路径不同 ---- 相对路径写法不同

绝对路径 解决相对路径,会根据当前地址改变问题。 例如: /day05/chengfabiao 、http://localhost/day05/chengfabiao
绝对路径 以/开始 /访问服务器根目录 
例如: 客户端访问服务器,不管当前路径是什么 --- / 服务器根目录 http://localhost
/day05 --- 找到虚拟目录day05工程  /day05/chengfabiao --- 找到 day05工程下配置 虚拟路径/chengfabiao

结论: 客户端路径 /工程虚拟目录/servlet虚拟路径 例如:/day05/chengfabiao
服务器端 配置web.xml 不需要写工程虚拟目录  只要直接写/servlet虚拟路径  例如:/chengfabiao 

掌握Servlet程序编写

通过路径 访问Servlet 程序
  • Servlet 生命周期

    init
    service
    destroy

学习init方法 init(ServletConfig) 通过ServletConfig 获得Servlet初始化参数

1、创建一个Servlet
2、在web.xml 中 <servlet> 标签内 通过 <init-param> 标签 为Servlet配置初始化参数
        <init-param>
            <param-name>itcast</param-name>
            <param-value>传智播客</param-value>
        </init-param>

3、在Servlet程序中通过ServletConfig对象 获得itcast对应数据 
    getInitParameter   通过name获得value
    getInitParameterNames  获得所有name 

  Servlet获取配置信息

    ServletConfig作用:

        1.获取servlet配置信息
        2.获取ServletContext对象 ServletContext对象代表整个应用,且一个应用应用只有一个ServletContext对象 作用:在一定范围之内使得多个Servlet共享数据

* 思考 :如何在doGet 或 doPost 方法中 获得 Servlet初始化参数 
    1.将ServletConfig对象保存实例成员变量 
    2.GenericServlet 已经将ServletConfig 保存成员变量   在子类中通过 getServletConfig方法 获得 初始化参数

结论:子类Servlet不需要覆盖 init(ServletConfig) , 只需要通过GenericServlet中 getServletConfig() 获得ServletConfig对象 

应用:在init-param 指定配置文件位置和名称,配置Servlet随服务器启动创建 load-on-startup  

* ServletConfig 配置初始化数据,只能在配置Servlet获得,其它Servlet无法获得   每个Servlet程序都对应一个ServletConfig对象 

ServletContext 是Servlet上下文对象 
    每一个工程 对会创建 单独ServletContext对象,这个对象代表当前web工程 
    操作ServletContext 必须通过ServletConfig 获得对象 

应用:
1、 获得整个web应用初始化参数
2、 实现全局数据共享
3、 实现服务器端转发功能
4、 读取web工程资源文件

1、获取WEB应用的初始化参数 和 ServletConfig 对象不同 
* ServletConfig对象 配置参数,只对配置Servlet有效,如果配置参数,所有Servlet都可以访问 通过ServletContext
    <context-param>

2、通过ServletContext 在多个Servlet间 共享数据 
    在ServletContext中 保存站点访问次数 ,每当一个用户访问站点,将访问次数+1 
    在CountServlet 初始化过程中,向ServletContext 保存访问次数   0    ServletContext  setAttribute 

      每次访问次数 +1  数据存放ServletContext中  所有Servlet都可以获得该数据

    * 在ServletContext中保存数据,所有Servlet都可以访问 

3、通过ServletContext 完成服务器程序转发 
    什么是转发? 转发和重定向区别 ?
    getRequestDispatcher(java.lang.String path)   完成转发

    使用转发还是重定向?   转发性能好于重定向,请求次数好 

    统计字母次数
    request.getParameter("content") 获得form 提交内容 content 就是 textarea name属性

4、利用ServletContext对象读取资源文件

    使用java application 读取文件,读取当前工程下所有文件    使用相对路径读取文件
    使用Servlet读取文件 只能读取WebRoot下所有文件    必须使用绝对磁盘路径读取文件 

    通过站点根目录绝对路径 获得磁盘绝对路径   getServletContext().getRealPath(“/WEB-INF/info.txt”)

    * 因为 WEB-INF/classes 非常特殊 (存放.class文件目录),被类加载器加载,通过Class类对象读取 该目录下文件 
    String filename3 = c.getResource("/a1.txt").getFile();    / 代表 /WEB-INF/classes 

结论:在web工程中,必须将 文件路径转换绝对磁盘路径 c:\xxx e:\xxx\xxx     getServletContext().getRealPath("/xxx");  /代表WebRoot 
      如果读取文件 恰好位于 WEB-INF/classes   通过 类名.class.getResource("/文件名").getFile(); 获得绝对磁盘路径  / 代表 /WEB-INF/classes 


    缺省Servlet 功能:处理其他Servlet都不处理请求 
    tomcat/conf/web.xml  org.apache.catalina.servlets.DefaultServlet 作为缺省Servlet 

总结:
1、编写Servlet HelloServlet
2、修改Servlet模板
3、Servlet生命周期 理论重点掌握
4、Servlet url三种写法 完全、目录、扩展名
5、路径问题:绝对路径 案例 九九乘法表
将web.xml 配置路径复制到网页 在路径前 /工程名
6、ServletConfig 和ServletContext 读取初始化参数区别 ?
7、ServletContext数据共享案例 统计访问次数
8、ServletContext转发案例 统计字母出现次数
9、读取web工程中资源文件 绝对路径
在web工程中,必须将 文件路径转换绝对磁盘路径 c:\xxx e:\xxx\xxx getServletContext().getRealPath(“/xxx”); /代表WebRoot
如果读取文件 恰好位于 WEB-INF/classes 通过 类名.class.getResource(“/文件名”).getFile(); 获得绝对磁盘路径 / 代表 /WEB-INF/classes
10、缺省Servlet 了解功能将静态资源数据内容读取写给客户端


Servlet 基本训练

1.在Servlet中实现请求转发

    在Servlet中实现页面转发,使用的是RequestDispatcher对象的 forward()方法。可以在Servlet中通过 forward()方法将当前的请求转发到其他web组件(Servlet、JSP、HTML)。

2.在Servlet中处理表单提交的数据

    Java Web的核心组件Servlet的主要功能就是处理客户端的表单请求数据,然后再Servlet中首先对这些数据进行验证,然后可能会封装到JavaBean。
    接下来调用数据库的业务逻辑方法将数据保存或者进行其他操作,最后Servlet控制将响应结果返回到客户端。

    注意:在实际开发中,Servlet处理完表单数据之后,通常会根据表单数据更新数据库,最后再由Servlet进行控制转发

3.在Servlet中实现页面重定向

    实现页面重定向主要应用在HttpServletResponse对象的sendRedirect()方法

4.动态生成HTML文档

5.在Servlet中向客户端写cookie信息

HttpServletResponse HttpServletRequest(关键:了解HTTP协议)

Web服务器(TOMCAT)收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象、和代表响应的response对象。
所以既然response/request对象代表请求和响应,那么获取客户端提交过来的数据只需要找request对象就可以,然后向容器输出数据,只需要找response对象就可以了。

1.HttpServletResponse(该对象代表服务器的响应 此对象中封装了向客户端发送数据、发送响应头、发送响应状态码的方法)

1.响应行 setStatus(int sc);//设置相应状态码 HTTP/1.1 200
2.相应头 setHeader(String name,String value);//设置响应头信息 
      请求重定向 sendRedirect(String location);

  浏览器使用的码表(告知)
    response.setHeader("content-type","text/html;charset=utf-8");

  告知客户端不缓存
    response.setHeader("pragma","no-cache");
    response.setHeader("cache-control","no-cache");
    response.setDataHeader("expires",0);
3.相应正文
    setCharacterEncoding(String charset);//告知服务器使用什么编码
    setContentType(String type);

扩展:response常见应用

    向客户端输出中文数据:response.getOutputStream().write(“中国”.getBytes()));//以默认编码发送数据

    注意:当浏览器中出现乱码,通过以下方法:response.setContentType("text/html;charset=UTF-8"); 解决
    总结:程序以什么编码输出就需要告诉客户端以什么编码显示

    response细节:

        1.getOutputStream和getWriter方法分别用于得到输出二进制数据、输出文本数据的ServletOuputStream、Printwriter对象。
        2.getOutputStream和getWriter这两个方法互相排斥,调用了其中的任何一个方法后,就不能再调用另一方法。  会抛异常。
        3.Servlet程序向ServletOutputStream或PrintWriter对象中写入的数据将被Servlet引擎从response里面获取,Servlet引擎将这些数据当作响应消息的正文,然后再与响应状态行和各响应头组合后输出到客户端。 
        4.Serlvet的service方法结束后,Servlet引擎将检查getWriter或getOutputStream方法返回的输出流对象是否已经调用过close方法,如果没有,Servlet引擎将调用close方法关闭该输出流对象。 

2.HttpServletRequest(代表客户端的请求,当客户端通过HTTP协议访问服务器时,Http请求头中的所有信息都封装到这对象,开发人员通过此对象的方法获取客户的这些信息)

1.请求行
  Get方式 URL链接:http://localhost:8080/day09/servlet/req1?username=zs  协议:http/1.1

    getMethod(); 获得请求方式
    getRequestURL();返回客户端发出请求时的完整URL。
    getRequestURI(); 返回请求行中的资源名部分。
    getContextPath(); 当前应用的虚拟目录 /day09_01_request
    getQueryString() ; 返回请求行中的参数部分。

2.请求消息头

  String getHeader(String name)  根据头名称得到头信息值
  Enumeration   getHeaderNames()  得到所有头信息name
  Enumeration   getHeaders(String name)  根据头名称得到相同名称头信息值

3.请求正文

获取表单数据相关的方法
<input type="text" name="username" />
  getParameter(name) 根据表单中name属性的名,获取value属性的值方法 
  getParameterValues(String name)专业为复选框取取提供的方法
  getParameterNames() 得到表单提交的所有name的方法 
  getParameterMap 到表单提交的所有值的方法   //做框架用,非常实用
  getInputStream  以字节流的方式得到所有表单数据

扩展:
    操作非表单数据相关的方法(request是一个域对象)
        void setAttribute(String name, Object value);
        Object getAttribute(String name);
        void removeAttribute(String name);

    请求转发相关方法

        //得到请求转发或请求包含的协助对象
        RequestDispatcher getRequestDispatcher(String path)
        forward(ServletRequest request, ServletResponse response) //转发的方法
        include(ServletRequest request, ServletResponse response) //请求包含

    请求编码相关方法

        //解决post方式编码
        request.setCharacterEncoding("UTF-8"); //告诉服务器客户端什么编码,只能处理post请求方式

        //解决get方式编码
        String name = new String(name.getBytes(“iso-8859-1”),”UTF-8”);

一、什么Servlet?

servlet 是运行在 Web 服务器中的小型 Java 程序(即:服务器端的小应用程序)。servlet 通常通过 HTTP(超文本传输协议)接收和响应来自 Web 客户端的请求。

1.1、编写一个servlet程序:
a、写一个java类,实现servlet接口

public class ServletDemo implements Servlet{
    //接收用户请求,并作出响应
    public void service(ServletRequest request,ServletResponse response) throws ServletException,IOException{
        response.getWriter().write("hello ServletDemo");
    }
}

b、修改web.xml文件,给servlet提供一个可访问的URI地址

<!--创建Servlet实例-->
<servlet>
    <servlet-name>servletDemo</servlet-name>
    <servlet-class>servletDemo的实现类</servlet-class>
</sevlet>

<!--给servlet提供(映射)一个可以访问的URI地址-->
<servlet-mapping>
    <servlet-name>servletDemo</servlet-name>
    <url-pattern>/demo</url-pattern>
</servlet-mapping>

c、部署应用到tomcat服务器

d、测试:http://locahost:8080/day08_servlet/demo1

二、执行过程

1.首先在浏览器输入地址,然后地址转到Tomcat找到servlet应用
2.然后在servlet应用中找到相应的访问的demo
3.在找到对应的demo之后实例化后调用初始化方法
4.然后再次调用service方法
5.最后将信息响应给客户端并调用distory方法

三、Servlet生命周期(重要)
实例化–>初始化–>服务->销毁
出生:(实例化–>初始化)第一次访问Servlet就出生(默认情况下)
活着:(服务)应用活着,servlet就活着
死亡:(销毁)应用卸载了servlet就销毁。

//默认第一次请求,创建Servlet实例,当应用存在那么实例就存在,应用卸载,相应的实例就销毁
public class ServletDemo implements Servlet{
    //生命周期的方法:实例化对象
    //第一次被访问时调用
    public ServletDemo(){
        System.out.println("ServletDemo被调用");
    }
    //生命周期的方法:初始化方法
    //第一次被访问时调用
    public void init(ServletConfig config) throws ServletException{
        System.out.println("init方法被调用");
    }
    //生命周期方法:服务方法
    //接受用户请求,并做出相应
    //每次请求都被调用
    public void service(ServletRequest request,ServletResponse response) throws ServletException,IOException{
        System.out.println("service方法被调用");
    }
    //生命周期的方法:销毁
    //当应用被卸载时调用
    public void destroy(){
        System.out.println("destroy被调用");
    }
}

小知识:
如何让servlet在服务器启动时就创建。

<servlet>
    <servlet-name>servletDemo</servlet-name>
    <servlet-class>servletDemo的实现类</servlet-class>
    <!--使得当前servlet在服务器启动时创建-->
    <load-on-startup>2</load-no-startup>
</sevlet>

四、Servlet的三种创建方式
4.1、实现javax.servlet.Servlet接口(参见:编写一个servlet程序:)

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

public class ServletDemo extends GenericServlet{
    public void service(ServletRequest request,ServletResponse response) throws ServletException,IOException{
        System.out.println("servletDemo");
    }
}

4.3、继承javax.servlet.http.HttpServlet类(模板方法设计模式)
(开发中常用方式)

//不要重写父类的service方法
public class ServletDemo extends HttpServlet{
    protected void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{
        System.out.println("servletDemo doGet方法被调用");
    }
    protected void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{

    }
}

Servlet –> GenericServlet –> HttpServlet (继承HttpServlet)
曾祖父 爷爷 爸爸 孙子

小技巧:使生成的servlet更清新一些
找到:MyEclipse\Common\plugins目录
把com.genuitec.eclipse.wizards_9.0.0.me201108091322.jar复制到上面目录
servet映射细节:
servet映射细节1:

<!--配置多个映射路径-->
<servlet-mapping>
    <servlet-name>ServletDemo1</servlet-name>
    <url-pattern>/ServletDemo1</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>ServletDemo2</servlet-name>
    <url-pattern>/ServletDemo2</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>ServletDemo3</servlet-name>
    <url-pattern>/ServletDemo3</url-pattern>
</servlet-mapping>

servet映射细节2: 通配符 代表任意字符串
url-pattern:
.do 以.字符串的请求都可以访问 注:不要加/
url-pattern: /
任意字符串都可以访问
url-pattern: /action/* 以/action开头的请求都可以访问
匹配规则:
优先级:从高到低
绝对匹配–> /开头匹配 –> 扩展名方式匹配

如果url-pattern的值是/,表示执行默认映射。所有资源都是servlet

五、Servlet的线程安全
单实例:每次访问多线程
解决线程安全问题的最佳办法,不要写全局变量,而写局部变量。

六、Servlet获取配置信息
ServletConfig的使用
作用1:可以获取servlet配置信息
方式1:

private ServletConfig config;
//使用初始化方法回复到ServletConfig对象,此对象由服务器创建
public void init(ServletConfig config) throws ServletException{
    this.config = config;
}

public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{
    String value = config.getInitParameter("encoding");//根据配置文件中的名,得到值
    System.out.println(value);
}

方式2:

//通过使用继承父类的方法得到ServletConfig对象
String value = this.getServletConfig().getInitParameter("encoding");
System.out.println(value);

方式3:

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

作用2:可以获得ServletContext对象

七、ServletContext(重要)
ServletContext: 代表的是整个应用。一个应用只有一个ServletContext对象。单实例。
作用:
域对象:在一定范围内(当前应用),使多个Servlet共享数据。
常用方法:
void setAttribute(String name,object value);//向ServletContext对象的map中添加数据
Object getAttribute(String name);//从ServletContext对象的map中取数据
void rmoveAttribute(String name);//根据name去移除数据

获取全局配置信息:

修改web.xml文件:

<!--配置当前应用的全局信息-->
<context-param>
    <param-name>encoding</param-name>
    <param-value>UTF-8</param-value>
</context-param>

String getInitParameter(String name) //根据配置文件中的key得到value

//获取全局配置信息
String encoding = sc.getInitParameter("encoding");
System.out.println(encoding);

获取资源路径:
String getRealPath(String path);//根据资源名称得到资源的绝对路径.
可以得到当前应用任何位置的任何资源。

实现Servlet的转发。

RequestDispatcher getRequestDispatcher(String path) ;//参数表示要跳转到哪去


案例:

题目1 在Servlet中实现请求转发

1.index.jsp
<form action="forward" method="post">
       <table align="center">
           <tr>
               <td>用户名:</td>
               <td><input type="text" name="name" /></td>
           </tr>
           <tr>
               <td>密码:</td>
               <td><input type="password" name="pwd" /></td>
           </tr>
           <tr>
               <td colspan="2"><input type="submit" value="登 录" /></td>
           </tr>
       </table>
   </form>

   2.forwardServlet.java
   public class ForwardServlet extends HttpServlet {
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");//设置请求的字符编码格式
        String name = request.getParameter("name");    //获得请求表单中的用户名
        String pwd = request.getParameter("pwd");//获得请求表单的密码
        if ((name != null && !name.equals(""))&& (pwd != null && !pwd.equals(""))) {
            if (name.equals("zhaoyanliang") && pwd.equals("123")) {
                request.getRequestDispatcher("success.jsp").forward(request,response);    //使用RequestDispatcher对象将页面请求转发到success.jsp页
            } else {
                request.getRequestDispatcher("error.jsp").forward(request,response);
            }
        }else {

        }
    }
}

3.web.xml
<servlet>
    <servlet-name>ForwardServlet</servlet-name>
    <servlet-class>cn.itcast.servlet.ForwardServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>ForwardServlet</servlet-name>
    <url-pattern>/forward</url-pattern>
</servlet-mapping>

题目2 在Servlet中处理表单提交的数据

1.index.jsp
<form action="login" method="post">
       <table align="center">
           <tr>
               <td>用户名:</td>
               <td><input type="text" name="name" /></td>
           </tr>
           <tr>
               <td>密码:</td>
               <td><input type="password" name="pwd" /></td>
           </tr>
           <tr>
               <td>性别:</td>
               <td>
                   <input type="radio" name="sex" value="男" />男
                   <input type="radio" name="sex" value="女" />女
               </td>
           </tr>
           <tr>
               <td>年龄:</td>
               <td><input type="text" name="age" /></td>
           </tr>
           <tr>
               <td>Email:</td>
               <td><input type="text" name="email" /></td>
           </tr>
           <tr>
               <td colspan="2" align="center">
                   <input type="submit" value="注 册" />
                   <input type="reset" value="重 置" />
               </td>
           </tr>
       </table>
   </form>

2.LoginServlet.java
public class LoginServlet extends HttpServlet {
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("GBK");            //设置请求的字符编码格式
        String name = request.getParameter("name");        //获得用户名
        String pwd = request.getParameter("pwd");        //获得密码
        String sex = request.getParameter("sex");        //获得性别
        String age = request.getParameter("age");        //获得年龄
        String email = request.getParameter("email");    //获得Email
        request.getRequestDispatcher("logininfo.jsp").forward(request, response);
    }
}

3.logininfo.jsp
<table align="center">
    <tr>
        <td>用户名:</td>
        <td><%=request.getParameter("name")%></td>
    </tr>
    <tr>
        <td>密码:</td>
        <td><%=request.getParameter("pwd")%></td>
    </tr>
    <tr>
        <td>性别:</td>
        <td>
            <%=request.getParameter("sex")%>
        </td>
    </tr>
    <tr>
        <td>年龄:</td>
        <td><%=request.getParameter("age")%></td>
    </tr>
    <tr>
        <td>Email:</td>
        <td><%=request.getParameter("email")%></td>
    </tr>
   </table>

题目3 在Servlet中实现页面重定向

1.index.jsp
<form action="redirect" method="post">
       <table align="center">
           <tr>
               <td>用户名:</td>
               <td><input type="text" name="name" /></td>
           </tr>
           <tr>
               <td>密码:</td>
               <td><input type="password" name="pwd" /></td>
           </tr>
           <tr>
               <td colspan="2"><input type="submit" value="登 录" /></td>
           </tr>
       </table>
   </form>

2.RedirectServlet.java
public class RedirectServlet extends HttpServlet {
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");        //设置请求数据的字符编码格式
        String name = request.getParameter("name"); //获得请求表单中的用户名
        String pwd = request.getParameter("pwd");    //获得请求表单中的密码
        if((name!=null&&!name.equals(""))&&(pwd!=null&&!pwd.equals(""))){
            if(name.equals("mr")&&pwd.equals("123")){
                //使用SendRedirect()方法将页面重定向到success.jsp
                response.sendRedirect("success.jsp");
            }else{
                //使用SendRedirect()方法将页面重定向到error.jsp
                response.sendRedirect("error.jsp");
            }
        }
    }
}

题目4 动态生成HTML文档

public class ServletHTML extends HttpServlet {

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

        //设置响应的字符集格式为UTF-8
        response.setCharacterEncoding("UTF-8");
        //设置响应正文的MIME类型
        response.setContentType("text/html");
        //返回一个PrintWriter对象,Servlet使用它来输出字符串形式的正文数据
        PrintWriter out = response.getWriter();
        //以下为输出的HTML正文数据
        out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
        out.println("<HTML>");
        out.println("<HEAD><TITLE>动态生成HTML文档</TITLE>");
        out.println("<link rel=;stylesheet' type='text/css' href='css/style.css'>");
        out.println("</HEAD>");
        out.println("<BODY>");
        out.println("<table border='1' align='center'>");
        out.println("<tr><td>动态生成HTMl文档</td></tr>");
        out.println("<tr><td>表格</td></tr>");
        out.println("</table>");
        out.println("</BODY>");
        out.println("</HTML>");
        out.flush();
        out.close();
    }

}

题目5 在Servlet中向客户端写Cookie信息

1.index.jsp
<form action="cookieservlet" method="post">
       <table align="center">
           <tr>
               <td>用户名:</td>
               <td>
               <input type="text" name="name" />
               </td>
           </tr>
           <tr>
               <td>密码:</td>
               <td><input type="password" name="pwd" /></td>
           </tr>
           <tr>
               <td colspan="2"><input type="submit" value="登 录" /></td>
           </tr>
       </table>
   </form>

2.CookieServlet.java
public class CookieServlet extends HttpServlet {
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");
        String name = request.getParameter("name");            // 获取用户名
        //创建一个Cookie对象,并将用户名保存到Cookie对象中
        Cookie nameCookie = new Cookie("userName", name);
        //设置Cookie过期之前的时间,单位为秒
        nameCookie.setMaxAge(60);
        //通过response的addCookie()方法将此Cookie对象保存到客户端浏览器的Cookie中
        response.addCookie(nameCookie);
        request.getRequestDispatcher("success.jsp").forward(request, response);
    }
}

3.在index.jsp中读取所有客户端的Cookie,通过循环Cookie数组找到保存用户名的Cookie
<%
      String userName=null;//用于保存从cookie中读取出的用户名
      Cookie cookieArr[] = request.getCookies();//获取客户端的所有Cookie
      if(cookieArr!=null&&cookieArr.length>0){
          for(Cookie c:cookieArr){
              if(c.getName().equals("userName")){ //如果Cookie中有一个名为
                                                           userName的Cookie
                  userName =c.getValue();            //获得此cookie的值 
              }
          }
      }
%>   

4.将获取到的用户名Cookie的值赋值给用户名文本框
<input type="text" name="name" value="<%if(userName!=null){out.print(userName);}%>"/>

注意:在创建Cookie对象时,由于不可以直接将中文字符作为Cookie的值,因此在将中文字符保存到Cookie对象之前,应该使用java.net.URLEncoder类的encode()方法对中文字符进行编码。在获取该Cookie对象中的值时,需要使用java.net.URLDecoder类的decode()方法对已经编码过的字符进行解码,还原字符串的初始值。


本文标题:Servlet

文章作者:Bangjin-Hu

发布时间:2019年10月15日 - 09:22:26

最后更新:2020年03月30日 - 08:51:54

原始链接:http://bangjinhu.github.io/undefined/Servlet/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

Bangjin-Hu wechat
欢迎扫码关注微信公众号,订阅我的微信公众号.
坚持原创技术分享,您的支持是我创作的动力.