第四章 Servlet技术

4.1 Servlet基础

4.1.1 Servlet概述

​ Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。

4.1.2 Servlet特点

​ 功能强大;可移植;性能高效;安全性高;可扩展

4.1.3 Servlet接口

​ Servlet接口有5种方法。分别为:init,service和destroy是servlet的生命周期方法。这些方法由web容器调用。

方法描述
public void init(ServletConfig config)初始化servlet,它是servlet的生命周期方法,由web容器调用一次。
public void service(ServletRequest request,ServletResponse response)为传入的请求提供响应。它由Web容器的每个请求调用。
public void destroy()仅被调用一次,并且表明servlet正在被销毁
public ServletConfig getServletConfig()返回ServletConfig对象。
public String getServletInfo()返回有关servlet的信息,如作者,版权,版本等。

1)HttpServlet 继承了 GenericServlet,并实现了 service 方法。在 service 方法中,将 ServletRequest 和 ServletResponse 转换为了 HttpServletRequest 和 HttpServletResponse,用来专门处理我们的 Http 请求。

2)Servlet(ServletRequest, ServletResponse) 方法在完成对请求和响应的强转之后调用了方法,在被调用的方法中对请求类型进行了判断,各种请求调用自己相应的 doXXX 方法。而我们常用的就是 doGet() 和 doPost() 方法。

3)在我们以后的使用中,都使用继承 HttpServlet 的方式,重写 doGet 和 doPost 方法即可。
在浏览器发送请求的时候,如果是 get 请求,将会调用 doGet()方法,如果是 post 请求,将会调用 doPost()方法。

4)继承 HttpServlet 的新的 Servlet 写法如下(web.xml 配置与之前相同)

4.2 Servlet开发入门

4.2.1 实现Servlet程序
@WebServlet( "/hello")
public class HelloServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       resp.getWriter().write("Hello Servlet!");
    }
}
4.2.2 Servlet的配置

1.使用web.xml配置Servlet

对 webapps\servletDemo\WEB-INF 目录的 web.xml 中进行配置,具体配置代码如下。

 <servlet> 
	<servlet-name>MyServlet</servlet-name>        
	<servlet-class>net.biancheng.www.MyServlet</servlet-class>  
</servlet> 

<servlet-mapping>    
	<servlet-name>MyServlet</servlet-name>     
	<url-pattern>/MyServlet</url-pattern>  <!--/url-pattern 地址前面要加上/--!>
</servlet-mapping>

web.xml 中各元素含义及用法如下:

  • : 根元素。

  • :用于注册 Servlet,即给 Servlet 起一个独一无二的名字。

  • 包含两个主要的子元素 和 ,分别用于指定 Servlet 的名称和 Servlet 的完整限定名(包名+类名)。

  • :用于定义 Servlet 与 URL 之间的映射。

  • 包含两个子元素 和 ,分别用于指定 Servlet 的名称和虚拟路径。

2.使用@WebServlet注解配置Servlet

属性名类型标签描述
nameString指定 Servlet 的 name 属性。如果没有显式指定,则取值为该 Servlet 的完全限定名,即包名+类名。
valueString[ ]该属性等价于 urlPatterns 属性,两者不能同时指定。如果同时指定,通常是忽略 value 的取值。
urlPatternsString[ ]指定一组 Servlet 的 URL 匹配模式。
loadOnStartupint指定 Servlet 的加载顺序。
initParamsWebInitParam[ ]指定一组 Servlet 初始化参数。
asyncSupportedboolean声明 Servlet 是否支持异步操作模式。
descriptionString指定该 Servlet 的描述信息。
displayNameString指定该 Servlet 的显示名。
@WebServlet("/MyServlet") 			省略了 urlPatterns 属性名

@WebServlet(urlPatterns = "/MyServlet")。		完整的写法

注:多个属性之间用逗号隔开!!!!

4.2.3 Servlet的生命周期
Servlet 生命周期
@WebServlet("/servlet")
public class ServletDemo  extends GenericServlet{
    @Override
    public void init() throws ServletException {
        System.out.println("Servlet 初始化啦!! init....");
    }
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("Servlet 启动服务啦!! service....");
    }
    @Override
    public void destroy() {
        System.out.println("Servlet 消亡啦!! destroy....");
    }

