2021-05-28

第四章深入Servlet技术

1.Servlet概述:

是用Java编写的服务器程序,主要功能在于交互式地浏览和修改数据,生成动态Web内容,是JavaWeb的核心程序,是Java Web的三大组件之一(Servlet,Filter,Listener)
作用:用来处理客户端发送来的请求,并对请求做出响应
1.获取客户端的请求数据
2.处理请求
3.将处理的结果通过响应发送给客户端

2.编写第一个Servlet:

1.创建一个类继承HTTPServlet
2.复写HttpServlet中的doGet()、doPost()
3.配置web.xml文件
在这里插入图片描述
4.部署项目通过浏览器访问servlet
5.servlet的执行流程

3.使用Servlet3.0创建一个Servlet:

在这里插入图片描述
Java EE version最好要在7以上版本

4.HttpServeltRequest获取请求行和请求头:

  1. 获取请求行相关信息的相关方法:
    1.getMethod()方法:返回请求方法,请求方法通常是GET或POST,也有可能是HEAD、 PUT或DELETE
    2.getRequestURI()方法:返回URI
    3.getRemoteAddr()方法:获取请求客户端的IP地址
    4.getRemoteport()方法:获取请求客户端的端口号
    5.getLocalAddr():获取服务器当前接收的IP地址
    6.getContextPath():用于获取URL中属于web应用程序的路径
    7.getProtocol():用于获取请求行中的协议名和版本
        //获取请求行
       System.out.println("接收到get请求");
       System.out.println("请求方式:"+request.getMethod());
        System.out.println("URI:"+request.getRequestURI());
        System.out.println("发出请求客户端IP地址:"+request.getRemoteAddr());
        System.out.println("服务点接收请求的IP地址:"+request.getLocalAddr());
        System.out.println("访问客户端的端口号:"+request.getRemotePort());
        System.out.println("web应用路径:"+request.getContextPath());
        System.out.println("http协议和版本:"+request.getProtocol());
  1. 获取请求头的相关方法:
    1.getHeader(String name):获取指定字段的值,如果请求头不包含该字段则返回null,如果包含多个字段的值则获取第一个值
    2.getIntHeader(String name):获取指定头字段的值,并将其转换为Int类型,不存在则返回-1,如果得到的值不能转换为Int,发生NumberFormatException异常
    3.getDateHeaders(String name):获取指定头字段的值,并按照GMT时间转换成一个代表日期/时间的长整数
    4.getHeaderNames():获取所有包含请求头字段Enumberation
        //获取请求头
       Enumeration<String> headerNames = request.getHeaderNames();
       while (headerNames.hasMoreElements()){
           String element = headerNames.nextElement();
          System.out.println(element+":"+request.getHeader(element));
        }
  1. 获取请求参数:
    1.getParameter(String name):获取某个指定名称的参数值,如果请求中没有包含指定名称的参数,则返回Null,如果有指定参数但没有确定的值,则返回空串“”,如果包含多个参数的值则返回第一个值
    2.getParameterNames():返回一个包含请求消息中所有参数名的Enumernation
    3.getParameterMap():将请求中的所有参数和值装入一个map对象然后返回
    在页面中输入账号和密码,点击提交按钮,就会在控制台上打印出获取到的账号和密码参数
     //获取请求参数
       String name = request.getParameter("name");
       String passWord = request.getParameter("passWord");
      System.out.println("用户名:"+name);
       System.out.println("密码:"+passWord);
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

</body>
<center>


    <h2>发起HTTP get请求</h2>
    <form action="hi" method="get">
        <p>账号: <input type="text" name="name" /></p>
        <p>密码: <input type="text" name="passWord" /></p>
        <input type="submit" value="提交" />
    </form>




</center>
</html>

