Web后端 - 第3章 - Servlet

一、Servlet

  1. Servlet规范来自于JAVAEE规范中的一种
    宏观地讲,Servlet 是连接 Web服务器与 服务端Java程序的协议,是一种通信规范。
    这个规范是以一套接口的形式体现的。

   通常来说,Servlet 是宏观上 Servlet规范中的一个具体的接口,
   Servlet规范中包含一套接口。而 Servlet 接口仅仅是其中之一。

   微观地讲,Servlet 是 Servlet接口实现类的一个实例对象,是运行在服务器上的一段 Java小程序,
   即 Server Applet,也就是 Servlet 这个单词的来历。
   Servlet 的主要功能是根据客户端提交的请求,调用服务器端相关 Java代码,完成对请求的处理与运算。

  1. 作用:
    1)在Servlet规范中,指定【动态资源文件】开发步骤
    2)在Servlet规范中,指定Http服务器调用动态资源文件规则
    3)在Servlet规范中,指定Http服务器管理动态资源文件实例对象规则

二、Servlet接口实现类

  1. Servlet接口来自于Servlet规范下一个接口,这个接口存在于Http服务器(我这里安装的是Tomcat)提供的jar包中

  2. Tomcat服务器下lib文件有一个servlet-api.jar,存放Servlet接口(javax.servlet.Servlet接口)

  3. Servlet规范中认为,Http服务器能调用的【动态资源文件】必须是一个Servlet接口实现类

class Student{
	//不是动态资源文件,Tomcat无权调用
}

class Teacher implements Servlet{
	//合法动态资源文件,Tomcat有权利调用

	Servlet obj = new Teacher();
	obj.doGet()
}

三、Servlet接口实现类开发步骤

  • 第一步:创建一个Java类继承于HttpServlet父类,使之成为一个Servlet接口实现类
              extends         extends           implements
    Servlet接口实现类————> HttpServlet ————>GenericServlet ————> Servlet接口

  • 第二步:重写HttpServlet父类两个方法。doGet或者doPost
          get
    浏览器 ————> oneServlet.doGet()
          post
    浏览器 ————> oneServlet.doPost()

  • 第三步:将Servlet接口实现类信息【注册】到Tomcat服务器

   【网站】————>【web】————>【WEB-INF】————> web.xml

<!--将Servlet接口实现类类路径地址交给Tomcat-->
<servlet>
	<servlet-name>mm</servlet-name>     <!--声明一个变量存储servlet接口实现类类路径-->
	<servlet-class>com.yuming.controller.OneServlet</servlet-class>    <!--声明servlet接口实现类类路径-->
</servlet>

Tomcat服务器读取到这个信息,就会转换为:
   String mm = “com.yuming.controller.OneServlet”

<!--为了降低用户访问Servlet接口实现类难度,需要设置简短请求别名-->
<servlet-mapping> 
	<servlet-name>mm</servlet-name>
	<url-pattern>/one</url-pattern>    <!--设置简短请求别名,别名在书写时必须以"/"为开头-->
</servlet-mapping>

如果现在浏览器向Tomcat索要OneServlet时地址:
   http://localhost:8080/myWeb/one

发布网站(把网站交给Tomcat管理)。。。
   【Run】————>【Edit Configuration】,选择中间第二个:【Deployment】部署
   点右边的【+】————>【Artifact】,就会在中间页面出现自己的网站【02_Servlet接口实现类】
   在下面的【Application content:】后面可以写网站的别名,但是要注意一定要以/开头
   我的是:/myWeb02
   点【Apply】,点【OK】

启动Tomcat。。。
   IDEA下面【Application Servers】窗口,找到【启动和关闭Tomcat的按钮】,点击左侧run或者debug

关闭Tomcat。。。
   IDEA下面【Application Servers】窗口,找到【启动和关闭Tomcat的按钮】,点击左侧的stop

四、Servlet生命周期

  • Servlet 生命周期: Servlet 对象的创建、初始化、服务的执行,及最终 Servlet 对象被销毁的整个过程。
    实例化————> 初始化————> 服务————> 销毁
    在这里插入图片描述
  1. 网站中所有的Servlet接口实现类的实例对象,只能由Http服务器负责创建
    开发人员不能手动创建Servlet接口实现类的实例对象

  2. 在默认的情况下,Http服务器接收到对于当前Servlet接口实现类 第一次请求时,自动创建这个Servlet接口实现类的实例对象

   只有在手动配置情况下,要求Http服务器在启动时就自动创建某个Servlet接口实现类的实例对象:

  <servlet>
       <servlet-name>mm</servlet-name>        <!--声明一个变量存储servlet接口实现类类路径-->
       <servlet-class>com.yuming.controller.OneServlet</servlet-class>
	    
	<!--通知Tomcat在启动时负责创建 OneServlet 实例对象--> 
       <load-on-startup>30</load-on-startup>     <!--填写一个大于0的整数即可-->
  </servlet>

   这段配置: < l o a d − o n − s t a r t u p > 30 < / l o a d − o n − s t a r t u p > <load-on-startup>30</load-on-startup> <loadonstartup>30</loadonstartup>,什么作用呢?

   标记是否在Http服务器(这里是Tomcat)启动时创建并初始化这个 Servlet 实例,
   即是否在 Http服务器启动时调用执行该 Servlet 的无参构造器方法与 init()方法,
   而不是在真正访问时才创建。
      (1)它的值必须是一个整数。
      (2)当值大于等于 0 时,表示容器在启动时就加载并初始化这个 Servlet,
        数值越小,该 Servlet的优先级就越高,其被创建的也就越早;
      (3)当值小于 0 或者没有指定时,则表示该 Servlet 在真正被使用时才会去创建。
      (4)当值相同时,容器会自己选择创建顺序。

  1. 在Http服务器运行期间,一个Servlet接口实现类只能被创建出一个实例对象

  2. 在Http服务器关闭时刻,自动将网站中所有的Servlet对象进行销毁

附:在IDEA中可以直接创建一个Servlet类:
    在包上面右键————》new ————》Servlet ————》填写类名、包名

    切记:不必要勾选【Create Java EE 6 annotated class】这个选项,
    这个选项是表示支持Servlet的相关注解的,我们此处暂时不用。

【热部署:同步更新设置】

当启动服务器之后,要想对自己IDEA中的代码进行修改,然后得重新启动一次服务器
才可以看到修改后的效果,很麻烦!!

解决方法:
    【Run】————>【Edit Configuration】————>选择中间第一个:【server】
    在【server】下面的 【On “Update” action:】后面选择【Update classes and resources】
    以及【On frame deactivation:】后面也选择【Update classes and resources】

    表示更新动态资源文件【java文件】和静态资源文件 【图片,html, css, js】时,
    会重新加载到服务器里面,就不需要每次都重启Tomcat了,只需要刷新浏览器页面即可
    但注意要以【debug】方式启动才有效果

在这里插入图片描述

五、HttpServletResponse接口

5.1 介绍:

  1. HttpServletResponse接口 来自于Servlet规范中,在Tomcat中存在于servlet-api.jar

  2. HttpServletResponse接口实现类 由Http服务器负责提供

  3. HttpServletResponse接口 负责将doGet/doPost方法执行结果写入到【响应体】交给浏览器

  4. 开发人员习惯于将HttpServletResponse接口修饰的对象称为响应对象

5.2 主要功能:

  1. 将执行结果以二进制形式写入到【响应体】

  2. 设置【响应头】中【content-type】属性值,从而控制浏览器使用
    对应编译器将响应体二进制数据编译为【文字,图片,视频,命令】

  3. 设置【响应头】中【location】属性,将一个请求地址赋值给location.
    从而控制浏览器向指定服务器发送请求

【实例】:

public class MyServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

	//设置【响应头】中的content-type属性
	response.setContentType("text/html;charset=utf-8"); //输出的有文本内容、html内容、中文字符

	//--------响应对象将结果写入到【响应体】-------------start

	//1.通过响应对象,向Tomcat索要 输出流
	PrintWriter out = response.getWriter();

	//2.通过输出流,将执行结果以二进制形式写入到响应体
	out.write(97);   //a  (out.writer() 可以将【字符】,【字符串】,【ASCII码】写入到响应体)
	out.print(97);  //97  (out.print() 将真实数据写入到响应体)

	out.print("Hello World <br/> 你好 <br/> 世界"); //既有文字信息又有HTML标签命令,文字信息还含有中文,所以需要事先设置【响应头】中的content-type属性
	/*
	Hello World
	你好
	世界
	*/

	//--------响应对象将结果写入到响应体--------------end

	//通过响应对象,将地址赋值给【响应头】中location属性, 达到跳转页面的目的
	//response.sendRedirect("http://www.baidu.com?userName=mike"); //[响应头  location="http://www.baidu.com?userName=mike"]

    }	//doGet执行完毕
    //Tomcat将响应包推送给浏览器
}

