《遇见狂神说》Javaweb之Servlet技术

2021/7/2 22:21:46

本文主要是介绍《遇见狂神说》Javaweb之Servlet技术,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

0.概述

  1. JDBC笔记,复习回顾使用
  2. 秦疆老师配套视频直达:Javaweb入门到实战

1.Servlet

1.1Servlet简介

  • Servlet就是sun公司开发动态web的一门技术

  • Sun在API中提供了一个接口:Servlet ,如果逆向开发一个Servlet程序,只需要完成两个小步骤

    • 编写一个类,实现Servlet接口
    • 把开发好的Java类部署到web服务器中
  • 把实现了Servlet接口的Java程序叫做Servlet

1.2HelloServlet

  1. 构建一个普通的Maven项目

在这里插入图片描述

在这里插入图片描述

  1. 删掉里面的Src目录,这个空的工程就是Maven主工程

  2. pom.xml 里面添加 servlet,jsp 依赖

    • Maven Repository仓库里面搜索 javax-servlet-api 和 javax.servlet.jsp-api

    在这里插入图片描述

    <dependencies>
            
    <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</version>
    </dependency>
    
    
    <!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>javax.servlet.jsp-api</artifactId>
        <version>2.3.3</version>
    </dependency>
    
        
    </dependencies> 
    
  3. Load Maven changes

在这里插入图片描述

  1. 新建Module

    在这里插入图片描述

在这里插入图片描述

  1. 父项目的pom.xml文件有
<modules>
        <module>servlet-01</module>
</modules>
  1. 子项目的pom.xml文件有
<parent>
        <artifactId>javaweb-02-maven</artifactId>
        <groupId>com.kuang</groupId>
        <version>1.0-SNAPSHOT</version>
</parent>

父项目中的java子项目可以直接使用,意思其实就是子项目可以不用再次导入jar包,直接可以使用父项目导入的jar包

7.在 main 目录下新建Directory javaresources

​ 并且右键-Mark Directory as ,分别转换为java目录和资源目录

在这里插入图片描述

  1. Maven环境优化

    • 修改web.xml为最新的

      <?xml version="1.0" encoding="UTF-8"?>
      <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
              http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
              version="4.0"
              metadata-complete="true">
      </web-app>
      

上面这一大串我们是从哪里来的呢?其实我们是在tomcat\webapps\ROOT\WEB-INF\web.xml 中与tomcat相对应的版本

  1. 新建包,新建类

在这里插入图片描述

  1. 写一个Servlet程序,Serlvet接口Sun公司有两个默认的实现类:HttpServlet,GenericServlet
  • 编写一个普通类
  • 实现Servlet接口,这里我们直接继承HttpServlet
  • 重写doGet、doPost方法
  • 编写Servlet的映射

为什么需要映射?

  • 我们写的是JAVA程序,但是要通过浏览器访问,而浏览器需要连接web服务器,所以我们需要再web服务中注册我们写的Servlet,还需给他一个浏览器能够访问的路径

2.继承HttpServlet

public class HelloServlet extends HttpServlet {
    // get或者post只是请求实现的不同方式,可以互相调用,业务逻辑都一样
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("进入doGet方法");
        // 在这边重写完方法之后就去web.xml里面编写Servlet的映射
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}

3.编写Servlet的映射

<!--    1.注册servlet-->
<servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>com.kuang.servlet.HelloServlet</servlet-class>
</servlet>
<!--    servlet的请求路径-->
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
</servlet-mapping>
  1. 配置并启动tomcat

在这里插入图片描述

在这里插入图片描述

  1. 请求我们自己映射的/hello

在这里插入图片描述

1.3Mapping问题

  1. 一个Servlet可以指定一个映射路径
<!--当我们这样配置映射路径的时候,我们访问的名称为:localhost:8080/s1/hello-->
<servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello</url-pattern>
</servlet-mapping>
  1. 一个Servlet可以指定多个映射路径