4.3 ServletConfig和ServletContext

4.3.1 ServletConfig接口
返回值类型方法功能描述
StringgetInitParameter(String name)根据初始化参数名 name,返回对应的初始化参数值。
EnumerationgetInitParameterNames()返回 Servlet 所有的初始化参数名的枚举集合,如果该 Servlet 没有初始化参数,则返回一个空的集合。
ServletContextgetServletContext()返回一个代表当前 Web 应用的 ServletContext 对象。
StringgetServletName()返回 Servlet 的名字,即 web.xml 中 元素的值。
@WebServlet(urlPatterns = "/servletDemo01",
 initParams = {@WebInitParam(name="encoding",value="utf-8")})

public class servletDemo01 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       //获取ServletConfig对象
        ServletConfig config = this.getServletConfig();
        //获取Servlet的名字
        String name = config.getServletName();
        //获取ServletContext对象
        ServletContext context = config.getServletContext();
        //获取初始化参数
        String encoding = config.getInitParameter("encoding");
        resp.getWriter().write("name : "+name+"\tcontext : "+context+"\tencoding : "+encoding);
    }
}
4.3.2 ServletContext接口

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

<context-param>
    <param-name>city</param-name>
    <param-value>anhui</param-value>
</context-param>
<context-param>
    <param-name>subject</param-name>
    <param-value>java web</param-value>
</context-param>
@WebServlet(urlPatterns = "/servletDemo02",
 initParams = {@WebInitParam(name="encoding",value="utf-8")})

public class servletDemo02 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       //获取Web应用的初始化参数
        //单个getInitParameter
        ServletContext context = this.getServletContext();
        String city = context.getInitParameter("city");
        String subject = context.getInitParameter("subject");
        resp.getWriter().write("    city : "+city+" subject: "+subject);
        //多个getInitParameterNames
        Enumeration<String> names = context.getInitParameterNames();
        while (names.hasMoreElements()){
            String name = names.nextElement();
            String parameter = context.getInitParameter(name);
            resp.getWriter().write(name+" : "+parameter);
        }
    }
}

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

@WebServlet(urlPatterns = "/servletDemo02",
 initParams = {@WebInitParam(name="encoding",value="utf-8")})

public class servletDemo02 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //实现多个Servlet对象数据共享
          //获取ServletContext对象
        ServletContext context = this.getServletContext();
        //通过setAttribute()设置属性值
        context.setAttribute("data","this servlet save data");
    }
}

@WebServlet(urlPatterns = "/servletDemo03",
 initParams = {@WebInitParam(name="encoding",value="utf-8")})

public class servletDemo03 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //实现多个Servlet对象数据共享
        //获取ServletContext对象
        ServletContext context = this.getServletContext();
        //通过getAttribute()获取属性值
        String data = (String) context.getAttribute("data");
        resp.getWriter().write(data);
    }
}

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

xiao.properties

Company=sanLian
Address=heFei
@WebServlet(urlPatterns = "/servletDemo04",
 initParams = {@WebInitParam(name="encoding",value="utf-8")})

public class servletDemo04 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取ServletContext对象
        ServletContext context = this.getServletContext();
        //获取文件路径
        InputStream is = context.getResourceAsStream("/WEB-INF/xiao.properties");
        Properties properties = new Properties();
        properties.load(is);
        String company = properties.getProperty("Company");
        String address = properties.getProperty("Address");
        resp.getWriter().write("company : "+company+"  address: "+address);
    }
}

4.4 HttpServletResponse 接口

4.4.1 发送状态码相关的方法
  1. public void setStatus ( int statusCode )
    该方法设置一个任意的状态码。setStatus 方法接受一个 int(状态码)作为参数。

    如果您的响应包含了一个特殊的状态码和文档,请确保在使用 PrintWriter 实际返回任何内容之前调用 setStatus。

  2. public void sendRedirect(String url)
    该方法生成一个 302 响应,连同一个带有新文档 URL 的 Location 头。

  3. public void sendError(int code, String message)
    该方法发送一个状态码(通常为 404),连同一个在 HTML 文档内部自动格式化并发送到客户端的短消息。