六、HttpServletRequest 接口

6.1 介绍:

  1. HttpServletRequest接口 来自于Servlet规范中,在Tomcat中存在于servlet-api.jar

  2. HttpServletRequest接口实现类 由Http服务器负责提供

  3. HttpServletRequest接口 负责在doGet/doPost方法运行时读取Http请求协议包中的信息

  4. 开发人员习惯于将HttpServletRequest接口修饰的对象称为请求对象

6.2 作用:

  1. 可以读取Http请求协议包中【请求行】信息
  2. 可以读取保存在Http请求协议包中【请求头】(get)或者【请求体】(post)(二进制)中请求参数信息
  3. 可以代替浏览器向Http服务器申请资源文件调用

【实例】:

//1.通过请求对象,读取【请求行】中【URL】信息
String url = request.getRequestURL().toString();

//2.通过请求对象,读取【请求行】中【method】信息
String method = request.getMethod();

//3.通过请求对象,读取【请求行】中 【URI】信息
String uri =  request.getRequestURI();
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	//通知请求对象,使用utf-8字符集对【请求体】二进制内容进行一次重写解码,否则如果传的参数是中文的话,输出结果会是乱码-----》???
	request.setCharacterEncoding("utf-8");
	//通过请求对象,读取【请求体】参数信息
	String s = request.getParameter("username");  // 一定要确认这里的"username"是和three.html 中form表单里面<input>的name属性的值一致!
	System.out.println("从【请求体】得到参数值 "+s);

}

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

	//通过请求对象,读取【请求头】参数信息
	String userName = request.getParameter("username"); // 一定要确认这里的"username"是和three.html 中form表单里面<input>的name属性的值一致!
	System.out.println("从<<请求头>>得到参数值 "+userName);
}

七、请求对象和响应对象的生命周期

  1. 在Http服务器接收到浏览器发送的【Http请求协议包】之后,
    自动为当前的【Http请求协议包】生成一个【请求对象request】和一个【响应对象response】

  2. 在Http服务器调用doGet/doPost方法时,负责将【请求对象】和【响应对象】作为实参
    传递到方法,确保doGet/doPost正确执行

  3. 在Http服务器准备推送Http响应协议包之前,负责将本次请求关联的【请求对象】和【响应对象】
    销毁

    ***【请求对象】和【响应对象】生命周期贯穿一次请求的处理过程中
    ***【请求对象】和【响应对象】相当于用户在服务端的代言人

【实例】在线考试管理系统

1、开发环境搭建

    任务:在线考试管理系统----用户信息管理模块
    子任务:
        用户信息注册
        用户信息查询
        用户信息删除
        用户信息更新

    准备工作:

	   1.创建用户信息表 Users.frm
	      CREATE TABLE Users(
		    userId int  primary key auto_increment, #用户编号
		    userName varchar(50),    #用户名称
		    password varchar(50),    #用户密码
		    sex      char(1),        #用户性别 '男' 或 '女'
		    email    varchar(50)     #用户邮箱
	      )

	      auto_increment,自增序列    i++
	      在插入时,如果不给定具体用户编号,此时根据auto_increment的值递增添加
	      添加auto_increment后,userId的值永远不可能相同,即使把表里面的数据都删除了,
	      重新添加数据,也是从最后一次的userId的值往后加1的

	   2.在src下 新建:com.yuming.entity.Users 实体类

	   3.在src下 新建:com.yuming.util.JdbcUtil 工具类【复用】

	   4.在src下 新建:com.yuming.dao.UserDao  数据库操作类,进行数据的增删改查

	   5.在src下 新建:com.yuming.controller包,存放servlet类

	   6.在web下WEB-INF下创建:lib文件夹,存放mysql提供JDBC实现jar包
		把jar包粘贴进来之后,记住,【右键该jar包】-点击【Add as Library...】

在这里插入图片描述

public class JdbcUtil {

    /**
     * 工具类当中的构造方法都是私有的,为了防止别人new对象
     * 因为工具类当中的方法都是静态的,不需要new对象,直接采用类名调用: 类名.方法名
     */
    private JdbcUtil() {
    }

    //静态代码块,在类加载时执行,且只执行一次
    static {
        try {
            //注册驱动
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取数据库连接对象
     *
     * @return 连接对象
     * @throws SQLException
     */
    public static Connection getConnection() {
        Connection conn = null;
        try {
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/zt", "root", "123456");
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }

    /**
     * 释放资源
     *
     * @param conn 连接对象
     * @param stmt 数据库操作对象
     * @param rs   结果集
     */
    public static void close(Connection conn, Statement stmt, ResultSet rs) {
        //方法参数中选择Statement没有选择PreparedStatement,是因为Statement是父接口,更通用,
        // 即使未来传过来的是PreparedStatement,也会自动向下转型

        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (stmt != null) {
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
public class UserDao {

    //用户注册(添加)
    public boolean add(Users user) {
        //1、注册驱动 + 2、获取连接
        Connection conn = JdbcUtil.getConnection();
        PreparedStatement ps = null;
        int result = 0;
        try {
            //3、获取 预编译的数据库操作对象
            String sql = "insert into users(userName,password,sex,email) values(?,?,?,?)";
            ps = conn.prepareStatement(sql);
            //给?占位符赋值
            ps.setString(1, user.getUserName());
            ps.setString(2, user.getPassword());
            ps.setString(3, user.getSex());
            ps.setString(4, user.getEmail());
            //4、执行sql语句
            result = ps.executeUpdate();
            //5、处理查询结果集(此处没有,因为不是做查询操作,而是添加)
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //6、释放资源
            JdbcUtil.close(conn, ps, null);
        }
        // 如果从数据库中执行结果返回的是1,则返回true(添加成功),否则为false(添加失败)
        return result == 1 ? true : false;
    }

    //查询所有用户信息
    public List findAll() {
        //1、注册驱动 + 2、获取连接
        Connection conn = JdbcUtil.getConnection();
        Statement stmt = null;
        ResultSet rs = null;
        List userList = new ArrayList();
        try {
            //3、获取 数据库操作对象
            stmt = conn.createStatement();
            //4、执行sql语句
            String sql = "select * from users";
            rs = stmt.executeQuery(sql);
            //5、处理查询结果集
            while (rs.next()) {
                Integer userId = rs.getInt("userId");
                String userName = rs.getString("userName");
                String password = rs.getString(3);
                String sex = rs.getString(4);
                String email = rs.getString("email");
                Users user = new Users(userId, userName, password, sex, email);
                // 创建一个ArrayList存放user对象
                userList.add(user);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //6、释放资源
            JdbcUtil.close(conn, stmt, rs);
        }
        return userList;
    }

    //根据userId删除 用户信息
    public boolean delete(String userId) {
        //1、注册驱动 + 2、获取连接
        Connection conn = JdbcUtil.getConnection();
        PreparedStatement ps = null;
        int result = 0;
        try {
            //3. 获取 预编译的数据库操作对象
            String sql = "delete from users where userId = ?";
            ps = conn.prepareStatement(sql);
            //给?占位符赋值
            ps.setInt(1, Integer.parseInt(userId));
            //注意,Integer.valueOf(xx) ——>>将int数字或者String数字,转换成一个Integer对象
            //Integer.parseInt(“数字”) ——>>将String类型的数字,转换成 int类型
            //4. 执行sql语句
            result = ps.executeUpdate();
            //5.处理查询结果集(无)
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //6.释放资源
            JdbcUtil.close(conn, ps, null);
        }
        return result == 1 ? true : false;
    }

    //登录验证
    public boolean login(String userName, String password) {
        //1.注册驱动+ 2.获取连接
        Connection conn = JdbcUtil.getConnection();
        PreparedStatement ps = null;
        ResultSet rs = null;
        int count = 0;
        try {
            // 3.获取 预编译的数据库操作对象
            String sql = "select count(*) from users where userName = ? and password = ?";
            ps = conn.prepareStatement(sql);
            //给占位符?赋值
            ps.setString(1, userName);
            ps.setString(2, password);
            //4.执行sql语句
            rs = ps.executeQuery();
            //5.处理查询结果集
            while (rs.next()) {
                count = rs.getInt("count(*)");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //6.释放资源
            JdbcUtil.close(conn, ps, rs);
        }
        //return count == 1 ? true : false;
        return count >= 1 ? true : false;  //userId为主键,那么可能存在userName和password都一样的用户,这样查询结果就大于1了
    }
}

2、添加

(1)用户信息注册流程图:【007-用户注册信息-流程图.png】
(2)用户注册页面:user_Add.html的开发
(3)UserAddServlet的开发
(4)发布网站、测试

(1)
在这里插入图片描述
(2)

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

    <center>
        <form action="/myWeb/user/add" method="get">
            <table border="1">
                <tr>
                    <td>用户姓名</td>
                    <td><input type="text" name="userName"/></td>
                </tr>
                <tr>
                    <td>用户密码</td>
                    <td><input type="password" name="password"/></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="email"/></td>
                </tr>
                <tr>
                    <td><input type="submit" value="用户注册"/></td>
                    <td><input type="reset" /></td>
                </tr>
            </table>
        </form>
    </center>

</body>
</html>

(3)

public class UserAddServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //http://localhost:63342/myWeb/user/add?userName=mike&password=123&sex=男&email=13%40qq.com
        String userName,password,sex,email;
        Users user = null;
        UserDao dao = new UserDao();
        boolean result =false;
        PrintWriter out = null;

        //1.【调用请求对象】读取【请求头】中的参数信息,得到用户注册的信息
        userName = request.getParameter("userName");   //注意getParameter()方法中传的参数名一定要和
        password = request.getParameter("password"); //html中form表单里面<input>的name属性的值一致!
        sex = request.getParameter("sex");
        email = request.getParameter("email");

        //2.【调用UserDao】将用户信息填充到INSERT命令中,并借助JDBC规范发送到数据库服务器
        user = new Users(null,userName,password,sex,email);//第一个参数为null,因为用户不用填这个,id是序号自增的
        result= dao.add(user);

        //3.【调用响应对象】将【处理结果】以二进制形式写到【响应体】中
        response.setContentType("text/html;charset=utf-8");
        out = response.getWriter();
        if(result){ //如果result的值为true
            out.print("<font style='color:red;font-size:40px;'>用户信息注册成功</font>");
        }else{
            out.print("<font style='color:red;font-size:40px;'>用户信息注册失败</font>");
        }
    }
    // doGet执行完毕之后,Tomcat负责 销毁【请求对象】和【响应对象】
    // Tomcat负责将【Http响应协议包】发送到 发起请求的【浏览器】上面
    // 【浏览器】根据【响应头】中的【content-type】指定编译器对【响应体】的二进制内容进行编译
    //【浏览器】将编译后的结果在 浏览器窗口中展示给用户——【结束】
}

(4)
在这里插入图片描述
在这里插入图片描述

3、查询

(1)查看所有用户信息-流程图:【008-查看所有用户信息-流程图.png】
(2)UserFindServlet的开发
(3)启动tomcat、测试

(1)
在这里插入图片描述
(2)

public class UserFindServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        UserDao dao = new UserDao();
        PrintWriter out = null;

        //1、【调用UserDao】将查询命令发送到数据库服务器上,得到所有用户信息【List】
        List<Users> usersList = dao.findAll();

        //2、【调用响应对象】将用户信息结合<table>标签命令以二进制的形式写入到【响应体】中
        response.setContentType("text/html;charset=utf-8");
        out = response.getWriter();
        out.print("<table border='2' align='center'>");
        out.print("<tr>");
        out.print("<td>用户编号</td>");
        out.print("<td>用户姓名</td>");
        out.print("<td>用户密码</td>");
        out.print("<td>性别</td>");
        out.print("<td>邮箱</td>");
        out.print("<td>操作</td>");
        out.print("</tr>");
        for (Users user : usersList) {
            out.print("<tr>");
            out.print("<td>" + user.getUserId() + "</td>");
            out.print("<td>" + user.getUserName() + "</td>");
            out.print("<td>******</td>");
            out.print("<td>" + user.getSex() + "</td>");
            out.print("<td>" + user.getEmail() + "</td>");
            out.print("<td><a href='/myWeb/user/delete?userId=" + user.getUserId() + "'>删除用户</a></td>");
            out.print("</tr>");
        }
        out.print("</table>");

    }
}

(3)
在这里插入图片描述

4、【导航页面】!!!

< frameset >标签
    1) < frameset>标签:定义一个框架集,它被用来组织多个窗口。每个窗口都是一个独立的html界面。
    2) < frameset>有两个参数,rows和cols。
        rows:行的数目和尺寸。
        cols:列的数目和尺寸。
      要特别注意的:< frameset>中只能用一个参数。要么是 rows,要么是 cols,不能同时定义。
    3) < frameset>< /frameset>和< body>< /body>不能一起使用,即不能出现在同一个html页面中。
    4) 用< frameset>标签前要将前言中的"DTD"改为"Frameset DTD"。