<!--当我们这样配置映射路径的时候,我们访问的名称为:localhost:8080/s1/hello-->
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
</servlet-mapping>
<!--当我们这样配置映射路径的时候,我们访问的名称为:localhost:8080/s1/hello2-->
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello2</url-pattern>
</servlet-mapping>
<!--当我们这样配置映射路径的时候,我们访问的名称为:localhost:8080/s1/hello3-->
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello3</url-pattern>
</servlet-mapping>
<!--当我们这样配置映射路径的时候,我们访问的名称为:localhost:8080/s1/hello4-->
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello4</url-pattern>
</servlet-mapping>
<!--当我们这样配置映射路径的时候,我们访问的名称为:localhost:8080/s1/hello5-->
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello5</url-pattern>
</servlet-mapping>
  1. 一个Servlet可以指定通用映射路径
<!--当我们这样配置映射路径的时候,我们访问的名称为:localhost:8080/s1/hello/****-->
<!--	****代表任何字母数字均可	-->
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello/*</url-pattern>
</servlet-mapping>
  1. 默认请求路径
<!--默认请求路径-->
<!--当我们这样配置映射路径的时候,我们访问的名称为:localhost:8080/s1/****-->
<!--	****代表任何字母数字均可	-->
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>
  1. 指定一些后缀或者前缀等等….
<!--可以自定义后缀实现请求映射-->
<!--注意点,*前面不能加项目映射的路径-->
<!--当我们这样配置映射路径的时候,我们访问的名称为:localhost:8080/s1/hello/****.qinjiang-->
<!--  例如:localhost:8080/s1/hello/sajdlkajda.qinjiang	-->
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>*.qinjiang</url-pattern>
</servlet-mapping>
  1. 优先级问题
<servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello</url-pattern>
</servlet-mapping>

<!--404-->
<servlet>
    <servlet-name>error</servlet-name>
    <servlet-class>com.kuang.servlet.Error</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>error</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

例如当我们配置上方映射时候,我们访问:localhost:8080/s1/hello ,思考此时是访问/*还是访问/hello呢?

  • 答案是访问/hello
  • 指定了固有的映射路径优先级最高,如果找不到就会走默认的处理请求

2.ServletContext

概念:web容器在启动的时候,它会为每个web程序都创建一个对应的ServletContext对象,它代表了当前的web应用

白话:代表整个web应用,可以和程序的容器(服务器)来通信

2.1共享数据

  • 我们在这个Servlet中保存的数据,可以在另外一个servlet中拿到
  • 但是都是通过同一个ServletContext对象拿到的
    • context.setAttribute(String name,Object value)
    • context.getAttribute(String name)

HelloServlet.java

public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        //数据
        String username = "秦疆"; 
         //将一个数据保存在了ServletContext中,名字为:username 。值 username
        context.setAttribute("username",username);

    }
}
  • 编写mapper映射
<!--    存放username数据	-->
<servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>com.kuang.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
</servlet-mapping>

GetServlet.java

public class GetServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        String username = (String) 			context.getAttribute("username");

        resp.setContentType("text/html");
        resp.setCharacterEncoding("utf-8");
         // 将获取到的数据显示在网页中
        resp.getWriter().print("名字"+username);

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}
  • 编写mapper映射
<!--    获取username数据-->
<servlet>
    <servlet-name>getc</servlet-name>
    <servlet-class>com.kuang.servlet.GetServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>getc</servlet-name>
    <url-pattern>/getc</url-pattern>
</servlet-mapping>

测试访问结果;

  • 先输入 : http://localhost:8080/s2/hello 访问/hello会存放数据
  • 再输入:http://localhost:8080/s2/getc 访问/getc会取出数据

在这里插入图片描述

在这里插入图片描述

2.2获取初始化参数(了解)

我们可以在web.xml中配置一些web应用初始化参数,在ServletContext对象中可以拿到它

GetInitParameter.java

/**
 * 获取初始化参数
 */