5.Response对象发送响应行和响应头和响应消息体:

  1. 发送响应行:
    1.setStatus(int status):当Servlet向客户端回送响应消息时,需要设置一个状态码,该方法用于设置HTTP响应消息的状态码,并生成响应状态行,由于响应状态行中的描述直接和状态码相关,因此只需要设置该方法,就可以发送一个响应行,正常情况下,web服务器会默认发送一个200的状态码
    2.sendError(int code):该方法用于发送表示错误信息的状态码,例如404找不到访问资源,还有一种重载形式sendError(int code,String errorMessage),errorMessage可以以文本形式显示在客户端浏览器
  2. 发送响应头:
    1.addHeader(String name,String value):用于设置HTTP协议的响应头字段,其中Name时响应头字段,Value是响应字段的值
    2.setHeader(String name,String value):同上,唯一区别在于可以重复添加同名的响应头字段,会覆盖之前添加的同名的响应头
    3.setContentLength():设置HTTP响应消息的内容大小,单位是字节
    4.setContentType()L设置Servlet输出内容的类型
    5.setCharacterEncoding(String charset)
    发送响应消息体:
    1.getOutputStream()方法:获取字节流输出对象为ServletOutputStream类型,
    2.getWrite()方法:获得的字符输出流对象是PrintWriter类型由于它可以直接输出文本类型,因此需要输出网页文档,需要使用这个方法
       String result="恭喜您登录成功";
       ServletOutputStream outputStream = response.getOutputStream();
       outputStream.write(result.getBytes());
       response.setContentType("text/html;charset=utf-8");
       PrintWriter writer = response.getWriter();
       writer.write(result);
       request.setAttribute("name","123");
       request.getRequestDispatcher("/index.jsp").forward(request,response);

案例:实现网站登录中验证码的切换功能:

创建一个Servelt:

package net.zixue.servlet;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;

@WebServlet(name = "VerifyCodeServlet", urlPatterns = "/code")
public class VerifyCodeServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        int width = 100;
        int height = 30;

        String data = "abcdefghijklmnopqrstuvwxyz01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ";

        Random random = new Random();
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

        Graphics graphics = image.getGraphics();
        graphics.setColor(Color.gray);
        graphics.fillRect(0, 0, width, height);


        graphics.setColor(Color.black);
        for (int i = 0; i < 4; i++) {

            int position = random.nextInt(data.length());
            String randomStr = data.substring(position, position + 1);
            graphics.drawString(randomStr, width / 5 * (i + 1), 15);

        }


        ImageIO.write(image, "jpg", response.getOutputStream());


    }
}

前端html页面:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

</body>
<center>


    <h2>切换验证码</h2>

<script>
    function changeImageCode() {
        document.getElementById('btn').isDisabled=true;
        document.getElementById('identity').src='code?ts='+new Date().getTime();

    }

</script>
    <image  src="code"  id="identity" onload="btn.disable=false;" style="cursor:pointer; vertical-align:middle">

</image>
    <input type="button" value="看不清,请求更换验证码" onclick="changeImageCode()" id="btn"  style="vertical-align:middle">
</center>
</html>

6.Servlet的生命周期

Servlet接口:
javax.servlet.Servlet接口:
init(ServletConfig)方法,初始化方法
service(ServletRequest,ServletResponse)方法,每次访问都会调用来处理请求
destory()方法:销毁servlet方法
HttpServlet方法接口:javax.servlet.http:
继承自Servlet接口,并重新实现了service方法,根据不同请求方式调用不同的处理方法。
service(HttpServletRequest,HttpServletResponse)方法,获取请求方式,分别调用deGet(),或者doPost()方法

  1. 当服务器关闭或者项目被移出服务器,destory方法会执行,生命周期结束
    代码演示:
package net.zixue.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(name = "LifeServlet",urlPatterns = "/life")
public class LifeServlet extends HttpServlet {
    @Override
    public void init() throws ServletException {
        System.out.println("init 被执行了");
        super.init();
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("service 被执行了");
        super.service(req, resp);
    }

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

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("do get被执行了");
    }

    @Override
    public void destroy() {
        System.out.println("destroy被执行了");
        super.destroy();
    }
}

7.ServletConfig对象

ServletConfig对象是它所对应的Servlet对象的相关配置信息
特点:

  • 每一个servlet对象都有一个ServletConfig对象和它相对应
  • ServletConfig对象在多个Servlet对象之间是不能共享的
    常见的ServletConfig对象的方法:
  • getInitParameter(String name):返回一个初始化变量的值
  • getInitParameterNames():返回servlet初始化参数的所有名称
  • getServletContext():获取ServletContext对象
  • getServletNmae():获取Servlet的name配置值
    web.xml页面展示
    <context-param>
    <param-name>encoding</param-name>
    <param-value>utf-8</param-value>
</context-param>
public class HelloServlet extends HttpServlet{