frameset中怎么实现 点击左边窗体时,在右面窗体中显示相应内容 ?
    在left.html里面的超链接,添加target属性,属性值为右窗口name值
    < a href=“/myWeb/user_Add.html” target=“右窗口name值”>用户信息注册< /a>

(1)index.html:

<html>
<head>
    <meta charset="UTF-8">
    <title>导航页面</title>
</head>

<frameset rows="15%,85%">

    <frame name="top" src="/myWeb/top.html">

    <frameset cols="15%,85%">
        <frame name="left" src="/myWeb/left.html">
        <frame name="right">
    </frameset>

</frameset>

</html>

(2)top.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body style="background-color: green">
        <center>
            <font style="color:red;font-size: 40px">在线考试管理系统</font>
        </center>
</body>
</html>	

(3)left.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
        <ul>
            <li>用户信息管理
                <ol>           <!-- target="right"表示,在name值为“right”的窗口(右窗口)打开-->
                    <li><a href="/myWeb/user_Add.html" target="right">用户信息注册</a></li>
                    <li><a href="/myWeb/user/find" target="right">用户信息查询</a></li>
                </ol>
            </li>
            <li>试题信息管理</li>
            <li>考试信息管理</li>
        </ul>
</body>
</html>

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5、删除

UserDeleteServlet的开发

public class UserDeleteServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String userId = null;
        UserDao dao = new UserDao();
        boolean result = false;
        PrintWriter out = null;

        //1.【调用请求对象】读取【请求头】参数(用户编号)
        userId = request.getParameter("userId");

        //2.【调用UserDao】将用户编号填充到delete命令,并发送到数据库服务器
        result = dao.delete(userId);

        //3.【调用响应对象】将处理结果以二进制写入到【响应体】,交给浏览器
        response.setContentType("text/html;charset=utf-8");
        out = response.getWriter();
        if (result){
            out.print("<font style='color:red;font-size:40px;'>用户信息删除成功</font>");
        }else{
            out.print("<font style='color:red;font-size:40px;'>用户信息删除失败</font>");
        }
    }
}	

6、登录验证

(1)【009-用户登录-流程图.png】
(2) login.html的开发
(3) LoginServlet的开发
(4) login_error.html的开发

(1)
在这里插入图片描述
(2)

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

<center>
    <form action="/myWeb/login" method="post">
        <table border="1">
            <tr>
                <td>登录名</td>
                <td><input type="text" name="userName"/></td>
            </tr>
            <tr>
                <td>密码</td>
                <td><input type="password" name="password"/></td>
            </tr>
            <tr>
                <td><input type="submit" value="登录"/></td>
                <td><input type="reset" /></td>
            </tr>
        </table>
    </form>
</center>

</body>
</html>

(3)

public class LoginServlet extends HttpServlet {

    //post
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String userName, password;
        UserDao dao = new UserDao();
        boolean result = false;
        //1. 通知【请求对象】使用utf-8对【请求体】中的内容进行重新编辑
        request.setCharacterEncoding("utf-8");
        //2. 调用【请求对象】读取【请求体】中参数的信息
        userName = request.getParameter("userName");
        password = request.getParameter("password");
        //3.调用【UserDao】类将查询验证命令 推送给 数据库服务器
        result = dao.login(userName, password);
        //4. 调用【响应对象】,根据验证结果,将不同的【资源文件地址】 写入【响应头】
        if (result) { //result为true,用户存在
            response.sendRedirect("/myWeb/index.html");
        } else {
            response.sendRedirect("/myWeb/login_error.html");
        }
    }
}

(4)

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

<center>
    <font style='color:red;font-size: 30px;'>登录信息不存在,请重新登录</font>
    <form action="/myWeb/login" method="post">
        <table border="1">
            <tr>
                <td>登录名</td>
                <td><input type="text" name="userName"/></td>
            </tr>
            <tr>
                <td>密码</td>
                <td><input type="password" name="password"/></td>
            </tr>
            <tr>
                <td><input type="submit" value="登录"/></td>
                <td><input type="reset" /></td>
            </tr>
        </table>
    </form>
</center>

</body>
</html>

在这里插入图片描述

八、欢迎资源文件

    1.前提:
        用户可以记住网站名,但是不会记住网站资源文件名

    2.默认欢迎资源文件:

	用户发送了一个针对某个网站的【默认请求】时,
	此时由Http服务器自动从当前网站返回的资源文件

	正常请求: http://localhost:8080/myWeb/index.html

	默认请求: http://localhost:8080/myWeb/