public class GetInitParameter extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        String url = context.getInitParameter("url");
        // 将获取到的url打印到网页上
        resp.getWriter().print(url);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}

在web.xml中配置初始化参数和注册servlet映射为 /gp

<!--    配置初始化参数 参数为url,值为 jdbc:mysql://localhost:3306/mybatis  -->
<context-param>
    <param-name>url</param-name>
    <param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
</context-param>
<!--   注册servlet映射 -->
    <servlet>
        <servlet-name>gp</servlet-name>
        <servlet-class>com.kuang.servlet.GetInitParameter</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>gp</servlet-name>
        <url-pattern>/gp</url-pattern>
    </servlet-mapping>
</web-app>
  • 我们访问测试

在这里插入图片描述

2.3请求转发

GetRequestDispatcher.java

/**
 * 请求转发
 */
public class GetRequestDispatcher extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("进入了GetRequestDispatcher");
        ServletContext context = this.getServletContext();
        //RequestDispatcher requestDispatcher = context.getRequestDispatcher("/gp"); 转发的请求路径
        //requestDispatcher.forward(req,resp); 调用forward实现请求转发;
        context.getRequestDispatcher("/gp").forward(req,resp);

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}

在web.xml中配置servlet映射为 /gr

<!--    请求转发    -->
<servlet>
    <servlet-name>gr</servlet-name>
    <servlet-class>com.kuang.servlet.GetRequestDispatcher</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>gr</servlet-name>
    <url-pattern>/gr</url-pattern>
</servlet-mapping>
  • 测试访问

在这里插入图片描述

原因:我们请求/gr,但是/gr通过getRequestDispatcher转发到另外一个页面,但是我们访问的路径不变,依旧是localhost:8080/s2/gr

请求转发的特点:

  1. 浏览器地址栏路径不发生变化
  2. 只能转发到当前服务器内部资源中
  3. 转发是一次请求

请求转发:A想要拿到C中的资源,但是A没法与C连接,A只能与B连接,于是A对B说,B你去C里面给我拿,B就与C建立连接,C把资源给B,B再把资源给A

重定向:A连接B说想要拿到C中的资源,B对A说,你去连接C然后拿资源

2.4读取资源文件

  • 在resources目录下新建db.properties

db.properties

username=root12312
password=zxczxczxc

PropertiesServlet.java

/**
 * 读取资源文件properties
 */
public class PropertiesServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        // 思路:需要一个文件流
        // 这里的路径:
        InputStream is = context.getResourceAsStream("/WEB-INF/classes/db.properties");
        Properties prop = new Properties();
        prop.load(is);

        String username = prop.getProperty("username");
        String password = prop.getProperty("password");
        resp.getWriter().print(username +":"+password );

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}

  • 这里的路径示意图
  • 因为java目录和resources目录均被打包至classes目录

在这里插入图片描述

  • 去web.xml中配置servlet映射
<!--    获取资源文件properties    -->
<servlet>
    <servlet-name>pr</servlet-name>
    <servlet-class>com.kuang.servlet.PropertiesServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>pr</servlet-name>
    <url-pattern>/pr</url-pattern>
</servlet-mapping>
  • 测试启动

在这里插入图片描述

3.HttpServletResponse

web服务器接收到客户端的http请求,针对这个请求,分别创建一个代表请求HttpServletRequest对象,代表响应的一个HttpServletResponse对象

  • 如果要获取客户端请求过来的参数:找HttpServletRequest
  • 如果要给客户端响应一些信息:找HttpServletResponse

HttpServletrequest对象和HttpServleresponse对象的原理

  1. request和response对象是由服务器创建的。我们来使用它们
  2. request对象是来获取请求消息,response对象是来设置响应消息

3.1响应数据