4.4.2 发送响应消息头相关的方法
方法 & 描述
void setHeader(String name, String value) 设置一个带有给定的名称和值的响应报头。
void addHeader(String name, String value) 添加一个带有给定的名称和值的响应报头。
void setIntHeader(String name, int value) 设置一个带有给定的名称和整数值的响应报头。
void addIntHeader(String name, int value) 添加一个带有给定的名称和整数值的响应报头。
void addCookie(Cookie cookie) 把指定的 cookie 添加到响应
void setLocale(Locale loc) 如果响应还未被提交,设置响应的区域。
void setCharacterEncoding(String charset) 设置被发送到客户端的响应的字符编码(MIME 字符集)例如,UTF-8。
void setContentType(String type) 如果响应还未被提交,设置被发送到客户端的响应的内容类型。
4.4.3 发送响应消息体相关的方法
返回值类型方法描述
ServletOutputStreamgetOutputStream()用于获取字节输出流对象。
PrintWritergetWriter()用于获取字符输出流对象。

4.5 HttpServletResponse应用

4.5.1实现请求重定向

​ HttpServletResponse 接口中的 sendRedirect() 方法用于实现重定向。

重定向流程图
方法描述
void sendRedirect(String location)向浏览器返回状态码为 302 的响应结果,让浏览器访问新的 URL。若指定的 URL 是相对路径,Servlet 容器会将相对路径转换为绝对路径。参数 location 表示重定向的URL。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>welcome</title>
</head>
<body>
欢迎您登录成功!!!
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>login</title>
</head>
<body>
<form action="/LoginServlet" method="post">
    用户名:  <input type="text" name="username" /><br />
    密码:     <input type="password" name="password" /><br />
    <input type="submit" name="登录" value="登录" />
</form>

</body>
</html>
@WebServlet(name = "LoginServlet",urlPatterns = "/LoginServlet")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       resp.setContentType("text/html;charset=utf-8");
       //用HttpServletRequest获取账号,密码
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        //假设密码并登录
        if (("zhangsan").equals(username) && ("123").equals(password)){
            //如果账号密码正确,则重定向到welcome
            resp.sendRedirect("/welcome.html");
        }else{
            //如果账号密码错误,则定向到login
            resp.sendRedirect("/login.html");
        }
    }
}
4.5.2 解决中文乱码问题
1. 使用字节流输出中文

使用字节流向页面输出中文是否会出现乱码问题?不一定。

ServletOutputStream outptuStream = response.getOutputStream();
outputStream.write(“字节流”.getBytes());
乱码原因:

字节流输出中文是否出现乱码,取决于中文转成字节数组时与浏览器打开时采用的字符集是否一致。若两者保持一致,则不会出现乱码问题,若不一致就会出现乱码问题。

解决方案:

将中文转成字节数组时和浏览器默认采用的字符集保持一致即可,代码如下。

response.setHeader("Content-Type", "text/html;charset=UTF-8");
// 获取字节输出流
OutputStream os = response.getOutputStream();
byte[] str = "字节流".getBytes("UTF-8");
// 输出中文
os.write(str);
2. 使用字符流输出中文

使用字符流向页面输出中文是否会出现乱码问题?一定乱码。

乱码原因:

通过字符流输出的内容是存放在 response 缓冲区的,response 缓冲区的默认字符集是 ISO-8859-1,该字符集不支持中文。

解决方案:

将 response 缓冲区和浏览器采用的字符集保持一致即可,有如下 2 种的方式。

第一种方式:
	// 设置response缓冲区的编码
	response.setCharacterEncoding("UTF-8");
	// 设置浏览器打开文件所采用的编码
	response.setHeader("Content-Type", "text/html;charset=UTF-8");
	// 输出中文
	response.getWriter().write("字符流");
第二种方式:
	response.setContentType("text/html;charset=UTF-8");
	response.getWriter().write("字符流");

4.6 HttpServletRequest对象

