目录
第一节:Servlet声明周期
第一个:Servlet的声明周期——servlet谁new 什么时候new new几次
new出来之后他都干啥:每一次请求他都干了什么。
什么时候它死了:才会释放资源。
第二个:
书写一个案例:连接数据库做查询
第三个:
登录案例
1. 面试题目
Get和Post的区别
Servlet的生命周期
转发和重定向
JSP原理
2.本节内容:
1. Servlet的生命周期
2. Servlet API :request和response
3. 转发与重定向
3. 具体内容
3.1 Servlet工作基本原理
A. 访问流程
1.浏览器端发出请求--url路径--->
2.匹配路径映射:web.xml 注解:反正无论如何一个类都有一个或者多个路径映射。——一个路径只能对应一个类,但是一个类可以对应多个路径
3.开始这行这个类的:service方法
4.最终结果响应到客户端
B. 生命周期
通过Servlet类图讲述生命周期三个方法的由来以及意义。
tomcat启动的时候:
此时判断servlet配置的时候是否配置了启动即加载——如果配置了启动即加载,那么此时就会将你的servlet给new出来。
请求来临的时候:tomcat会判断是否已经new,如果没有new那么将会new对象——同时将会直接调用init初始化方法。——说明:servlet是在项目启动或者第一次请求的时候new一次
每一次请求:分配线程,调用service方法,service会判断请求方式,如果get请求调用doGet方法 post请求调用doPost方法。
tomcat关闭的时候销毁Servlet,销毁之前会调用destory方法
C. Servlet容器怎样装载Servlet:——Servlet的生命周期
Servlet容器启动时自动装载某些Servlet,需要在web.xml中为Servlet设置的<load-on-startup>,数字越小加载优先级越高。
在Servlet容器启动后或者第一次请求时,会加载Servlet。只加载一次。
Servlet的类文件被更新后,重新装载Servlet
加载Servlet需要执行init方法
每次请求都会调用Service方法(service会根据请求的类别不同——get请求调用doGet如果post请求调用dopost)
关闭项目工程执行destory方法
D. Resquest和Response对象
Request对象:接口封装了客户请求信息,如客户请求方式、参数、客户使用的协议、以及发出请 求的远程主机信息等,其主要方法:
a) String getParamter(String paramName);//获取请求参数
b) String[] getParamterValues(String paramName);//复选框:
c) void setCharacterEncoding(String encode);//接受参数乱码
d) void setAttribute(String name,Object value);
e) Object getAttribute(String name);
f) RequestDispatcher getRequestDispatcher(String rediret)
Response对象:接口封装了服务器响应信息,主要用来输出信息到客户端浏览器,其主要方法:
response.setCharacterEncoding();//相应乱码
Response.setContentType("text/html;charset=utf-8");//设置响应的内容类型
PrintWriter response.getWriter();获得响应的输出流//输出相应
response.sendRedirect(redirect);重定向到指定的网址
E. 转发和重定向
转发:服务器端通过路径找对应资源的一个过程。
特点:客户端地址栏不改变 只能在本工程中进行转发 效率相对快 公用同一个request对象(可以带参:request域中参数各个serlvet都可以拿到,并且也都可以放里边放入参数,放入一周其他的也可以用)
重定向:服务器向客户端响应了地址,客户端地址栏重新发起一个请求。
特性:
地址栏会改变 可跨工程 效率慢 并且:每一个请求都是独立的request对象,所以不能带参。
3.2 登录验证
找servlet:项目根/servlet的urlPatterns
<!--action是路径:路径需要带着项目根:/login:urlPatterns = "/login"-->
<form action="/ser02_war_exploded/login" method="get">
<input type="text" name="username" /><br/>
<input type="text" name="pwd" /><br/>
<input type="submit" value="登录" /><br/>
</form>
@WebServlet(name = "Login",urlPatterns = "/login")
访问静态文件:以web为根进行找。
name对应到参数?参数的位置
getParamter:?参数的名--最终其实是和name做对应的:来获取表单元素值。
3.3 查询功能
public class StudentDaoImpl implements StudentDao{
DbUtil dbUtil=new DbUtil();
@Override
public List<Map> selectStudent() throws SQLException {
//get就相当于之前main方法:
String sql="select * from studentinfo";
List<Map> list=dbUtil.executeQuery(sql,null);
return list;
}
}
@WebServlet(name = "SelectStudent",urlPatterns = "/selectStudent")
public class SelectStudent extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//get里调用post或者post中调用get:只有一套
this.doGet(request,response);
}
//sql 业务 页面的生成 路径的处理--dao模式:sql 业务分离
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//get就相当于之前main方法:
StudentDao studentDao=new StudentDaoImpl();
try {
List<Map> list=studentDao.selectStudent();
//生成html
String ht="";
ht=ht+"<html>"; ht=ht+"<head>";
ht=ht+"<title> 学生列表 </title>";
ht=ht+"</head>"; ht=ht+"<body>";
for(Map m:list){
//这个位置需要完全和数据库列表一致:DButil封装的时候:列名做的key
ht=ht+m.get("StudentName")+"=="+m.get("Gender")+"<br/>";
}
ht=ht+"</body>"; ht=ht+"</html>";
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
//获取响应流
PrintWriter out=response.getWriter();
out.println(ht);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
3.4 改造查询:
利用转发和重定向重构刚才的案例
登录:成功重定向到查询
查询转发到展示
第二节:MVC和EL以及JSTL
1. 面试题目
MVC模式
JSP的9大内置对象
EL表达式的内置的对象
JSP运行原理
2.具体内容
2.1 MVC模式:
分层:好处目前只表现表面——固定格式
代码维护 方便扩展 代码复用。
什么是MVC模式:全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。
Model(模型)是应用程序中用于
通常模型对象负责在数据库中存取数据。
View(视图)是应用程序中处理数据显示的部分。
通常视图是依据模型数据创建的。
Controller(控制器)是应用程序中处理用户交互的部分。
通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据
MVC只分三层
MVC+DAO模式:四层:Model一份为二:Servlet+dao
dao层只负责写sql
/**
* @author KiwiFruit jitongke
* @create 2022-05-14 9:08
* @description
*/
public class StudentDaoImpl implements StudentDao{
DbUtil dbUtil=new DbUtil();
@Override
public List<Map> selectStudent() throws SQLException {
return dbUtil.executeQuery("select * from studentinfo",null);
}
}
service负责业务逻辑:没有业务他就是一个传话筒
Plain Text
public class StudentDaoImpl implements StudentDao{
DbUtil dbUtil=new DbUtil();
@Override
public List<Map> selectStudent() throws SQLException {
return dbUtil.executeQuery("select * from studentinfo",null);
}
}
控制层接收请求:如果需要展示转发给view展示,如果需要控制走向他进行转发重定向
@WebServlet(name = "SelectStudent",urlPatterns = "/selectStudent")
public class SelectStudent extends HttpServlet {
//获取数据 和拼接字符串响应拆开
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
StudentService studentService=new StudentServiceImpl();
try {
List<Map> list=studentService.selectStudent();
request.setAttribute("list",list);
//转发不用谢项目根:不能跨工程---可以带参数
request.getRequestDispatcher("/showStudent")
.forward(request,response);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
View:用来书写html——如果这个html是动态拼接的,需要用到servlet拼接
@WebServlet(name = "ShowStudent",urlPatterns = "/showStudent")
public class ShowStudent extends HttpServlet {
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<Map> list= (List<Map>) request.getAttribute("list");
//生成html
String ht="";
ht=ht+"<html>";
ht=ht+"<head>";
ht=ht+"<title> 学生列表 </title>";
ht=ht+"</head>";
ht=ht+"<body>";
for(Map m:list){
//这个位置需要完全和数据库列表一致:DButil封装的时候:列名做的key
ht=ht+m.get("StudentName")+"=="+m.get("Gender")+"<br/>";
}
ht=ht+"</body>";
ht=ht+"</html>";
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
//获取响应流
PrintWriter out=response.getWriter();
out.println(ht);
}
}
2.2 JSP基本概念:
JSP中可以直接书写html元素,java需要特殊形式来书写。
本质上——JSP就是serlvet,最终运行的时候都会转换成servlet来运行。
JSP就是在Servlet基础上进行提升,让程序员直接在html基础上进行编程,而让Servlet容器替程序员将JSP翻译成Servlet。
JSP中允许在HTML标签中用<% %>嵌入java代码来控制页面元素
<!--servlet:可以直接书写java html需要字符串拼接-->
<!--JSP:可以直接html java需要拼接-->
<% List<Map> list= (List<Map>) request.getAttribute("list"); %>
<table>
<tr>
<td>学号</td>
<td>姓名</td>
<td>性别</td>
<td>年龄</td>
</tr>
<%
for(Map m:list){
%>
<tr>
<td><%out.print(m.get("StudentID"));%></td>
<td><%=m.get("StudentName")%></td>
<td><%=m.get("Gender")%></td>
<td><%=m.get("Age")%></td>
</tr>
<%
}
%>
</table>
JSP运行流程
JSP的本质是一个Servlet。
请求到达JSP-->转换成servlet.java--->编译成Class文件,然后运行响应出结果。
JSP效率很低——以后没人JSP
JSP内置对象
JSP中可以直接拿来使用的对象。由容器进行初始化
JSP 隐式对象是 Web 容器加载的一组类
它不像一般的Java对象那样用”new”去获取实例,而是可以直接在JSP页面使用的对象
隐式对象的名称是 JSP 的保留字
JSP 使用 Java 定义的隐式对象来访问网页的动态内容
JSP运行流程
JSP的内置对象
MVC模式——三个背会
2.3 EL:
EL:用来展示信息的:rqeust域 session Application
概念:
JSP表达式语言(EL):简化JSP中访问数据的一个表达式语言。
隐式对象:
Page:页面。有效范围就是当前页
Request:请求。里边可以放参数,有效时间——一次请求。
Session:会话。里边可以放参数,有效时间——一次会话(不会随着请求结束而结束)
Application:上下文。里边方参数。有效时间——项目启动-项目结束。所有用户所用功能全部共享。——项目里目前有多少人在线。整个项目共享
requestscope可以省略:aa:先去page --request--session--application
param:paramvalue:reqeust.getParamter():?表单传递--JSP跳转JSP传递的参数? 表单传递:在JSP如何获取
EL隐式对象 | 说明 | |
pageScope | 映射对象 | 页面中作用域的属性 |
requestScope | ||
sessionScope | ||
applicationScope | ||
param | 请求参数映射(JSP跳转jsp)——request.getPara(); | |
paramValues | ||
header | 头文件值映射 | |
headerValues | ||
cookie | cookie值映射 | |
initParam | 上下文的初始化参数映射 | |
pageContext | 是pageContext对象的实际引用。 |
<body>
<h1>这是java读取出来的</h1>
读取Map:<%=( (Map)request.getAttribute("stu") ).get("name")%><br/>
读取单一字符串:<%=request.getAttribute("testname")%><br/>
读取实体:<%=( (TabStudent)request.getAttribute("student") ).getName()%><br/>
读取的列表:<%=( (List<TabStudent>)request.getAttribute("stus") ).get(0).getName()%><br/>
读取的列表:<%=( (List<TabStudent>)request.getAttribute("stus") ).get(1).getName()%><br/>
<h1>EL表达式的语法:</h1>
读取Map:<%--${requestScope.req放入的时候的名字.key}--%>${requestScope.stu.name}--${requestScope.stu["name"]}<br/>
读取单一字符串:<%--${requestScope.req放入的时候的名字}--%>${requestScope.testname}<br/>
读取实体:<%--${requestScope.req放入的时候的名字.实体属性名}--%>${requestScope.student.name}--${requestScope.student["name"]}<br/>
读取列表:<%--${requestScope.req放入的时候的名字[0]}--%>${requestScope.stus[0].name}<br/>
requestScope可以省略:${stus[1].name}
</body>
/**
* @author KiwiFruit jitongke
* @create 2022-05-17 9:17
* @description
*/
@WebServlet("/el")
public class ElTest extends HttpServlet {
//这个类就是为了往req中放入各种参数信息——目的是为了学习El表达式如何从这里把值取出来的。
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Map map=new HashMap();
map.put("name","zhangsan");
map.put("age","11");
req.setAttribute("stu",map);
req.setAttribute("testname","这是一个el表达式的测试案例");
TabStudent student=new TabStudent(1,"AAA","110");
req.setAttribute("student",student);
List<TabStudent> stus=new ArrayList<>();
stus.add(new TabStudent(1,"AAA","110"));
stus.add(new TabStudent(2,"BBB","111"));
stus.add(new TabStudent(3,"CCC","112"));
stus.add(new TabStudent(4,"DDD","113"));
req.setAttribute("stus",stus);
req.getRequestDispatcher("/test/elTest.jsp").forward(req,resp);
}
}
EL表达式的运算符
算术运算符: + - * /(div)
比较运算符: >(gt) <(lt) >=(ge) <=(le) == (eq) ${a eq b}
逻辑运算符:&& and || or !
判空:empty
2.4 JSTL:
if 和 for 为了简化不写java代码:JSTL
<!--test中是判断式,必须书写true false;el表达式返回的true或者false-->
<c:if test="${empty stus}">
列表中没有数据<br/>
</c:if>
<c:if test="${!empty stus}">
<!--items:是遍历的对象 s:每一行的对象变量名:s=stus.get(下标) varStatus:行号的有关封装:第几行 下标-->
<c:forEach items="${stus}" var="s" varStatus="sa">
${sa.index+1}----${s.name}--${s.phone}<br/>
</c:forEach>
</c:if>
3.综合案例
3.1 查询:
先访问servlet:进行数据查询
//别名可以不写,如果写了全工程不能重名 路径必须得写:必须/ 路径不能重复
@WebServlet(name = "SelectStudent",urlPatterns = "/selectStudent")
public class SelectStudent extends HttpServlet {
//获取数据 和拼接字符串响应拆开
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
StudentService studentService=new StudentServiceImpl();
try {
List<Map> list=studentService.selectStudent();
request.setAttribute("list",list);
//转发不用谢项目根:不能跨工程---可以带参数
request.getRequestDispatcher("/html/showStudent.jsp")
.forward(request,response);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
servlet转发到jsp进行展示
<table>
<tr>
<td>学号</td>
<td>姓名</td>
<td>性别</td>
<td>年龄</td>
</tr>
<c:forEach items="${list}" var="m">
<tr>
<td>${m.StudentID}</td>
<td>${m.StudentName}</td>
<td>${m.Gender}</td>
<td>${m.Age}</td>
</tr>
</c:forEach>
</table>
3.2 新增
表单:
<form action="/servlet04_war_exploded/addStudent" method="post">
<input type="text" name="studentname"/><br/>
<input type="text" name="age"/><br/>
<input type="text" name="gender"/><br/>
<input type="submit" value="新增"/>
</form>
servlet:
@WebServlet(name = "AddStudent",urlPatterns = "/addStudent")
public class AddStudent extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");//解决接收参数的乱码
//接收前台参数:封装一下-Map或者实体
String n=request.getParameter("studentname");//前台的name对应 ?studentname=zs
String age=request.getParameter("age");
String gender=request.getParameter("gender");
Map map=new HashMap();
map.put("a",n);//这是可以不对应的一个案例:作为了解,不要用。
map.put("age",age);
map.put("gender",gender);//map的key可以随便写,你怎么写回头别的层怎么取--为了保证好理解--一般保持一致——一般通篇和数据库列名保持一致
//传递给service--service调用dao层最后将成功还是失败返回
StudentService studentService=new StudentServiceImpl();
try {
boolean s=studentService.insertStudent(map);
if(s){
response.sendRedirect("/servlet04_war_exploded/selectStudent");
}else{
response.sendRedirect("/servlet04_war_exploded/html/addStudent.jsp");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
//如果成功回查询 如果失败:去新增页面
}
}
service:
@Override
public boolean insertStudent(Map map) throws SQLException {
int i=studentDao.insertStudent(map);
if(i>0){
return true;
}
return false;
}
dao:
Plain Text
/* Map map=new HashMap();
map.put("a",n);//这是可以不对应的一个案例:作为了解,不要用。
map.put("age",age);
map.put("gender",gender);*/
@Override
public int insertStudent(Map map) throws SQLException {
String sql="insert into Studentinfo(studentname,gender,age)values(?,?,?)";
return dbUtil.executeUpdate(sql,new Object[]{map.get("a"),map.get("gender"),map.get("age")});
}
前台录入:控制层接收参数 --service传话--dao写sql
3.3 修改
3.3.1 回显:先显示已有的值
select * from studentinfo where studentid=?
1.如何在表格中每一行都加一个修改按钮并传输一个id到控制层
<tr>
<%-- ListMap中过来的,dbutil查询出来的--数据库列名需要保持一致:
数据库的列名作为了map的key封装了。
map.put(metaData.getColumnName(i).toLowerCase(),rs.getObject(i));--%>
<td>${ss.studentid}</td>
<td>${ss.studentname}</td>
<td>${ss.gender}</td>
<td>${ss.age}</td>
<td>
<a href="/tongbu_war_exploded/selectStudentById?studentid=${ss.studentid}">修改</a>
</td>
</tr>
2.控制层获取id,然后service--调用dao执行根据id获取map
@WebServlet(name = "SelectStudentById",urlPatterns = "/selectStudentById")
public class SelectStudentById extends HttpServlet {
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//前台过来的参数都是字符串——需要转换成int——int和字符串就不是一个体系_一般依赖Integer.parseInt
String studentidStr=request.getParameter("studentid");
Integer studentid=Integer.parseInt(studentidStr);
//控制层找service
StudentService studentService=new StudentServiceImpl();
try {
Map map=studentService.selectStudentById(studentid);
request.setAttribute("stu",map);
request.getRequestDispatcher("/html/editStudent.jsp").forward(request,response);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
3.转发到前台表单进行回显:map的取值——el
<!-- request.setAttribute("stu",map); stu.studentid:这个是数据库的列名转换到map中的key:封装全部转换了小写-->
<form action="/tongbu_war_exploded/editStudent" method="post">
<input type="text" name="studentid" value="${stu.studentid}"/><br/>
<input type="text" name="studentname" value="${stu.studentname}"/><br/>
<input type="text" name="age" value="${stu.age}"/><br/>
<input type="text" name="gender" value="${stu.gender}"/><br/>
<input type="text" name="phone" value="${stu.phone}"/><br/>
<input type="submit" value="修改"/>
</form>
3.3.2 入库修改
update studentinfo set ? where studentid=?
@WebServlet(name = "EditStudent",urlPatterns = "/editStudent")
public class EditStudent extends HttpServlet {
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
String studentid=request.getParameter("studentid");
String studentname=request.getParameter("studentname");
String gender=request.getParameter("gender");
String age=request.getParameter("age");
String phone=request.getParameter("phone");
Map map=new HashMap();
map.put("studentid",studentid);
map.put("studentname",studentname);
map.put("gender",gender);
map.put("age",age);
map.put("phone",phone);
//调用service完成修改:
StudentService studentService=new StudentServiceImpl();
try {
boolean s=studentService.updateStudent(map);
if(s){
response.sendRedirect("/tongbu_war_exploded/selectStudent");
}else{
//去根据id查询然后跳转到回显页面重回修改页面
response.sendRedirect("/tongbu_war_exploded/selectStudentById?studentid="+studentid);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
第三节:异步操作
同步:浏览器同一时间只能发出一个请求,这个请求如果涉及过多得serlvet转发,那么耗时过长,前台长时间大白板,体验很不好。
缺点:体验不好,有可能长时间得大白板,突然就都出来了;跳来跳去。
同步:脱离增删改查来说:所有请求是按照顺序走得,第一个走不完就不可能开启第二个请求。
事实上:京东 淘宝:信息展示并不是突然都出来,而是陆陆续续出来得。
异步概念
同一时间可以发起多个请求,并且多个请求之间互补等待,各自信息反馈之后做自己得局部刷新即可
常规得地址栏:只能有一个路径,所以做不多同一时间发起多个请求——
——JS可以可以做到发起异步请求。——XMLRequest,代码比较长——Jquery中封装了ajax可以发起异步请求。
好处:同一时间可以发起多个请求,可以做到局部刷新,感觉更好。
1.异步操作
1.1 Ajax:
Asynchronous JavaScript And XML
jquery提供的一个异步操作。
- 异步请求
- 局部刷新——DOM操作
- javascript
1.2 ajax语法
1.
$.ajax({
url:"路径",//不会体现在浏览器的地址栏,浏览器显示的index.jsp
data:{},//传递的参数
type:"post",//请求方式
dataType:"text",//响应的数据是什么格式——服务器给客户端的数据是什么格式——text文本字符串 xml JSON字符串
success:function (returnData){//reData接收的是服务器响应的内容
//自己写局部刷新:DOM
}
})
2.
$.get("url",{},function (reData){},"text");
3.
$.post("url",{},function (reData){},"text");
1.3 异步查询
@WebServlet(name = "Select01",urlPatterns = "/select01")
public class Select01 extends HttpServlet {
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
StudentService studentService=new StudentServiceImpl();
try {
List list=studentService.selectStudent();
response.setCharacterEncoding("utf-8");
PrintWriter out=response.getWriter();
//前台不认识list所以换成json字符串
ObjectMapper objectMapper=new ObjectMapper();
String jsonStr =
objectMapper.writeValueAsString(list);
out.print(jsonStr);
//String s="[{\"name\":\"张三\"},{\"name\":\"张三\"},{\"name\":\"张三\"},{\"name\":\"张三\"}]";
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
这是logo<br/>
这是广告<br/>
<div>
<table>
<tr>
<td>学号</td>
<td>姓名</td>
<td>性别</td>
</tr>
<tbody id="div1"></tbody>
</table>
</div>
<div id="div2"></div>
<div id="div3"></div>
<script src="/ajax01_war_exploded/jquery-3.5.0.min.js"></script>
<script>
//json可以做对象和自己穿之间得转换——JSON是一个固定格式得字符串
/* var json='{"name":"张三","age":11}';
var jsonObject=$.parseJSON(json);*/
$.ajax({
url:"/ajax01_war_exploded/select01",//路径
data:{},//传递得参数
dataType:"json",//后台out.print响应得是什么——text代表字符串 json:自动以json形式来接受响应得字符串
type:"get",//type:请求方式
success:function (resultdata){//执行成功后得回调函数--resultdata名字随便写,用来接受out.print信息
/*var jsons=$.parseJSON(resultdata);
console.log(jsons);*/
//成功之后得局部刷新:遍历json数组,拼接一行,追加到尾部
for(var i=0;i<resultdata.length;i++){
var stu=resultdata[0];
var html="<tr>\n" +
" <td>"+stu.studentid+"</td>\n" +
" <td>"+stu.studentname+"</td>\n" +
" <td>"+stu.gender+"</td>\n" +
" </tr>"
$("#div1").append(html)
}
}
})
$.get("/ajax01_war_exploded/select02",{},function (redata){
$("#div2").html(redata)
},"text")
//post请求 第一个url 第二个:data 第三个:回调函数 第四个:返回得数据类型
$.post("/ajax01_war_exploded/select03",{},function (redata){
$("#div3").html(redata)
},"text")
</script>
</body>
</html>
1.4 异步新增
<input type="button" value="新增" id="addBtn"/>
//事件绑定:给id为addBtn的按钮做点击事件
$("#addBtn").click(function (){
window.location.href="/html/addStudent.html"//路径跳转:没有项目根目前,所有直接从web往下
});
入库:
<body>
<form id="stuForm" >
studentname:<input type="text" name="studentname" id="studentname"/><br/>
gender: <input type="text" name="gender" id="gender"/><br/>
age:<input type="text" name="age" id="age"/><br/>
phone:<input type="text" name="phone" id="phone"/><br/>
<input type="button" value="新增" id="saveBtn"/><br/>
</form>
<script src="/static/jquery-3.5.0.min.js"></script>
<script>
//给保存按钮做事件绑定
$("#saveBtn").click(function (){
$.ajax({
url:"/addStduent",
data:{
//:前边是参数名 后边是参数值:参数名怎么写,一会request.getParamter(“gender01”)接参怎么写--这个案例中name没用
"studentname":$("#studentname").val(),
"gender01":$("#gender").val(),//一般都对应,目前留一个不对应是告诉你们后天是根据属性名来获取参数值
"age":$("#age").val(),
"phone":$("#phone").val()
},
dataType:"json",
type:"post",
success:function (redata){
//我想提示:如果成功直接回查询 如果失败提示失败:那么后台回来的信息我希望:有成功失败标识 还希望:提示信息的字符串
if(redata.code==200){
window.location.href="/html/stuList.html"
}else{
alert(redata.msg)
}
}
})
})
</script>
@WebServlet(name = "AddStudent",urlPatterns = "/addStduent")
public class AddStudent extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/* //:前边是参数名 后边是参数值:参数名怎么写,一会request.getParamter(“gender01”)接参怎么写--
"studentname":$("#studentname").val(),
"gender01":$("#gender").val(),*/
String studentname=request.getParameter("studentname");
String gender=request.getParameter("gender01");
String age=request.getParameter("age");
String phone=request.getParameter("phone");
Map map=new HashMap();
map.put("studentname",studentname);
map.put("gender",gender);
map.put("age",age);
map.put("phone",phone);
StudentService studentService=new StudentServiceImpl();
try {
ResultData resultData = studentService.insertStudent(map);
//结果转换成json字符串响应会前台。
response.setCharacterEncoding("utf-8");
PrintWriter out=response.getWriter();
ObjectMapper mapper=new ObjectMapper();
out.print(mapper.writeValueAsString(resultData));
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
异步修改
1.先跳转页面,对方要获取属性值
function getQueryString(name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
var r = window.location.search.substr(1).match(reg);
var context = "";
if (r != null)
context = r[2];
reg = null;
r = null;
return context == null || context == "" || context == "undefined" ? "" : context;
}
var stuid=getQueryString("studentid");
console.log(stuid);
2.根据id获取对象——回显
//2:根据id获取对象开始回显
$.get("/selectStudentById",{"studentid":stuid},function (redata){
//根据id响应的map:到redata:中是一个json对象,中含有当前对象的所有信息
console.log(redata);
//给表单的元素赋值
$("#studentname").val(redata.studentname);
$("#gender").val(redata.gender);
$("#age").val(redata.age);
$("#phone").val(redata.phone);
$("#studentid").val(redata.studentid);
},"json");
@WebServlet(name = "SelectStudentById",urlPatterns = "/selectStudentById")
public class SelectStudentById extends HttpServlet {
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String studentid=request.getParameter("studentid");
StudentService studentService=new StudentServiceImpl();
try {
Map map=studentService.selectStudentById(Integer.parseInt(studentid));
//响应到前端的回调——回调中使用dom进行回显
response.setCharacterEncoding("utf-8");
PrintWriter out=response.getWriter();
ObjectMapper mapper=new ObjectMapper();
String s=mapper.writeValueAsString(map);
out.print(s);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
3.保存按钮做入库
//给保存按钮做事件绑定
//表单序列化:将表单中的所有参数:以name作为属性名 录入值作为属性值自动封装然后传递:这种name有用$("#saveBtn").click(function (){
$.ajax({
url:"/editStudent",
data:$("#stuForm").serialize(),
dataType:"json",
type:"post",
success:function (redata){
if(redata.code==200){
window.location.href="/html/stuList.html"
}else{
alert(redata.msg);
}
}
})
})
唯一性校验
@Override
public List<Map> selectStudentPhone(String phone,String studentid) throws SQLException {
String sql="select * from studentinfo where phone=?";
if(studentid!=null&&!studentid.equals("")){
//这说明修改过来的:修改过来的需要排除自己又id
sql=sql+" and studentid!=?";
return dbUtil.executeQuery(sql,new Object[]{phone,studentid});
}else{
return dbUtil.executeQuery(sql,new Object[]{phone});
}
}
@Override
public ResultData checkPhone(String phone,String studentid) throws SQLException {
List list=studentDao.selectStudentPhone(phone,studentid);
if(list!=null&&list.size()>0){return new ResultData(0,"已经存在不可使用");}
return new ResultData(200,"可以正常使用");
}
@WebServlet(name = "CheckPhone",urlPatterns = "/checkPhone")
public class CheckPhone extends HttpServlet {
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String phone=request.getParameter("phone");
String studentid=request.getParameter("studentid");//新增没有就是null
StudentService studentService=new StudentServiceImpl();//找servide要查询结果
try {
ResultData data=studentService.checkPhone(phone,studentid);
response.setCharacterEncoding("utf-8");
PrintWriter out=response.getWriter();
ObjectMapper mapper=new ObjectMapper();
String s=mapper.writeValueAsString(data);
out.print(s);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
$("#phone").blur(function (){
$.post("/checkPhone",{"phone":$("#phone").val(),studentid:$("#studentid").val()},function (redata){
if(redata.code==200){
$("#phone").next().html(redata.msg);//phone的下一个对象,改变标签体
$("#saveBtn").prop("disabled",false)
}else{
$("#phone").next().html(redata.msg);
$("#saveBtn").prop("disabled",true)
}
},"json")
})
$("#phone").blur(function (){
$.post("/checkPhone",{"phone":$("#phone").val()},function (redata){
if(redata.code==200){
$("#phone").next().html(redata.msg);//phone的下一个对象,改变标签体
$("#saveBtn").prop("disabled",false)
}else{
$("#phone").next().html(redata.msg);
$("#saveBtn").prop("disabled",true)
}
},"json")
})
检索条件
-- 这种sql叫动态sql:根据传入参数的不同,动态拼接sql语句:但凡时根据传入参数不同动态拼接出来的sql都叫动态
-- 需要用刀if for循环等。
-- 1=1的作用:如果都没有传递,语句时完整的,而且不影响结果
-- 不用考虑谁第一个出现——第一个出现的不写and——都作为后出现
select * from studentinfo where 1=1
-- 前台传递studentname?
and studentname like '%张%'
-- 前台传递 city没有
and city='周口'
-- 前台传递studentno没有
and studentno like '410%'
@Override
public List<Map> selectStudent(Map map) throws SQLException {
/* select * from studentinfo where 1=1
-- 前台传递studentname?
and studentname like '%张%'
-- 前台传递 city没有
and city='周口'
-- 前台传递studentno没有
and studentno like '410%'*/
StringBuffer buffer=new StringBuffer("select * from studentinfo where 1=1 ");
if(map!=null){
if(map.get("studentname")!=null&&!map.get("studentname").equals("")){
buffer.append(" and studentname like '%"+map.get("studentname")+"%'");
}
if(map.get("city")!=null&&!map.get("city").equals("")){
buffer.append(" and city like '%"+map.get("city")+"%'");
}
if(map.get("studentno")!=null&&!map.get("studentno").equals("")){
buffer.append(" and studentno like '%"+map.get("studentno")+"%'");
}
}
System.out.println(buffer.toString());
return dbUtil.executeQuery(buffer.toString(),null);
}
@WebServlet(name = "SelectStudent",urlPatterns = "/selectStudent")
public class SelectStudent extends HttpServlet {
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
String studentname=request.getParameter("studentname");
String studentno=request.getParameter("studentno");
String city=request.getParameter("city");
Map map=new HashMap();
map.put("studentname",studentname);
map.put("studentno",studentno);
map.put("city",city);
StudentService studentService=new StudentServiceImpl();//调用service要数据
try {
List list=studentService.selectStudent(map);
//同步:转发给servlet或者jsp去展示
//响应给异步的回调函数去做局部刷新:
response.setCharacterEncoding("utf-8");
PrintWriter out=response.getWriter();
ObjectMapper mapper=new ObjectMapper();
String ss=mapper.writeValueAsString(list);//固定的:将list map 实体都会转换成json字符串
out.print(ss);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
$("#searchBtn").click(function (){
loadTable()
})
function loadTable(){
$.ajax({
url:"/selectStudent",//没有项目根了:可以直接书写urlPatterns
data:$("#searchForm").serialize(),
dataType:"json",//后台回来的json字符串都会转换成json对象或者json数组
type:"post",
success:function (redata){
$("#content").empty();//清空原有内容,重新刷新这个内容tbody
for(var i=0;i<redata.length;i++){
var html="<tr>\n" +
" <td>"+redata[i].studentid+"</td>\n" +
" <td>"+redata[i].studentname+"</td>\n" +
" <td>"+redata[i].gender+"</td>\n" +
" <td>"+redata[i].age+"</td>\n" +
" <td>"+redata[i].phone+"</td>\n" +
" <td>"+redata[i].city+"</td>\n" +
" <td>"+redata[i].studentno+"</td>\n" +
" <td>" +
" <input type='button' value='修改' id='"+redata[i].studentid+"'/>" +
" <input type='button' value='删除' id='"+redata[i].studentid+"'/>" +
" </td>\n" +
" </tr>"
$("#content").append(html);
}
}
})
}
分页
-- 已知是当前第几页 每页多少条
-- 返回的:当前页数据 总共多少页 总共多少条
Plain Text
-- 总共6条
select count(1) from studentinfo where 1=1
and city='郑州'
-- 每页想展示2条 我想要的是第1页的数据
select * from studentinfo where 1=1
and city='郑州'
limit 0,2 -- 从0开始 2:读取几条
-- 每页想展示2条 我想要的是第2页的数据
select * from studentinfo where 1=1
and city='郑州'
limit 2,2 -- 从2开始 2:读取几条
-- 每页想展示2条 我想要的是第3页的数据
select * from studentinfo where 1=1
and city='郑州'
limit 4,2 -- 从4开始 2:读取几条
select * from studentinfo where 1=1
and city='郑州'
-- limit (当前页-1)*每页多少条,每页多少条
-- 总共多少页?——这个来决定有没有下一页
-- 从第一条sql中计算出来总共有6条 已知:每页2条:6/2 总共9条 每页2条: 9/2:整除是4.5--向上求整5
实现分页:
public class PageEntity {
private Integer pageNum;//当前第几页
private Integer pageSize;//每页多少条
private Integer count;//总共多少条
private Integer allPage;//总共多少页
private List list;//当前页数据
public class PageUtil {
DbUtil dbUtil=new DbUtil();
public PageEntity selectPage(String buffer,PageEntity pageEntity) throws SQLException {
//总共多少条:
String countSql="select count(1) as countinfo from(\n" +
buffer.toString() +//在基础sql的基础上,拼接上一个select count计数,计算总条数
")a";
List<Map> countList=dbUtil.executeQuery(countSql,null);
//说明有数据:就是一条
int count=0;//默认没有数据
if(countList!=null&&countList.size()>0){
//countList.get(0)获取第一行map get("countinfo")根据列名取列值取出条数信息 转换成字符串,最终转换成Int
count=Integer.parseInt( countList.get(0).get("countinfo").toString() );
}
pageEntity.setCount(count);//必须赋值给pageENtity:pageENtity一会返回
int begin=(pageEntity.getPageNum()-1)*pageEntity.getPageSize();//(当前页-1)*每页多少条
String limitSql=buffer.toString()+//在基础sql基础上拼接limt :计算当前页数据
" limit "+begin+" , "+pageEntity.getPageSize();//,每页多少条 limit前后加空格
List<Map> dqList=dbUtil.executeQuery(limitSql,null);
pageEntity.setList(dqList);
//总共多少页:--不能整除+1 能整除直接整除 也可以用向上求整的函数
int allPage;
if(count%pageEntity.getPageSize()==0){
allPage=count/pageEntity.getPageSize();
}else{
allPage=count/pageEntity.getPageSize()+1;
}
pageEntity.setAllPage(allPage);
return pageEntity;
}
}
public PageEntity selectStudent(Map map, PageEntity pageEntity) throws SQLException {
//基础sql:查询班级-sql动 老师 不同的表或者不同的业务或者不同检索条件都有可能变动
StringBuffer buffer=new StringBuffer("select * from studentinfo where 1=1 ");
if(map!=null){
if(map.get("studentname")!=null&&!map.get("studentname").equals("")){
buffer.append(" and studentname like '%"+map.get("studentname")+"%'");
}
if(map.get("city")!=null&&!map.get("city").equals("")){
buffer.append(" and city like '%"+map.get("city")+"%'");
}
if(map.get("studentno")!=null&&!map.get("studentno").equals("")){
buffer.append(" and studentno like '%"+map.get("studentno")+"%'");
}
}
PageUtil pageUtil=new PageUtil();
return pageUtil.selectPage(buffer.toString(),pageEntity);
}
controller:
PageEntity pageEntity=new PageEntity();
//接收参数:
String pageNumStr=request.getParameter("pageNum");
String pageSizeStr=request.getParameter("pageSize");
int pageNum=1;//默认第一页
if(pageNumStr!=null&&!pageNumStr.equals("")){
pageNum=Integer.parseInt(pageNumStr);
}
int pageSize=2;//默认每页两条
if(pageSizeStr!=null&&!pageSizeStr.equals("")){
pageSize=Integer.parseInt(pageSizeStr);
}
pageEntity.setPageNum(pageNum);
pageEntity.setPageSize(pageSize);
StudentService studentService=new StudentServiceImpl();//调用service要数据
try {
pageEntity=studentService.selectStudent(map,pageEntity);
前端:
<form id="searchForm">
姓名:<input type="text" name="studentname" id="studentname"/><br/>
城市:<input type="text" name="city" id="city"/><br/>
身份账号:<input type="text" name="studentno" id="studentno"/><br/>
当前第几页:<input type="text" name="pageNum" id="pageNum" value="1"/><br/>
每页多少条:<input type="text" name="pageSize" id="pageSize" value="2"/><br/>
<input type="button" id="searchBtn" value="查询"/>
</form>
<input type="button" value="新增" id="addBtn"/>
<table>
<tr>
<td>学号</td>
<td>姓名</td>
<td>性别</td>
<td>年龄</td>
<td>电话</td>
<td>城市</td>
<td>身份证号</td>
<td>操作</td>
</tr>
<tbody id="content"></tbody>
</table>
<div>
<input type="button" value="上一页" id="prevBtn"/>
<span id="countSpan"></span> <span id="pageNumSpan"></span>/<span id="allPageSpan"></span>
<input type="button" value="下一页" id="nextBtn"/>
</div>
<!--目前项目根是一个空,所以直接从web往下找路径就可以:找静态的都是这么找-->
<script src="/static/jquery-3.5.0.min.js"></script>
<script>
//事件绑定:给id为addBtn的按钮做点击事件
$("#addBtn").click(function (){
window.location.href="/html/addStudent.html"//路径跳转:没有项目根目前,所有直接从web往下
});
$("#searchBtn").click(function (){
loadTable()
})
$("#nextBtn").click(function (){
var pageNum=Number($("#pageNum").val())+1;
$("#pageNum").val(pageNum)
loadTable()
})
$("#prevBtn").click(function (){
var pageNum=Number($("#pageNum").val())-1;
$("#pageNum").val(pageNum)
loadTable()
})
function loadTable(){
$.ajax({
url:"/selectStudent",//没有项目根了:可以直接书写urlPatterns
data:$("#searchForm").serialize(),
dataType:"json",//后台回来的json字符串都会转换成json对象或者json数组
type:"post",
success:function (redata){//此时这个redata是一个pageEntity:遍历的是当前页数据
$("#content").empty();//清空原有内容,重新刷新这个内容tbody
$("#countSpan").html(redata.count)
$("#pageNumSpan").html(redata.pageNum)
$("#pageNum").html(redata.pageNum)
$("#allPageSpan").html(redata.allPage)
//当前第一页上一页不可用 当前最后一页不可用
if(redata.pageNum==1){
$("#prevBtn").prop("disabled",true)
}else{
$("#prevBtn").prop("disabled",false)
}
if(redata.pageNum==redata.allPage){
$("#nextBtn").prop("disabled",true)
}else{
$("#nextBtn").prop("disabled",false)
}
for(var i=0;i<redata.list.length;i++){
var html="<tr>\n" +
" <td>"+redata.list[i].studentid+"</td>\n" +
" <td>"+redata.list[i].studentname+"</td>\n" +
" <td>"+redata.list[i].gender+"</td>\n" +
" <td>"+redata.list[i].age+"</td>\n" +
" <td>"+redata.list[i].phone+"</td>\n" +
" <td>"+redata.list[i].city+"</td>\n" +
" <td>"+redata.list[i].studentno+"</td>\n" +
" <td>" +
" <input type='button' value='修改' id='"+redata.list[i].studentid+"'/>" +
" <input type='button' value='删除' id='"+redata.list[i].studentid+"'/>" +
" </td>\n" +
" </tr>"
$("#content").append(html);
}
}
})
}
loadTable()
第四节:Session和Cookie
1. 面试题目
Session和cookie的区别
JSP的9大内置对象
JSP的运行原理
Servlet的生命周期
2. 课程内容
熟练使用Session
理解会话跟踪原理
理解cookie和使用cookie
3. 具体内容
同步的增删改查:el jstl jsp servlet
异步增删改查 异步分页 异步检索——
html ajax jquery servlet
接下来的两章:东西相对简单——理论偏多,代码很少
回顾:
request对象:其实就是一个存储参数的对象——可以存值:浏览器过来paramter获取:setA放入可以getAtt来获取。一个请求有一个request对象,互不干扰。请求结束自动销毁。
Session对象:也是一个存储参数的对象——可以存值。创建时间 销毁时间不一样。
appliaction对象:也是一个存储参数的对象——可以存值。创建时间 销毁时间不一样。
page对象:也是一个存储参数的对象——可以存值。创建时间 销毁时间不一样。
application>session>request>page(只是一个页面,基本不用)
Session对象:什么时候创建 什么时候销毁 可以创建多少个
效果:
可以让一个会话的参数长期生效:比如当前登录人信息。
3.1 cookie:
Cookie作用:
1.记录一些信息 2.服务器需要依赖sessionid认识每一个客户端,这个sessionid就写在cookie里。
cookie是存储在自己本地(客户端)文本文件。里边存储着所有服务器以及浏览器自身书写进去的内容。
1)本地需要读取显示的时候 2)服务器需要根据cookie来获取一些相关的客户端信息
Cookie概念:
Cookie,有时也用其复数形式 Cookies。类型为“小型文本文件”,是某些网站为了辨别用户身份,进行Session跟踪而储存在用户本地终端上的数据(通常经过加密),由用户客户端计算机暂时或永久保存的信息
Cookie的常用方法
Cookie(name,value);构造方法——key:value
setPath(path):设置响应路径——指明我的cookie对哪个项目有效
setPath("/工程")对当前工程有效
setMaxAge(int):设置Cookie生存时间,单位毫秒——什么时候失效
int>0 如果设置了生存时间,那么会书写到cookie文件里。
删除文件、时间到了也没有
默认是一次会话或者书写<0也是一次会话。
getName():获得当前Cookie的名称
getValue():获得Cookie的值
添加Cookie到response:response.addCookie(cookie);
response.setHeader();
获取请求中cookie:HttpServletRequest getCookie()获取一个数组cookie
请求中都带着cookie:
Eg:实现登陆存储cookie和展示cookie
@WebServlet(name = "Login",urlPatterns = "/login")
public class Login extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username=request.getParameter("username");
String pwd=request.getParameter("pwd");
UserService userService=new UserServiceImpl();
try {
ResultData resultData = userService.login(username,pwd);
if(resultData.getCode()==200){
//获取session:如果这个session已经创建过:第一次请求服务器就已经自动创建过了,如果login是第一次请求那么此时session还没有:
// get(true)会自动创建然后返回 如果以前已经见过了:它不是第一次请求:那么会将之前创建的返回。
//HttpSession session=request.getSession(true);
// get(false):如果之前有:直接获取之前没有返回null
// HttpSession session=request.getSession(false);
HttpSession session=request.getSession();//默认为true
//当前登录人信息放入session域:域request类似就是活的久:可以跨请求不可以跨用户
session.setAttribute("user",resultData.getData());
//登录成功的时候可以把用户名和密码书写到cookie中就可以记住密码:下一次就直接从cookie中读取显示即可
//:同步:
Cookie cookie=new Cookie("username",username);
cookie.setPath("/");
cookie.setMaxAge(-1);
response.addCookie(cookie);
Cookie cookie2=new Cookie("pwd",pwd);
cookie2.setPath("/");
cookie2.setMaxAge(-1);
response.addCookie(cookie2);
}
response.sendRedirect("/html/stuList.html");
//结果转换成json字符串响应会前台。
response.setCharacterEncoding("utf-8");
PrintWriter out=response.getWriter();
ObjectMapper mapper=new ObjectMapper();
out.print(mapper.writeValueAsString(resultData));*/
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
3.2 Session:
Session的作用
session:是第一次请求的时候创建的 主动退出或者浏览器关闭的时候session就销毁了——一次会话结束(主动退出 浏览器关闭 长期不操作自动结束会话)
有效范围:当前会话——各个用户互不干扰。——不可以跨用户可以跨请求。
session的作用就是存储一些用户信息:这些信息多个页面需要。这些信息可以跨请求但是不可以跨用户
session实现原理
浏览器第一次发起请求:本次请求一般都没有sessionid:请求到达服务器端,服务器会来识别这个请求,看看有没有sessionid,如果有,直接读取服务器端的session对象;如果没有直接创建一个sesion对象,然后将sessionid响应到本地浏览器的cookie文件中
第二次请求开始:都需要带着之前的sessionid在找服务器。
意外存在:
1)浏览器关闭了或者cookie文件清除了:本地没有了sesionid,服务器虽然session对象还在但是找不到,会重新创建一个新的,原有数据全部丢失。
2)如果会话超时、服务器宕机,服务器端的sesion对象会被清除,请求再过来,虽然有sessionid,但是服务器端找不到对应的对象,一样会重新创建session对象,然后将新的id响应到客户端。
获得Session:
Session不用new,第一次请求的时候服务器就自动开辟了一个。
request.getSession():无论如何都要给调用者一个Session实例,相当于getSession(true)
如果这个人之前来过,就直接获取,如果之前没来过,就new一个
request.getSession(true/false):根据传入的参数决定当前没有Session时,是否要创建一个新的Session对象。
try {
ResultData resultData = userService.login(username,pwd);
if(resultData.getCode()==200){
//获取session:如果这个session已经创建过:第一次请求服务器就已经自动创建过了,如果login是第一次请求那么此时session还没有:
// get(true)会自动创建然后返回 如果以前已经见过了:它不是第一次请求:那么会将之前创建的返回。
//HttpSession session=request.getSession(true);
// get(false):如果之前有:直接获取之前没有返回null
// HttpSession session=request.getSession(false);
HttpSession session=request.getSession();//默认为true
//当前登录人信息放入session域:域request类似就是活的久:可以跨请求不可以跨用户
session.setAttribute("user",resultData.getData());
}
//结果转换成json字符串响应会前台。
response.setCharacterEncoding("utf-8");
PrintWriter out=response.getWriter();
ObjectMapper mapper=new ObjectMapper();
out.print(mapper.writeValueAsString(resultData));
} catch (SQLException throwables) {
throwables.printStackTrace();
}
<%
Map map = (Map) session.getAttribute("user");
out.print(map);
%>
<br/>
${sessionScope.user}
删除Session:
1)Session超时:超时指的是连续一定时间服务器没有收到该Session所对应客户端的请求,并且 这个时间超过了服务器设置的Session超时的最大时间。
可以通过配置文件设置Session的有效期,默认是30分钟
编码设置Session的有效时间:setMaxInactiveInterval(int):单位是毫秒
2)程序调用HttpSession.invalidate()
3)服务器关闭、重启或停止服务、浏览器的关闭、清除cookie
3.3 Session和cookie的区别
A:session存在服务器端;cookie存在客户端
B:session由服务器自己创建;Cookie也是服务器写进去响应到客户端,也可以js书写进去
C:session存的对象,每一个客户端独立;Cookie存的是字符串,也是每一个客户端独享
D:客户端访问服务器,会带着一个sessionid,如果没有服务器自动创建一个响应给客户端,下次请求带着,session靠sessionid进行识别用户。
第五节:过滤器和监听器
Cookie:
存储在客户端:浏览器的一个文件:就是一个文本文件——可以通过服务器往这个文件这个文件中写东西也可以通过js操作
cookie:每一次浏览器发起请求都需要带着cookie走。服务器可以获取这个里边里边的信息。
Session:
请求来了系统贵判断是不是第一次:cookie中有没有sessionid,没有创建一个给他,有的话害得看服务器端有没有对应,如果有直接返回如果没有接着新建。
sessionid是用来识别用户的:Session对象的存活时间一次会话,只要用户没有中止本次访问(退出 超时 关闭浏览器),session才会没了否则一直在。
Session可以跨请求不可跨用户。
一. 目标
过滤器
监听器
二. 内容
2.1 过滤器
2.1.1 概念
过滤:水比较脏,过滤一下——净水器:过滤脏东西。
过滤:家里种地:从粮食中将土筛出来——过滤。
过滤:作用:将想要的和不想要的给分开。
过滤器:将服务器想要和不想要的路径给分开--->对于符合要求的可以到达服务器否则将会转发重定向走
过滤器主要针对的是客户端(浏览器发起的请求)
-->重定向的路径一定会被在次(查询-->不合格,你去登录-->过滤器拦截不合格去登录-->又拦截了....)
-->转发的话不经过客户端,直接服务器接到请求说你不合格直接转发到另外一个请求了?此时过滤器拦截还是不拦截????
2.1.2 过滤器的定义
过滤器:主要是对请求服务器的路径进行一些拦截验证,如果符合要求可以继续,如果不符合要求,将会根据过滤器的规则(比如转发***或者重定向**地方或者直接响应个什么json)
常见的一些场景:
登录拦截:如果已经登录可以继续如果没有将会重定向到登录
乱码处理:接收参数的乱码处理也可以直接在过滤器中
注意:
如果重定向:会在此被拦截
如果转发——默认不拦截
public class MyFilter implements Filter {
public void destroy() {
System.out.println("销毁方法:MyFilter");
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request= (HttpServletRequest) req;
System.out.println("拦截方法MyFilter:"+request.getRequestURI());
chain.doFilter(req, resp);//请求继续就是放行的意思
}
public void init(FilterConfig config) throws ServletException {
System.out.println("初始化方法:MyFilter");
}
}
<filter>
<filter-name>aaa</filter-name>
<filter-class>com.filter.MyFilter</filter-class>
</filter>
<!--将一批的路径的合格和不合格的分开 如果固定路径拦截:过滤器就没意义 这个位置一般书写规则-->
<filter-mapping>
<filter-name>aaa</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2.1.3 登陆过滤
如果已经登录可以访问查询 新增等页面,如果没有登录哪里去不了重定向会登录页面。
/*<init-param>
<param-name>white</param-name>
<param-value>login.jsp,/login,.js,.css,.png,.jpg</param-value>
</init-param>*/
public class LoginFilter implements Filter {
//String[] whites=new String[]{"login.jsp","/login",".js",".css",".png",".jpg"};
String[] whites;
public void init(FilterConfig config) throws ServletException {
whites=config.getInitParameter("white").split(",");
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//判断是否已经登录,如果登录可以请求继续,如果没有登录重定向到登录
HttpServletRequest request= (HttpServletRequest) req;
HttpServletResponse response= (HttpServletResponse) resp;
System.out.println("拦截方法LoginFilter:"+request.getRequestURI());
String next="不能继续";//默认是要判断session
for(String s:whites){
if(request.getRequestURI().endsWith(s)){
next="继续";//但凡有一个白名单路径匹配上了,将直接请求继续。
break;
}
}
if(next.equals("继续")){//如果是白名单中的某一个路径那么不尽兴session的判断
chain.doFilter(req, resp);
}else{
HttpSession session=request.getSession();
Object obj=session.getAttribute("user");
if(obj==null){//如果未null没登录session.setAttribute("user",resultData.getData());
response.sendRedirect("/html/login.jsp");//重定向拦截
//转发默认是不拦截的:转发地址栏不改变。
//request.getRequestDispatcher("/html/login.jsp").forward(request,response);
}else{
chain.doFilter(req, resp);
}
}
}
public void destroy() {
}
}
<filter>
<filter-name>bbb</filter-name>
<filter-class>com.filter.LoginFilter</filter-class>
<init-param>
<param-name>white</param-name>
<param-value>login.jsp,/login,.js,.css,.png,.jpg</param-value>
</init-param>
</filter>
<!--将一批的路径的合格和不合格的分开 如果固定路径拦截:过滤器就没意义 这个位置一般书写规则-->
<filter-mapping>
<filter-name>bbb</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2.1.4 过滤乱码
Plain Text
public class MyFilter implements Filter {
public void destroy() {
System.out.println("销毁方法:MyFilter");
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request= (HttpServletRequest) req;
request.setCharacterEncoding("utf-8");
request.setAttribute("aa","zz");
System.out.println("拦截方法MyFilter:"+request.getRequestURI());
chain.doFilter(req, resp);
}
public void init(FilterConfig config) throws ServletException {
System.out.println("初始化方法:MyFilter");
}
}
两大种:一种符合规则继续不符合进行转发或者重定向——--可以拒绝请求。
仅仅是拦截之后补充一些参数就让请求继续。
2.1.4 过滤器链
过滤器链:一个项目可以有多个过滤器,如果一个请求开始路径匹配匹配了多个过滤器将会按照顺序执行
如果是配置:按照配置顺序
如果注解:按照类名的字符串排序进行执行
@WebFilter(filterName="a",urlPatterns = "/*")
public class MyFilter implements Filter {
@WebFilter(filterName="b",urlPatterns = "/*",
initParams = {@WebInitParam(name = "white",value = "login.jsp,/login,.js,.css,.png,.jpg")})
public class LoginFilter implements Filter {
//String[] whites=new String[]{"login.jsp","/login",".js",".css",".png",".jpg"};
String[] whites;
public void init(FilterConfig config) throws ServletException {
whites=config.getInitParameter("white").split(",");
}
过滤器:
目的就是服务器针对一组路径,进行相关的验证,如果符合要求请求继续不符合将会根据业务进行转发或者重定向。
默认:转发不走过滤器重定向会走
转发如果想拦截可以设置:
Plain Text
:dispatcherTypes
FORWARD,--forwad形式转发的会被拦截
INCLUDE,-- inclue:形式的转发的会被拦截
REQUEST,-- 重定向会被拦截
ASYNC,-- 异步拦截...
ERROR;
2.2 监听器
监听器:
监听:日常——班级被监听 监视——摄像头。比如在班里打游戏,老师看到了(通过摄像头)--老师进班--把你给批评一顿。
以学生的角度来看:
感觉:一打游戏老师就来了。——打游戏就跟一个事件源一样:只要一发生,另外一个事件处理就来了。
监听设计模式:
设计模式:一套规则,这是一套解决思路。MVC:你这么分层会更好。设计模式是一套别人总结好的代码思路,没有具体实现。
监听设计模式:就是为了实现刚才:咱们说的这个一个事情发生会自动触发另外一个事情的执行的这种效果。这套思路叫监听设计模式。
先写一个事件源 在写一个监听器 在一个事件 进行事件处理
学习写监听器 事件 事件源:如何做到学生一打游戏老老师就来了这个效果???---不学
真正:Servlet中已经书写好的监听器:servlet中提供了8个监听器——分别监听request session application的相关创建 销毁 属性添加 属性移除 属性替换等事件。
在这八个监听中:已经书写好固定的事件 书写好了固定的事件源 监听的过程也已经写好:咱们需要写的仅仅是事件处理。
2.2.1 监听设计模式
监听器,字面上的理解就是监听观察某个事件(程序)的发生情况,当被监听的事件真的发生了的时候,事件发生者(事件源) 就会给注册该事件的监听者(监听器)发送消息,告诉监听者某些信息,同时监听者也可以获得一份事件对象,根据这个对象可以获得相关属性和执行相关操作。
监听器模型涉及以下三个对象:
(1)事件:用户对组件的一个操作,或者说程序执行某个方法,称之为一个事件,如机器人程序执行工作。
(2)事件源:发生事件的组件就是事件源,也就是被监听的对象,如机器人可以工作,可以跳舞,那么就可以把机器人看做是一个事件源。
(3)事件监听器(处理器):监听并负责处理事件的方法,如监听机器人工作情况,在机器人工作前后做出相应的动作,或者获取机器人的状态信息。
2.3 Servlet规范中的监听器
Servlet中已经定义好了八个监听器接口,监听的对象分别是request、session、servletContext(application)对象。
触发的时间:是三个对象的创建、销毁以及域空间的属性添加、删除、修改。
request:请求开始创建 响应结束销毁--这是一个作用域:时间是一次请求 请求之间不能交叉。 request.setA removeA setA名字一样就叫修改也叫覆盖
session域:一次请求:第一次请求的时候创建。退出或者关闭工程 或者超时的时候session对象会销毁——关闭浏览器 清除cookie这两个是本地的cookie没了,服务起对象还在,但是对应不上了一样会重新创建。
session.set romve set名字一样就是覆盖或者修改。
servletContext(application):范围最大——项目启动的时候创建 项目关闭的时候销毁,并且仅仅创建一次,全工程共享。
.set romve set名字一样就是覆盖或者修改。
2.3.1 ServletContextListener接口
[接口方法] contextInitialized()与 contextDestroyed()
[接收事件] ServletContextEvent
[触发场景] 在Container加载Web应用程序时(例如启动 Container之后),会呼叫contextInitialized(),而当容器移除Web应用程序时,会呼叫contextDestroyed ()方法。
2.3.2 ServletContextAttributeListener
[接口方法] attributeAdded()、 attributeReplaced()、attributeRemoved()
[接收事件] ServletContextAttributeEvent
[触发场景] 若有对象加入为application(ServletContext)对象的属性,则会呼叫attributeAdded(),同理在置换属性与移除属性时,会分别呼叫attributeReplaced()、attributeRemoved()。
2.3.3 ServletRequestListener
[接口方法] requestInitialized()与 requestDestroyed()
[接收事件] ServletRequestEvent
[触发场景] 在request(HttpServletRequest)对象建立或被消灭时,会分别呼叫这两个方法。
2.3.4、ServletRequestAttributeListener
[接口方法] attributeAdded()、 attributeReplaced()、attributeRemoved()
[接收事件] ServletRequestAttributeEvent
[触发场景] 若有对象加入为request(HttpServletRequest)对象的属性,则会呼叫attributeAdded(),同理在置换属性与移除属性时,会分别呼叫attributeReplaced()、 attributeRemoved()。
2.3.5、HttpSessionListener
[接口方法] sessionCreated()与sessionDestroyed ()
[接收事件] HttpSessionEvent
[触发场景] 在session (HttpSession)对象建立或被消灭时,会分别呼叫这两个方法。
2.3.6、HttpSessionAttributeListener
[接口方法] attributeAdded()、 attributeReplaced()、attributeRemoved()
[接收事件] HttpSessionBindingEvent
[触发场景] 若有对象加入为session(HttpSession)对象的属性,则会呼叫attributeAdded(),同理在置换属性与移除属性时,会分别呼叫attributeReplaced()、 attributeRemoved()。
<listener>
<listener-class>com.listener.MyListener</listener-class>
</listener>
public class MyListener implements ServletContextListener,
HttpSessionListener, ServletRequestListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("application被创建");
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("application被销毁");
}
@Override
public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
System.out.println("request被销毁");
}
@Override
public void requestInitialized(ServletRequestEvent servletRequestEvent) {
System.out.println("request被创建");
}
@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
System.out.println("session被创建");
}
@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
System.out.println("session被销毁");
}
}
案例:在线人数(已经登录)——浏览人数 (不用登录)
在线人数登录:
监听session——监听登录放入session的那一个属性——如果监听创建那么第一次请求就已经+1,没有登录——监听的是登录:登录的时候放入一个user属性所以监听user属性的添加。
退出的是时候应该在线人数-1:监听session的销毁。
在线人数应该存储在那个域中?这个域应该能跨用户并且全工程共享——application:context。
public class CountLinstener implements HttpSessionAttributeListener, HttpSessionListener {
@Override
public void attributeAdded(HttpSessionBindingEvent httpSessionBindingEvent) {
System.out.println("属性被添加:"+httpSessionBindingEvent.getName()+":"+httpSessionBindingEvent.getValue());
if(httpSessionBindingEvent.getName().equals("user")) {
int count = 0;//如果c为null 说明当前第一个人,默认值为0
ServletContext appliction = httpSessionBindingEvent.getSession().getServletContext();
Object c = appliction.getAttribute("count");//如果第一个人这个里边没值
if (c != null) {
count = Integer.parseInt(c.toString());//将原有存入的值转换成数字
}
count++;
appliction.setAttribute("count", count);//只有第一个人来过之后里边才会有值
}
}
@Override
public void attributeRemoved(HttpSessionBindingEvent httpSessionBindingEvent) {
System.out.println("属性被移除:"+httpSessionBindingEvent.getName()+":"+httpSessionBindingEvent.getValue());
if(httpSessionBindingEvent.getName().equals("user")) {
int count = 0;//如果c为null 说明当前第一个人,默认值为0
ServletContext appliction = httpSessionBindingEvent.getSession().getServletContext();
Object c = appliction.getAttribute("count");//如果第一个人这个里边没值
if (c != null) {
count = Integer.parseInt(c.toString());
}
count--;
appliction.setAttribute("count", count);//只有第一个人来过之后里边才会有值
}
}
@Override
public void attributeReplaced(HttpSessionBindingEvent httpSessionBindingEvent) {
System.out.println("属性被修改:"+httpSessionBindingEvent.getName()+":"+httpSessionBindingEvent.getValue());
}
@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
System.out.println("session被创建:"+httpSessionEvent.getSession().getId());
}
@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
System.out.println("session被销毁:"+httpSessionEvent.getSession().getId());
}
}
----------------------------------------
放入session的对象:现在想将User对象放入session:放入的时候触发 这个被移除的时候触发什么。
2.3.7、HttpSessionBindingListener
[接口方法] valueBound()与valueUnbound()
[接收事件] HttpSessionBindingEvent
[触发场景] 实现HttpSessionBindingListener接 口的类别,其实例如果被加入至session(HttpSession)对象的属性中,则会呼叫 valueBound(),如果被从session(HttpSession)对象的属性中移除,则会呼叫valueUnbound(),实现 HttpSessionBindingListener接口的类别不需在web.xml中设定。
public class UserEntity implements HttpSessionBindingListener {
private int id;
private String name;
public UserEntity(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public void valueBound(HttpSessionBindingEvent httpSessionBindingEvent) {
System.out.println("userEntity对象被放入session时触发");
}
@Override
public void valueUnbound(HttpSessionBindingEvent httpSessionBindingEvent) {
System.out.println("userEntity对象被移除session时触发");
}
}
UserEntity userEntity=new UserEntity(1,"张三");
session.setAttribute("userEntity",userEntity);
session.removeAttribbute("userEntity")
2.3.8、HttpSessionActivationListener
[接口方法] sessionDidActivate()与 sessionWillPassivate()
[接收事件] HttpSessionEvent
[触发场景] 当前类的对象从内存中书写到硬盘中(钝化,会触发对象的序列化)之前会触发sessionWillPassivate;反之即从硬盘读取到内存中(活化,反序列化)之后会触发sessionDidActivate方法。不需要配置
注意:
(一)钝化:当服务器正常关闭时,还存活着的session(在设置时间内没有销毁) 会随着服务器的关闭被以文件(“SESSIONS.ser”)的形式存储在tomcat 的work 目录下,这个过程叫做Session 的钝化。——序列化:session对象存储到文件里
(二)活化:当服务器再次正常开启时,服务器会找到之前的“SESSIONS.ser” 文件,从中恢复之前保存起来的Session 对象,这个过程叫做Session的活化。——将文件中的内容重新加载到工程,反序列化