通常来说,Servlet 是宏观上 Servlet规范中的一个具体的接口, Servlet规范中包含一套接口。而 Servlet 接口仅仅是其中之一。
微观地讲,Servlet 是 Servlet接口实现类的一个实例对象,是运行在服务器上的一段 Java小程序, 即 Server Applet,也就是 Servlet 这个单词的来历。 Servlet 的主要功能是根据客户端提交的请求,调用服务器端相关 Java代码,完成对请求的处理与运算。
作用: 1)在Servlet规范中,指定【动态资源文件】开发步骤 2)在Servlet规范中,指定Http服务器调用动态资源文件规则 3)在Servlet规范中,指定Http服务器管理动态资源文件实例对象规则Servlet接口来自于Servlet规范下一个接口,这个接口存在于Http服务器(我这里安装的是Tomcat)提供的jar包中
Tomcat服务器下lib文件有一个servlet-api.jar,存放Servlet接口(javax.servlet.Servlet接口)
Servlet规范中认为,Http服务器能调用的【动态资源文件】必须是一个Servlet接口实现类
class Student{ //不是动态资源文件,Tomcat无权调用 } class Teacher implements Servlet{ //合法动态资源文件,Tomcat有权利调用 Servlet obj = new Teacher(); obj.doGet() }第一步:创建一个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接口实现类的实例对象,只能由Http服务器负责创建。 开发人员不能手动创建Servlet接口实现类的实例对象
在默认的情况下,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> <load−on−startup>30</load−on−startup>,什么作用呢?
标记是否在Http服务器(这里是Tomcat)启动时创建并初始化这个 Servlet 实例, 即是否在 Http服务器启动时调用执行该 Servlet 的无参构造器方法与 init()方法, 而不是在真正访问时才创建。 (1)它的值必须是一个整数。 (2)当值大于等于 0 时,表示容器在启动时就加载并初始化这个 Servlet, 数值越小,该 Servlet的优先级就越高,其被创建的也就越早; (3)当值小于 0 或者没有指定时,则表示该 Servlet 在真正被使用时才会去创建。 (4)当值相同时,容器会自己选择创建顺序。
在Http服务器运行期间,一个Servlet接口实现类只能被创建出一个实例对象
在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】方式启动才有效果
5.1 介绍:
HttpServletResponse接口 来自于Servlet规范中,在Tomcat中存在于servlet-api.jar
HttpServletResponse接口实现类 由Http服务器负责提供
HttpServletResponse接口 负责将doGet/doPost方法执行结果写入到【响应体】交给浏览器
开发人员习惯于将HttpServletResponse接口修饰的对象称为响应对象
5.2 主要功能:
将执行结果以二进制形式写入到【响应体】
设置【响应头】中【content-type】属性值,从而控制浏览器使用 对应编译器将响应体二进制数据编译为【文字,图片,视频,命令】
设置【响应头】中【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将响应包推送给浏览器 }6.1 介绍:
HttpServletRequest接口 来自于Servlet规范中,在Tomcat中存在于servlet-api.jar
HttpServletRequest接口实现类 由Http服务器负责提供
HttpServletRequest接口 负责在doGet/doPost方法运行时读取Http请求协议包中的信息
开发人员习惯于将HttpServletRequest接口修饰的对象称为请求对象
6.2 作用:
可以读取Http请求协议包中【请求行】信息可以读取保存在Http请求协议包中【请求头】(get)或者【请求体】(post)(二进制)中请求参数信息可以代替浏览器向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); }在Http服务器接收到浏览器发送的【Http请求协议包】之后, 自动为当前的【Http请求协议包】生成一个【请求对象request】和一个【响应对象response】
在Http服务器调用doGet/doPost方法时,负责将【请求对象】和【响应对象】作为实参 传递到方法,确保doGet/doPost正确执行
在Http服务器准备推送Http响应协议包之前,负责将本次请求关联的【请求对象】和【响应对象】 销毁
***【请求对象】和【响应对象】生命周期贯穿一次请求的处理过程中 ***【请求对象】和【响应对象】相当于用户在服务端的代言人
任务:在线考试管理系统----用户信息管理模块 子任务: 用户信息注册 用户信息查询 用户信息删除 用户信息更新
准备工作:
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了 } }(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)
(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)
< 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>
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>"); } } }(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>
创建监听器接口实现类:
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(); } } } }用户登录恶意行为:用户不经过登录页面,直接在地址栏输出登录之后的页面,而达到进入其他页面的目的
第一种方法:令牌机制 使用【令牌机制】,这里以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