3.Tomcat对于默认欢迎资源文件定位规则

	1)规则位置:Tomcat安装位置/conf/web.xml

	2)规则命令:
				<welcome-file-list>
                       <welcome-file>index.html</welcome-file>
                       <welcome-file>index.htm</welcome-file>
                       <welcome-file>index.jsp</welcome-file>
                 </welcome-file-list>

4.设置当前网站的默认欢迎资源文件规则

	1) 规则位置:  网站/web/WEB-INF/web.xml

	2) 规则命令:  
				<welcome-file-list>
					<welcome-file>index.html</welcome-file>
					<welcome-file>login.html</welcome-file>
					<welcome-file>user/find</welcome-file>   <!--动态资源文件-->
		      </welcome-file-list>

	3) 可以设置多个,会依次寻找,都找不到会报404
	4) 动态资源文件(Servlet接口实现类)也可以设为欢迎页面,直接写它的别名就可以,但不要开头的斜杠
	5) 当网站设置自定义默认文件定位规则,此时Tomcat自带定位规则将失效

九、Http状态码

1.介绍:
	1) 由三位数字组成的一个符号。

	2) Http服务器在推送响应包之前,根据本次请求处理情况,
	   将Http状态码写入到响应包中【状态行】上

	3) 如果Http服务器针对本次请求,返回了对应的资源文件。
	   通过Http状态码通知浏览器应该如何处理这个结果

	   如果Http服务器针对本次请求,无法返回对应的资源文件,
	   通过Http状态码向浏览器解释不能提供服务的原因

2.分类:
	1)组成  100---599;分为5个大类
	
	2)1XX :
		最有特征 100; 通知浏览器本次返回的资源文件,
		并不是一个独立的资源文件,需要浏览器在接收响应包之后,
		继续向Http服务器索要依赖的其他资源文件

	3) 2XX:
		最有特征 200,通知浏览器本次返回的资源文件,
		是一个完整独立资源文件,浏览器在接收到之后,
		不需要索要其他关联文件

	4)3xx:
		最有特征 302,通知浏览器本次返回的不是一个资源文件内容,
		而是一个资源文件地址,需要浏览器根据这个地址
		自动发起请求 来索要这个资源文件

		response.sendRedirect("资源文件地址")写入到响应头中
		location
		而这个行为导致Tomcat将302状态码写入到状态行

	5)4XX:
		404: 通知浏览器,由于在服务端 没有定位到 被访问的资源文件,
		     因此无法提供帮助

		     例如地址栏的大小写写错了等等

		405:通知浏览器,在服务端已经 定位到 被访问的资源文件(Servlet),
		     但是这个Servlet对于浏览器采用的 请求方式不能处理

		     例如:通过在地址栏上输入url(get请求方式)访问一个动态资源,
		     而该动态资源中如果没有doGet方法的话,就会报405

	6)5xx:
		500:通知浏览器,在服务端已经定位到被访问的资源文件(Servlet),
		    这个Servlet也可以接收浏览器采用请求方式,
		    但是Servlet在处理请求期间,由于Java异常导致处理失败。
		    (例如doGet方法里面出现了空指针异常)

		------------------------------------------------------------------------------------------------------------------
		public class Servlet1 extends HttpServlet {

		    protected void doGet(...)...{

				Map map = new HashMap(); //新建一个map集合,里面什么也没有

				int num = (int) map.get("key1"); //浏览器访问该servlet时,报500,原因是这一行抛NullPointerException
			
				// Integer num = (Integer) map.get("key1"); //这样可以 【避免这里的空指针异常】《--------------
		    }
		}
		------------------------------------------------------------------------------------------------------------------

		【int型为null问题】
			1、不能把null赋值给基本数据类型,否则编译器报错:
				// int a = null; //编译器直接报错

			2、可以把null赋值给所有引用类型(它们的默认值也都是null):
				Integer b = null;
				System.out.println(b);// 控制台输出:null

			3、将值为null的包装类 拆箱为基本类型时会抛出空指针异常,如:
				Integer isNull = null;
				int i = isNull; //编译时不会报错,运行后在这一行抛NullPointerException

3.总结:
	1XX——————服务器收到请求,需要请求者继续操作   
	2XX——————成功 
	3XX——————重定向 
	4XX——————客户端错误  !!!
	5XX——————服务器错误  !!!

【关于新建一个Servlet】

先建好一个web类型的项目:
	【File】——> 【New】——> 【Module】
	点击左侧第二个【Java Enterprise】即Java企业级应用(Java EE)
	再勾选中间的【Web Application】
	点击【Next】--给这个Module(也就是我们这个网站)命名 ——> 【Finish】
	(虽然给网站命名不能为中文,但是我们可以在发布给Tomcat的时候给起一个别名)

然后在src下面新建一个Servlet接口实现类:
	右键【src】——> 【New】——> 【Servlet】 ——> 给Servlet命名,然后要注意下面一个地方:
	如果选中了【Create Java EE6 annotated class】,会默认创建带上注解的Serlvet,
	就不会自动在web.xml文件中自动去配置相关信息了

说明下:JDK8下支持3种Selvet

	1.注解一
		@WebServlet("/Hello")

	2.注解二
		@WebServlet(name = "HelloServlet", urlPatterns = {"/Hello"})

	3.非注解,传统的XML模式
		<servlet>
			<servlet-name>HelloServlet</servlet-name>
			<servlet-class>com.w1.HelloServlet</servlet-class>
		</servlet>
		<servlet-mapping>
			<servlet-name>HelloServlet</servlet-name>
			<url-pattern>/Hello</url-pattern>
		</servlet-mapping>

十、多个Servlet之间调用规则

	1.前提条件:
		某些来自于浏览器发送请求,往往需要服务端中多个Servlet协同处理。
		但是浏览器一次只能访问一个Servlet,导致用户需要手动通过浏览器
		发起多次请求才能得到服务。
		这样增加用户获得服务难度,导致用户放弃访问当前网站

	2.提高用户使用感受规则:

		无论本次请求涉及到多少个Servlet,用户只需要【手动】通知浏览器
		发起一次请求即可

	3.多个Servlet之间调用规则:

		1)【重定向】解决方案

		2)【请求转发】解决方案

十一、重定向

1.工作原理: 
	用户第一次通过【手动方式】通知浏览器访问OneServlet。
	OneServlet工作完毕后,通过【响应对象】将TwoServlet地址写入
	到[响应头]的location属性中,导致Tomcat将302状态码写入到[状态行]。

	在浏览器接收到响应包之后,会读取到302状态。此时浏览器
	【自动】根据响应头中location属性地址发起【第二次请求】,
	访问TwoServlet去完成请求中剩余任务
	
	【010-重定向 原理.png】

2.实现命令:
	【   response.sendRedirect("请求地址"); 】
	将地址写入到响应包中响应头中location属性

3.特征:
	1) 请求地址:
		既可以把【当前网站】的资源文件地址发送给浏览器 (/网站名/资源文件名)
		也可以把【其他网站】的资源文件地址发送给浏览器(http://ip地址:端口号/网站名/资源文件名)
	
	2) 请求方式:
		重定向解决方案中,通过【地址栏】通知浏览器发起下一次请求,
		因此:
		通过重定向解决方案调用的资源文件接收的请求方式一定是【GET】

	3) 请求次数
		浏览器 发送【至少两次请求】,但是只有第一次请求是用户手动发送。
		后续请求都是浏览器自动发送的。

4.缺点:
	重定向解决方案需要在浏览器与服务器之间进行多次往返,
	大量时间消耗在往返次数上,增加了用户等待服务时间。。。wait。。slow。。。

在这里插入图片描述

十二、请求转发

1.工作原理:   
	用户第一次通过【手动方式】要求浏览器访问OneServlet。
	OneServlet工作完毕后,通过当前的【请求对象】代替浏览器
	向Tomcat发送请求,申请调用TwoServlet。
	Tomcat在接收到这个请求之后,自动调用TwoServlet来
	完成剩余任务
	
	【011-请求转发 原理.png】

2.实现命令: 请求对象代替浏览器向Tomcat发送请求

	  //1.通过当前请求对象生成【资源文件申请报告】对象
	   RequestDispatcher  report = request.getRequestDispatcher("/资源文件名");  // 一定要以"/"为开头!!!
	  //2.将该对象【发送】给Tomcat
	   report.forward(request,response);
	
	//合并:
	request.getRequestDispatcher("/资源文件名").forward(request,response);

3.特征:
	1)请求地址
		只能向Tomcat服务器申请调用【当前网站下】资源文件地址
		request.getRequestDispathcer("/资源文件名")         ****不要写网站名****

	2)请求方式
		在请求转发过程中,浏览器只发送一个了个Http请求协议包。
		参与本次请求的所有Servlet共享同一个请求协议包,因此
		这些Servlet接收的请求方式【与最初浏览器发送的请求方式保持一致】

	3) 请求次数
		在请求转发过程中,浏览器只发送【一次请求】