    @Override
    public void init() throws ServletException {
        super.init();
        ServletConfig servletConfig = this.getServletConfig();
        String encoding = servletConfig.getInitParameter("encoding");
        System.out.println("encoding="+encoding);
    }

8.ServletContext-获取项目初始值

ServletContext定义
即Servlet上下文环境,表示当前web应用环境信息
获取ServletContext对象

  • 通过ServletConfig的getServletContext()方法可以得到ServletContext对象
  • HttpServelt中直接通过this.getServletConext()获取
    域对象
    ServletContext对象通常称为Context域对象,ServletContext是我们学习的第一个域对象
   <context-param>
    <param-name>encoding</param-name>
    <param-value>utf-8</param-value>
</context-param>
package net.zixue.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(name = "ServletTest3",urlPatterns = "/test3")
public class ServletTest3 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String encoding = this.getServletContext().getInitParameter("encoding");
    this.getServletContext().getInitParameterNames();
    System.out.println(encoding);
    }
}

9.ServletContext对象-在多个Servlet之间共享数据

这是ServletTest1中定义的name

package net.zixue.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(name = "ServletTest1",urlPatterns = "/test1")
public class ServletTest1 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            String data="xiaozhang";
            this.getServletContext().setAttribute("name",data);
    }
}

在servletTest2中:

package net.zixue.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(name = "ServletTest2",urlPatterns = "/test2")
public class ServletTest2 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
       String name =(String)this.getServletContext().getAttribute("name");
       System.out.println(name);
    }
}

10.ServletContext-读取项目的资源文件

配置文件有两种:
1.web.xml
2.propertise:数据之间没有具体关联
连接数据库:
在这里插入图片描述

package net.zixue.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

@WebServlet(name = "ServletTest4",urlPatterns = "/test4")
public class ServletTest4 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        InputStream resourceAsStream = this.getServletContext().getResourceAsStream("/db.properties");//读取文件的路径,返回文件流
        Properties properties = new Properties();
        properties.load(resourceAsStream);
       String name = properties.getProperty("name");
       String pass =  properties.getProperty("passWord");
       String url = properties.getProperty("url");
        System.out.println(url);
        System.out.println(pass);
        System.out.println(name);
    }
}

11.请求转发

Servlet之间可以实现跳转,从一个Servlet跳转到另一个Servlet,利用Servlet的跳转技术可以很方便的把一块业务模块分开,比如使用一个Servlet接受用户提交数据,使用另一个Servlet读取数据库,最后跳转到另一个Servlet把结果处理展示出来,MVC模式

  1. 转发Forward简介:
    在Servlet中如当前的web资源不想处理请求时,可以通过forward方法将当前的请求传递给其他的Web资源处理,这样的方式称为请求转发
    请求转发的相关方法:
    RequestDispatcher对象,可以通过request.getRequestDisptcher()方法获取调用这个对象的forward方法就可以实现请求转发
    转发过程中携带数据:
    request本身也是一个域对象,request可以携带数据传递给其他web资源
    setAttribute方法:
    getAttriubute 方法:
    removeAttribute方法:
    getAttributeNames方法:在这里插入图片描述

  2. 重定向:
    重定向是根据服务器返回的状态码来实现的。客户端浏览器在请求服务器的时候,服务端会返回一个状态码,服务器通过HttpServletResponse的setStatus(int status)方法来设置状态码。如果服务器这个时候返回的状态码是301或者是302,则浏览器就会到新的网址重新请求该资源。服务的响应中会带着这个新资源的地址。

  3. 请求转发和重定向的区别:
    1.重定向的地址栏会发生变化,转发不会
    2.重定向是两次请求两次响应,转发是一次请求一次响应
    3.重定向路径需要加工程名,转发路径不需要加工程名
    4.重定向可以跳转到任何网站,转发只能在服务器内部进行
    5.图示区别:
    在这里插入图片描述
    在这里插入图片描述

12.实验3-登录错误时显示错误界面

登录的页面代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

</body>
<center>


    <h2>用户登录</h2>
    <form action="login" method="get">
        <p>账号: <input type="text" name="name" /></p>
        <p>密码: <input type="text" name="passWord" /></p>
        <input type="submit" value="登录" />
    </form>




</center>
</html>

Login.Servlet