4.6.1 获取请求行信息的相关方法
方法声明描述
String getMethod()该方法用于获取 HTTP 请求方式(如 GET、POST 等)。
String getRequestURI()该方法用于获取请求行中的资源名称部分,即位于 URL 的主机和端口之后,参数部分之前的部分。
String getQueryString()该方法用于获取请求行中的参数部分,也就是 URL 中“?”以后的所有内容。
String getContextPath()返回当前 Servlet 所在的应用的名字(上下文)。对于默认(ROOT)上下文中的 Servlet,此方法返回空字符串""。
String getServletPath()该方法用于获取 Servlet 所映射的路径。
String getRemoteAddr()该方法用于获取客户端的 IP 地址。
String getRemoteHost()该方法用于获取客户端的完整主机名,如果无法解析出客户机的完整主机名,则该方法将会返回客户端的 IP 地址。
@WebServlet("/RequestLine")
public class RequestLine extends HttpServlet {
    private static final long serialVersionUID = 1L;
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter writer = response.getWriter();
        writer.println("请求方式:" + request.getMethod() + "<br/>" +
                "客户端的 IP 地址:" + request.getRemoteAddr() + "<br/>" +
                "应用名字(上下文):" + request.getContextPath() + "<br/>" +
                "URI:" + request.getRequestURI() + "<br/>" +
                "请求字符串:" + request.getQueryString() + "<br/>" +
                "Servlet所映射的路径:" + request.getServletPath() + "<br/>" +
                "客户端的完整主机名:" + request.getRemoteHost() + "<br/>"
        );
    }
}
requestLine
4.6.2 获取请求头信息的相关方法
方法声明描述
String getHeader(String name)该方法用于获取一个指定头字段的值。如果请求消息中包含多个指定名称的头字段,则该方法返回其中第一个头字段的值。
Enumeration getHeaders(String name)该方法返回指定头字段的所有值的枚举集合,在多数情况下,一个头字段名在请求消息中只出现一次,但有时可能会出现多次。
Enumeration getHeaderNames()该方法返回请求头中所有头字段的枚举集合。
String getContentType()该方法用于获取 Content-Type 头字段的值。
Int getContentLength()该方法用于获取 Content-Length 头字段的值 。
String getCharacterEncoding()该方法用于返回请求消息的字符集编码 。
@WebServlet("/RequestHeader")
public class RequestHeader extends HttpServlet {
    private static final long serialVersionUID = 1L;
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter writer = response.getWriter();
        //获得所有请求头字段的枚举集合
        Enumeration<String> headers = request.getHeaderNames();
        while (headers.hasMoreElements()) {
            //获得请求头字段的值
            String value = request.getHeader(headers.nextElement());
            writer.write(headers.nextElement() + ":" + value + "<br/>");
        }
    }
}
requestHeader
4.6.3 请求转发

Servlet 可以通过 2 种方式获得 RequestDispatcher 对象:

  1. 调用 ServletContext 的 getRequestDispatcher(String path) 方法,参数 path 指定目标资源的路径,必须为绝对路径;
  2. 调用 ServletRequest 的 getRequestDispatcher(String path) 方法,参数 path 指定目标资源的路径,可以为绝对路径,也可以为相对路径。

RequestDispatcher 接口中提供了以下方法。

方法功能描述
void forward(ServletRequest request,ServletResponse response)用于将请求转发给另一个 Web 资源。该方法必须在响应提交给客户端之前被调用,否则将抛出 IllegalStateException 异常
void include(ServletRequest request,ServletResponse response)用于将其他的资源作为当前响应内容包含进来
请求转发的工作原理

在 Servlet 中,通常使用 forward() 方法将当前请求转发给其他的 Web 资源进行处理。请求转发的工作原理如下图所示。

请求转发流程图
请求转发的特点

请求转发具有以下特点:

  1. 请求转发不支持跨域访问,只能跳转到当前应用中的资源。
  2. 请求转发之后,浏览器地址栏中的 URL 不会发生变化,因此浏览器不知道在服务器内部发生了转发行为,更无法得知转发的次数。
  3. 参与请求转发的 Web 资源之间共享同一 request 对象和 response 对象。
  4. 由于 forward() 方法会先清空 response 缓冲区,因此只有转发到最后一个 Web 资源时,生成的响应才会被发送到客户端。