4.优点:
	1)无论本次请求涉及到多少个Servlet, 用户只需要手动通过浏览器发送一次请求

	2) Servlet之间调用发生在服务端计算机上,节省服务端与浏览器之间往返次数,
	   增加处理服务速度     ——————>quickly!!!

在这里插入图片描述

【总结:转发和重定向】

  1. 转发是在服务器端完成的,重定向是在客户端发生的;
  2. 转发的速度快,重定向速度慢;
  3. 转发是同一次请求,重定向是两次请求;
  4. 转发地址栏没有变化,重定向地址栏有变化;
  5. 转发必须是在同一台服务器下完成,重定向可以在不同的服务器下完成。

十三、多个Servlet之间实现数据共享

1.数据共享:OneServlet工作完毕后,将产生数据交给TwoServlet来使用

2.Servlet规范中提供四种数据共享方案:

	(1)ServletContext接口

	(2)Cookie类

	(3)HttpSession接口

	(4)HttpServletRequest接口

	四种方案存储数据,都类似于往Map集合中存,一次存一个键值对:【key,value】
	其中三种接口实现数据共享的方法类似。

十四、ServletContext接口 —————>数据共享

1.介绍:
	1)来自于Servlet规范中一个接口。在Tomcat中存在于servlet-api.jar
	    其实现类由Http服务器提供。在Tomcat中负责提供这个接口实现类。

	2)如果两个Servlet来自于同一个网站。
	   可通过网站的ServletContext实例对象实现 数据共享

	3)开发人员习惯于将ServletContext对象称为【全局作用域对象】
		Context翻译:上下文

2.工作原理:
	每一个网站都存在一个【全局作用域对象】。这个全局作用域对象【相当于】一个【Map】
	当前网站中 OneServlet  可以将一个数据 存入到全局作用域对象,
	当前网站中 其他Servlet 此时都可以从全局作用域对象 得到这个数据进行使用。
	
	【012-全局作用域对象.png】

3.全局作用域对象生命周期:

	1)在Http服务器启动过程中,为当前网站在内存中
	   【自动创建】一个全局作用域对象

	2)在Http服务器运行期间时,一个网站只有一个全局作用域对象。【全局唯一】

	3)在Http服务器运行期间,全局作用域对象一直处于【存活】状态

	4)在Http服务器准备关闭时,负责将当前网站中全局作用域对象进行
	   【销毁处理】

	*****全局作用域对象的生命周期贯穿网站整个运行期间***

4.命令实现: 
	【同一个网站】的OneServlet将数据共享给TwoServlet:

	 OneServlet{		 
	    public void doGet(HttpServletRequest request,HttpServletResponse response){
	     
			//1.通过【请求对象】向Tomcat索要当前网站中【全局作用域对象】
			ServletContext application = request.getServletContext();

			//2.将数据添加到全局作用域对象,作为【共享数据】
			application.setAttribute("key1",数据)		    
	    }		 
	 }

	 TwoServlet{		 
	   public void doGet(HttpServletRequest request,HttpServletResponse response){
	   
			//1.通过【请求对象】向Tomcat索要当前网站中【全局作用域对象】
			ServletContext application = request.getServletContext();

			//2.从全局作用域对象得到指定关键字对应的【数据】
			Object 数据 =  application.getAttribute("key1");
	   }		 
	 }

在这里插入图片描述

十五、Cookie类 ——————>数据共享

1.介绍:
	1) Cookie来自于Servlet规范中一个工具类,存在于Tomcat提供servlet-api.jar中

	2) 如果两个Servlet来自于同一个网站,并且为同一个浏览器/用户提供服务,
	   此时可借助于Cookie对象进行数据共享

	3) Cookie存放当前用户的私人数据,在共享数据过程中提高服务质量

	4) 在现实生活场景中,Cookie相当于用户在服务端得到【会员卡】

2.原理:
	用户通过浏览器第一次向MyWeb网站发送请求申请 LoginServlet。
	登录成功后 创建一个Cookie对象,存储与当前用户相关数据,
	LoginServlet 工作完毕后,【将Cookie写入到响应头】交还给当前浏览器。

	浏览器收到响应包之后,将cookie存储在浏览器的缓存中。

	一段时间之后,
	用户通过【同一个浏览器】再次向【myWeb网站】发送请求,这时申请 StudentListServlet 时,
	【浏览器需要无条件的将myWeb网站之前推送过来的Cookie,写入到请求头】发送过去,
	此时 StudentListServlet 在运行时,就可以通过读取请求头中cookie中信息,得到
	LoginServlet提供的共享数据
	
	【013-Cookie原理.png】

3.命令实现:  
	【同一个网站】 OneServlet 与  TwoServlet 借助于Cookie实现数据共享:
		
	(1)浏览器/用户 -----------------> 访问 OneServlet   

	OneServlet{
	    public void doGet(HttpServletRequest request,HttpServletResponse response){
	    
		    //1.创建一个cookie对象,保存共享数据(当前用户数据)
		    Cookie card1 = new Cookie("userName","mike");
		    Cookie card2 = new Cookie("money","100");

		    /*	cookie相当于一个Map集合.
			但一个cookie中只能存放一个键值对.
			这个键值对的key与value只能是 String.
			键值对中,key 不能是中文.
		    */

		    //2. 将cookie写入到响应头,交给浏览器
		    response.addCookie(card1);
		    response.addCookie(card2)
	    }			
	}

	(2)浏览器/用户  <--------------响应包
						【状态行:状态码200】
						【响应头:Cookie: userName=mike; money=100】
						【空白行:         】
						【响应体:处理结果】

	(3)浏览器/用户 ------------------------> 访问 TwoServlet
				请求包	【请求行: url:/myWeb/two ; method:get	】
					    【请求头:
					    		请求参数:xxxx
					    		Cookie  :  userName=mike; money=100
																】
						【空白行:								】
						【请求体:								】
	TwoServlet{
	 
	    public void doGet(HttpServletRequest request,HttpServletResponse resp){
	    
		 //1.调用请求对象从请求头得到浏览器返回的Cookie
		  Cookie  cookieArray[] = request.getCookies();
		 //2.循环遍历数据得到每一个cookie的key 与 value
		  for(Cookie card:cookieArray){
			  String key =   card.getName();   //第一轮 读取key  "userName"	 |第二轮 读取key  "money" 
			  Strign value = card.getValue();  //	    读取value "mike"	     |	  读取value "100" 
			  提供较好的服务。。。。。。。。
		  }
	    }
	 }

4.Cookie销毁时机:

	1.在默认情况下,Cookie 对象存放在浏览器的缓存中。
	  因此只要浏览器关闭,浏览器的缓存会自动清空,
	  Cookie 对象就被销毁掉

	2.在手动设置情况下,可以要求浏览器将接收的Cookie存放在客户端计算机上硬盘上,
	   同时需要指定 Cookie 在硬盘上存活时间。在存活时间范围内,
	   关闭浏览器、关闭客户端计算机、关闭服务器,都不会导致Cookie被销毁。
	   在存活时间到达时,Cookie自动从硬盘上被删除。  ————————【十天内免登录】
	
		Cookie cookie = new Cookie("name","tom");
		cookie.setMaxAge(60); //cookie对象在硬盘上存活1分钟

【Cookie应用实例——订餐会员卡】	、【014-Cookie模拟点餐.png】

在这里插入图片描述
在这里插入图片描述

十六、HttpSession接口 ——————>数据共享

1.介绍:
	1)HttpSession接口来自于Servlet规范下一个接口。存在于Tomcat中servlet-api.jar。
	   其实现类由Http服务器提供。在Tomcat中负责提供这个接口实现类。

	2)如果两个Servlet来自于同一个网站,并且为同一个浏览器/用户提供服务,此时
           可借助于HttpSession对象进行数据共享

	3)开发人员习惯于将HttpSession接口修饰对象称为【会话作用域对象】
	
	【015-HttpSession原理.png】

2.HttpSession 与  Cookie 区别:【面试题】

	1)存储位置:    一个在地下,一个在天上
			Cookie:存放在【客户端】计算机(浏览器内存 或 硬盘)
			HttpSession:存放在【服务端】计算机内存

	2)数据类型:
			Cookie对象存储共享数据类型只能是String
			HttpSession对象可以存储任意类型的共享数据Object

	3) 数据数量:
			一个Cookie对象只能存储一个共享数据;
			HttpSession使用map集合存储共享数据,可以存储任意数量的共享数据

	4)参照物:
			Cookie ————>> 客户的【个人会员卡】
			HttpSession ————>> 客户的【个人保险柜】