package net.zixue.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(name = "LoginServlet",urlPatterns = "/login")
public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String userName = "xiaoming";
    String userPass = "123456";
    String name =  request.getParameter("name");
    String passWord =  request.getParameter("passWord");
        if(!name.equals(userName))
        {//账户不存在
            request.setAttribute("errorMessage","账户不存在");
            request.getRequestDispatcher("/loginError.jsp").forward(request,response);
        }else if(!passWord.equals(userPass)){
        //密码错误
            request.setAttribute("errorMessage","密码错误");
            request.getRequestDispatcher("/loginError.jsp").forward(request,response);
        }
    }
}

13.实验4-登录后跳转网站首页

package net.zixue.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(name = "LoginServlet",urlPatterns = "/login")
public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String userName = "xiaoming";
    String userPass = "123456";
    String name =  request.getParameter("name");
    String passWord =  request.getParameter("passWord");
        if(!name.equals(userName))
        {//账户不存在
            request.setAttribute("errorMessage","账户不存在");
            request.getRequestDispatcher("/loginError.jsp").forward(request,response);
        }else if(!passWord.equals(userPass)){
        //密码错误
            request.setAttribute("errorMessage","密码错误");
            request.getRequestDispatcher("/loginError.jsp").forward(request,response);
        }
        else{
        //实现请求重定向方法
//            response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
//            response.setHeader("Location","https://www.baidu.com/");//会跳转到百度的页面
            response.sendRedirect("/hello/home.html");
        }
    }
}

14.网页自动刷新

在遇到页面乱码问题时的解决办法:
在方法体内插入这段话即可解决乱码问题

response.setContentType("text/html;charset=utf-8");
package net.zixue.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(name = "RefreshServlet",urlPatterns = "/refresh")
public class RefreshServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String message="<meta http-equiv='refresh' content='3;url=/hello/home.html'>3秒后会自动跳回首页</meta>";
        request.setAttribute("message",message);
        request.getRequestDispatcher("/index.jsp").forward(request,response);
    }



    private void RefreshDemol(HttpServletResponse response) throws IOException {
        response.setContentType("text/html;charset=utf-8");
        response.setHeader("refresh","3;url='/hello/home.html");
        response.getWriter().print("3秒钟自动刷新");
    }
}

15.Servlet线程安全

当多个客户端并发访问一个Servlet时,web服务器会为每一个客户端的访问创建一个线程,并在这个线程上调用Servlet的service方法,因此service方法内部如果访问了同一个资源的话,就可能引发线程安全问题

package net.zixue.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(name = "ThreadServlet",urlPatterns = "/thread")
public class ThreadServlet extends HttpServlet {
    int i=0;
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        synchronized (this)
        {
            i++;//第一次访问i已经是1,第二次访问i=2
            try{
                Thread.sleep(5*1000);
            }catch(InterruptedException e)
            {
                e.printStackTrace();
            }
            response.setContentType("text/html;charset=utf-8");
            response.getWriter().write(i+"");
        }

    }
}

16.实验5-文件下载

1.通过文件输入输出流将文件返回给客户端浏览器
2.通过对应的http响应头通知浏览器下载该文件

  • 告知浏览器文件的类型:response.setContentType(文件的MIME类型)
  • 告知浏览器文件的打开方式是下载:
    response.setHeader(“Content-Disposition”,“attachment;filename=文件名称”);
package net.zixue.servlet;

import jdk.management.resource.internal.inst.FileInputStreamRMHooks;
import sun.misc.BASE64Encoder;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;

@WebServlet(name = "DownloadServlet",urlPatterns = "/download")
public class DownloadServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                //如何将服务器上的资源返回给浏览器

//                 response.getWriter();//输出字节码文件的
//        response.getOutputStream();//输出二进制文件的
        String filename=request.getParameter("filename");

        String filenameEncoder = URLEncoder.encode(filename, "utf-8");

        //如何通知浏览器不要预览,而是下载文件
        String mimeType = this.getServletContext().getMimeType(filename);
        response.setContentType(mimeType);
        response.setHeader("Content-Disposition","attachment;filename="+filenameEncoder);

        //通过ServletContext对象获取文件的绝对路径
        String realPath = this.getServletContext().getRealPath("download/"+filename);
        InputStream in=new FileInputStream(realPath);
        ServletOutputStream outputStream = response.getOutputStream();//输出二进制文件的

        //文件拷贝
        int len=0;
        byte[] buffer=new byte[1024];
        while ((len=in.read(buffer))>0){
        outputStream.write(buffer,0,len);

        }
        in.close();


    }
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值