4.6.4 获取请求参数
方法声明功能描述
String getParameter(String name)返回指定参数名的参数值。
String [ ] getParameterValues (String name)以字符串数组的形式返回指定参数名的所有参数值(HTTP 请求中可以有多个相同参数名的参数)。
Enumeration getParameterNames()以枚举集合的形式返回请求中所有参数名。
Map getParameterMap()用于将请求中的所有参数名和参数值装入一个 Map 对象中返回。
@WebServlet("/RequestParam")
public class RequestParam extends HttpServlet {
    private static final long serialVersionUID = 1L;
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter writer = response.getWriter();
        // 获取内容,做其他操作
        // 获取姓名
        String username = request.getParameter("username");
        // 获取密码
        String password = request.getParameter("password");
        writer.write("用户名:" + username + "<br/>" + "密码:" + password +<br/>");
    }
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
    <form action="/httpServletRequestDemo/RequestParam" method="post">
        <table border="1" width="50%">
            <tr>
                <td colspan="2" align="center">编程帮wwww.biancheng.net</td>
            </tr>
            <tr>
                <td>输入姓名</td>
                <td><input type="text" name="username" /></td>
            </tr>
            <tr>
                <td>输入密码</td>
                <td><input type="password" name="password" /></td>
            </tr>
            <tr>
                <td colspan="2"><input type="submit" value="提交" /></td>
            </tr>
        </table>
    </form>
</body>
</html>
4.6.5 通过Request对象传递数据
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	//将数据保存到request对象的属性域中
	request.setAttribute("attrName", "attrValueInRequest");
	//两个Servlet要想共享request对象中的数据,必须是转发的关系
	request.getRequestDispatcher("/ReceiveServlet").forward(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	//从request属性域中获取数据
	Object attribute = request.getAttribute("attrName");
	System.out.println("attrValue="+attribute);
}

4.6.6解决中文乱码问题

根据请求方式的不同,请求一般可以被分为两种:GET 请求和 POST 请求。这两种请求方式都可能会产生中文乱码问题,下面我们分别对它们产生乱码的原因及其解决方案进行介绍。

POST 请求

乱码的原因:POST 提交的数据在请求体中,其所使用的编码格式时页面一致(即 utf-8)。request 对象接收到数据之后,会将数据放到 request 缓冲区,缓冲区的默认字符集是 ISO-8859-1(该字符集不支持中文),两者使用的字符集不一致导致乱码。

解决方案:在获取请求参数之前设置 request 缓冲区字符集为 utf-8 ,代码如下。

//修改request缓冲区的字符集为UTF-8
request.setCharacterEncoding("utf-8");
// 获取用户名
String username = request.getParameter("username");
GET 请求

乱码的原因:Get 请求将请求数据附加到 URL 后面作为参数,浏览器发送文字时采用的编码格式与页面编码保持一致(utf-8)。如果 Tomcat 没有设置字符集,接收 URL 时默认使用 ISO-8859-1 进行解码,ISO-8859-1 不兼容中文,无法正确解码,导致出现乱码。

需要注意的是,在 Tomcat 8 中已解决了 get 方式提交请求中文乱码的问题,使用 Tomcat 8 及以上版本的同学不必再考虑此问题了,如果您使用的是 Tomcat 7 或更早的版本,出现乱码问题可以使用如下的方案解决。

解决方案:解决 GET 请求中文乱码问题,有以下 3 种解决方案。

  1. 修改 tomcat/conf/server.xml 中的配置,代码如下。
<Connector port="80" protocol="HTTP/1.1"    
           connectionTimeout="20000"   
           redirectPort="8443" URIEncoding="UTF-8"/>
  1. 使用 URLEncoder 和 URLDecoder 进行编码和解码的操作(逆向编解码)。
//得到TOMCAT通过ISO8859-1解码的字符串
String username = request.getParameter("username");
//对字符串使用ISO8859-1进行编码,得到最初浏览器使用UTF-8编码的字符串
username = URLEncoder.encode(username, "ISO8859-1");
//将使用UTF-8编码的字符串使用UTF-8进行解码,得到正确的字符串
username = URLDecoder.decode(username, "UTF-8");
  1. 使用 String 的构造方法:String(byte[] bytes, String charset) ,对字节数组(bytes)按照指定的字符集(charset)进行解码,返回解码后的字符串,解决乱码问题(推荐使用)。
//获取姓名
String username = request.getParameter("username");
//使用String的构造方法解决乱码的问题
username = new String(username.getBytes("ISO-8859-1"),"UTF-8");
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值