3.命令实现:   
	同一个网站(myWeb)下OneServlet将数据传递给TwoServlet:

	OneServlet{		      
		public void doGet(HttpServletRequest request,HttpServletResponse response){
		 
			//1.调用请求对象向Tomcat索要当前用户在服务端的私人储物柜
			     HttpSession   session = request.getSession();
			//2.将数据添加到用户私人储物柜
			    session.setAttribute("key1",共享数据)			 
		}		      
	}

	浏览器访问/myWeb中TwoServlet

	TwoServlet{		      
		public void doGet(HttpServletRequest request,HttpServletResponse response){
		       //1.调用请求对象向Tomcat索要当前用户在服务端的私人储物柜
			HttpSession   session = request.getSession();
		       //2.从会话作用域对象得到OneServlet提供的共享数据
			Object 共享数据 = session.getAttribute("key1");
		}		      
	}

4.Http服务器如何将【用户】与【HttpSession】关联起来?

	通过Cookie关联
	【016-HttpSession与Cookie结合.png】

5. getSession() 与 getSession(false)

	 1)getSession():	如果当前用户在服务端已经拥有了自己的私人储物柜.
			     要求tomcat将这个私人储物柜进行返回;
				如果当前用户在服务端尚未拥有自己的私人储物柜
			     要求tocmat为当前用户【创建一个全新的私人储物柜】

	 2) getSession(false):	如果当前用户在服务端已经拥有了自己的私人储物柜.
			     要求tomcat将这个私人储物柜进行返回;
				如果当前用户在服务端尚未拥有自己的私人储物柜
			     此时Tomcat将【不会创建新储物柜】【返回null】

6.HttpSession销毁时机:

	1. 用户与HttpSession关联时使用的Cookie只能存放在浏览器缓存中.
	2. 在浏览器关闭时,意味着用户与他的HttpSession关系被切断
	3. 由于Tomcat无法检测浏览器何时关闭,因此在浏览器关闭时并不会
	   导致Tomcat将浏览器关联的HttpSession进行销毁
	4. 为了解决这个问题,Tomcat为每一个HttpSession对象设置【空闲时间】,默认为30分钟。
	   如果当前HttpSession对象空闲时间达到30分钟,此时Tomcat认为用户已经
	   放弃了自己的HttpSession,此时Tomcat就会销毁掉这个HttpSession

7.HttpSession空闲时间手动设置

	设置Session存活时间常用的 3 种方式:

	(1)session.setMaxInactiveInterval(interval);----通过java代码设置,参数的单位为秒。

	(2)当前网站/web/WEB-INF/web.xml里配置如下信息
		<session-config>
			<session-timeout>时间长度(单位为分钟)</session-timeout>
		</session-config>

	(3)在tomcat/conf/web.xml下加入上面内容

在这里插入图片描述
在这里插入图片描述

十七、HttpServletRequest 接口 ———>数据共享

1.介绍:
	1) 在同一个网站中,如果两个Servlet之间通过【请求转发】方式进行调用,
		request.getRequestDispatcher("/资源文件名").forward(request,response);
	    彼此之间共享同一个[请求协议包]。而一个请求协议包只对应一个请求对象,
	    因此servlet之间【共享同一个请求对象】,
	    此时可以利用这个请求对象在两个Servlet之间实现数据共享

	2) 在请求对象实现Servlet之间数据共享功能时,开发人员将请求对象称为【请求作用域对象】


2.命令实现: 
	OneServlet通过[请求转发]调用TwoServlet时,给TwoServlet提供共享数据:

	OneServlet{
		public void doGet(HttpServletRequest req,HttpServletResponse response){
			//1.将数据添加到【请求作用域对象】中attribute属性
			req.setAttribute("key1",数据);    //数据类型可以任意类型Object
			//2.向Tomcat申请调用TwoServlet
			req.getRequestDispatcher("/two").forward(req,response)
		}
	}

	TwoServlet{
		public void doGet(HttpServletRequest req,HttpServletResponse response){
			//从当前请求对象得到OneServlet写入到共享数据
			Object 数据 = req.getAttribute("key1");
		}
	}

十八、【扩展】监听器接口(Listener)

1.介绍:
	1)【一组】来自于Servlet规范下的接口,共有8个接口。在Tomcat存在于servlet-api.jar包

	2)监听器接口需要由开发人员【亲自实现】,Http服务器提供jar包并没有对应的实现类(不同于之前)

	3)监听器接口用于监控【作用域对象-生命周期变化时刻】以及【作用域对象-共享数据变化时刻】

2.作用域对象:

	1)在Servlet规范中,认为在【服务端内存】中可以在某些条件下为两个Servlet之间提供
	   数据共享方案的对象,被称为【作用域对象】

	2)Servlet规范下作用域对象:
		ServletContext:      全局作用域对象
		HttpSession   :       会话作用域对象
		HttpServletRequest:   请求作用域对象

3.监听器接口实现类的开发步骤:三步

	1)根据监听的实际情况,选择对应的监听器接口进行【实现】

	2)【重写】监听器接口中所声明的 [监听事件处理方法]

	3)在web.xml文件将监听器接口实现类【注册】到Http服务器

		<listener>
			<listener-class>监听器接口实现类的【完整类名】</listener-class>
		</listener>		


八种监听器接口,这里讲解其中两个:

4.ServletContextListener接口:

	1)作用:通过这个接口合法的检测【全局作用域对象】被初始化时刻以及被销毁时刻

	2)监听事件处理方法:
		public void contextInitlized():    在全局作用域对象被Http服务器【初始化】时被调用
		public void contextDestory():      在全局作用域对象被Http服务器【销毁】时触发调用

5.ServletContextAttributeListener接口:

	1) 作用:通过这个接口合法的检测【全局作用域对象】共享数据变化时刻

	2)监听事件处理方法:
		public void contextAdd():	    在全局作用域对象【添加】共享数据时被调用
		public void contextReplaced():	在全局作用域对象【更新】共享数据........
		public void contextRemove():	在全局作用域对象【删除】共享数据........
		
6.全局作用域对象-共享数据变化时刻

	ServletContext application = request.getServletContext();

	application.setAttribute("key1",100); //新增共享数据

	application.setAttribute("key1",200); //更新共享数据

	application.removeAttribute("key1");  //删除共享数据

7、作用:监听器接口可提高程序运行速度【难度较大,争取看懂】
	在之前“【实例】在线考试管理系统”中的 controller/UserAddServlet.java
	通过监听器,看看用户注册到底花了多长时间?如何缩短时间?
	
	添加代码过程:controller/UserAddServlet.java ————> Dao/UserDao.java ————>.....
public class UserAddServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {     
        String userName,password,sex,email;
        Users user = null;
        UserDao dao = new UserDao();
        boolean result =false;
        PrintWriter out = null;

        //1.【调用请求对象】读取【请求头】中的参数信息,得到用户注册的信息
        userName = request.getParameter("userName");  
        password = request.getParameter("password"); 
        sex = request.getParameter("sex");
        email = request.getParameter("email");

        //2.【调用UserDao】将用户信息填充到INSERT命令中,并借助JDBC规范发送到数据库服务器
        user = new Users(null,userName,password,sex,email);//第一个参数为null,因为用户不用填这个,id是序号自增的
        //result= dao.add(user);

  // ---------【UserAddServlet.java 、UserDao.java等等修改部分,是学了【监听器】之后来写的】-----------------------------
        Date starDate = new Date();    //开始时间
        //result= dao.add(user); //调用第一个add方法
        result= dao.add(user,request);//调用【第二个add方法】----------------->>(方法重载)
        Date endDate = new Date();   //结束时间
        System.out.println("执行添加功能耗时:"+(endDate.getTime()-starDate.getTime())+"毫秒");
        //通过多次添加用户,最后趋于稳定的平均值:
        //     调用第一个add方法时: 在12毫秒左右
        //     调用第二个add方法时:在3毫秒左右-----------》》显著提升了运行速度!!!

  // -----------------------------------------------------------------------------------------------------------------


