使用纯servlet做一个单表的CRUD操作–老杜
-
实现步骤
-
第一步:准备一张数据库表(sql脚本)
-
CREATE TABLE `dept` ( `deptno` int(11) NOT NULL, `dname` varchar(255) DEFAULT NULL, `loc` varchar(255) DEFAULT NULL, PRIMARY KEY (`deptno`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8
-
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gm2RN213-1688370861032)(C:\Users\26332\AppData\Roaming\Typora\typora-user-images\1.png)]](/https://i-blog.csdnimg.cn/blog_migrate/1c41996e77b14b92e669b379817898a3.png)
----
第二步:准备一套HTML页面(项目原型)先把页面项目跑通即可,后面会做功能具体实现
-
把HTML页面准备好
-
然后将HTML页面中的链接都能跑通
-
应该设计那些页面呢?
-
新增页面:add.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>新增部门</title> </head> <body> <h1>新增部门</h1> <hr> <form action="list.html" method="get"> 部门编号<input type="text" name="deptno"><br> 部门名称<input type="text" name="dname"><br> 部门位置<input type="text" name="loc"><br> <input type="submit" value="保存"> </form> </body> </html>
-
修改页面:edit.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>修改部门</title> </head> <body> <h1>修改部门</h1> <hr> <form action="list.html" method="get"> 部门编号<input type="text" name="deptno" value="20"><br> 部门名称<input type="text" name="dname" value="销售部"><br> 部门位置<input type="text" name="loc" value="北京"><br> <input type="submit" value="保存"> </form> </body> </html>
-
查看详情页面:detail.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>部门详情</title> </head> <body> <h1>部门详情</h1> <hr> 部门编号:20 <br /> 部门名称:销售部<br /> 部门位置:北京<br /> <input type="button" value="后退" onclick="window.history.back()"> </body> </html>

-
欢迎页面(首页面):index.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>欢迎使用OA系统</title> </head> <body> <a href="list.html">查看部门列表</a> </body> </html> -
列表页面:list.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>部门列表</title> </head> <body> <h1 align="center">部门列表</h1> <hr> <table border="1px" align="center" width="50%"> <tr> <th>序号</th> <th>部门编号</th> <th>部门名称</th> <th>操作</th> </tr> <tr> <td>1</td> <td>10</td> <td>销售部</td> <td> <a href="">删除</a> <a href="edit.html">修改</a> <a href="detail.html">详情</a> </td> </tr> <tr> <td>2</td> <td>20</td> <td>研发部</td> <td> <a href="">删除</a> <a href="edit.html">修改</a> <a href="detail.html">详情</a> </td> </tr> <tr> <td>3</td> <td>30</td> <td>运营部</td> <td> <a href="">删除</a> <a href="edit.html">修改</a> <a href="detail.html">详情</a> </td> </tr> </table> <hr> <a href="add.html">新增部门</a> </body> </html>
-
-
-
第三步:分析我们这个系统包括了哪些功能?
- 什么叫做一个功能?
- 只要这个操作连接了数据库,就表示一个独立的功能
- 包括哪些功能?
- 查看部门列表
- 新增部门
- 删除部门
- 查看部门详细信息
- 跳转到修改页面
- 修改部门
- 什么叫做一个功能?
-
第四步:在IDEA当中搭建开发环境
-
创建一个webapp(给这个webapp添加servlet-api.jar和jsp-api.jar)
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6Tp7EFLf-1688370813816)(C:\Users\26332\AppData\Roaming\Typora\typora-user-images\6.png)]
-
向webapp中添加数据库jar包(MySQL驱动)
- 必须在WEB-INF目录下新建lib目录,然后将MySQL的驱动jar包拷贝到这个lib目录下,这个必须叫lib。
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sq1keVDP-1688370813817)(C:\Users\26332\AppData\Roaming\Typora\typora-user-images\7.png)]
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7lhGjMxq-1688370813817)(C:\Users\26332\AppData\Roaming\Typora\typora-user-images\8.png)]
-
JDBC的工具类
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XM9ynL2H-1688370813817)(C:\Users\26332\AppData\Roaming\Typora\typora-user-images\9.png)]
-
//配置文件信息 driver=con.musql.jdbc.Driver url=jdbc:mysql://localhost:3306/javaweb user=root password=123456 -
package ServletProject.utils; import java.beans.Statement; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ResourceBundle; /* * JDBC的工具类 * */ public class DButil { //静态变量,在类加载时执行 private static ResourceBundle bundle=ResourceBundle.getBundle("resources.jdbc");//绑定资源文件 //根据属性配置文件的key配置value private static String driver=bundle.getString("driver"); private static String url=bundle.getString("url"); private static String user=bundle.getString("user"); private static String password=bundle.getString("password"); static { //注册驱动,执行一次即可,放在静态代码块当中,类加载时执行 try{ //"com.mysql.jdbc.Driver"是链接数据库的驱动,不能写死,因为以后可能会链接Oracle数据库,以防修改Java源代码,保证项目正常。 Class.forName(driver); }catch (ClassNotFoundException e){ e.printStackTrace(); } } //获取数据库链接对象 public static Connection getConnection() throws SQLException { //获取链接 Connection conn= DriverManager.getConnection(url,user,password); return conn; } //释放资源 public static void close(Connection conn, Statement ps, ResultSet rs){ if (rs!=null){ try { rs.close(); }catch (SQLException e){ e.printStackTrace(); } } if (ps!=null){ try { ps.close(); }catch (SQLException e){ e.printStackTrace(); } } if (conn!=null){ try { conn.close(); }catch (SQLException e){ e.printStackTrace(); } } } }
-
-
-
第五步:实现第一个功能:查看部门列表
-
第一:先修改前端欢迎页面的超链接,因为用户先点击的就是这个超链接
-
<!-- 前端超链接发送请求的时候,请求路径以/开始,并且要带着项目名--> <a href="/ServletProject/dept/list">查看部门列表</a>
-
-
第二:编写web.xm文件
<servlet> <servlet-name>list</servlet-name> <servlet-class>????</servlet-class> </servlet> <servlet-mapping> <servlet-name>list</servlet-name> <url-pattern>/dept/list</url-pattern> </servlet-mapping> -
第三:编写DeptListServlet类继承HttpServlet类,然后重写doGet方法
-
package action; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; public class DeptListServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } }
-
-
第四:在DeptListServlet类的doGet方法中链接数据库,查询所有的部门,动态展示部门列表页面
-
分析list.html页面那部分是固定死的,那部分是变化的
-
package action; //查看部门列表功能实现 import ServletProject.utils.DButil; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class DeptListServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out= response.getWriter(); out.print(" <!DOCTYPE html>"); out.print("<html>"); out.print(" <head>"); out.print(" <meta charset='utf-8'>"); out.print(" <title>部门列表</title>"); out.print(" </head>"); out.print(" <body>"); out.print(" <h1 align='center'>部门列表</h1>"); out.print(" <hr>"); out.print(" <table border='1px' align='center' width='50%'>"); out.print(" <tr>"); out.print(" <th>序号</th>"); out.print(" <th>部门编号</th>"); out.print(" <th>部门名称</th>"); out.print(" <th>操作</th>"); out.print(" </tr>"); //连接数据库,查询所有的部门 Connection conn=null; PreparedStatement ps=null; ResultSet rs=null; try { conn=DButil.getConnection(); String sql="select deptno,dname,loc from dept"; ps=conn.prepareStatement(sql); rs= ps.executeQuery(); int i=0; //获取到的数据库数据 while (rs.next()){ String deptno=rs.getString("deptno"); String dname=rs.getString("dname"); String loc=rs.getString("loc"); out.print(" <tr>"); out.print(" <td>"+(++i)+"</td>"); out.print(" <td>"+deptno+"</td>"); out.print(" <td>"+dname+"</td>"); out.print(" <td>"); out.print(" <a href=''>删除</a>"); out.print(" <a href='edit.html'>修改</a>"); out.print(" <a href='detail.html'>详情</a>"); out.print(" </td>"); out.print(" </tr>"); } } catch (SQLException throwables) { throwables.printStackTrace(); }finally { //释放资源 DButil.close(conn,ps,rs); } out.print(" </table>"); out.print(" <hr>"); out.print(" <a href='add.html'>新增部门</a>"); out.print(" </body>"); out.print("</html>"); } } -
现在写完这个功能之后,你会有一种感觉,感觉开发很繁琐,只写servlet代码太麻烦了
-
-
-
第六步:实现查看部门详情功能(从前端往后端一步一步实现)
-
首先要考虑的是,用户点击的是什么?用户点击的东西在哪里?
-
一定要先找到用户点的“详情”在哪里,在Java程序中找到了
-
<a href='/ServletProject/dept/detail'>详情</a> -
详情这里是需要连接数据库的,所以这个超链接点击之后也是需要执行一段Java代码的,所以这里的超链接路径是servlet对象
-
注意:修改路径之后,这个路径是需要加项目名的(前端向后端发送请求需要加项目名)
-
又因为根路径可以动态获取,所以这里可以更改为:
//获取应用的根路径 String contextPath = request.getContextPath(); <a href='"+contextPath+"/dept/detail'>详情</a> -
又又因为详情需要有判断具体是那个部门的详情,所以在发送请求的时候可以把部门编号发送给后端,后端通过数据库来选出相应的部门信息:
<a href='"+contextPath+"/dept/detail?"+deptno+"'>详情</a> -
接下来就需要完成该功能的servlet对象就行了:
-
编写一个DeptDetailServlet继承HttpServlet,重写doGet方法,在该方法中连接数据库,查询相应部门信息
-
package action; import ServletProject.utils.DButil; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class DeptDetailServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取请求中url的部门编号 String deptno=request.getParameter("deptno"); response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.print(" <!DOCTYPE html>"); out.print("<html>"); out.print(" <head>"); out.print(" <meta charset='utf-8'>"); out.print(" <title>部门详情</title>"); out.print(" </head>"); out.print(" <body>"); out.print(" <h1>部门详情</h1>"); out.print(" <hr>"); out.print(" 部门编号:"+deptno+" <br />"); //连接数据库,根据部门编号查询信息 Connection conn=null; PreparedStatement ps=null; ResultSet rs=null; try { conn= DButil.getConnection(); String sql="select deptno,dname,loc from dept where deptno=?"; ps=conn.prepareStatement(sql); ps.setString(1,deptno); rs= ps.executeQuery(); if (rs.next()){ String dname= rs.getString("dname"); String loc=rs.getString("loc"); out.print(" 部门名称:"+dname+"<br />"); out.print(" 部门位置:"+loc+"<br />"); } } catch (SQLException throwables) { throwables.printStackTrace(); }finally { DButil.close(conn,ps,rs); } out.print(" <input type='button' value='后退' οnclick='window.history.back()'>"); out.print(" </body>"); out.print("</html>"); } }
-
-
-
-
第七步:删除功能
-
怎么开始?从哪开始?从前端页面开始,用户点击删除按钮,提示用户是否删除
<a href='javascript:void(0)' onclick='del("+deptno+")'>删除</a> out.print(" <script type='text/javascript'>"); out.print(" function del(dno){"); out.print(" if (window.confirm('亲,删了就不能恢复了哦')){"); out.print(" document.location.href='/ServletProject/dept/delete?deptno='+dno"); out.print(" }"); out.print(" }"); out.print(" </script>"); -
以上的前端程序要写到后端的Java代码当中
-
删除功能servlet对象代码
package action; import ServletProject.utils.DButil; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class DeptDeleteServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String deptno=request.getParameter("deptno"); response.setContentType("text/html"); PrintWriter out = response.getWriter(); int count=0; Connection conn=null; PreparedStatement ps=null; try { conn=DButil.getConnection(); //开启事务 conn.setAutoCommit(false); String sql ="delete from dept where deptno=?"; ps=conn.prepareStatement(sql); ps.setString(1,deptno); //返回值是影响了数据库几行数据的结果 count=ps.executeUpdate(); //事务提交 conn.commit(); } catch (SQLException throwables) { //遇到异常回滚 if (conn!=null){ try { conn.rollback(); } catch (SQLException e) { e.printStackTrace(); } } }finally { DButil.close(conn,ps,null); } if (count==1){ //删除成功,回到列表页面 //目前使用转发 request.getRequestDispatcher("/dept/list").forward(request,response); }else { //删除失败 } } }
-
-
第八步:新增部门
-
package action; import ServletProject.utils.DButil; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class DeptSaveServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取部门信息 //注意乱码问题(tomcat10不会出现) request.setCharacterEncoding("UTF-8"); String deptno=request.getParameter("deptno"); String dname=request.getParameter("dname"); String loc=request.getParameter("loc"); //连接数据库执行插入语句 Connection conn=null; PreparedStatement ps=null; ResultSet rs=null; int count=0; try { conn=DButil.getConnection(); String sql="INSERT INTO dept (deptno, dname, loc) VALUES ('"+deptno+"', '"+dname+"', '"+loc+"')"; ps=conn.prepareStatement(sql); count=ps.executeUpdate(); } catch (SQLException throwables) { throwables.printStackTrace(); }finally { DButil.close(conn,ps,rs); } if (count==1){ //保存成功跳转到列表页面 //转发是一次请求,之前是post请求,后面也是post请求,转发的下一个servlet对象只有doget方法,所以会报405错误 //解决这个错误的暂时的方法是在list对象中写一个dopost方法,该方法调用doget方法 request.getRequestDispatcher("/dept/list").forward(request,response); }else { //保存失败跳转到错误页面 } } }
-
-
第九步:跳转到修改部门的界面
-
package action; import ServletProject.utils.DButil; import jakarta.servlet.RequestDispatcher; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class DeptEditServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取部门编号 String deptno = request.getParameter("deptno"); response.setContentType("text/html"); PrintWriter out = response.getWriter(); Connection conn=null; PreparedStatement ps=null; ResultSet rs=null; out.print(" <!DOCTYPE html>"); out.print("<html>"); out.print(" <head>"); out.print(" <meta charset='utf-8'>"); out.print(" <title>修改部门</title>"); out.print(" </head>"); out.print(" <body>"); out.print(" <h1>修改部门</h1>"); out.print(" <hr>"); out.print(" <form action='/dept/list' method='post'>"); out.print(" 部门编号<input type='text'' name='deptno' value='"+deptno+"'><br>"); try { conn= DButil.getConnection(); String sql="select deptno,dname,loc from dept where deptno=?"; ps=conn.prepareStatement(sql); ps.setString(1,deptno); rs=ps.executeQuery(); if (rs.next()){ String dname=rs.getString("dname"); String loc=rs.getString("loc"); out.print(" 部门名称<input type='text'' name='dname'' value='"+dname+"'><br>"); out.print(" 部门位置<input type='text' name='loc' value='"+loc+"'><br>"); String sql1="INSERT INTO dept (deptno, dname, loc) VALUES ('"+deptno+"', '"+dname+"', '"+loc+"')"; } } catch (SQLException throwables) { throwables.printStackTrace(); }finally { DButil.close(conn,ps,rs); } out.print(" <input type='submit'' value='保存'>"); out.print(" </form>"); out.print(" </body>"); out.print("</html>"); } }
-
-
第十步:修改部门功能实现
package action; import ServletProject.utils.DButil; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class DeptModifyServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); response.setContentType("text/html"); PrintWriter out = response.getWriter(); //获取修改表单的数据 String deptno = request.getParameter("deptno"); String dname = request.getParameter("dname"); String loc = request.getParameter("loc"); //连接数据库,执行更新语句 Connection conn=null; PreparedStatement ps=null; int count=0; try { conn= DButil.getConnection(); String sql="update dept set dname=?,loc=? where deptno=?"; ps=conn.prepareStatement(sql); ps.setString(1,dname); ps.setString(2,loc); ps.setString(3,deptno); count=ps.executeUpdate(); } catch (SQLException throwables) { throwables.printStackTrace(); }finally { DButil.close(conn,ps,null); } if (count==1){ //更新成功,返回list页面 request.getRequestDispatcher("/dept/list").forward(request,response); } } }-
注意犯得一个错误:
String sql="update dept set dname=?,loc=? where deptno=?"; ps=conn.prepareStatement(sql);//这一句要写在下面三句前面,否则无法执行成功,浏览器报500错误 ps.setString(1,dname); ps.setString(2,loc); ps.setString(3,deptno);
-
-
最后的项目结构:![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fpk3riYg-1688370813818)(C:\Users\26332\AppData\Roaming\Typora\typora-user-images\10.png)]](/https://i-blog.csdnimg.cn/blog_migrate/896eae1c43dab9a7db874bfe67a3a7d7.png)
在一个web应用中应该如何完成一个资源的跳转
-
在一web应用中有两种方式完成资源的跳转:
-
转发:在一次请求中,像是通过当前网页加载到要跳转的网页,请求资源不变(张三问李四借钱,李四没钱就向王五借了钱,然后李四把借到的钱再借给张三)
request.getRequestDispatcher("/dept/list").forward(request,response); -
重定向:不在一次请求中,像当前网页告诉web应用它像进入的网页的url,然后web应用再通过该网址加载到目标网页(张三问李四借钱,李四没钱,就告诉张三,王五有钱,然后张三就直接向王五借到钱)
response.sendRedirect(request.getContextPath()+"/dept/list");
-
将项目中的资源跳转修改为合适的跳转方式
- 删除之后,重定向
- 修改之后,重定向
- 保存之后,重定向
Servlet注解,简化配置
-
分析项目中的web.xml文件
- 现在只是一个增删改查的项目,配置文件就需要写很多代码,那么对一个大项目来说,这个配置文件就会很大。
- 并且在web.xml文件中继续Servlet的信息配置,开发效率也比较低,该文件的信息也基本不改变,可不可以直接放到Java代码里面?可以的。
-
Servlet3.0版本之后,推出了各种Servlet基于注解开发
-
优点:开发效率高,不要编写大量配置信息。
-
有一些需要改变的信息,还是要配置到web.xml文件。
XML文件的内容 <servlet> <servlet-name>delete</servlet-name> <servlet-class>action.DeptDeleteServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>delete</servlet-name> <url-pattern>/dept/delete</url-pattern> </servlet-mapping>
-
-
我们的第一个注解:
import jakarta.servlet.annotation.WebServlet;-
在Servlet类上使用:@WebServlet,WebSevlet注解中有哪些属性呢?
-
name属性:用来指定Servlet的名字,等同于delete
-
urlPatterns属性:用来指定Servlet的映射路径,可以指定多个字符串,等同于/dept/delete
-
loadOnStartup属性:用来指定在服务器启动阶段是否加载该Servlet,等同于
-
value属性,等同于urlPatterns,在此以上还可以省略value不写**@WebServlet(“/url”)**
package servlet; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebInitParam; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet(name="hello",urlPatterns = {"/hello1","/hello2"},loadOnStartup = 1, initParams = {@WebInitParam(name="username",value ="root"),@WebInitParam(name = "password",value = "123456")}) public class HelloServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { } }
-
-
注解对象使用格式:
- @注解名称(属性名=属性值,属性名=属性值…)
-
使用模板方法设计模式优化项目
-
上面的注解解决了配置文件的问题,但是现在项目仍然存在一个比较臃肿的问题:
- 一个单表的CRUD,就写了6个Servlet。如果一个复杂的业务系统,这种开发方式,显然会导致类爆炸。
- 怎么解决这个类爆炸问题?可以使用模板方法设计模式。
-
怎么解决类爆炸问题?
- 以前的设计是一个请求一个Servlet类。100个请求对应100个Servlet类,导致类爆炸。
- 可以这样做:一个请求对应一个方法,一个业务对应一个Servlet类。
package action; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import utils.DButil; import java.io.IOException; import java.io.PrintWriter; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @WebServlet({"/dept/list","/dept/save","/dept/edit","/dept/detail","/dept/delete","/dept/modify"}) public class DeptServlet extends HttpServlet { @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取Servlet Path String servletPath=request.getServletPath(); if ("/dept/list".equals(servletPath)){ doList(request,response); }else if ("/dept/save".equals(servletPath)){ doSave(request,response); }else if ("/dept/edit".equals(servletPath)){ doEdit(request,response); }else if ("/dept/detail".equals(servletPath)){ doDetail(request,response); }else if ("/dept/delete".equals(servletPath)){ doDel(request,response); }else if ("/dept/modify".equals(servletPath)){ doModify(request,response); } } private void doModify(HttpServletRequest request, HttpServletResponse response) throws IOException { request.setCharacterEncoding("UTF-8"); response.setContentType("text/html"); PrintWriter out = response.getWriter(); //获取修改表单的数据 String deptno = request.getParameter("deptno"); String dname = request.getParameter("dname"); String loc = request.getParameter("loc"); //连接数据库,执行更新语句 Connection conn=null; PreparedStatement ps=null; int count=0; try { conn= DButil.getConnection(); String sql="update dept set dname=?,loc=? where deptno=?"; ps=conn.prepareStatement(sql); ps.setString(1,dname); ps.setString(2,loc); ps.setString(3,deptno); count=ps.executeUpdate(); } catch (SQLException throwables) { throwables.printStackTrace(); }finally { DButil.close(conn,ps,null); } if (count==1){ //更新成功,返回list页面 response.sendRedirect(request.getContextPath()+"/dept/list"); } } private void doDetail(HttpServletRequest request, HttpServletResponse response) throws IOException { //获取请求中url的部门编号 String deptno=request.getParameter("deptno"); response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.print(" <!DOCTYPE html>"); out.print("<html>"); out.print(" <head>"); out.print(" <meta charset='utf-8'>"); out.print(" <title>部门详情</title>"); out.print(" </head>"); out.print(" <body>"); out.print(" <h1>部门详情</h1>"); out.print(" <hr>"); out.print(" 部门编号:"+deptno+" <br />"); //连接数据库,根据部门编号查询信息 Connection conn=null; PreparedStatement ps=null; ResultSet rs=null; try { conn= DButil.getConnection(); String sql="select deptno,dname,loc from dept where deptno=?"; ps=conn.prepareStatement(sql); ps.setString(1,deptno); rs= ps.executeQuery(); if (rs.next()){ String dname= rs.getString("dname"); String loc=rs.getString("loc"); out.print(" 部门名称:"+dname+"<br />"); out.print(" 部门位置:"+loc+"<br />"); } } catch (SQLException throwables) { throwables.printStackTrace(); }finally { DButil.close(conn,ps,rs); } out.print(" <input type='button' value='后退' οnclick='window.history.back()'>"); out.print(" </body>"); out.print("</html>"); } private void doEdit(HttpServletRequest request, HttpServletResponse response) throws IOException { //获取部门编号 String deptno = request.getParameter("deptno"); //获取根路径 String contextPath = request.getContextPath(); response.setContentType("text/html"); PrintWriter out = response.getWriter(); Connection conn=null; PreparedStatement ps=null; ResultSet rs=null; out.print(" <!DOCTYPE html>"); out.print("<html>"); out.print(" <head>"); out.print(" <meta charset='utf-8'>"); out.print(" <title>修改部门</title>"); out.print(" </head>"); out.print(" <body>"); out.print(" <h1>修改部门</h1>"); out.print(" <hr>"); out.print(" <form action='"+contextPath+"/dept/modify' method='post'>"); out.print(" 部门编号<input type='text'' name='deptno' value='"+deptno+"'><br>"); try { conn= DButil.getConnection(); String sql="select deptno,dname,loc from dept where deptno=?"; ps=conn.prepareStatement(sql); ps.setString(1,deptno); rs=ps.executeQuery(); if (rs.next()){ String dname=rs.getString("dname"); String loc=rs.getString("loc"); out.print(" 部门名称<input type='text'' name='dname'' value='"+dname+"'><br>"); out.print(" 部门位置<input type='text' name='loc' value='"+loc+"'><br>"); } } catch (SQLException throwables) { throwables.printStackTrace(); }finally { DButil.close(conn,ps,rs); } out.print(" <input type='submit'' value='保存'>"); out.print(" </form>"); out.print(" </body>"); out.print("</html>"); } private void doSave(HttpServletRequest request, HttpServletResponse response) throws IOException { //获取部门信息 //注意乱码问题(tomcat10不会出现) request.setCharacterEncoding("UTF-8"); String deptno=request.getParameter("deptno"); String dname=request.getParameter("dname"); String loc=request.getParameter("loc"); //连接数据库执行插入语句 Connection conn=null; PreparedStatement ps=null; ResultSet rs=null; int count=0; try { conn=DButil.getConnection(); String sql="INSERT INTO dept (deptno, dname, loc) VALUES ('"+deptno+"', '"+dname+"', '"+loc+"')"; ps=conn.prepareStatement(sql); count=ps.executeUpdate(); } catch (SQLException throwables) { throwables.printStackTrace(); }finally { DButil.close(conn,ps,rs); } if (count==1){ //保存成功跳转到列表页面 //转发是一次请求,之前是post请求,后面也是post请求,转发的下一个servlet对象只有doget方法,所以会报405错误 //解决这个错误的暂时的方法是在list对象中写一个dopost方法,该方法调用doget方法 response.sendRedirect(request.getContextPath()+"/dept/list"); }else { //保存失败跳转到错误页面 } } private void doList(HttpServletRequest request, HttpServletResponse response) throws IOException { //获取应用的根路径 String contextPath = request.getContextPath(); response.setContentType("text/html"); PrintWriter out= response.getWriter(); out.print(" <!DOCTYPE html>"); out.print("<html>"); out.print(" <head>"); out.print(" <meta charset='utf-8'>"); out.print(" <title>部门列表</title>"); out.print(" <script type='text/javascript'>"); out.print(" function del(dno){"); out.print(" if (window.confirm('亲,删了就不能恢复了哦')){"); out.print(" document.location.href='/ServletProject/dept/delete?deptno='+dno"); out.print(" }"); out.print(" }"); out.print(" </script>"); out.print(" </head>"); out.print(" <body>"); out.print(" <h1 align='center'>部门列表</h1>"); out.print(" <hr>"); out.print(" <table border='1px' align='center' width='50%'>"); out.print(" <tr>"); out.print(" <th>序号</th>"); out.print(" <th>部门编号</th>"); out.print(" <th>部门名称</th>"); out.print(" <th>操作</th>"); out.print(" </tr>"); //连接数据库,查询所有的部门 Connection conn=null; PreparedStatement ps=null; ResultSet rs=null; try { conn=DButil.getConnection(); String sql="select deptno,dname,loc from dept"; ps=conn.prepareStatement(sql); rs= ps.executeQuery(); int i=0; //获取到的数据库数据 while (rs.next()){ String deptno=rs.getString("deptno"); String dname=rs.getString("dname"); String loc=rs.getString("loc"); out.print(" <tr>"); out.print(" <td>"+(++i)+"</td>"); out.print(" <td>"+deptno+"</td>"); out.print(" <td>"+dname+"</td>"); out.print(" <td>"); out.print(" <a href='javascript:void(0)' οnclick='del("+deptno+")'>删除</a>"); out.print(" <a href='"+contextPath+"/dept/edit?deptno="+deptno+"'>修改</a>"); out.print(" <a href='"+contextPath+"/dept/detail?deptno="+deptno+"'>详情</a>"); out.print(" </td>"); out.print(" </tr>"); } } catch (SQLException throwables) { throwables.printStackTrace(); }finally { //释放资源 DButil.close(conn,ps,rs); } out.print(" </table>"); out.print(" <hr>"); out.print(" <a href='"+contextPath+"/add.html'>新增部门</a>"); out.print(" </body>"); out.print("</html>"); } private void doDel(HttpServletRequest request, HttpServletResponse response) throws IOException{ String deptno=request.getParameter("deptno"); response.setContentType("text/html"); PrintWriter out = response.getWriter(); int count=0; Connection conn=null; PreparedStatement ps=null; try { conn=DButil.getConnection(); //开启事务 conn.setAutoCommit(false); String sql ="delete from dept where deptno=?"; ps=conn.prepareStatement(sql); ps.setString(1,deptno); //返回值是影响了数据库几行数据的结果 count=ps.executeUpdate(); //事务提交 conn.commit(); } catch (SQLException throwables) { //遇到异常回滚 if (conn!=null){ try { conn.rollback(); } catch (SQLException e) { e.printStackTrace(); } } }finally { DButil.close(conn,ps,null); } if (count==1){ //删除成功,回到列表页面 response.sendRedirect(request.getContextPath()+"/dept/list"); }else { //删除失败 } } }
分析使用纯粹的Servlet开发web应用的缺陷
- 在Servlet当中编写HTML/CSS/JS等前端代码,有什么问题?
- Java程序中编写前端代码,编写难度大,麻烦、程序耦合度非常高、代码不美观、维护成本太高。
- 思考以下,如果是你,你准备怎么解决这个问题?
- 使用JSP
关于B/S结构系统的会话机制(session机制)
-
什么是会话:
- 用户打开浏览器,进行一系列操作,最后关闭浏览器,这个过程叫一次会话。会话在服务器端也有一个对应的Java对象session。
- 回顾:什么是一次请求:用户在浏览器上点击了一下,然后页面停下来,可以粗略地看做一次请求(因为代码里可能在第一次请求的时候会有转发和重定向的代码)。请求对应服务器的Java对象是request。
- 一次会话包含多次请求。
-
在Java的servlet规范中,session对应的类名:HttpSession()
-
session机制属于B/S结构的一部分:如果使用php语言开发web项目也有这个机制
-
session对象最主要的作用是:保存会话状态(用户登陆成功了,这是一种登陆成功的状态,你怎么把登陆成功的状态一直抱持,使用session对象可以保留会话状态)
-
为什么要session对象来保存会话状态?
- HTTP协议是一种无状态协议
- 什么是无状态协议:点开百度页面请求的时候,B和S是连接的,百度页面呈现在页面时,B和S断开。为什么要这么做:减轻服务器压力。
-
打开一个浏览器发送请求,分配一个session对象,在同一浏览器下再次发送请求,同样时这一个session对象,直至浏览器关闭,重新打开,或者打开其他的浏览器发送请求。
-
为什么不使用request对象保存会话状态?为什么不使用ServletContext对象保存会话状态?
- request对象是一次请求一个对象,该对象域太小
- ServletContext对象是服务器启动创建,服务器关闭销毁,该对象域太大。
- request请求域(HttpServletRequest)<session(HttpSession)<application域(ServletContext)(把这三个域比作购物:request是一次只能拿一个商品,再拿第二个商品时前面的就销毁了,application是使用服务器的用户都可以拿商品放进去,不能分清楚是谁的商品,session正好像自己的购物车,每个人进入一个浏览器九相当于进入一个超市,进入超市后每个人都拿一辆自己的购物车,自己往自己购物车放商品,如果去了其他的超市,这个购物车不能推出去,只能在后去的超市推一辆属于自己的购物车,购物结束后离开城市,归还购物车,离开超市就相当于关闭浏览器)
-
session对象的实现原理:
-
HttpSession session = request.getSession();-
这段代码很神奇,为什么每个人访问的都是自己的session?
- 浏览器访问服务器,服务器会新建一个专属的session对象,然后session对象是存储在服务器当中,相当于浏览器对该session对象做了一个标记,如果在同一浏览器,并且浏览器没关闭的时候再次访问,可以找到该专属的session。
-
session对象什么时候销毁:
- 因为HTTP协议是无状态协议,服务器不知道浏览器什么时候关闭,所以在关闭浏览器的时候session对象不一定销毁,而是服务器有一个session超时机制,如果界面长时间不操作,session对象也会销毁
-
session对象怎么获取:
- HttpSession session = request.getSession();(从服务器获取当前session对象,如果没有获取到任何session对象,则新建)
- HttpSession session = request.getSession(false);(从服务器中获取当前session对象,如果获取不到session对象,则返回一个null)
-
session的实现原理:
- 在web服务中有一个session列表,类似map集合。
- 这个map集合的key存储的是session的编号
- 这个map集合的value存储的是对应的session对象
- 用户发送第一次请求的时候:服务器会创建一个新的session对象,同时给session对象生成一个id,然后web服务器会将session的id发送给浏览器,浏览器将session的编号保存在浏览器的缓存中(session的编号是以cookie的形式保存在浏览器里的)。
- 用户发送第二次请求的时候:会自动将浏览器缓存中的session的编号自动发送给服务器,服务器获取到编号,从session列表去获得对应的session对象。
- 关闭浏览器后,浏览器缓存消失,浏览器中的session对象的编号消失了,web服务器的session对象依靠session超时机制,在一段时间后自动销毁,或者系统提供了安全退出功能,手动安全退出会话,手动销毁。
-
//在web.xml文件中设置session超时时间 <session-config> <session-timeout>20</session-timeout> </session-config> -
Cookie禁用了,session还能找到吗?
- cookie禁用:服务器正常发送cookie给浏览器,但是浏览器拒收了。
- 不能找到,每次请求都会新建一个session对象。
- cookie禁用了,session机制还能实现吗?
- 可以,使用URL重写机制(http://localhost:8080/ServletSession/sessions;JSESSIONID=A161F7B87F8AE95D86A793D8BBCD57F1)
- 在URL后面加上sessionid,就可以找到session对象。但是重写机制会提高开发者成本,大部分网站设计的是如果你禁用cookie,网站就不能正常显示。
-
总结一下目前我们所了解的域对象:
request请求域(HttpServletRequest)<session(HttpSession)<application域(ServletContext)
他们三个域都有一下三个公共方法:setAttribute,getAttribute,removeAttribute,向域当中绑定数据及管理数据
-
-
-
session掌握之后,怎么解决项目中的登录问题,怎么让登录起作用?
-
登录成功后,将用户的登录信息存储到session当中。也就是说session中如果有用户的信息就代表用户登录成功了。
-
注意:在jsp中会默认创建一个session,所以在获取session对象时只判断是否存在session对象是不能判断登录成功,如果想访问jsp不自动生成session,则可以添加这样一条语句:
-
<%--访问jsp的时候不生成session对象--%> <%@page session="false" %>package action; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; import utils.DButil; import java.io.IOException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @WebServlet("/dept/login") public class UserServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //验证用户名和密码是否正确 //获取用户名和密码 String username = request.getParameter("username"); String password = request.getParameter("password"); //链接数据库验证 Connection conn=null; PreparedStatement ps=null; ResultSet rs=null; int count=0; try { conn=DButil.getConnection(); String sql="select * from users where username=? and password=?"; ps=conn.prepareStatement(sql); ps.setString(1,username); ps.setString(2,password); rs=ps.executeQuery(); if (rs.next()){ count=1; } } catch (SQLException throwables) { throwables.printStackTrace(); }finally { DButil.close(conn,ps,rs); } if (count==1){ //登陆成功 //获取session对象 HttpSession session = request.getSession(); session.setAttribute("username",username); response.sendRedirect(request.getContextPath()+"/dept/list"); }else { response.setContentType("text/html"); System.out.println("登陆失败"); } } }package action; import bean.Dept; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; import utils.DButil; import java.io.IOException; import java.io.PrintWriter; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; @WebServlet({"/dept/list","/dept/save","/dept/edit","/dept/detail","/dept/delete","/dept/modify"}) public class DeptServlet extends HttpServlet { @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取session,不需要新建,只是获取当前session HttpSession session = request.getSession(false); if (session != null && session.getAttribute("username") != null) { //获取Servlet Path String servletPath = request.getServletPath(); if ("/dept/list".equals(servletPath)) { doList(request, response); }/*else if ("/dept/save".equals(servletPath)){ doSave(request,response); }else if ("/dept/edit".equals(servletPath)){ doEdit(request,response); }else if ("/dept/detail".equals(servletPath)){ doDetail(request,response); }else if ("/dept/delete".equals(servletPath)){ doDel(request,response); }else if ("/dept/modify".equals(servletPath)){ doModify(request,response); }*/ } } private void doList(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { //获取应用的根路径 String contextPath = request.getContextPath(); //设置一个存放部门信息的集合 List<Dept> depts=new ArrayList(); //连接数据库,查询所有的部门 Connection conn=null; PreparedStatement ps=null; ResultSet rs=null; try { conn=DButil.getConnection(); String sql="select deptno,dname,loc from dept"; ps=conn.prepareStatement(sql); rs= ps.executeQuery(); int i=0; //获取到的数据库数据 while (rs.next()){ String deptno=rs.getString("deptno"); String dname=rs.getString("dname"); String loc=rs.getString("loc"); //将以上零散的数据封装成Java对象,并装入集合再通过请求域提交到jsp文件 Dept dept=new Dept(); dept.setDeptno(deptno); dept.setDname(dname); dept.setLoc(loc); depts.add(dept); } } catch (SQLException throwables) { throwables.printStackTrace(); }finally { //释放资源 DButil.close(conn,ps,rs); } //将集合放入请求域 request.setAttribute("deptList",depts); //转发(不能重定向,因为重定向是不同的请求,请求域存放的信息不能传给其他使用) request.getRequestDispatcher("/list.jsp").forward(request,response); } private void doModify(HttpServletRequest request, HttpServletResponse response) throws IOException { request.setCharacterEncoding("UTF-8"); response.setContentType("text/html"); PrintWriter out = response.getWriter(); //获取修改表单的数据 String deptno = request.getParameter("deptno"); String dname = request.getParameter("dname"); String loc = request.getParameter("loc"); //连接数据库,执行更新语句 Connection conn=null; PreparedStatement ps=null; int count=0; try { conn= DButil.getConnection(); String sql="update dept set dname=?,loc=? where deptno=?"; ps=conn.prepareStatement(sql); ps.setString(1,dname); ps.setString(2,loc); ps.setString(3,deptno); count=ps.executeUpdate(); } catch (SQLException throwables) { throwables.printStackTrace(); }finally { DButil.close(conn,ps,null); } if (count==1){ //更新成功,返回list页面 response.sendRedirect(request.getContextPath()+"/dept/list"); } } private void doDetail(HttpServletRequest request, HttpServletResponse response) throws IOException { //获取请求中url的部门编号 String deptno=request.getParameter("deptno"); response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.print(" <!DOCTYPE html>"); out.print("<html>"); out.print(" <head>"); out.print(" <meta charset='utf-8'>"); out.print(" <title>部门详情</title>"); out.print(" </head>"); out.print(" <body>"); out.print(" <h1>部门详情</h1>"); out.print(" <hr>"); out.print(" 部门编号:"+deptno+" <br />"); //连接数据库,根据部门编号查询信息 Connection conn=null; PreparedStatement ps=null; ResultSet rs=null; try { conn= DButil.getConnection(); String sql="select deptno,dname,loc from dept where deptno=?"; ps=conn.prepareStatement(sql); ps.setString(1,deptno); rs= ps.executeQuery(); if (rs.next()){ String dname= rs.getString("dname"); String loc=rs.getString("loc"); out.print(" 部门名称:"+dname+"<br />"); out.print(" 部门位置:"+loc+"<br />"); } } catch (SQLException throwables) { throwables.printStackTrace(); }finally { DButil.close(conn,ps,rs); } out.print(" <input type='button' value='后退' οnclick='window.history.back()'>"); out.print(" </body>"); out.print("</html>"); } private void doEdit(HttpServletRequest request, HttpServletResponse response) throws IOException { //获取部门编号 String deptno = request.getParameter("deptno"); //获取根路径 String contextPath = request.getContextPath(); response.setContentType("text/html"); PrintWriter out = response.getWriter(); Connection conn=null; PreparedStatement ps=null; ResultSet rs=null; out.print(" <!DOCTYPE html>"); out.print("<html>"); out.print(" <head>"); out.print(" <meta charset='utf-8'>"); out.print(" <title>修改部门</title>"); out.print(" </head>"); out.print(" <body>"); out.print(" <h1>修改部门</h1>"); out.print(" <hr>"); out.print(" <form action='"+contextPath+"/dept/modify' method='post'>"); out.print(" 部门编号<input type='text'' name='deptno' value='"+deptno+"'><br>"); try { conn= DButil.getConnection(); String sql="select deptno,dname,loc from dept where deptno=?"; ps=conn.prepareStatement(sql); ps.setString(1,deptno); rs=ps.executeQuery(); if (rs.next()){ String dname=rs.getString("dname"); String loc=rs.getString("loc"); out.print(" 部门名称<input type='text'' name='dname'' value='"+dname+"'><br>"); out.print(" 部门位置<input type='text' name='loc' value='"+loc+"'><br>"); } } catch (SQLException throwables) { throwables.printStackTrace(); }finally { DButil.close(conn,ps,rs); } out.print(" <input type='submit'' value='保存'>"); out.print(" </form>"); out.print(" </body>"); out.print("</html>"); } private void doSave(HttpServletRequest request, HttpServletResponse response) throws IOException { //获取部门信息 //注意乱码问题(tomcat10不会出现) request.setCharacterEncoding("UTF-8"); String deptno=request.getParameter("deptno"); String dname=request.getParameter("dname"); String loc=request.getParameter("loc"); //连接数据库执行插入语句 Connection conn=null; PreparedStatement ps=null; ResultSet rs=null; int count=0; try { conn=DButil.getConnection(); String sql="INSERT INTO dept (deptno, dname, loc) VALUES ('"+deptno+"', '"+dname+"', '"+loc+"')"; ps=conn.prepareStatement(sql); count=ps.executeUpdate(); } catch (SQLException throwables) { throwables.printStackTrace(); }finally { DButil.close(conn,ps,rs); } if (count==1){ //保存成功跳转到列表页面 //转发是一次请求,之前是post请求,后面也是post请求,转发的下一个servlet对象只有doget方法,所以会报405错误 //解决这个错误的暂时的方法是在list对象中写一个dopost方法,该方法调用doget方法 response.sendRedirect(request.getContextPath()+"/dept/list"); }else { //保存失败跳转到错误页面 } } private void doDel(HttpServletRequest request, HttpServletResponse response) throws IOException{ String deptno=request.getParameter("deptno"); response.setContentType("text/html"); PrintWriter out = response.getWriter(); int count=0; Connection conn=null; PreparedStatement ps=null; try { conn=DButil.getConnection(); //开启事务 conn.setAutoCommit(false); String sql ="delete from dept where deptno=?"; ps=conn.prepareStatement(sql); ps.setString(1,deptno); //返回值是影响了数据库几行数据的结果 count=ps.executeUpdate(); //事务提交 conn.commit(); } catch (SQLException throwables) { //遇到异常回滚 if (conn!=null){ try { conn.rollback(); } catch (SQLException e) { e.printStackTrace(); } } }finally { DButil.close(conn,ps,null); } if (count==1){ //删除成功,回到列表页面 response.sendRedirect(request.getContextPath()+"/dept/list"); }else { //删除失败 } } }<%@ page import="java.util.List" %> <%@ page import="bean.Dept" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>部门列表</title> </head> <body> <h1>欢迎<%=session.getAttribute("username")%></h1> <h1 align="center">部门列表</h1> <hr> <table border="1px" align="center" width="50%"> <tr> <th>序号</th> <th>部门编号</th> <th>部门名称</th> <th>操作</th> </tr> <% //从request取集合 List<Dept> deptList = (List) request.getAttribute("deptList"); int i=0; for (Dept dept:deptList){ %> <tr> <td><%=++i%></td> <td><%=dept.getDeptno()%>></td> <td><%=dept.getDname()%>></td> <td> <a href="">删除</a> <a href="<%=request.getContextPath()%>/edit.jsp">修改</a> <a href="<%=request.getContextPath()%>/detail.jsp">详情</a> </td> </tr> <% } %> </table> <hr> <a href="<%=request.getContextPath()%>/add.jsp">新增部门</a> </body> </html>
-
-
-
项目增加一个安全退出(手动销毁session对象)
private void doLOginout(HttpServletRequest request, HttpServletResponse response) throws IOException { //获取session对象,销毁session HttpSession session = request.getSession(); if (session!=null){ //手动销session对象 session.invalidate(); //跳转到登陆页面 response.sendRedirect(request.getContextPath());
Cookie
-
session的实现原理中,每一个session对象都会关联一个sessionId,例如:
- JSESSIONID=EA251A29E8E17C955D9A4C3A47730F83
- 以上这个键值对数据其实就是cookie对象。
- 对于session关联的cookie来说,这个cookie是被保存在浏览器的“运行内存”当中。
- 只要浏览器不关闭,用户再次发送请求的时候,会自动将运行内存中的cookie发送给服务器。
- 例如这个cookie:JSESSIONID=EA251A29E8E17C955D9A4C3A47730F83就会再次发送给浏览器。
- 服务器就是根据:EA251A29E8E17C955D9A4C3A47730F83这个值来找到对应的session对象。
-
cookie怎么生成?保存在什么地方?有什么用?什么时候发送cookie?发送哪些cookie?
- cookie最终是保存在浏览器客户端,可以保存在运行内存中(浏览器关闭就消失),也可以保存在硬盘文件上(永久保存)。
- cookie和session机制都是为了保存会话状态;cookie是将会话保存在浏览器客户端上,session将会话状态保存在服务器端。
-
cookie的经典案例:
- 旧版京东商城,在未登录情况下,向购物车放商品,然后关闭浏览器,再次打开浏览器,访问京东商城,购物车的商品还在。这就是将购物车商品编号放到cookie中并存储在到硬盘文件,再次打开会自动读取硬盘cookie,将cookie里的商品自动添加到购物车。
- 123邮箱中的十天内免登录功能:用户输入正确的账号密码,登录成功后,浏览器客户端会保存一个cookie,这个cookie保存了用户名和密码等信息,这个cookie是保存在硬盘文件当中的。十天内有效。十天内再次访问123邮箱,浏览器自动提交cookie给服务器,服务器接收到cookie之后,获取用户名和密码,验证。通过,自动登陆成功。
- 浏览器ctrl+shift+delete可以选择清除cookie。
-
cookie和session机制都不属于Java的机制,实际上是HTTP协议的一部分。
-
HTTP协议中规定:然后一个cookie都是由name和value组成的,name和value都是字符串类型。
-
在Java的servlet中,对cookie提供了哪些支持呢?
- 提供了一个Cookie类来专门表示cookie数据:Jakarta.servlet.http.Cookie;
-
关于cookie的path,cookie关联的路径:(只要路径正确,浏览器就会自动发送cookie给服务器)
-
假设现在发送的请求路径是“localhost:8080/Servlet_cookie/servletcookie”生成的路径,如果cookie没有设置path,默认的path是什么?
- 默认的path是localhost:8080/Servlet_cookie以及它的子路径。
- 也就是说:以后浏览器的请求路径是localhost:8080/Servlet_cookie这个路径以及其子路径,cookie都会被发送到服务器。
-
手动设置cookie的path
-
cookie.setPath("/Servlet-cookie");//表示只要是这个Servlet-cookie项目的请求路径,都会提交cookie给浏览器
-
-
-
浏览器发送cookie给服务器,服务器的Java程序怎么接收?
-
Cookie[] cookies=request.getCookies();
-
-
使用Cookie实现十天免登录功能:
- 先实现登录功能
- 登陆成功
- 跳转到部门页面
- 登陆失败
- 登陆成功
- 修改前端界面
- 在登录页面给一个复选框,是否同样十天免登录
- 修改servlet的登录方法
- 如果登陆成功,用户名密码存储到cookie,有效期十天
- 用户再次访问该网页的时候,有两个走向:
- 要么跳转到部门列表页面
- 要么跳转到登录页面
- 先实现登录功能
-
Cookie使用方法:
//创建cookie对象存储用户账号密码 Cookie cookie1 = new Cookie("username", username); Cookie cookie2 = new Cookie("password", password); //设置cookie有效期10天 cookie1.setMaxAge(60*60*24*10); cookie2.setMaxAge(60*60*24*10); //设置cookie的path cookie1.setPath(request.getContextPath()); cookie2.setPath(request.getContextPath()); //响应cookie给浏览器 response.addCookie(cookie1); response.addCookie(cookie2); //获取cookie Cookie[] cookies = request.getCookies(); String username=null; String password=null; if (cookies!=null){ for (Cookie cookie : cookies) { String name = cookie.getName(); if ("username".equals(name)){ username = cookie.getValue(); }else if ("password".equals(name)){ password = cookie.getValue(); } } }
JSP
-
jsp的底层原理:
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xtW36Vgq-1688627297644)(C:\Users\26332\AppData\Roaming\Typora\typora-user-images\image-20230705144414215.png)]
- Using CATALINA_BASE: “C:\Users\26332\AppData\Local\JetBrains\IntelliJIdea2021.1\tomcat\df5d7a1b-693a-40d9-9a66-d3042ad40355”(找到这个路径,在文件管理器打开,再点击work文件,直到看到最后有一个后缀为Java和class的文件)
- 所以:JSP是由Tomcat翻译成Java文件,再编译Java文件生成class文件,所以访问index.jsp,底层执行的就是index.jsp.class这个Java程序
-
JSP实际上就是一个Servlet
- index.jsp访问的时候,会自动翻译成index.jsp.java,会自动编译成index_jsp.class,那么index_jsp就是一个类。
- index.jsp类继承HttpJspBase,而HttpJspBase类继承的是HttpServlet。所以index_jsp类就是一个Servlet类。
- jsp的生命周期和Servlet的生命周期完全相同。完全就是一个东西,没有任何区别。
-
jsp文件第一次访问比较慢,为什么?
- 运维人员在给客户演示项目的时候,为什么提前先把所有的jsp文件先访问一遍?
- 因为jsp文件第一次执行需要翻译成Java文件,再编译成class文件,需要一定的时间。
- 运维人员在给客户演示项目的时候,为什么提前先把所有的jsp文件先访问一遍?
-
jsp的基础语法
-
在jsp文件中直接编写文字,都会被翻译到哪里?
- 翻译到Servlet类的service方法的out.write(“翻译到这里”),被Java程序当做普通的字符串打印到浏览器。
-
jsp的page指令,解决响应时的中文乱码问题:
-
通过page指令来设置响应的内容类型,在内容类型的最后面添加:charset=UTF-8
-
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
-
-
-
怎么在jsp中编写Java程序:
-
<%Java语句; %> //该符号中语句写在service方法里的Java代码 <%System.out.println("hello jsp");%>-
在这个符号当中编写的被视为Java程序,被翻译到Servlet类的service方法内部。
-
在这个符号里写代码,就相当于在Servlet类的service方法里写代码,所以该符号里的代码要遵守方法里代码的书写规范。
-
jsp中的专业注释:<%–jsp专业注释,不会被翻译到Java源代码中–%>
-
在同一个jsp当中可以写多个<%%>
-
-
<%!Java语句;%> //该符号中语句写在service方法外的Java代码 <%! //成员变量 private String name="hetao"; //静态代码块 static { System.out.println("静态方法执行"); } //方法 public static void m1(){ System.out.println("m1方法执行了"); } %> -
<%! %>这个语法基本上不建议用,因为在service方法外写静态变量和实例变量,都会存在线程安全问题,jsp就是servlet,servlet是单例的,多线程并发的环境下,这个静态变量和实例变量一旦有修改操作,必然会存在线程安全问题。
-
jsp的输出语句:怎么把一个Java变量输出到浏览器?
-
在<%%>符号里写out.print(“输出的变量”)
-
直接写在<%= %>符号里
-
<%-- Created by IntelliJ IDEA. User: 26332 Date: 2023/7/5 Time: 15:40 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <% int a=1; out.print(a); %> <%=a%> </body> </html>
-
-
-
JSP基础语法总结:
-
JSP中直接编写普通字符串
- 翻译到service方法的out.write(“这里”)
-
<% %>
*翻译到service方法体内部,里面是一条条Java语句。
-
<%= %>
- 翻译到service方法体之外,不建议使用,易引发线程安全问题。
-
<%= %>
- 翻译到service方法体内部,翻译成:out.print()。
-
<%@ page contentType=“text/html;charset=UTF-8” language=“java” %>
- 配置指令,通过contentType属性用来设置响应的内容类型。
-
-
使用Servlet+JSP完成项目的改造
-
使用Servlet处理业务,收集数据。
-
使用JSP展示数据。
-
将之前原型中的HTML文件全部修改为jsp文件,然后再jsp文件头部添加page指令(指定contextType防止中文乱码,将所有的jsp直接拷贝到web目录下)
-
完成所有的页面正常跳转。
-
在Servlet中连接数据库,查询所有的部门,遍历结果集。
-
遍历所有结果集的过程中,取出部门编号、部门号、位置等信息,封装成Java对象。
-
将Java对象存放到List集合。
-
将List集合存储到request域当中。
-
转发到jsp。
-
Servlet代码:
@WebServlet({"/dept/list","/dept/save","/dept/edit","/dept/detail","/dept/delete","/dept/modify"}) public class DeptServlet extends HttpServlet { @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取Servlet Path String servletPath=request.getServletPath(); if ("/dept/list".equals(servletPath)){ doList(request,response); } private void doList(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { //获取应用的根路径 String contextPath = request.getContextPath(); //设置一个存放部门信息的集合 List<Dept> depts=new ArrayList(); //连接数据库,查询所有的部门 Connection conn=null; PreparedStatement ps=null; ResultSet rs=null; try { conn=DButil.getConnection(); String sql="select deptno,dname,loc from dept"; ps=conn.prepareStatement(sql); rs= ps.executeQuery(); int i=0; //获取到的数据库数据 while (rs.next()){ String deptno=rs.getString("deptno"); String dname=rs.getString("dname"); String loc=rs.getString("loc"); //将以上零散的数据封装成Java对象,并装入集合再通过请求域提交到jsp文件 Dept dept=new Dept(); dept.setDeptno(deptno); dept.setDname(dname); dept.setLoc(loc); depts.add(dept); } } catch (SQLException throwables) { throwables.printStackTrace(); }finally { //释放资源 DButil.close(conn,ps,rs); } //将集合放入请求域 request.setAttribute("deptList",depts); //转发(不能重定向,因为重定向是不同的请求,请求域存放的信息不能传给其他使用) request.getRequestDispatcher("/list.jsp").forward(request,response); }
-
-
在jsp中:
-
从request域当中取出List集合。
-
遍历List集合,取出每个部门对象,动态生成tr
-
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>欢迎使用OA系统</title> </head> <body> <a href="<%=request.getContextPath()%>/dept/list">查看部门列表</a> </body> </html> -
<%@ page import="java.util.List" %> <%@ page import="bean.Dept" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>部门列表</title> </head> <body> <h1 align="center">部门列表</h1> <hr> <table border="1px" align="center" width="50%"> <tr> <th>序号</th> <th>部门编号</th> <th>部门名称</th> <th>操作</th> </tr> <% //从request取集合 List<Dept> deptList = (List) request.getAttribute("deptList"); int i=0; for (Dept dept:deptList){ %> <tr> <td><%=++i%></td> <td><%=dept.getDeptno()%>></td> <td><%=dept.getDname()%>></td> <td> <a href="">删除</a> <a href="<%=request.getContextPath()%>/edit.jsp">修改</a> <a href="<%=request.getContextPath()%>/detail.jsp">详情</a> </td> </tr> <% } %> </table> <hr> <a href="<%=request.getContextPath()%>/add.jsp">新增部门</a> </body> </html>
-
-
jsp文件的拓展名必须是XXX.jsp吗?
-
jsp文件的拓展名是可以配置的
-
在CATALINA_HOME/conf/web.xml,在这个文件当中配置jsp文件的拓展名。
-
<servlet-mapping> <servlet-name>jsp</servlet-name> <url-pattern>*.jsp</url-pattern> <url-pattern>*.jspx</url-pattern> </servlet-mapping> -
xxx.jsp文件对Tomcat来说就是普通文档,web容器都会将该文件翻译成Java程序,真正执行时,和jsp文件没关系。
-
当前的项目存在的问题:需要一个登录功能
-
实现登录功能步骤:
- 步骤1:先实现一个登录页面。
- 登录页面应该有一个登录表单,提交用户名和密码。form是post方式提交
- 步骤2:数据库添加一个用户表(users)。
- 步骤3:后台要有一个Servlet处理登录请求。
jsp代码 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>欢迎使用OA系统</title> </head> <body> <%--<a href="<%=request.getContextPath()%>/dept/list">查看部门列表</a>--%> <h1>用户登录</h1><hr> <form action="<%=request.getContextPath()%>/dept/login" method="post"> username:<input type="text" name="username"><br> password:<input type="password" name="password"><br> <input type="submit" value="登录"> </form> </body> </html>Servlet代码 package action; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import utils.DButil; import java.io.IOException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @WebServlet("/dept/login") public class UserServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //验证用户名和密码是否正确 //获取用户名和密码 String username = request.getParameter("username"); String password = request.getParameter("password"); //链接数据库验证 Connection conn=null; PreparedStatement ps=null; ResultSet rs=null; int count=0; try { conn=DButil.getConnection(); String sql="select * from users where username=? and password=?"; ps=conn.prepareStatement(sql); ps.setString(1,username); ps.setString(2,password); rs=ps.executeQuery(); if (rs.next()){ count=1; } } catch (SQLException throwables) { throwables.printStackTrace(); }finally { DButil.close(conn,ps,rs); } if (count==1){ response.sendRedirect(request.getContextPath()+"/dept/list"); }else { response.setContentType("text/html"); System.out.println("登陆失败"); } } } - 步骤1:先实现一个登录页面。
JSP的指令
-
指令的作用:指导JSP翻译引擎如何翻译JSP文件
-
指令包括哪些?
- include指令:包含指令,在JSP中完成静态包含,很少有。
- taglib指令:引入标签库的指令。这个到JSTL标签库的时候再学习。
- page指令:目前重点学习page指令。
-
指令的使用语法是什么?
- <%@指令名 属性名=属性值 属性名=属性值…%>
-
关于page当中都有哪些常用的指令:
//是否启用JSP内置对象session,false表示不启用 <%@page session="false" %> //设置响应的内容类型和响应时的字符集 <%@page contentType="text/html;" pageEncoding="UTF-8" %> //导包 <%@page import="com" %> //当前页面出错就跳转到这个页面,errorPage这个属性用来指定出错之后的跳转位置 <%@page errorPage="2.jsp" %> //跳转到出错页面后,无异常信息,程序员不知道要怎么做,则使用下面的标签和九大内置对象之一,把错误信息打印到控制台 <%@page isErrorPage="true" %> <% exception.printStackTrace(); %>
JSP九大内置对象
-
jakarta.servlet.jsp.PageContext pageContext 页面作用域
-
jakarta.servlet.http.HttpServletRequest request 请求作用域
-
jakarta.servlet.http.HttpSession session 会话作用域
-
jakarta.servlet.ServletContext application 应用作用域
-
pageContext<request<session<application
-
以上四个域都有setAttribute、getAttribute、removeAttribute方法。
-
以上作用域使用原则:尽可能使用小的域
-
-
java.lang.Throwable exception
-
jakarta.servlet.ServletConfig config
-
java.lang.Object page (其实时this,当前servlet对象)
-
jakarta.servlet.jsp.JspWriter out (负责输出)
-
jakarta.servlet.http.HttpServletResponse response (负责响应)
EL表达式
-
EL表达式是干什么用的?
- Expression Language(表达式语言)
- EL表达式可以代替JSP中的Java代码,让JSP文件中的程序看起来更加整洁美观
- EL表达式属于JSP的一部分
-
EL表达式出现在JSP中只要是:
- 从某个作用域中取数据,然后将其转换成字符串,然后将其输出到浏览器,这就是EL表达式的功效。三大功效:
- 第一功效:从某个域中取数据
- 四个域:
- pageContext
- request
- session
- application
- 四个域:
- 第二功效:将取出数据转成字符串
- 如果是一个Java对象,也会调用Java的toString方法将其转换成字符串
- 第三功效:将字符串输出到浏览器
- 和<%=%>这个效果一样,将其输出到浏览器。
- 第一功效:从某个域中取数据
- 从某个作用域中取数据,然后将其转换成字符串,然后将其输出到浏览器,这就是EL表达式的功效。三大功效:
-
EL表达式很好用,基本的语法格式:
-
${表达式}
-
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <% //向request作用域存储username为hetao request.setAttribute("username","hetao"); %> <%=request.getAttribute("username")%> <%--使用EL表达式--%> ${username}
-
-
EL表达式的使用:
-
<%@ page import="action.User" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <% //创建User对象 User user = new User(); user.setUsername("hetao"); user.setPassword("20021008"); user.setAge(20); //将User对象存储到request域当中(将对象存储到某个域当中,一定要存,因为EL表达式只能从某个范围中取数据) //数据必须存储到四大范围之一 request.setAttribute("userObj",user); %> <%--使用EL表达式,从request域当中,取出User对象,并将其输出到浏览器--%> <%--1、EL表达式会自动从某个范围取出数据。2、将其转换成字符串。3、将其输出到浏览器--%> ${userObj} //输出结果:User{username='hetao', password='20021008', age=20} <br> <%--输出user对象的username属性--%> ${userObj.username} //输出结果:hetao <br> ${userObj.email} //输出结果:hetao //看下面的User类中的getEmail方法 ${"userObj"}和${userObj}的区别: 第一个总结输出字符串userObj,后面的输出User{username='hetao', password='20021008', age=20}package action; import jakarta.servlet.annotation.WebServlet; public class User { private String username; private String password; private int age; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "User{" + "username='" + username + '\'' + ", password='" + password + '\'' + ", age=" + age + '}'; } public String getEmail(){ return "hetao"; } }
-
-
总结:EL表达式中,这个语法实际上就是调用底层的getxxx()方法,如果没有对应的get方法,浏览器就会报500错误。如果同时多个域都有相同名字的变量,EL表达式优先从小域取。
-
${userObj.username}相当于${userObj["username"]} 加入一个名称为abc.a的要输出,第一种方式输出为空白,第二种方式可以正常输出
摘抄EL表达式
版权声明:本文为CSDN博主「代码怎么撕」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_57151612/article/details/123693754
JSTL标签库
-
什么是JSTL标签库
- Java Standard Tag Lib(Java标准的标签库)
- JSTL标签库通常结合EL表达式一起使用,目的是让JSP中的Java代码消失
-
使用JSTL标签库的步骤
-
第一步:引入JSTL标签库对应的jar包
- Tomcat10之后引入的jar包是:
- jakarta.servlet.jsp.jstl-2.0.0.jar
- jakarta.servlet.jsp.jstl-api-2.0.0.jar
- 在IDEA当中怎么引入?
- 在WEB-INF下新建lib目录,然后将jar包拷贝到lib当中
- 想MySQL驱动一样
- 什么时候需要将jar包放到lib下:Tomcat服务器没有的jar包
- Tomcat10之后引入的jar包是:
-
第二步:在JSP中引入需要的标签库(使用taglib指令引入标签库)
-
JSTL提供了多种标签,你要引入哪个标签???(重点掌握核心标签库)
-
<%--引入标签库,这里引入的是JSTL的核心标签库--%> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> 这就是核心标签库 prefix="" 这里随便起一个名字就行了,核心标签库,大家默认的叫做c url是对应jar包里的东西
-
-
第三步:在需要标签的位置使用即可(表面是标签,实际上是Java程序)
-
-
JSTL标签的原理
-
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> 以上uri 后面的路径实际上指向了一个xx.tld文件。 tld文件实际上是一个xml配置文件。 在tld文件中描述了“标签”和java类之间的关系。 以上核心标签库对应的tld文件是c.tld文件。 在jakarta.servlet.jsp.jstl-2.0.0.jar里面META-INF目录下,有一个c.tld文件。 -
源码解析:配置文件tld解析
-
<tag> <description>对该标签的描述 </description> <name>catch</name> 标签的名字 <tag-class>org.apache.taglibs.standard.tag.common.core.CatchTag</tag-class>标签对应的java类。 <body-content>JSP</body-content>标签体当中可以出现的内容,如果是JSP,就表示标签体重可以出现符合JSP所有语法的代码。例如EL表达式。 <attribute> <description>对这个属性的描述 </description> <name>var</name> 属性名 <required>false</required> false 表示该属性不是必须的。true表示该属性是必须的。 <rtexprvalue>false</rtexprvalue>这个描述说明了该属性是否支持EL表达式。false表示不支持。true表示支持EL表达式。 </attribute> </tag> <c:catch 属性名=“”> JSP... </c:catch>
-
-
总结JSTL标签的格式就是:
-
<库名:标签名 标签中包含的属性名=“”> 其他代码(JSP什么的) </库名:标签名>
-
-
-
JSTL中的核心标签库core当中有哪些常用的标签呢?
-
<%--核心库的if标签 test属性是必须的,test属性支持EL表达式 test属性只能是Boolean类型 if标签还有var属性,不是必须的 if标签还有scope属性,用来指定var的存储域,var是保存test属性的值,也不是必须的(四个域) --%> <c:if test="${empty param.username}"> <h1>用户名不能为空</h1> </c:if> <c:if test="${not empty param.username}"> <h1>欢迎您${param.username}</h1> </c:if> <%--存储到request域--%> <c:if test="${not empty param.username}" var="v" scope="request"> <h1>欢迎您${param.username}</h1> </c:if> <%--通过EL表达式将request域当中的v取出--%> ${v} -
1. <%-- forEach标签: var用来指定循环中的变量 begin开始 end结束 step步长 底层实际会将i存储到pageContext域当中 --%> <c:forEach var="i" begin="1" end="10" step="1"> ${i}<br> </c:forEach> 2. <%-- forEach标签: 取集合元素 --%> <% List<User> users=new ArrayList<>(); User user=new User(); user.setAge(20); user.setUsername("hetao"); user.setPassword("20021008"); User user1=new User(); user1.setAge(20); user1.setUsername("hetao"); user1.setPassword("20021008"); User user2=new User(); user2.setAge(20); user2.setUsername("hetao"); user2.setPassword("20021008"); users.add(user); users.add(user1); users.add(user2); request.setAttribute("users",users); %> <%--var="s"这个s代表的是集合中的每个User对象--%> <c:forEach items="${users}" var="s"> user:${s.username},password=${s.password},age=${s.age}<br> </c:forEach> 3. <%--varStatus这个属性表示var的状态对象,这里是一个Java对象,这个Java对象代表了var的状态 varStatus=”名字随意“ varStatus这个状态对象有count属性,可以直接使用 --%> <c:forEach items="${users}" var="s" varStatus="userStatus"> <%--主要用于编号,从1递增--%> 编号:${userStatus.count}, user:${s.username},password=${s.password},age=${s.age}<br> </c:forEach> -
<%-- 相当于if else --%> <c:choose> <c:when test="${param.age<18}">青少年</c:when> <c:when test="${param.age<35}">青年</c:when> <c:when test="${param.age<55}">中年</c:when> <c:otherwise>老年</c:otherwise> </c:choose>
-
-
拓展:HTML头部的标签,可以用来设置根路径
Filter过滤器
-
当前的项目有什么缺陷?
- 每个Servlet都需要判断session对象用户是否登录,要写字符集类型…重复代码,怎么解决?
- 使用Servlet规范的Filter过滤器
- 每个Servlet都需要判断session对象用户是否登录,要写字符集类型…重复代码,怎么解决?
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sjuMoGDt-1688717410092)(C:\Users\26332\AppData\Roaming\Typora\typora-user-images\image-20230707111134150.png)]
-
Filter是什么,有什么用,执行原理是什么?
- Filter是过滤器
- Filter可以在Servlet这个目录程序执行之前添加代码,以可以在Servlet执行之后添加代码,之前之后都可以添加过滤规则
- 一般情况下,都是在过滤器当中编写公共代码
-
一个过滤器怎么写呢?
- 第一步:编写一个Java类实现一个接口import jakarta.servlet.Filter,并且实现接口中所有的方法
- init方法:在Filter对象第一次创建之后调用,并且只调用一次
- doFilter方法:只要用户发生一次请求,就执行一次
- dostroy方法:在Filter对象被释放/销毁之前调用,只调用一次
- 第二步:在web.xml配置,类似Servlet配置
- 第一步:编写一个Java类实现一个接口import jakarta.servlet.Filter,并且实现接口中所有的方法
-
注意:
- Servlet对象默认情况下,在服务器启动的时候是不会新建对象的。
- Filter对象默认情况下,在服务器启动的时候会新建对象。
- Servlet是单例的,Filter也是单例的(单实例)。
-
package action; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet("/a.do") public class Aservlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("Aservlet的doget方法执行..."); } } -
package action; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet("/b.do") public class BServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("Bservlet的doget方法执行..."); } } -
//过滤器 package action; import jakarta.servlet.*; import jakarta.servlet.annotation.WebFilter; import java.io.IOException; @WebFilter("*.do") public class Filter1 implements Filter { public Filter1() { System.out.println("构造方法执行"); } @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("init。。。"); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("doFilter开始执行。。。"); //执行下一个过滤器,如果下面没有过滤器,则执行目标Servlet chain.doFilter(request,response); //如果执行完Servlet,就会在执行下面这条语句 System.out.println("doFilter方法执行结束"); } @Override public void destroy() { System.out.println("dostroy。。。"); } } -
Filter优先级高于Servlet
Listener(监听器)
-
什么是Listener
- 监听器也是servlet规范的一员,就像Filter一样是规范之一。
- 在Servlet中,所有的监听器接口都是以“Listener”结尾
-
监听器有什么用?
- 监听器实际上是Servlet规范留给我们JavaWeb程序员的特殊时机。
- 特殊时刻如果想执行这段代码,你需要想到使用对应的监听器。
-
Servlet规范中提供了哪些监听器?(监听器里的方法调用根据域,例如Context,服务器启动执行一个方法,服务器关闭执行一个方法。request一次请求就执行一次)
- jakarta.servlet包下:
- ServletContextListener(服务器启动调用init方法,关闭调用destroy方法)
- ServletContextAttributeListener(向ServletContext域存数据调用add方法,删除数据调用remove方法,替换数据调用replace方法)
- ServletRequestListener(一次请求启动调用init方法,请求结束调用destroy方法)
- ServletRequestAttributeListener(向request域存数据调用add方法,删除数据调用remove方法,替换数据调用replace方法)
- jakarta.servlet.http包下:
- HttpSessionListener(session创建调用init方法,session销毁调用destroy方法)
- HttpSessionAttributeListener(向Session域存数据调用add方法,删除数据调用remove方法,替换数据调用replace方法)
- HttpSessionBindingListener(该监听器不需要使用@WebListener进行标注。假设User类实现了该监听器,那么User对象在被放入session的时候触发bind事件,User对象从session中删除的时候,触发unbind事件。假设Customer类没有实现该监听器,那么Customer对象放入session或者从session删除的时候,不会触发bind和unbind事件。)
- HttpSessionIdListener(session的id发生改变的时候,监听器中的唯一一个方法就会被调用。)
- HttpSessionActivationListener(监听session对象的钝化和活化的。钝化:session对象从内存存储到硬盘文件。活化:从硬盘文件把session恢复到内存。)
- jakarta.servlet包下:
-
实现一个监听的步骤:(以ServletContextListener为例:)
-
第一步:编写一个类实现ServletContextListener接口。并且实现里面的方法
-
import jakarta.servlet.ServletContextEvent; import jakarta.servlet.ServletContextListener; public class MyServletContextListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { } @Override public void contextDestroyed(ServletContextEvent sce) { } }
-
-
第二步:在web.xml文件中对MyServletContextListener进行配置:
-
<listener> <listener-class>MyServletContextListener</listener-class> </listener> -
也可以使用注解:
-
import jakarta.servlet.ServletContextEvent; import jakarta.servlet.ServletContextListener; import jakarta.servlet.annotation.WebListener; @WebListener public class MyServletContextListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { } @Override public void contextDestroyed(ServletContextEvent sce) { } }
-
-
-
-
注意:所有监听器中的方法都是不需要javaweb程序员调用,由服务器自动调用。什么时候调用?
- 监听器的方法不需要手动调用。是发生某个事件服务器自动调用。
-
做网站用户在线人数功能:
- 什么代表了用户登录在线?
- session.setAttribute(“user”,userObj);user类型对象只要往session中存储,表示新用户登录
- 什么代表用户退出?
- session对象移除
- session销毁(session超时)
- 什么代表了用户登录在线?
本文详细介绍了使用纯Servlet进行单表的CRUD操作,从项目结构到功能实现,涵盖数据库交互、页面跳转、Servlet注解、模板方法模式优化、会话机制、Cookie和JSP等内容,旨在揭示传统Web开发流程和技巧。


被折叠的 条评论
为什么被折叠?