接收到客户端的请求后,可以通过 HttpServletResponse 对象直接进行响应,响应时需要获取输出流,服务器输出字符数据到浏览器有两种方法:

步骤:

  1. 获取字符输出流
  2. 输出数据

两种形式

  • resp.getWriter():获取字符流(只能响应回字符)
  • resp.getOutputStream():获取字节流(能响应一切数据)

注意:两者不能同时使用

// 字符输出流(输出字符串)
PrintWriter writer = resp.getWriter();
writer.write("hello");
writer.write("<h2>Hello</h2>");

在这里插入图片描述


// 字节输出流(输出一切数据)
ServletOutputStream out = resp.getOutputStream();
out.write("hello".getBytes());
out.write("<h2>Hello</h2>".getBytes());

在这里插入图片描述


为什么不能带标签式的输出呢?是因为我们没有指定编码,要使服务器输出字符数据到浏览器,必须指定编码,否则就按字符串格式显示在浏览器上

resp.setContentType("text/html;charset=UTF-8");
PrintWriter writer = resp.getWriter();
writer.write("hello");
writer.write("<h1>hello</h1>");

我们加上指定编码,再次访问此时:

在这里插入图片描述

3.2中文乱码问题

  • resp.setContentType("text/html;charset=UTF-8");

​ 在响应中,如果我们响应的内容中含有中文,则有可能出现乱码,这是因为服务器响应的数据也会经过网络传输,服务器有一种编码方式,在客户端也存在一种编码方式。当两端使用的编码方式不同时则会出现乱码。

​ 要解决该种乱码只能在服务器端告知服务器使用一种能够支持中文的编码格式,比如我们通常使用的"UTF-8"

// 指定服务器编码
resp.setCharacterEncoding("UTF-8");	
// 指定客户端编码
resp.setHeader("Content-type","text/html;charset=UTF-8");
  • 以上两端编码的指定也可以使用一句代替,同时指定服务器和客户端
resp.setContentType("text/html;charset=UTF-8");

示例:

resp.setContentType("text/html;charset=UTF-8");
// 得到字符输出流
PrintWriter writer = resp.getWriter();
writer.write("<h2>你好</h2>");

访问测试:

在这里插入图片描述

3.3重定向

在这里插入图片描述

一个web资源B收到客户端A的请求后,它B会通知A客户端去访问另一个web资源C,这个过程叫重定向

常见场景:

  • 用户登录
void sendRedirect(String var1);

RedirectServlet.java

/**
 * 实现重定向
 */
public class RedirectServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         /* 重定向的原理
        resp.setHeader("Location","/r/img");
        resp.setStatus(302);
         */
        resp.sendRedirect("/r/img");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}
  • 在web.xml中映射
<!--    重定向redirect注册映射     '/red'-->
<servlet>
    <servlet-name>redirect</servlet-name>
    <servlet-class>com.kuang.servlet.RedirectServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>redirect</servlet-name>
    <url-pattern>/red</url-pattern>
</servlet-mapping>
  • 测试启动

在这里插入图片描述

3.4面试题

请求转发(req.getRequestDispatcher().forward())重定向(resp.sendRedirect())
  • 请求转发的地址栏不会发送改变,重定向的地址栏会发送改变

  • 请求转发只有一次请求,重定向有两次请求

  • 请求转发 request 对象可共享,重定向时 request 对象不共享

  • 请求转发是服务器行为,重定向是客户端行为

  • 请求转发时的地址只能是当前站点(当前项目)的资源,重定向时可以是任何地址

两者都可进行跳转,根据实际需求选取即可

  • 重定向的原理
resp.setHeader("Location","/r/img");
resp.setStatus(302);

3.5Response登录案例

index.jsp

<html>
<body>
<h2>Hello World!</h2>
<%--这里提交的路径,需要寻找到项目的路径--%>
<%--${pageContext.request.contextPath}代表当前的web项目--%>
<form action="${pageContext.request.contextPath}/login" method="get">
    用户名:<input type="text" name="username"><br>
    密码:<input type="password" name="password"><br>
    <input type="submit">