        //3.【调用响应对象】将【处理结果】以二进制形式写到【响应体】中
        response.setContentType("text/html;charset=utf-8");
        out = response.getWriter();
        if(result){ 
            out.print("<font style='color:red;font-size:40px;'>用户信息注册成功</font>");
        }else{
            out.print("<font style='color:red;font-size:40px;'>用户信息注册失败</font>");
        }
    }
}
public class UserDao {

// ----------------------------------------------------------------------------------------------------------------------------------------
    /*       在JDBC规范中,Connection的创建与销毁最花费时间。--------那么如何【提高add方法的速度】?————>不去创建与销毁Connection?!
         ------>在网站启动的时候,预先创建一批Connection,等到add方法运行时,就可以拿一个空闲的Connection来使用,
         当add方法用完之后,不销毁Connection,而是留着,等待add方法下一次使用
         ----->    问题来了,怎么知道Tomcat在什么时候启动?
         在Tomcat被启动的时候,会创建一个【全局作用域对象】!
         而监听器其中的ServletContextListener接口可以检测【全局作用域对象】被创建和销毁的时刻
             1、写一个【监听器接口实现类】(com.yuming.listener.OneListener.java),创建一批Connection
             2、去com.yuming.util包的JdbcUtil类,再写个getConnection()方法(方法重载)
                    ocp原则:对扩展开放,对修改关闭。最好不要修改原来已经写好的方法,尽量扩展--》方法重载
             3、然后在这里再写一个add方法,也是方法重载,里面调用方法重载后的getConnection()方法,就可以【提高add方法的速度】:
    */
    //用户注册(添加)
    public boolean add(Users user, HttpServletRequest request) { //写的第二个add方法----->方法重载
        //1、注册驱动 + 2、获取连接
        Connection conn = JdbcUtil.getConnection(request); //调用第二个JdbcUtil.getConnection方法 ----> 方法重载
        PreparedStatement ps = null;
        int result = 0;
        try {
            //3、获取 预编译的数据库操作对象
            String sql = "insert into users(userName,password,sex,email) values(?,?,?,?)";
            ps = conn.prepareStatement(sql);
            //给?占位符赋值
            ps.setString(1, user.getUserName());
            ps.setString(2, user.getPassword());
            ps.setString(3, user.getSex());
            ps.setString(4, user.getEmail());
            //4、执行sql语句
            result = ps.executeUpdate();
            //5、处理查询结果集(此处没有,因为不是做查询操作,而是添加)
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //6、释放资源
            //JdbcUtil.close(conn, ps, null);
            JdbcUtil.close(null, ps, null);//这个conn也不要销毁,下次调用add方法还要用的,conn在关闭服务器时会在OneListener中销毁
        }
        // 如果从数据库中执行结果返回的是1,则返回true(添加成功),否则为false(添加失败)
        return result == 1 ? true : false;
    }
 // ---------------------------------------------------------------------------------------------------------------------------------------

    //用户注册(添加)
    public boolean add(Users user) {
        //1、注册驱动 + 2、获取连接
        Connection conn = JdbcUtil.getConnection();
        PreparedStatement ps = null;
        int result = 0;
        try {
            //3、获取 预编译的数据库操作对象
            String sql = "insert into users(userName,password,sex,email) values(?,?,?,?)";
            ps = conn.prepareStatement(sql);
            //给?占位符赋值
            ps.setString(1, user.getUserName());
            ps.setString(2, user.getPassword());
            ps.setString(3, user.getSex());
            ps.setString(4, user.getEmail());
            //4、执行sql语句
            result = ps.executeUpdate();
            //5、处理查询结果集(此处没有,因为不是做查询操作,而是添加)
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //6、释放资源
            JdbcUtil.close(conn, ps, null);
        }
        // 如果从数据库中执行结果返回的是1,则返回true(添加成功),否则为false(添加失败)
        return result == 1 ? true : false;
    }
    
    //其他方法......  
    //public List findAll() {}   
    //public boolean delete(String userId) {} 
    //public boolean login(String userName, String password) {}
}

创建监听器接口实现类:

public class OneListener implements ServletContextListener {
    //在Tomcat启动的时候,预先创建20个Connection, 在UserDao.add方法执行时,
    // 将事先创建好的Connection交给add方法
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        Connection conn = null;
        Map map = new HashMap();
        for (int i = 0; i < 20; i++) {
            conn = JdbcUtil.getConnection();
            System.out.println("创建了:" + conn);
            map.put(conn, true); //true表示这个通道处于空闲状态,false表示通道正在占用
        }
        //为了在Http服务器运行期间,保证20个Connection一直可以使用,将connection保存
        //向Tomcat索要当前网站中【全局作用域对象】
        //ServletContext application = request.getServletContext();//不行,没有request对象
        ServletContext application = sce.getServletContext(); //在本方法内,有一个参数ServletContextEvent sce
        application.setAttribute("key1", map);

    }//这个方法执行完之后,局部变量map就被销毁了

    //在Http服务器关闭的时候,将Connection释放掉
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        ServletContext application = sce.getServletContext(); //在本方法内,有一个参数ServletContextEvent sce
        Map map = (Map) application.getAttribute("key1");
        //map.keySet(); //把map集合的key部分(也就是所有的Connection对象)取出来放到一个Set集合中
        //通过集合对象的迭代器对象Iterator,进行遍历,销毁各个Connection对象
        Iterator it = map.keySet().iterator();
        while (it.hasNext()) {
            Connection conn = (Connection) it.next();
            if (conn != null) {
                System.out.println(conn + "被销毁了");
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}//再去到web.xml文件将该【监听器接口实现类】注册到Http服务器

web.xml 添加相关信息

<!--注册 监听器接口实现类 到Http服务器-->
<listener>
    <listener-class>com.yuming.listener.OneListener</listener-class>
</listener>

jdbc连接工具类方法的添加:

public class JdbcUtil {

// ------------------通过【全局作用域对象】得到Connection的方法(和下面的getConnection方法构成方法重载)----------
    public static Connection getConnection(HttpServletRequest request) {
        //1、通过【请求对象】向Tomcat索要当前网站中【全局作用域对象】
        ServletContext application = request.getServletContext();
        //2、从全局作用域对象中得到存储Connection的集合:map集合
        Map map = (Map) application.getAttribute("key1");
        //3、通过集合对象的迭代器对象Iterator,进行遍历
        Iterator it = map.keySet().iterator();
        Connection conn = null;
        while (it.hasNext()) {
            conn = (Connection) it.next();
            boolean b = (boolean) map.get(conn);
            if (b == true) { //说明该Connection对象处于空闲状态 ,可以用,退出for循环
                map.put(conn,false); //先把这个connection占用,以免其他被占用
                break;
            }
        }
        return conn;
    }
// -----------------通过【全局作用域对象】得到Connection的方法(方法重载)----------------------------------------

    /**
     * 工具类当中的构造方法都是私有的,为了防止别人new对象
     * 因为工具类当中的方法都是静态的,不需要new对象,直接采用类名调用: 类名.方法名
     */
    private JdbcUtil() {
    }

    //静态代码块,在类加载时执行,且只执行一次
    static {
        try {
            //注册驱动
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取数据库连接对象
     *
     * @return 连接对象
     * @throws SQLException
     */
    public static Connection getConnection() {
        Connection conn = null;
        try {
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/zt", "root", "201705375");
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }

    /**
     * 释放资源
     *
     * @param conn 连接对象
     * @param stmt 数据库操作对象
     * @param rs   结果集
     */
    public static void close(Connection conn, Statement stmt, ResultSet rs) {
        //方法参数中选择Statement没有选择PreparedStatement,是因为Statement是父接口,更通用,
        // 即使未来传过来的是PreparedStatement,也会自动向下转型

        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (stmt != null) {
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

十九、【扩展】过滤器接口(Filter)

1.介绍:
	1)来自于Servlet规范下的一个接口,在Tomcat中存在于servlet-api.jar包

	2)Filter接口实现类由【开发人员负责提供】,Http服务器不负责提供

	3) Filter接口在Http服务器调用资源文件之前,对Http服务器进行拦截

2.具体作用:
	1)拦截Http服务器,帮助Http服务器检测当前请求合法性

	2)拦截Http服务器,对当前请求进行增强操作

3.Filter接口实现类开发步骤:三步

	1)【创建】一个Java类,实现Filter接口

	2)【重写】 Filter接口中 doFilter方法

	3)web.xml将过滤器接口实现类【注册】到Http服务器

		<!--1、将过滤器类路径地址交给Tomcat-->
		<filter>
			<filter-name>OneFilter</filter-name>
			<filter-class>com.yuming.filter.OneFilter</filter-class>
		</filter>

		<!--2、通知Tomcat在调用何种资源文件时需要被当前过滤器拦截-->
		<filter-mapping>
			<filter-name>OneFilter</filter-name>
			<url-pattern>/car.jpg</url-pattern>
		</filter-mapping>

