赞
踩
【存在问题】
原先仅使用Servlet开发的项目,需要利用Servlet的request对象的out.print方法将html标签输出到浏览器上,导致Servlet类中存在众多html代码。
简而言之,java程序中编写html代码存在以下问题:
【解决方案】
思考:
能否让程序员只需编写Servlet程序中的“前端代码“,然后让机器将我们写的“前端代码”自动翻译生成“Servlet”这种“java”程序。再自动将“java程序”编译成“class”程序,再使用JVM调用这个class中的方法。
---jsp技术!
(1)浏览器中访问index.jsp文件
实际上Tomcat会先将index.jsp文件翻译生成index_jsp.java文件,然后进行编译生成index_jsp.class文件,最后执行class文件,显示浏览器页面
(2)JSP实际上是一个Servlet
当index.jsp访问时,会自动翻译生成index_jsp.java,会自动编译生成index_jsp.class,那么index.jsp就是一个类。
inde_jsp继承HttpJspBase,而HttpJspBase类继承HttpSerlvet,所以inde_jsp就是一个Servlet类
(3)jsp生命周期与Servlet生命周期完全相同
两者是一个东西,无区别
两者都是单例的 (假单例)
【jsp生命周期】
(3)jsp第一次访问效率较低
jsp是一种动态网页开发技术
①JSP是一套规范,所有的web容器/web服务器都遵循该规范进行的翻译。
Servlet是javaEE的13个规范之一,JSP也是规范之一
遵循不同规范时,jsp会按照不同的符号输出在jsp文件的不同位置
每个Web容器,所有的服务器都内置一个JSP翻译引擎
②同时JSP是java程序,本质仍是一个Servlet
Tomcat会将jsp文件变为对应的xx_jsp.java文件,编译为xx_jsp.class文件
③由jsp生成的java文件在哪?
可在Tomcat启动时,打开Tomcat控制台找到CATALINA_BASE,复制其后路径,在本机中访问找到
(开发jsp,最高境界:眼前是jsp,脑里中java代码)
jsp文件中编写的内容,都会被自动翻译到对应的Java文件,从而编译生成.class文件
jsp文件中编写的文字
---翻译到servlet类的service方法的out.write("");中
类似于Servlet中的out.print("")
不同的标签会生成在jsp(生成的java程序)的不同位置
【存在问题】
当在jsp页面中输出中文,会发现页面出现中文乱码问题
【解决方案】
可利用jsp的page指令解决,响应的乱码问题
<%@page contentType="text/html;charset=UTF-8" %> <%--表示响应的内容是text/html,采用的字符集为UTF-8--%>
原理:
添加该指令后,jsp对应的java文件中代码就会相应更改
①<%xxx%>翻译到哪?
②<%xxx%>翻译成了什么?
③<%%>什么时候用?
用于在jsp页面中编写java语句
当jsp中不添加特殊标记的内容,默认输出为字符串到service方法的out.write("")中
当想在jsp页面中编写java语句,应采用<%%>
<%java语句;%>
④注意:
①<%!%>翻译到哪里?
②<%!%>翻译成什么?
③<%!%>什么时候用?
<%!java代码; %>
【注意】同时使用<%①%>和<%!②%>时,java程序中②在上service方法外,①在下service方法里
①<%=xxxx%>会被翻译到哪?
②<%=xxx%>会被翻译成什么?
<%=100+200%> <%--被翻译为out.print(100+200) --%>
③<%=xx%>什么时候用?
<% int a = 5; int b = 4; int i =a+b; %> <%=i%>
jsp中的专业注释为:
<%-- jsp专业注释,不会被翻译到java源代码中 --%>
html注释为
<!--html注释不专业,仍会被翻译到java源代码中-->
要向浏览器输出一个java变量,可使用内置对象out的write("")方法
<% String name = "jack";out.write("name="+name);%>
①固定的内容输出,直接写在jsp文件中即可,不必写到<%%>中,因为jsp文件的内容,本身就会变为ou.write,然后输出到浏览中
②输出的内容若有变量,则利用<%java代码%>的形式展现
③输出内容有java代码,使用以下语法格式:
始终记得:jsp就是servlet,只是职责不同。
jsp的本质就是一个Servlet,但两者职责不同
jsp语法总结:
翻译到jsp生成的java文件serice()方法的out.write("")中
翻译成:out.write("");
当输出静态的字符串到浏览器时
如:html标签,不变的标题文字等等
翻译到jsp生成的java文件service()方法体中
一条条语句,写什么翻译为什么,只是放在service()方法体中
要在service()方法体中定义java代码时
翻译到jsp生成的java文件service方法体外
一条条代码语句,写什么翻译为什么,只是放在service()方法体外
要在service()方法体外定义java代码时
翻译到jsp生成的java文件的service方法中的out.print()中
翻译成: out.print();
要输出动态的内容
<%@page contentType="text/html;charset=UTF-8"%>
为page指令,通过contentType属性用来设置响应的内容类型
主要用于解决防止乱码
原先未改造的代码,主要利用纯servlet编写,弊端是:
采用jsp技术,去代替servlet展示html代码,好处是:
①创建一个新java工程,右键工程->Add Framework support添加web框架支持
②File->Project Structure->Modules ->选中项目->Dependencies添加servlet,jsp的jar
③Add configurations->添加Tomcat,并进行配置->Deployment 设置项目访问路径
④在工程的web->WEB-INF目录下,新建lib包用于存放数据库连接的jar包
⑤在src中创建utils包,放置DBUtils工具类,用于连接数据库
⑥正式开始编码
首先将所有html文件,变为.jsp文件
同时为防止中文乱码,在文件首行添加<%@page contentType="text/html;charset=UTF-8"%>语句
更改原先所有的html静态路径,变为jsp静态路径
注意页面的路径,需要携带/项目名
因为,当前端发送请求路径是绝对路径时,要以"/"开始,加项目名
同时项目名最好不要写死,在此jsp提供了<%=requet.getContextPat()%>解决这个问题
<a href="<%=request.getContextPath()%>/list.jsp">查看</a>
实现页面的正常流转
⑦编码思想,由servlet获取处理数据,jsp去展示
(1)action包下: 编写servlet代码,用于获取数据
@WebServlet({"请求路径,不带项目名"}) -写在类名上,表示该类中所有方法的请求路径前缀为/xx/xx
servlet中直接重写service方法 -request.getSerlvetPath();//得到servlet请求路径 -if结合"".equals(path);//判断请求路径为xx -调用对应的doXxx方法
⑧编写serlvet代码
@WebServlet({"/dept/list","/dept/detail","/dept/delete","/dept/add","/dept/update"}) public class DeptServlet extends HttpServlet { @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String servletPath = request.getServletPath(); if("/dept/list".equals(servletPath)){ doList(request,response); }else if("/dept/detail".equals(servletPath)){ doDetail(request,response); }else if("/dept/delete".equals(servletPath)){ doDel(request,response); }else if("/dept/add".equals(servletPath)){ doAdd(request,response); }else if("/dept/update".equals(servletPath)){ doUpdate(request,response); } } /** * 连接数据库,查询所有部门信息,再跳转至jsp做页面展示 * @param request * @param response * @throws ServletException * @throws IOException */ private void doList(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{ //1.连接数据库 Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; List<Dept> deptList = new ArrayList<>(); try { //2.获取连接 conn = DBUtils.getConnection(); //3.执行查询语句 String sql = "select deptno,dname,loc from dept"; ps = conn.prepareStatement(sql); rs = ps.executeQuery(); while(rs.next()){ String deptno = rs.getString("deptno"); String dname = rs.getString("dname"); String loc = rs.getString("loc"); //创建一个表实体类对象,将数据放入 Dept dept = new Dept(); dept.setDeptno(deptno); dept.setDname(dname); dept.setLoc(loc); //将存放数据的实体类,存入链表 deptList.add(dept); } } catch (SQLException e) { e.printStackTrace(); }finally { //4.释放资源 DBUtils.close(conn,ps,rs); } //将最后的链表数据存入请求域中 request.setAttribute("deptList",deptList); //转发,注意不可重定向,否则无法共享请求域数据 request.getRequestDispatcher("/list.jsp").forward(request,response); } private void doDetail(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException{ //从请求域中获取deptno String deptno = request.getParameter("deptno"); //获取数据库连接 Connection conn = null; ResultSet rs = null; PreparedStatement ps= null; Dept dept = new Dept(); try { conn = DBUtils.getConnection(); String sql = "select dname,loc,principal 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"); String principal = rs.getString("principal"); dept.setDeptno(deptno); dept.setDname(dname); dept.setLoc(loc); dept.setPrincipal(principal); } } catch (SQLException e) { e.printStackTrace(); }finally { DBUtils.close(conn,ps,rs); } request.setAttribute("dept",dept); String f = request.getParameter("f"); if(f.equals("m")){//转发到修改页面 request.getRequestDispatcher("/edit.jsp").forward(request,response); }else if(f.equals("d")){//转发到详情页面 request.getRequestDispatcher("/detail.jsp").forward(request,response); } } private void doDel(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException { String deptno = request.getParameter("deptno"); Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; int count = 0; try { conn = DBUtils.getConnection(); String sql = "delete from dept where deptno = ?"; ps = conn.prepareStatement(sql); ps.setString(1, deptno); count = ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } finally { DBUtils.close(conn, ps, rs); } if (count == 1) {//删除成功,重定向到列表页面 //要加项目名,因为此时是重新发起一次请求 response.sendRedirect(request.getContextPath() + "/dept/list"); } else {//删除失败 response.sendRedirect(request.getContextPath() + "/error.jsp"); } } private void doAdd(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException{ request.setCharacterEncoding("UTF-8"); String deptno = request.getParameter("deptno"); String dname = request.getParameter("dname"); String loc = request.getParameter("loc"); String principal = request.getParameter("principal"); Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; int i = 0; try { conn = DBUtils.getConnection(); String sql = "insert into dept(deptno,dname,loc,principal) values (?,?,?,?)"; ps = conn.prepareStatement(sql); ps.setString(1,deptno); ps.setString(2,dname); ps.setString(3,loc); ps.setString(4,principal); i = ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); }finally { DBUtils.close(conn,ps,rs); } if(i == 1){//添加成功 response.sendRedirect(request.getContextPath() +"/dept/list"); }else { response.sendRedirect(request.getContextPath() +"/error.jsp"); } } private void doUpdate(HttpServletRequest request,HttpServletResponse response)throws IOException,ServletException{ request.setCharacterEncoding("UTF-8"); String deptno = request.getParameter("deptno"); String dname = request.getParameter("dname"); String loc = request.getParameter("loc"); String principal = request.getParameter("principal"); Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; int i = 0; try { conn = DBUtils.getConnection(); String sql = "update dept set dname=?,loc=?,principal=? where deptno = ?"; ps = conn.prepareStatement(sql); ps.setString(4,deptno); ps.setString(1,dname); ps.setString(2,loc); ps.setString(3,principal); i = ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); }finally { DBUtils.close(conn,ps,rs); } if(i == 1){//添加成功 response.sendRedirect(request.getContextPath() +"/dept/list"); }else { response.sendRedirect(request.getContextPath() +"/error.jsp"); } } }
⑨在页面中通过请求域,获取动态展示的数据
<table border="1px" align="center" width="50%"> <tr> <th>序号</th> <th>部门编号</th> <th>部门名字</th> <th>部门地址</th> <th>操作</th> </tr> <% int index = 1; List<Dept> deptList = (List<Dept>) request.getAttribute("deptList"); for(Dept dept :deptList){ %> <tr> <td><%=index++%></td> <td><%=dept.getDeptno()%></td> <td><%=dept.getDname()%></td> <td><%=dept.getLoc()%>></td> <td> <a href="javascript:void(0)" οnclick="window.confirm('确认删除该部门信息吗?')">删除部门</a> <a href="<%=request.getContextPath()%>/edit.jsp">修改部门</a> <a href="<%=request.getContextPath()%>/detail.jsp">查看详情</a> </td> </tr> <%}%>
【拓展】:
如何清除服务器缓存
ctrl+shift+delete
可否只用jsp独立开发web应用?
仅用jsp也可以独立开发web应用,因其本质就是servlet,但是不推荐,会使代码解耦性变差
因为servlet用于数据获取及处理,jsp用于数据展示,jsp中的java代码越少越好
并非一定是jsp,可通过在Tomcat目录/conf/web.xml文件中进行配置即可
因为对于Tomcat而言,jsp就是一个普通的文本文件,web容器会将生成jsp_serlvet.java文件,再编译成class,最后运行调用java对象相关方法,真正执行与jsp文件无关。
<%@ page import="java.util.List" %> <%@ page import="com.bean.Dept" %> <%@page contentType="text/html;charset=UTF-8"%> <!DOCTYPE html> <html lang="en"> <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> <th>操作</th> </tr> <% int index = 1; List<Dept> deptList = (List<Dept>) request.getAttribute("deptList"); for(Dept dept :deptList){ %> <tr> <td><%=index++%></td> <td><%=dept.getDeptno()%></td> <td><%=dept.getDname()%></td> <td><%=dept.getLoc()%></td> <td> <a href="javascript:void(0)" οnclick="del(<%=dept.getDeptno()%>)">删除部门</a> <a href="<%=request.getContextPath()%>/dept/detail?f=m&deptno=<%=dept.getDeptno()%>">修改部门</a> <a href="<%=request.getContextPath()%>/dept/detail?f=d&deptno=<%=dept.getDeptno()%>">查看详情</a> </td> </tr> <%}%> </table> <hr> <a href="<%=request.getContextPath()%>/add.jsp">新增部门</a> <a href="javascript:void(0)" οnclick="del(no)" >删除</a> <script type="text/javascript"> function del(no){ if(window.confirm("亲,删了不可恢复哦!")){ document.location.href = "<%=request.getContextPath()%>/dept/delete?deptno=" + no; } } </script> </body> </html>
①步骤一:创建user表
②步骤二:编写登录页面
③步骤三:编写对应的Servlet处理登录请求
④步骤四:编写提供一个失败页面
【存在问题】:即便用户不登录,但若用户知晓请求路径,即可通过路径跨过登录页面,直接访问部门列表页
【解决方法】:利用session判断其是否登录
要解决用户不登录就可通过部门列表请求路径访问这个问题,可以将用户登录时的信息存储到session中,即当session中存在用户信息,则表示已登录。未存在用户信息则表示未登录
①登录成功时,获取Session对象,同时将登录信息存储在其中。
if(flag){ //获取session对象,没有则创建 HttpSession session = request.getSession(); session.setAttribute("username",username); //跳转到部门列表页面 response.sendRedirect(request.getContextPath()+"/dept/list"); }else { //跳转失败页面 response.sendRedirect(request.getContextPath()+"/error.jsp"); }
②在servlet中(跳转部门列表的请求中),判断是否取得当前session,获取到表示已登录,获取不到表示未登录
@Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //此处不一定要获取到Session对象,有表示曾登录过,无表示未登录过 HttpSession session = request.getSession(false); //获取session对象中的username属性 String username = (String)session.getAttribute("username"); if(session!=null && username!=null){ String servletPath = request.getServletPath(); if("/dept/list".equals(servletPath)){ doList(request,response); }else if("/dept/detail".equals(servletPath)){ doDetail(request,response); }else if("/dept/delete".equals(servletPath)){ doDel(request,response); }else if("/dept/add".equals(servletPath)){ doAdd(request,response); }else if("/dept/update".equals(servletPath)){ doUpdate(request,response); } }else { //跳转到登录页面,request.getContextPath()为项目路径 response.sendRedirect(request.getContextPath()); } }
在访问了jsp页面,就会创建session对象,因为jsp的九大内置对象之一,就有session。如果访问jsp时,不想升生成session,只需在jsp页面中添加以下配置指令:
<%@page session="false"%>
登录后,如果要在jsp页面中显示用户名,只需要利用jsp内置对象的session对象,取出username属性即可
<h1>欢迎<%=session.getAttribute("username")%></h1>
安全退出指将当前的Sesiion对象,进行手动销毁
核心servlet代码如下:
//获取session对象,不需要创建新的,因而为false HttpSession session = request.getSession(false); //如果存在session if(session != null){ //将其手动销毁 session.invalidate(); //跳转至登录页面 response.sendRedirect(request.getContextPath()+'/login.jsp'); }
指令的作用:指导当前jsp的翻译引擎如何进行工作
①include指令
②taglib指令
③page指令
<%@指令名 属性名=属性值 属性名=属性值....%>
<%@page session="true|false"%> true表示启用jsp的内置对象Session,若无session对象,则创建 false表示不启用jsp的内置对象Session,则当前jsp页面中无法使用内置对象session 若未设置,默认true
<%@page contentType="text/json"%> <!--contentType用于设置内容的响应类型,相当于response.setContentType("text/json");--> <%@page contentType="text/json;charset=utf-8"%> <!--在设置响应内容类型的同时,也设置响应时采用的字符集-->
<%@page contentType="text/json" pageEncoding="UTF-8"%> <!--相当于response.setConteneType("text/json;charset=UTF-8")-->
<%@page import="java.util.list, java.util.Date"%> <!-- import属性用于导包-->
<%@page errorPage="/路径地址" %>
【存在问题】:
当设置出错页面时,页面出错会跳转至错误页面,但此刻浏览器和控制台并不会输出错误信息,不利于程序员维护
【解决方法】:
可以启用页面的exception内置对象,其表示刚刚发生的异常对象
<%@page isErrorPage="true"%> <!--启用exception对象--> <% //利用以下语句,打印异常堆栈信息,输出到后台控制台 exception.printStackTrace(); %>
[注:我使用的是jdk17,因而javax.servlet更名为jakar.servlet]
【页面作用域】
包名 jakarta.servlet.jsp.PageContext pageContext
-----------------------------以上是四个作用域对象-----------------------------
1️⃣作用域的大小: pageContext<request<session<application
2️⃣四个作用域都有 :setAttribute、getAttribute、removeAttribute方法
3️⃣使用原则:尽可能使用小的域
----------------------------------------结束-----------------------------------------------
【当前页面对象=this】
包名 java.lang.Object page
【响应对象】
包名 jakarta.servlet.http.HttpServletResponse response
【输出对象】
包名 jakarta.servlet.jsp.JspWriter out
关于B/S结构的会话机制称为session机制
会话对应的英文是:session
一次会话:用户打开浏览器,进行一系列操作,最终关闭浏览器,整个过程称为“一次会话”。
【回顾】**一次请求:**用户在浏览器进行一次点击(超链接/按钮),然后跳转,可以粗略认为是“一次请求”。
java和session都是服务器端的java对象,都在JVM中
①session主要作用:保存会话信息
②为什么需要session对象保存会话状态?
因为Http是一种无状态协议
java的servlet规范中,session对应的类名是:
request请求域(HttpServletRequest)< session会话域(HttpSession) < application应用域(ServletContext)
以上三者都具有getAttribute()【取】和setAttribute()【存】方法
但request作用域和ServletContext应用域,
因而request域太小,ServletContext域太大,最适合的还是session
①获取session对象代码如下:
//从WEB服务器中获取当前session对象,如果未获取session对象,则新建 HttpSession session = request.getSession(); //从Web服务中获取当前session对象,如果未获取到session对象,不新建则返回null HttpSession sesssion = request.getSession(false);
②存储数据,获取数据
session.setAttribute("存储名",存储值名); session.getAttribute("设置取到值的名",取值名);
1️⃣在web服务器中有一个session列表。
类似于map集合:
该map集合的key存储的是sessionid
该map集合的value存储的是session对象
**2️⃣当用户打开浏览器,第一次发起请求时:**服务器会创建一个新的session对象,同时为session对象生成一个session的id,然后web服务器会将session的id发送到浏览器,浏览器再将session的id保存到浏览器缓存中。
**3️⃣用户第二次发起请求时:**会自动将浏览器缓存中的sessionid自动发送给web服务器,服务器获取sessionid,根据sessionid查找其对应的session对象。
用户第N次发起请求,同上.....
4️⃣当用户关闭浏览器后,浏览器缓存中保存的sessionid会消失,下次重新打开浏览器后,缓存中找不到sessionid,就自然找不到web服务器中sessionid对应的session对象,而找不到session对象等同于会话结束,web服务器就会重新再创建一个session对象,生成其对应的sessionid,然后存到浏览器缓存中....
6️⃣同时当会话结束,但其对应的session对象并非立刻销毁,因为Http是无状态协议,所以Tomcat会根据会话超时机制,在某个时间后,若用户仍未请求Tomcat中的该session会话对象时,就将为该用户创建的该session对象给销毁。
7️⃣浏览器关闭会话一定结束么?
不一定,通常情况下,关闭浏览器后,缓存中的sessionid会消失,即访问web服务器就找不到原先对应的session对象。但有时当浏览器打开一定时间,超过一定时间没有进行操作,虽然并没有关闭浏览器,但根据session的超时机制,它也会销毁其对应的session对象。同时还有手动配置超时时长的情况。
8️⃣手动设置超时时长
如果想要手动设置session超时机制的时长,可在web.xml添加以下代码:
<!--在web.xml添加以下配置,表示设置session的超时机制中,超时时长为30分钟--> <session-config> <session-timeout>30</session-timeout> </session-config>
①session对象存储在服务器端,由服务器创建,并销毁
②一个session对象一个会话,浏览器打开->关闭
③一次会话内,包含多个请求
如:南京网友多次请求服务器,获取的都是同一个session1对象,前提是在一次会话内
若:第一次请求服务器,不存在session对象,服务器会为其创建一个新的session对象
而:原先的旧session对象并不会马上销毁,而是根据超时机制:当一定时间内,用户仍未访问该session对象,则销毁
④获取当前session对象时,有两种情况
未获取到,则新建
HttpSession session = request.getSession();
未获取到,则返回null
HttpSession session = request.getSession(false);
①当用户第一次发起请求时,不存在sessionid,因而服务器为其创建一个session对象,将sessionid响应给浏览器,放入缓存
②当用户第N次发起请求时,浏览器缓存以存在sessionid,会请求服务器,获取sessionid对应的session对象
在session的实现原理中,每个session对象都会关联一个sessionid
不同的路径所提交的cookie不同哦!
http协议中规定,任何一个cookie都是由name和value组成的,且都是字符串类型。
Cookie最终保存在浏览器客户端上
cookie和session机制都是为了保存会话状态
cookie和session存在的意义?因为http协议是无状态 无连接协议。
【经典案例】:京东商城,在未登录情况下,仍可以添加商品至购物车,而下次访问购物车商品仍存在
1️⃣做法:
【经典案例】:126邮箱的,十天内免登陆
1️⃣做法:
cookie机制和session机制,属于http协议的一部分,php开发中也有cookie和session机制,只要做web开发,都会使用到。
http协议中规定:当发送请求时,会自动携带该path下的cookie数据给服务器
利用response对象的addCookie()方法,设置不同的关联路径(URL),选择在哪些路径中携带cookie数据,最后发送给服务器
①关于cookie的关联路径:
若此时发送的请求路径为:http://localhost:8080/servlet/cookie/generate,由此生成了cookie
若cookie没有手动设置path,默认path是什么?
**若cookie手动设置了path,**则为设置的路径及其子路径,都会自动发送cookie到服务器
cookie.setPath("/xxx");
①编写jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>$Title$</title> </head> <body> <a href="<%=request.getContextPath()%>/cookie/generate">服务器生成cookie,然后cookie响应给浏览器,浏览器接受cookie,再将cookie放到客户端中</a> </body> </html>
②编写对应的servlet代码
@WebServlet({"/cookie/generate"}) public class GenerateCookie extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //创建Cookie对象 Cookie cookie = new Cookie("productid","1234567891245"); //设置cookie的失效时间 cookie.setMaxAge(30); //设置cookie的关联路径 cookie.setPat(request.getContextPath()); //将Cookie响应到浏览器上 response.addCookie(cookie); } }
③访问编写好的servlet请求路径即可
**接收时,利用request对象的getCookie方法接收**该路径携带的cookie数据,返回类型为String[]
①接收cookie
//通过request对象,接收浏览器发来的cookie Cookie[] cookies = request.getCookies();
①编写页面,跳转至servlet
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>$Title$</title> </head> <body> <a href="<%=request.getContextPath()%>/cookie/send">接收浏览器发来的cookie,在java中接收,同时遍历cookie数据</a> </body> </html>
②编写servlet,通过request对象的方法接收发来的cookie,同时进行遍历
@WebServlet({"/cookie/send"}) public class SendCookie extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取浏览器发送到服务器的cookie数据 //注意:如果浏览器没有提交cookie,则返回null Cookie[] cookies = request.getCookies(); //如果不是null,则遍历取出cookie if(cookies != null){ for (Cookie cookie : cookies){ String name = cookie.getName(); String value = cookie.getValue(); System.out.println(name+"+"+value ); } } } }
③访问路径,再查看控制台信息
①设置cookie的有效时间
cookie.setMaxAge()
未设置cookie的有效时间:默认保存在浏览器的运行内存中(浏览器关闭则消失)
若设置cookie的有效时间>0
若设置cookie的有效时间=0
表示该cookie被删除
使用场景:多用于删除同名cookie
若设置cookie的有效时间<0
②创建cookie
Cookie cookie = new Cookie("存储在cookie的数据名",存储的数据);
类似于126邮箱的30天免登陆
①首先实现基本的登录功能
②修改登录页面,添加勾选复选框[十天内免登陆]
<form action="<%=request.getContextPath()%>/user/login" method="post"> 用户名<input type="text" name="username" value="" > <br> 密码<input type="text" name="password" value=""> <br> <input type="checkbox" name="remember" value="1">十天内免登陆<br> <button type="submit" >登录</button> </form>
③修改Servlet中的login方法
if(flag){ //获取Session HttpSession session = request.getSession(); session.setAttribute("username",username); String remember = request.getParameter("remember"); if("1".equals(remember) ){//如果用户点击十天免登陆 //创建Cookie对象存储用户名,密码 Cookie cookie1 = new Cookie("username",username); Cookie cookie2 = new Cookie("password",password);//真实情况需要加密 // 设置失效时间 十天 cookie1.setMaxAge(60*60*24*10); cookie2.setMaxAge(60*60*24*10); //设置关联路径 cookie1.setPath(request.getContextPath()); cookie2.setPath(request.getContextPath()); //响应cookie给浏览器 response.addCookie(cookie1); response.addCookie(cookie2); } //跳转到部门列表页面 response.sendRedirect(request.getContextPath()+"/dept/list"); }else { //跳转失败页面 response.sendRedirect(request.getContextPath()+"/error.jsp"); }
④在web.xml配置文件中,设置欢迎页,跳转到servlet请求中
<!-- 会访问welcome,不用添加/,直接从根路径开始--> <welcome-file-list> <welcome-file>welcome</welcome-file> </welcome-file-list>
⑤编写WelcomeServlet
用户再次访问时:
要么跳转部门列表页面
要么跳转登录页面
@WebServlet({"/welcome"}) public class WelcomeServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取Cookie,不可能size=0,只可能size>0,或为null Cookie[] cookies = request.getCookies(); String username = null; String password = null; //如果获取的cookie不为空 if(cookies != null){ //循环遍历cookie,得到username和password for(Cookie cookie : cookies){ String name = cookie.getName(); if("username".equals(name)){ username = cookie.getValue(); }else if("password".equals(name)){ password = cookie.getValue(); } } //如果获取的username和password都不为空,跳转至部门列表页面 if(username !=null && password != null){ //进行登录的操作 Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; Boolean flag = false; try { conn = DBUtils.getConnection(); String sql = "select * from user where username = ? and password = ?"; ps = conn.prepareStatement(sql); ps.setString(1, username); ps.setString(2, password); System.out.println(sql); System.out.println(ps); rs = ps.executeQuery(); System.out.println(rs); if(rs.next()){ flag = true; } } catch (SQLException e) { e.printStackTrace(); }finally { DBUtils.close(conn,ps,rs); } if(flag) { //获取Session HttpSession session = request.getSession(); session.setAttribute("username", username); response.sendRedirect(request.getContextPath()+"/dept/list"); } }else { //否则跳转登录页面 response.sendRedirect(request.getContextPath()+"/index.jsp"); } } }
⑥到此,十天免登录功能完成
可访问浏览器,测试点击十天免登陆后成功登录时,再次访问该路径,是否直接跳过登录页面,进入到部门列表
cookie禁用是指,服务器正常发送cookie给浏览器,但浏览器拒收。
当cookie禁用,该怎样实现session机制?
利用URL重写机制
在路径后添加分号 (;),然后将sessionid的值拼接过去
但使用该机制,会大大提高开发者成本,开发者在编写所有请求路径时,都要添加一个Sessionid
因而正常若禁用cookie,就干脆不让用户使用
localhost:8080/项目名/访问资源;jsessionid=xxxxxxxxxxxxxxx
①一共有以下三个域对象:
②三个域的大小关系:
③三者都有以下的公共方法:
④使用原则:尽量使用小的域
EL(Expression Language)表达式语言,可以算是jsp语法的一部分,它有以下功能:
(从域中取+输出到浏览器)从某个域中取数据,然后转换为字符串,输出到浏览器
①作用一:从某个域中取数据
②作用二:将取出的数据转为字符串
③作用三:将字符串输出到浏览器
注意:不能加双引号 “ ”,会被看做字符串
面试题: abc和���和{"abc"}有什么区别?
${abc}表示从某个域中取数据,并且被取的数据name为“abc”,之前一定有该语句:域.setAttribute("abc",对象)
${“abc”}表示将“abc”作为字符串输出到浏览器,而并非从某个域中取到
①第一步:声明当前页面的字符集编码形式
<%page contentType="text/html;charset=UTF-8"%>
②第二步:通过作用域.setAttribute存数据
request.setAttribute("存的值",存储的名)
③第三步:通过EL表达式取数据,并输出浏览器
${xx.xx}
直接在${}中写“存储的数据名“,servlet中利用request.setAttribute("存储的数据名",存储的数据)
【示例】
1️⃣语法
${取值的名}
2️⃣示例
<% request.setAttribute("username","zhangsan") %> <!--取出userename的值--> ${username} 等同于<%=request.getAttribute("username")%>
1️⃣语法
不带引号,会将其看做变量。
带双引号,则会去找 对象的对应属性
常用于要取的属性name含有特殊字符:xxx.xx的情况
eg:${requestScope["adb.de"]}可取出属性名为 adb.de的值
${ 对象名.属性名 } ${ 对象名[" 属性名 "] }
2️⃣底层原理
先创建一个符合javabean规范的User类
底层上:是调用了其属性名对应的getter方法
但是EL省略get同时小写 ≈ 属性名
3️⃣示例
<% User user = new User(); user.setUsername("jackjson"); user.setPassword("1234"); user.setAge(50); //数据必须存储在某个域的范围内,因为EL表达式只能从某个域中取数据 request.setAttribute("userObj",user); %> <!--利用EL表达式取--> <!--输出内容:EL表达式会调用对应的toString()方法--> ${userObj} <!-- ${userObj}底层是从域中取出userObj对象,然后调用其toString()方法--> <!--输出内容:实体类对应的属性名username的值--> ${userObj.username} ${userObj["username"]} 使用这些语法的前提是:userObj对象有getUsername()方法
1️⃣语法
${实体类.对象名.属性值}
2️⃣示例
<% class User{ //创建User对象 private String username; User user = new User(); private String password; user.setUsername("jackjson"); private int age; user.setPassword("1234"); private Address addr; user.setAge(50); .......对应的get、set方法 } //创建Address对象 Address a = new Address(); class Address{ a.setCity("北京"); private String city; a.setStreet("大兴区"); private String street; a.setZipcode("1111"); private String zipcode; ....对应的get、set方法 //设置User对象的address } user.setAddr(a); //将user对象存入域中 request.setAttribute("userObj",user); %> <!-- 利用EL表达式 --> <!--取出user对象是哪个城市的--> ${userObj.addr.city} 相当于user对象.getAddr().getCity() <!--取出user对象是哪个街道的--> ${userObj,addr.street} 相当于user对象.getAddr().getStreet()
1️⃣语法
${集合名.对象名.属性名}
2️⃣示例
①先向域中存储一个Map类型的数据 <% Map<String,User> userMap = new HashMap<>(); User user = new User(); user.setUsername("lisi"); userMap2.put("user",user); request.setAttribute("hhh",userMap2); %> ②将域中的数据取出 <!--取出map的数据--> ${hhh.user.username}
1️⃣语法
${nameArray}
${nameArray[下标值]}
2️⃣示例
<% //①字符串数组对象 String[] usernames = {"zhangsan","lisi","wangwu"}; //向域中存储数组 request.setAttribute("nameArray",usernams); //②实体类数组对象 User u1 = new User(); u1.setUsername("zhangsan"); User u2 = new User(); u2.setUsername("lisi"); User[] user = {u1,u2}; request.setAttribute("userArray",users); %> ${nameArray[0]} <!--取出nameArray数组的第一个--> ${userArray[1].username} <!--取出userArray数组的第二个,其中的username值-->
1️⃣语法
${list列表名[下标值]}
2️⃣示例
<% List<String> list = new ArrayList<>(); list.add("abc"); list.add("def"); request.setAttribute("myList",list) %> ${myList} ${myList[0]} ${myList[1]}
1️⃣语法
2️⃣示例
<% Set<String> set = new HashSet<>(); set.add("a"); set.add("b"); request.setAttribute("set",set); %>
四个域存储数据的属性名一致的情况,取值的优先级是?
优先从小范围内读取数据
EL表达式有四个隐含的隐式的范围对象:
可利用隐含的范围对象,进行指定
${pageScope.data} 从pageContext域中获取data数据
${requestScope.data} 从request域中获取data数据
${sessionScope.data} 从session域中获取data数据
<% //四个域都存储了数据,且name相同 pageContext.setAttribute("data","pageContext"); request.setAttribute("data","request"); session.setAttribute("data","session"); application.setAttribute("data","application"); %> ${data} <!--从小范围到大范围中取:pageContext->request->session->application-->
EL表达式对空值会进行处理,如果利用request.getAttribute("")取值为空时,会输出null,而EL表达式输出,会将null变为一串空字符串,对应页面的展示更为友好。
即EL表达式表面为${},其实还是转化为jsp代码,再翻译为java去执行
${username} ===================等同于============ <%=request.getAttribute("username) == null ? "":request.getAttribute("username")%>
<%@page contentType="text/html;charset=UTF-8" isELIgnored="true"%>
\${username}
(1)作用
其常用于获取应用的根路径:${pageContext.request.contextPath}
【省流】:EL中需要获取request对象,而非直接得到,用pageContext隐含对象的getRequest()方法得到
jsp中九大内置对象就有pageContext,与EL表达式的隐式对象pageContext是同一个。
①jsp中获取request对象有两种方法:
①request ②pageContext.getRequest() 在jsp中的getRequet()似乎沒用,但其实是为在EL表达式中使用作铺垫的
②EL表达式中获取request对象:
${pageContext.request} <!--别忘了EL表达式中.request就是调用对应的getRequest()方法哦!-->
通过pageContext隐式对象的getRequest()方法得到request对象后,就可以调用.一切request相关的方法,只需注意EL表达式中getXxx()-->xxx即可
③获取应用的根路径
①jsp中可以写成 <!--此处需要强转,因为只有HttpSerlvet才有getContextPath()这个方法--> <%=(HttpServeltRequest)pageContext.getRequest()).getContextPath() %> <%=request.getContextPath()%> ②对应的EL表达式为 ${pageContext.request.contextPath}
(1)作用
用于获取页面路径中,请求参数一位数组的第一个参数
(2)语法
${param.参数名}
(3)示例
若用户在浏览器上提交单值数据:http://localhost:8080/jsp/15.jsp?username=lisi
①jsp中: 用户名:<%=request.getParameter("username")%> ②EL表达式中: 用户名:${param.username}
若用户在浏览器上提交多值数据:http://localhost:8080/jsp/15.jsp?aihao=study&aihao=dance&aihao=read
①jsp中 爱好:<%=request.getParameter("username")%> ②EL表达式中:会返回第一个值,正常应使用paramValues 爱好:${param.aihao}
(1)作用
获取页面路径中,请求参数一位数组的所有参数值
(2)语法
${paramValues.参数名}
(3)示例
若用户在浏览器上提交多值数据:http://localhost:8080/jsp/15.jsp?aihao=study&aihao=dance&aihao=read
①jsp中 爱好:<%=request.getParameterValues("aihao")%> ②EL表达式中 <!--获取的是一位数组--> 爱好:${paramValues.aihao} <!--获取数组中的元素--> 爱好:${paramVlaue.aihao[0]}
(1)作用
获取web.xml中配置的ServletContext的初始化参数
ServletContext是Serlvet上下文对象,对应的JSP九大内置对象中的:application
(2)语法
initParam.属性名
(3)示例
web.xml
<context-param> <param-name>pageSize</param-name> <param-value>20</param-value> </context-param> <context-param> <param-name>pageNum</param-name> <param-value>5</param-value> </context-param>
①jsp获取ServletContext中的初始化参数
<% String pageSize = application.getInitParameter("pageSize"); String pageNum = application.getInitParameter("pageNum"); %> 记录条数:<%=pageSize%> 页码: <%=pageNum%>
②EL表达式获取ServletContext中的初始化参数
记录条数:${initParam.pageSize} 页码: ${initParam.pageNum}
+ - * / %
①+号只进行求和,不进行拼接
${20+10} //30 ${20+"10"} //30 ${20+"abc"} //报错 ${"asd"+"axs"} //报错
== != < <= > >= eq
EL表达式的这两个运算符,都调用了equals方法,用于比较两个变量的引用是否相同
${"abc"=="abc"}//true ${"abc"}==${"abc"} //acb==abc
<% Object o1 = new Object(); request.setAttribute("o1",o1); request.setAttribute("o2",o1); %> ${o1 == o2} //true <% String s1 = "abc"; String s2 = "abc"; request.setAttribute("a",s1); request.setAttribute("b",s2); %> ${a == b}//false <% String s1 = new String(); String s2 = new String(); request.setAttribute("a",s1); request.setAttibute("b",s2); %> ${a == b}//true <% Object o1 = new Object(); Object o2 = new Object(); request.setAttribute("o1",o1); request.setAttribute("o2",o2); %> ${o1 == o2}//false
也调用equals方法, 表示取反
${!(stu1 eq stu2)}
! && || not and or
? :
示例:
${empty param.username ? "對不起,用戶名爲空" : "成功登录"}
[] 和 .
判断是否为空,空->true,非空->false
${empty param.user} <!--判断获取到user参数是否为空--> ${not empty param.user} <!--判断获取到user参数是否不为空--> ${!empty param.user} <!--判断是否获取到user参数是否不为空--> ${empty param.password == null} <!--前半部分是boolean类型,与null类型不相等,所以结果为false-->
JSTL全称Java Standard Tag Lib(java标准的标签库)
JSTL标签库通常结合EL表达式一起使用,目的是消除JSP中的java代码
引入标签库的uri后面路径,指向一个xxx.tld文件
而tld文件实际上是一个xml配置文件
在tld文件中,描述了“标签”和“java”类之间的关系
常用的核心标签库对应的tld文件是?
tld文件在哪?
源码解析:配置文件tld
<tag> <description> 对该标签的描述 </description> <name>标签名</name> <tag-class>标签对应的java类</tag-class> <body-content>标签体中可以出现的内容</body-content> <!--如果是jsp,表示该标签体中可以出现所有符合jsp语法的代码,如EL表达式--> <!--属性--> <attribute> <description>对属性的描述</description> <name>属性名</name> <required>是否为必须,false非必须,true表示必须</required> <rtexprvalue>该属性是否支持EL表达式</rtexprvalue> </attribute> </tag>
<c:forEach items="属性"> 标签 标签体 </c:forEach>
jar包下载地址:**http://tomcat.apache.org/taglibs/standard/
①点击download,耐心等待跳转页面
②下载taglibs-standard-impl-1.2.5.jar 和 taglibs-standard-spec-1.2.5.jar
在下载页面中一共有4个jar包:
Impl: taglibs-standard-impl-1.2.5.jar JSTL实现类库
Spec: taglibs-standard-spec-1.2.5.jar JSTL标准接口
EL: taglibs-standard-jstlel-1.2.5.jar JSTL1.0标签-EL相关
Compat: taglibs-standard-compat-1.2.5.jar 兼容版本
从README得知:
如果不使用JSTL1.0标签,可以忽略taglibs-standard-jstlel包, README没有介绍taglibs-standard-compat
包,估计应该是兼容以前版本
jakarta.servlet.jsp.jstl-2.0.0.jar下载地址
jakarta.servlet.jsp.jstl-api-2.0.0.jar下载地址
点击以上两个链接,耐心等待,即可直接下载,注意根据项目使用的jdk版本,需要使用引入对应的jar包,否则运行项目,会出现报错。
第一步:引入JSTL标签库对应的jar包
在IDEA中如何引入?
【IDEA->WEB-INF下新建lib目录,jar包拷贝到lib中,Add lib】
❗引入时,注意保持jar的版本一致,统一为tomcat10之前/之后
什么时候需要将引入的jar放入lib目录下?
当引入的jar包是Tomcat中没有的,必须放在lib目录下
tomcat10之后:
jakarta.servlet.jsp.jstl-2.0.0.jar
jakarta.servlet.jsp.jstl-api-2.0.0.jar
tomcat10之前:
javax.servlet.jsp.jstl.jar
taglibs-standard-impl-1.2.5.jar
taglibs-standard-spec-1.2.5.jar
第二步:在jsp页面中引入要使用的标签库(采用taglib指令)
<!--核心标签库--> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <!--格式化标签库:负责格式化操作--> <%@taglib prefix="fmt" uri="http://java.sum.com/jsp/jstl/fmt"%> <!--sql标签库--> <%@taglib prefix="sql" uri="http://java.cum.com/jsp/jstl/sql"%>
<%@page contentType="text.html;charset=UTF-8"%> <%@taglib prefix="c" uri="http://java.sum.com/hsp/jstl/core"%> <% //创建List集合 List<Student> stuList = new ArrayList<>(); //创建Stduent对象 Student s1 = new Student("110","经常"); Student s2 = new Student("220","获取"); Student s3 = new Student("330","消防车"); //添加到List集合中 stuList.add(s1); stuList.add(S2); stuList.add(s3); //将list集合存储到请求域中 request.setAttribute("stuList",stuList); %> <!--遍历stuList集合的数据,并输出到浏览器--> <!--①使用java代码--> <% List<Strudent> stuList = (List<Student>)request.getAttribute("stuList"); for(stu : stuList){ %> id:<%=stu.getId()%>,name:<%=stu.getName()%> <% } %> <!--②使用jstl标签库:forEach标签--> <c:forEach items="stuList" var="s"> id:${s.id} name:${s.name} </c:forEach>
1️⃣作用
forEach标签用于循环
2️⃣语法
①遍历集合
<%@page contentType="text/html;charset=UTF-8"%> <%@taglib prefix="c" uri="http://java.sum.com/jsp/jstl/core"%> <% //创建List集合 List<Student> stuList = new ArrayList<>(); //创建Stduent对象 Student s1 = new Student("110","经常"); Student s2 = new Student("220","获取"); Student s3 = new Student("330","消防车"); //添加到List集合中 stuList.add(s1); stuList.add(S2); stuList.add(s3); //将list集合存储到请求域中 request.setAttribute("stuList",stuList); %> <c:forEach items="stuList" var="s" varStatus="stuStatus"> 编号:${stuStatus.count} id:${s.id} </c:forEach>
②遍历变量
var:
begin
end
step
<c:forEach var="i" begin="1" end="10" step="1"> ${i} </c:forEach> 输出:1 2 3 4 5 6 7 8 9 10
3️⃣示例
<%@page contentType="text/html;charset=UTF-8"%> <%@taglib prefix="c" uri="http://java.sum.com/jsp/jstl/core"%> <% //创建List集合 List<Student> stuList = new ArrayList<>(); //创建Stduent对象 Student s1 = new Student("110","经常"); Student s2 = new Student("220","获取"); Student s3 = new Student("330","消防车"); //添加到List集合中 stuList.add(s1); stuList.add(S2); stuList.add(s3); //将list集合存储到请求域中 request.setAttribute("stuList",stuList); %> <c:forEach items="stuList" var="s"> id:${s.id} name:${s.name} </c:forEach>
1️⃣作用
if标签用于判断
2️⃣语法
test:
判断的条件,为真执行,为假不执行
是必须的
支持EL表达式
只能是Boolean类型
var:
每次判断的结果值 true/false
非必须的
支持EL表达式
scope:
<c:if test="" var="" scope=""> </c:if>
3️⃣示例
<%@page contentType="text/html;charset=UTF-8"%> <%@taglib prefix="c" uri="http://java.sum.com/jsp/jstl/core"%> <c:if test="${empty param.username}"> 如果param中的username为空,则显示该句话 </c:if> <c:if test="${not empty param.user}"> 如果param中的username不为空,则显示这句话 </c:if> <!--无else标签时,可用两个if替代-->
1️⃣作用
多分支条件选择,"当....执行....;否则...."
2️⃣语法
<c:choose> <c:when test=""></c:when> <c:when test=""></c:when> <c:when test=""></c:when> <c:otherwise> </c:otherwise> </c:choose>
3️⃣示例
<%@page contentType="text/html;charset=UTF-8"%> <%@taglib prefix="c" uri="http://java.sum.com/jsp/jstl/core"%> <c:choose> <c:when test="${param.age < 18}"> 青少年 </c:when> <c:when test="${param.age < 35 }"> 青年 </c:when> <c:when test="${param.age < 50}"> 中年 </c:when> <c:otherwise> 老年 </c:otherwise> </c:choose>
利用servlet+jsp+EL表达式+JSTL标签库
1️⃣EL表达式
将项目上下文路径由jsp的<%=request.getContextPath()%>变为${pageContext.request.contextPath}
2️⃣JSTL标签库
先向页面引入
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
forEach、if、choose标签,将jsp页面的循环,变为< c:forEach>< /c:forEach>+EL表达式${xx.xx}取值
3️⃣利用EL表达式和JSTL标签库,都是为了将jsp页面中的java代码尽可能的减少
因而只在jsp页面中添加改造的代码
(1)base标签
作用:设置整个页面的基础路径
常用写法:
写死的
http://localhost:8080/oa
动态的
协议://服务器IP:端口号/项目根路径/
${pageContext.request.scheme}://${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.contextPath}/
在HTML代码中,有一个base标签,能设置整个网页的基础路径
<title>首页</title> <base href="http://localhost:8080/oa"> <base href="${pageContext.request.scheme}://${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.contextPath}/"> <!--设置整个网页的基础路径写法有以上两种声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/386921
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。