</form>
</body>
</html>

RequestTest.java

/**
 * 简单的登录小案例
 */
public class RequestTest extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 设置utf-8编码,网站不乱码
        resp.setCharacterEncoding("utf-8");
        // 获取index.jsp中的参数
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        System.out.println(username + ":" + password);
        // 重定向,注意路径问题,否则会404
        resp.sendRedirect("/r/success.jsp");
       
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}
  • 在web.xml中注册servlet映射
<servlet>
    <servlet-name>request</servlet-name>
    <servlet-class>com.kuang.servlet.RequestTest</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>request</servlet-name>
    <url-pattern>/login</url-pattern>
</servlet-mapping>
  • 访问测试

在这里插入图片描述

在这里插入图片描述

控制台输出用户名和密码

4.HttpServletRequest

HttpServletRequest代表客户端的请求,用户通过Http协议访问服务器,HTTP请求中的所有信息会被封装到HttpServletRequest中

HttpServletRequest对象:主要作用是用来接收客户端发送过来的请求信息

例如:请求的参数,发送的头信息等都属于客户端发来的信息

4.1获取请求行数据

  • 获取虚拟目录:也就是tomcat中的虚拟目录,例如/r
    • String getContextPath()
  • 获取Servlet路径: 就是在web.xml中注册的servlet映射,例如/hello
    • String getServletPath()
  • 获取请求URI:/s1/hello
    • String getRequestURI(): /s1/hello
    • StringBuffer getRequestURL() http://localhost/s1/hello

4.2获取请求参数

  • req.getParameter(String name):根据参数名称获取参数值

  • req.getParameterValues(String name):根据参数名称获取参数值的数组

4.3中文乱码问题

  • req.setCharacterEncoding("utf-8")

  • get方式:tomcat 8 已经将get方式乱码问题解决了

  • post方式:会乱码

    • 解决在获取参数前,设置req的编码req.setCharacterEncoding("utf-8")
req.setCharacterEncoding("utf-8");

4.4请求转发

​ 请求转发,是一种服务器的行为,当客户端请求到达后,服务器进行转发,此时会将请求对象进行保存,地址栏中的URL地址不会变,得到相应后,服务器再将响应发送给客户端,从始至终只有一个请求发出,request数据可以共享

步骤:

  • 通过request对象获取请求转发器对象:getRequestDispatcher(String path)
  • 使用RequestDispatcher对象来进行转发:forward(ServletRequest request, ServletResponse response)
req.getRequestDispatcher("/gp").forward(req,resp);

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录</title>
</head>
<body>
<h1>登录</h1>
<div style="text-align: center">
<%--    这里表单表示的意思:以post方式提交表单,提交到我们的login请求--%>
<%--    ${pageContext.request.contextPath}代表我们的web项目--%>
    <form action="${pageContext.request.contextPath}/login" method="post">
            用户名:<input type="text" name="username"><br>
            密码:<input type="password" name="password"><br>
            爱好:
            <input type="checkbox" name="hobbies" value="女孩">女孩
            <input type="checkbox" name="hobbies" value="代码">代码
            <input type="checkbox" name="hobbies" value="唱歌">唱歌
            <input type="checkbox" name="hobbies" value="电影">电影
            <br>
            <input type="submit">
    </form>
</div>
</body>
</html>

LoginServlet.java

public class LoginServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");

        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String[] hobbies = req.getParameterValues("hobbies");
        System.out.println("----------------");
        System.out.println(username);
        System.out.println(password);
        System.out.println(Arrays.toString(hobbies));
        System.out.println("----------------");



        // 通过请求转发
        // 这里的 / 代表当前的web应用
        req.getRequestDispatcher("/success.jsp").forward(req,resp);

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}


这篇关于《遇见狂神说》Javaweb之Servlet技术的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程