4.Filter拦截地址格式

	1) 命令格式:
		<filter-mapping>
			<filter-name>oneFilter</filter-name>
			<url-pattern>拦截地址</url-pattern>
		</filter-mapping>

	2) 命令作用:
		拦截地址通知Tomcat在调用何种资源文件之前需要调用OneFilter过滤进行拦截

	3)举例:
		要求Tomcat在调用某一个具体文件之前,来调用OneFilter拦截
			<url-pattern>/img/mm.jpg</url-pattern>

		要求Tomcat在调用某一个文件夹下所有的资源文件之前,来调用OneFilter拦截
			<url-pattern>/img/*</url-pattern>

		要求Tomcat在调用任意文件夹下某种类型文件之前,来调用OneFilter拦截
			<url-pattern>*.jpg</url-pattern>

		要求Tomcat在调用网站中任意文件时,来调用OneFilter拦截
			<url-pattern>/*</url-pattern>

5、过滤器对拦截的请求进行增强操作
	
	比如说:所有的以post方式发送请求的,在doPost方法中,在获取参数前,需要对二进制的参数信息进行重新解码:
		      request.setCharacterEncoding("utf-8");
		否则,如果参数中含有中文的话,会出现乱码!!
		那么,如果一个项目中有100个Servlet,其中有非常多的doPost方法,每次都要设置解码格式吗?
		不用,使用过滤器进行增强操作,直接把这一工作放到过滤器中,然后在每次访问这些Servlet时,提前进行一个拦截就好。
	
	【017-过滤器-进行增强操作.png】

在这里插入图片描述

过滤器防止用户登录恶意行为

      用户登录恶意行为:用户不经过登录页面,直接在地址栏输出登录之后的页面,而达到进入其他页面的目的

  • 第一种方法:令牌机制
    使用【令牌机制】,这里以getSession() 与 getSession(false)为例。(这两个方法在之前的HttpSession接口那里讲过)
    在这里插入图片描述
    还是以之前“【实例】在线考试管理系统”为例:

     	1、正确流程:			
     		login.html ————> LoginServlet(登录验证):
     				if(确实存在该用户){
     					response.sendRedirect("/myWeb/index.html"); // 用户主页面,然后可以点击【查询所有用户信息】
     				}else(不存在该用户) {
     					response.sendRedirect("/myWeb/login_error.html"); //类似于login.html,【提示错误信息,重新登录】
     				}
     	
     	2、恶意登录:
     		直接访问用户主页面:
     		即http://localhost:8080/myWeb/index.html,然后可以点击查询所有用户信息【UserFindServlet】
    
     	3、进行LoginServlet、UserFindServlet相关修改,达到防止用户登录恶意行为
    
     		login.html ————> LoginServlet(登录验证):
     				if(确实存在该用户){
    
     					【在判定来访用户身份合法之后,通过请求对象向Tomcat申请为当前用户申请一个HttpSession】!!!!!
     					HttpSession session = request.getSession();
    
     					response.sendRedirect("/myWeb/index.html"); 
     				}else(不存在该用户) {
     					response.sendRedirect("/myWeb/login_error.html");
     				}
    
     		UserFindServlet:
     			protected void doGet(...){
     				
     				【在提供服务之前,先索要当前用户在服务端的HttpSession】
     				HttpSession session = request.getSession(false);
     				【判断】
     				if(session==null){ //说明该用户不是经过登录验证(login.html-->LoginServlet)来到这里的
     				    response.sendRedirect("/myWeb/login_error.html");
     				    return;
     				}
     				
     				【程序运行到这里,说明session不是null,是合法用户!!开始提供下面的服务】
     				服务代码......
     			}
     	
    
     		注:	request.getSession()方法————如果有HttpSession则返回,如果没有则新建一个
     			request.getSession(false)方法————如果有HttpSession则返回,如果没有HttpSession会返回null,而不是新建一个
     			
     	4、这种方式的缺点:
     		1) 增加了开发难度。每一个Servlet里面都要先判断来访者身份合法性
     		2) 不能对静态资源文件进行保护
    
  • 第二种方法:使用过滤器
    在这里插入图片描述还是以之前“【实例】在线考试管理系统”为例:

     	1、还是先修改LoginServlet(和方法一相同)	:
    
     		login.html ————> LoginServlet(登录验证):
     				if(确实存在该用户){
    
     					【在判定来访用户身份合法之后,通过请求对象向Tomcat申请为当前用户申请一个HttpSession】!!!!
     					HttpSession session = request.getSession();
    
     					response.sendRedirect("/myWeb/index.html"); 
     				}else(不存在该用户) {
     					response.sendRedirect("/myWeb/login_error.html");
     				}	
     				
     	2、写一个过滤器【 Filter接口实现类】
     		
     		public class OneFilter implements Filter {
     		    @Override
     		    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
     				//1、拦截后,通过请求对象,索要当前用户在服务端的HttpSession:
    
     				//HttpSession session = servletRequest.getSession(false); //不行
     				//这个getSession是ServletRequest接口 的子接口HttpServletRequest里面的方法,它用不了
     				//解决方法,强制转换
     				HttpServletRequest request = (HttpServletRequest)servletRequest;
     				HttpSession session = request.getSession(false);
    
     				//2、判断来访用户身份的合法性:
     				if(session==null){ //说明该用户不是经过登录验证(login.html-->LoginServlet)来到这里的
     			   	 	//请求转发
     			    	request.getRequestDispatcher("/login_error.html").forward(servletRequest,servletResponse);//提示错误信息,重新登录
     			    	return;
     				}
     				
     				//3、程序运行到这里,说明session不是null,是合法用户, 放行
     				filterChain.doFilter(servletRequest, servletResponse);//放行
    
     		    }
     		}// 记得要去web.xml将该过滤器接口实现类【注册】到Http服务器
     	
     	3、注册到web.xml中
    
     		    <!--注册 【过滤器】接口实现类 到Http服务器-->
     		    <filter>
     					<filter-name>OneFilter</filter-name>
     					<filter-class>com.yuming.filter.OneFilter</filter-class>
     		    </filter>
     		    <filter-mapping>
     					<filter-name>OneFilter</filter-name>
     					<url-pattern>/*</url-pattern>  <!--访问所有文件都要经过过滤器-->
     		    </filter-mapping>
    
     	4、测试结果
     		这个网站太"安全"了!!
     		虽然不能直接访问其他各种动态资源和静态资源了,但是自己正常登录,也不能登录成功了。。。。
    
     		因为登录原来是这样的:login.html ————> LoginServlet(登录验证)————> ...
    
     		而现在,因为在web.xml里面配置的是访问所有文件都要拦截过滤一下,
     		造成登录也被拦截了:
     			login.html ————>OneFilter【拦截】————> LoginServlet(登录验证)
     			因为 【通过请求对象向Tomcat申请为当前用户申请一个HttpSession】,是在LoginServlet
     			里面才创建的HttpSession,但是现在还没到LoginServlet,就被拦截下来了,
     			在OneFilter过滤器中,自然也就没有HttpSession了,所以被拦截了
     	
     	5、改进	:
     		首先可以去参考参考好的门户网站是怎么做的,例如网页的邮箱,登录界面,查看源代码
     		通过ctrl+f,搜索login发现,有61个结果。
    
     		我们应该让过滤器对关于登录的登录页面、登录验证的Servlt、css文件、JavaScript文件等都无条件放行。
     		这些关于登录的文件的命名,也都应该包含login。
    
     		现在去我们的过滤器OneFilter里面修改:
    
     	public class OneFilter implements Filter {
     		@Override
     		public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    
     			HttpServletRequest request = (HttpServletRequest) servletRequest;
    
     			//1.调用请求对象,读取请求包中请求行中URI,了解用户访问的资源文件是谁
     			String uri = request.getRequestURI(); //[/网站名/资源文件名] -----/myWeb/login.htmL  或者  /myWeb/login......
     			//2. 如果本次请求资源文件与登录相关 [login.htmL 或者 LoginServlet],此时应该【无条件放行】
     			if (uri.indexOf("login") != -1 || "/myWeb/".equals(uri)) {  //访问资源名称包含“login”或者访问的是网站的欢迎页面
     			    filterChain.doFilter(servletRequest, servletResponse);
     			    return;
     			}
     			//3.如果本次请求访问的是其他资源文件,需要得到用户在服务端HttpSession
     			HttpSession session = request.getSession(false);
     			if (session != null) {
     			    filterChain.doFilter(servletRequest, servletResponse);
     			    return;
     			}
     			//4.做拒绝请求
     			request.getRequestDispatcher("login_error.html").forward(servletRequest, servletResponse);
     		}
     	}
    

【浏览器缓存】

在有的时候,修改代码后,在浏览器中显示的还是以前的效果,那么可能就是浏览器的缓存的原因了
可以直接换个浏览器访问该页面,就是修改后的效果了。
如果不换浏览器,可以在该页面多刷新几次,或者直接清除浏览器里面的缓存,以谷歌浏览器为例:
	点击浏览器右上角,在【更多工具】中进入【清除浏览数据】,
	在【基本】或【高级】中勾选需要删除的项目,点击清除数据。

第三版 互联网通信流程图

在这里插入图片描述

传送门

上一章:Web后端 - 第二章 - Web服务器之Http网络协议与Tomcat服务器
下一章:Web后端 - 第四章 - JSP

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值