目录
Web
9.9
JavaWeb
JavaWeb,是在B/S模式下,使用Java开发综合性Web服务网站的技术
网站
用户通过浏览器访问某个域名或ip后,浏览到的综合性页面
实际是发布在服务器上的一个应用程序,通过浏览器访问
网页
- 静态页面:所有人访问的内容都一样
- 动态页面:不同的人看到页面中的数据不一致
网络服务器
部署web项目的平台
TomCat
由Apecha、Sun公司及其他公司和个人共同开发的网络服务器,免费、开源、轻量级,在中小型系统中普遍使用
下载
tomcat官网Apache Tomcat® - Welcome!
根据Jdk选择合适的版本
使用
下载成功后,无需安装,解压到某个盘下即可
目录 | 说明 |
---|---|
bin | 保存tomcat中的可执行文件,如启动tomcat的startup文件等 |
conf | 保存tomcat的配置文件,如server.xml文件可以修改默认的8080端口 |
lib | 保存tomcat运行时所需的jar文件 |
logs | 保存tomcat运行时产生的日志文件 |
temp | 保存tomcat运行时产生的临时文件 |
webapps | 保存发布在tomcat上的应用程序 |
work | 保存tomcat运行时产生的编译文件 |
启动tomcat
-
打开bin目录下的startup.bat文件,等待启动成功后,tomcat启动后,默认端口号为8080
-
在浏览器中输入localhost:8080或127.0.0.1:8080,即可进入tomcat默认启动页面
-
该页面位于ROOT目录下,名为Index.jsp,即localhost:8080表示访问ROOT下的Index.jsp文件
部署项目到tomcat中
将整个前端项目目录,保存到webapps下,在bin目录下打开startup.bat文件后,
浏览器中输入**“localhost:8080/自定义项目目录名/文件名**”访问对应的文件
- 若访问时只输入项目的根目录名,它会自动访问项目中名为Index的文件
- 若没有,则会出现404页面,表示资源不存在
修改主机名,即修改本地hosts文件
默认,本机127.0.0.1,对应的域名localhost
将本地hosts文件复制一份到桌面,最后新增自定义host名,而后替换系统中默认的hosts文件
127.0.0.1 xxx
最后启动tomcat后,即可通过xxx:8080代替localhost:8080
Maven
- 用于管理项目的工具
- 最主要的作用是管理项目所需的jar文件、打包项目等
- 通过在Maven项目中加入某个jar文件所需的dependency(依赖),让其自动从Maven仓库下载对应的jar文件
Maven依赖官网
Maven Repository: Search/Browse/Explore (mvnrepository.com),在这个网站查询所需的jar文件的依赖和文件
Maven本地仓库
Maven默认的配置文件会从官网下载jar文件,速度较慢,并且下载的文件保存在c盘
这里修改下载地址和保存路径,即为创建一个本地仓库
本地仓库配置文件
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<!--设置下载jar文件的仓库地址-->
<localRepository>D:\MavenRepository\maven_jar</localRepository>
<!--设置下载jar文件国内镜像-->
<mirrors>
<mirror>
<id>aliyunmaven</id>
<mirrorOf>*</mirrorOf>
<name>aliyun</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
</mirrors>
</settings>
Maven使用
-
官网下载
-
使用IDEA自带
在新建项目时选择Maven项目即可
设置IDEA中Maven项目配置
设置当前项目的Maven配置
点击顶端File中setting选项,搜索maven后,更改Maven中的User settings file为刚刚自行配置的本地文件
更改User settings file之前,必须先勾选最右边的Override,再进行配置文件更改
设置新项目的Maven配置
先点击顶端File中New Projects Setup选项,再选择Setting for New Projects….选项,而后与之前一致
搜索maven后,更改Maven中的User settings file为刚刚自行配置的本地文件
更改User settings file之前,必须先勾选最右边的Override,再进行配置文件更改
IDEA创建普通Maven项目:easyexcel实现读写excel文件
使用easyexcel实现读写excel文件
创建空Maven项目
项目目录
修改java版本号
11为java的版本号,与之对应
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
引入的依赖:Mysql依赖与esayexcel依赖
加入Mysql依赖,选择版本,复制依赖
在maven仓库官网搜索Mysql依赖,粘贴到项目的pom.xml文件的**<dependencies>
**标签下
普通的maven项目没有该标签,需要手动输入
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
刷新pom.xml文件,就会自动下载指定的依赖jar文件
两种刷新方式:
- 当对pom.xml文件进行改动的时,右上角会出现一个小刷新按钮
- 若变动后按钮没出现,可点击最右则的maven按钮,然后点击最顶端的刷新按钮
刷新方式一:
刷新方式二:
加入esayexcel依赖,选择版本,复制依赖
与mysql类似操作
<!--easyexcel-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.3.2</version>
</dependency>
读写数据的实体类:Employee
@ExcelProperty(“编号”) ,标记属性,即无所谓读取顺序,该类必须get/set方法,此代码中省略,也需要有无参构造与全参构造
import com.alibaba.excel.annotation.ExcelProperty;
import java.util.Date;
public class Employee {
//@ExcelProperty("编号") 标记属性 无所谓读取顺序,该类有get/set方法
@ExcelProperty("编号")
private int empNo;
@ExcelProperty("姓名")
private String empName;
@ExcelProperty("电话")
private String empPhone;
@ExcelProperty("入职时间")
private String joinDate;
@ExcelProperty("部门")
private String dept;
@ExcelProperty("工资")
private double salary;
@ExcelProperty("邮箱")
private String email;
//无参构造
public Employee() {
}
public Employee(int empNo, String empName, String empPhone, String joinDate, String dept, double salary, String email) {
this.empNo = empNo;
this.empName = empName;
this.empPhone = empPhone;
this.joinDate = joinDate;
this.dept = dept;
this.salary = salary;
this.email = email;
}
@Override
public String toString() {
return "Employee{" +
"empNo=" + empNo +
", empName='" + empName + '\'' +
", empPhone='" + empPhone + '\'' +
", joinDate='" + joinDate + '\'' +
", dept='" + dept + '\'' +
", salary=" + salary +
", email='" + email + '\'' +
'}';
}
}
读取excel中的数据
创建File对象,表示要读取的excel文件
EasyExcel.read(File对象,实体类.class,new PageReadListener< 类 >(lambda表达式)).sheet().doRead()
要读取的File对象文件
保存数据的实体类.class
实体类
参数为一个lambda表达式
//创建File对象,表示要读取的excel文件
File file = new File("C:\\Users\\Administrator\\Desktop\\员工信息表.xlsx");
//最好验证文件是否存在
if (file.exists()) {
//EasyExcel.read(要读取的File对象文件,保存数据的实体类.class,new PageReadListener(参数为一个lambda表达式)).sheet().doRead()
EasyExcel.read(file, Employee.class, new PageReadListener<Employee>(res -> {
//处理读取到的结果。这里使用增强for循环遍历
for (Employee emp : res) {
System.out.println(emp);
}
})).sheet().doRead();
}else{
System.out.println("文件路径有误");
}
写excel
创建File对象,表示要写到哪个excel文件中,该文件可以不存在
EasyExcel.write(File对象文件,实体类.class).sheet(“自定义子表名”).doWrite(数据集合);
要写入的目标创建File对象文件(可以不存在)
要写入的实体类.class
//要写入的文件,可以不存在
File target=new File("d:\\employee.xlsx");
//EasyExcel.write(要写入的目标文件(可以不存在),要写入的实体类.class).sheet(“自定义子表名”).doWrite(数据集合);
EmployeeDao employeeDao=new EmployeeDao();
EasyExcel.write(target, Employee.class)
.sheet("员工信息表")
.doWrite(employeeDao.queryAll());
在IDEA中创建基于Maven的Web项目
新建webapp模板
在src目录下新建目录用于创建java源文件
修改项目中web.xml版本为4.0
先删除原web.xml版本的文件
删除后,先点击apply,再进行添加
再添加web.xml版本为4.0的文件
配置tomcat服务器
点击右端的add configuration,再点击顶端的**+号,在add New configuration找到Tomcat Server并点击Local**
点击左侧Configure…选项,选择tomcat所在的根目录
这些会自动导入,可自行选择修改
部署项目到tomcat中
切换到Deployment下,点击+号,选择Artifact…选项,最后添加第二个带有exploded关键字的项目
添加成功后,在Deployment下的最底部可自定义项目默认的名称
启动项目,即启动Tomcat
启动Tomcat,启动成功后,自动打开指定浏览器并进入当前部署的系统
也可以自行访问http://localhost:8080/web01_war_exploded/
这里看到的界面是web项目中自带的index.jsp 文件
解决tomcat控制台中文乱码
点击顶端help中的Edit Custom VM Options…,再最后添加下面的语句
-Dfile.encoding=utf-8
必须确保该文件下没有中文存在,否则会出错
web项目目录结构
webapp下如果有index文件,访问项目后会自动访问index文件
如果没有index文件,会出现404,表示index 页面不存在
项目上下文路径
域名+端口号+项目名,称为项目上下文路径;如:localhost:8080/web01 就是项目上下文路径,可以理解为项目根目录
webapp目录中的内容直接通过项目上下文路径进行访问
更新了项目中的内容后,根据需求更新资源、重启或重新部署
Servlet
Servlet表示Server+Applet ,表示运行在服务器上的程序,是一个被动程序,每次请求时都会执行
编写Servlet的步骤
在项目中导入Servlet所需的依赖
粘贴到pom.xml文件中的dependecies标签下,刷新maven
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
在java目录下新建一个类,继承HttpServlet,重写方法
继承HttpServlet类,重写doGet和doPost方法
/*
* 新建一个类
* 1.继承HttpServlet类
* 2.重写doGet和dopost方法
* 3.二选一调用另一个方法,实现get或post请求都执行同样的代码
* */
public class FirstServlet extends HttpServlet {
/*
* 如果浏览器发送了get请求时,执行该方法
* */
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//控制台输出中文
System.out.println("有人访问我了");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//调用另一个方法
doGet(req, resp);
}
}
在web.xml文件中配置Servlet
-
声明一个Servlet
<servlet> <!--给Servlet命名--> <servlet-name>Servlet命名</servlet-name> <!--Servlet类的全限定名(路径+文件名)--> <servlet-class>Servlet类的路径+文件名</servlet-class> </servlet>
-
给声明的Servlet配置访问映射,必须以**/**开头
<servlet-mapping> <!--要配置映射的Servlet,对应上面声明的某个Servlet--> <servlet-name>对应上面声明的某个Servlet名</servlet-name> <!--设置请求映射,必须以/开头,在网站上访问的名字--> <url-pattern>/自定义</url-pattern> </servlet-mapping>
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--
声明一个Servlet
-->
<servlet>
<!--给Servlet命名-->
<servlet-name>firstServlet</servlet-name>
<!--Servlet类的全限定名(路径+文件名)-->
<servlet-class>com.hqyj.servlet_test.FirstServlet</servlet-class>
</servlet>
<!--
给声明的Servlet配置访问映射
-->
<servlet-mapping>
<!--要配置映射的Servlet,对应上面声明的某个Servlet-->
<servlet-name>firstServlet</servlet-name>
<!--设置请求映射。必须以/开头-->
<url-pattern>/first</url-pattern>
</servlet-mapping>
</web-app>
复制某个类的全限定名
解决控制台输出中文时的乱码
打开刚刚部署的tomcat服务器,在VM option处添加-Dfile.encoding=utf-8
9.11
Web项目的访问流程
下图整体,表示Web项目的访问流程
绿色部分,属于"三层架构"
浏览器发送的请求,称为request,得到的响应,称为response
三层架构——“高内聚,低耦合”
在软件开发中,并不是将所有的功能集成到一个类或文件中实现,而是要将其分层处理
满足三层架构,就是为了满足**“高内聚,低耦合”**的目的
- 高内聚是指,各个模块的功能不可再分
- 低耦合是指,降低各个模块之间的关联程度,便于开发和维护,各个模块各司其职
通常所说的三层架构中的三层,指"视图表现层"、“业务逻辑层"和"数据访问层”
-
视图表现层:用于展示和提供用户输入数据的渠道
-
在适当的情况下,输出业务逻辑层中的内容
-
如 html、jsp、servlet,都属于该层
-
-
业务逻辑层:用于处理业务逻辑
- 在适当的情况下,调用数据访问层
- servlet属于该层
-
数据访问层:用于连接数据库对数据进行读写
访问服务器某个URL的方式
- 在浏览器的地址栏中输入对应的URL,属于get请求
- 使用a标签,在href中输入对应的URL,属于同步get请求
- 使用form表单,在action中输入对应的URL
- 在method中定义get或post决定发送同步get或post请求
- 使用ajax,属于异步请求,可以是get、post、delete、put等请求
页面向服务器提交数据的方式
-
使用form表单显式提交
-
表单提交的数据会暴露在地址栏中
<form action="http://localhost:8080/xxx"> <input type="text" name="username"/> <input type="submit"> </form>
-
-
使用form表单隐式提交
-
表单提交的数据不会暴露在地址栏中
<form action="http://localhost:8080/xxx" method="post"> <input type="text" name="username"/> <input type="submit"> </form>
-
-
通过"?参数1=值&参数2=值"方式显示提交
-
浏览器的地址栏中输入
http://localhost:8080/xxx?id=123
-
使用a标签
<a href="http://localhost:8080/xxx?id=123&pwd=123">登录</a>
两种都属于get请求,会让提交的数据会暴露在地址栏中
-
注意
表单在提交时,如果action中有?的参数,该参数只会在post请求时有用,get请求无法识别action中?的参数
所以使用表单提交时,不建议在action中携带?的参数,建议使用隐藏域实现
使用隐藏域提交一些不希望被展示的数据
在使用表单时,有些数据不需要显示,但却需要提交时,使用隐藏域实现
<input type="hidden" name="id" value="xxx"/>
<input type="hidden" name="option" value="delete"/>
这样在提交表单时,会将id=xxx和option=delete一起发送到action的路径中
服务器端获取前端提交的数据
<a href="URL?id=123">提交id</a>
<form action="URL" method="post">
<input type="text" name="username">
<input type="submit">
</form>
在servlet中的doPost或doGet中获取的方式:
String str=req.getParameter("name的值或?后的参数");
protected void doGet(HttpServletRequest req,HttpServletResponse resp){
//获取a标签?后id的值
String id= req.getParameter("id");
//获取表单name="username"文本框中输入的值
String id= req.getParameter("username");
}
解决请求和响应中的乱码问题
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
//解决请求的乱码
req.setCharacterEncoding("utf-8");
//解决响应的乱码
resp.setContentType("text/html;charset=utf-8");
}
使用一个Servlet处理不同的业务逻辑
原理:在请求某个Servlet时,额外提交一个参数
如option参数,用来表示不同的业务,在Servlet中获取option的值后进行判断,再执行不同的业务
使用一个Servlet实现单表的CURD
package com.hqyj.curd_demo.seivlet;
import com.hqyj.curd_demo.dao.HeroDao;
import com.hqyj.curd_demo.entity.Hero;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
public class HeroServlet extends HttpServlet {
HeroDao heroDao=new HeroDao();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
String op=req.getParameter("op");
switch (op){
case "queryAll":
queryAll(req,resp);
break;
case "add":
add(req,resp);
break;
case "delete":
delete(req,resp);
break;
case "findById":
findById(req,resp);
case "update":
update(req,resp);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
public void queryAll(HttpServletRequest req, HttpServletResponse resp) throws IOException {
List<Hero> list=heroDao.queryAll();
PrintWriter writer=resp.getWriter();
writer.println("<html>");
writer.println("<body>");
writer.println("<a href='http://localhost:8080/web02_war_exploded/page/addform.html'>添加</a>");
writer.println("<table border=1>");
writer.println("<tr>");
writer.println("<td>编号</td>");
writer.println("<td>姓名</td>");
writer.println("<td>性别</td>");
writer.println("<td>价格</td>");
writer.println("<td>创建时间</td>");
writer.println("<td>定位</td>");
writer.println("<td conspan='2'> 操作</td>");
writer.println("</tr>");
for(Hero h:list){
writer.println("<tr>");
writer.println("<td>"+h.getId()+"</td>");
writer.println("<td>"+h.getName()+"</td>");
writer.println("<td>"+h.getSex()+"</td>");
writer.println("<td>"+h.getPrice()+"</td>");
writer.println("<td>"+h.getCreateDate()+"</td>");
writer.println("<td>"+h.getPosition()+"</td>");
writer.println("<td><a href='http://localhost:8080/web02_war_exploded/hero?op=findById&id="+h.getId()+"'>修改</a></td>");
writer.println("<td><a href='http://localhost:8080/web02_war_exploded/hero?op=delete&id="+h.getId()+"'>删除</a></td>");
writer.println("</tr>");
}
writer.println("</table>");
writer.println("</body>");
writer.println("</html>");
}
public void add(HttpServletRequest req, HttpServletResponse resp) throws IOException {
Hero hero=new Hero();
hero.setName(req.getParameter("name"));
hero.setSex(req.getParameter("sex"));
hero.setPrice(Double.valueOf(req.getParameter("price")));
hero.setPosition(req.getParameter("position"));
if (heroDao.insertHero(hero))
resp.sendRedirect("http://localhost:8080/web02_war_exploded/hero?op=queryAll");
else
resp.getWriter().println("添加失败");
}
public void delete(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String id=req.getParameter("id");
if (heroDao.deleteHero(Integer.valueOf(id)))
resp.sendRedirect("http://localhost:8080/web02_war_exploded/hero?op=queryAll");
else
resp.getWriter().println("删除失败");
}
public void findById(HttpServletRequest req, HttpServletResponse resp) throws IOException {
Hero hero=heroDao.findById(Integer.valueOf(req.getParameter("id")));
PrintWriter writer=resp.getWriter();
writer.println("<html>");
writer.println("<body>");
writer.println("<form action='http://localhost:8080/web02_war_exploded/hero' method='post'>");
writer.println("<input type='hidden' name='op' value='update'><br>");
writer.println("<input type='text' readonly name='id' value='"+hero.getId()+"'><br>");
writer.println("<input type='text' name='name' value='"+hero.getName()+"'><br>");
if("男".equals(hero.getSex()))
writer.println("<input type='radio' name='sex' value='男' checked>男<input type='radio' name='sex' value='女'>女<br>");
else
writer.println("<input type='radio' name='sex' value='男'>男<input type='radio' name='sex' value='女' checked>女<br>");
writer.println("<input type='number' name='price' value='"+hero.getPrice()+"'><br>");
writer.println("<input type='text' readonly name='createDate' value='"+hero.getCreateDate()+"'><br>");
writer.println("<input type='text' name='position' value='"+hero.getPosition()+"'><br>");
writer.println("<input type='submit',value='提交'>");
writer.println("</form>");
writer.println("</body>");
writer.println("</html>");
}
public void update(HttpServletRequest req, HttpServletResponse resp) throws IOException {
Hero h=new Hero();
h.setId(Integer.valueOf(req.getParameter("id")));
h.setName(req.getParameter("name"));
h.setSex(req.getParameter("sex"));
h.setPrice(Double.valueOf(req.getParameter("price")));
h.setPosition(req.getParameter("position"));
if (heroDao.updateHero(h))
resp.sendRedirect("http://localhost:8080/web02_war_exploded/hero?op=queryAll");
else
resp.getWriter().println("修改失败");
}
}
9.12
Servlet的生命周期
当第一次访问某个Servlet时,执行
1.构造方法一次
2.init()方法一次
3.由service()方法时执行service()方法一次,没有则执行doXXX()方法一次;如果都没有,报405状态码,表示请 求没有处理的方法(请求方法不允许)
4.当服务器停止时,执行destroy()一次
构造方法–>init()–>service()/doXXX()–>destroy()
使用注解开发Servlet——@WebServlet(“命名”)
/*
Servlet需要javax.servlet-api依赖
1.创建一个类,继承HttpServlet
2.重写service()方法
3.在类上添加@WebServlet("/映射名")注解
*/
@WebServlet("/test")
public class TestServlet extends HttpServlet {
public TestServlet() {
System.out.println("constructor");
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws
ServletException, IOException {
System.out.println("service");
}
@Override
public void init() throws ServletException {
System.out.println("init");
}
@Override
public void destroy() {
System.out.println("destroy");
}
}
JSP——Java Serve Pages
使用Java开发,运行在服务器上的页面,称为JSP
JSP页面的后缀名.jsp
由于最初由Servlet编写页面,会在Servlet中出现大量的HTML代码,使用极不方便
由SUN公司主导退出JSP,在HTML页面中嵌入Java代码,简化了页面的编写过程,页面也称为动态页面
JSP实际是一个Java类,具体为一个Servlet
- 第一次访问某个JSP时,相当于编译运行Servlet,所以会慢一些
- 之后再次访问则比较流畅
访问JSP的流程:编译对应的Servlet–>运行Servlet
JSP的生命周期,如同Servlet
JSP的组成
HTML元素***
指令
<%@ 指令名 属性=值 %>
- page指令
- 用于设置当前页面的属性
- include指令
- 用于嵌入其他页面
- taglib指令
- 用于导入其他标签库
Java脚本
<% Java代码; %>
表达式
得到某个变量的值。通常用于在页面中输出变量或字符串
<%=变量%>
注释
JSP注释在访问JSP页面时,不会在浏览器检查时看到;HTML的注释则会
<%-- 注释内容 --%>
声明
可以在这里定义方法,全局变量等,不建议使用
<%!
void fun(){}
%>
动作
使用标签定义一些功能
<jsp:单词 ></jsp:单词>
<jsp:useBean id=""></jsp:useBean>
路径问题
绝对路径
完整路径:http://localhost:8080/web03/loginPage
相对路径
- / :从项目根目录出发。即域名+端口号
- ./或直接写 :从当前位置出发
- …/ :退出当前目录后出发
如:当前位于localhost:8080/book_shop/pages/bookList.jsp
如果要访问位于pages/imgs/下的图片
绝对路径:
<img src="localhost:8080/book_shop/pages/imgs/xxx.jpg">
相对路径
<img src="/book_shop/pages/imgs/xxx.jpg">
<img src="./imgs/xxx.jpg">
<img src="imgs/xxx.jpg">
<img src="../pages/imgs/xxx.jpg">
跳转
页面跳转页面
<a href="另一个页面的路径">xx</a>
<form action="另一个页面的路径">
<input type="submit"/>
</form>
页面跳转Servlet
<a href="某个Servlet的URL映射">xxx</a>
<form action="某个Servlet的URL映射">
<input type="submit"/>
</form>
Servlet跳转页面
请求转发
request.getRequestDispatcher("目的路径").forward(request,response);
- 使用请求转发跳转后,浏览器的地址栏是最初访问的路径
- 可以在请求转发时,向request作用域中保存数据,跳转到目的地后,获取当时保存的数据
- 在执行查询时,通常会将查询到的集合保存到request中,使用请求转发跳转到jsp页面
- 执行增删改后,不建议使用请求转发,因为刷新页面会重复提交
重定向
response.sendRedirect("目的路径");
- 使用重定向跳转到目的地后,浏览器的地址栏是最终访问的目的地址
- 如果在重定向时使用request保存数据,后续无法获取保存的数据
- 在执行增删改后使用重定向跳转,防止表单重复提交
跳转过程中传递参数
由页面发送数据到servlet或jsp,通常使用表单元素或在某个URL后使用"?参数=值"两种方式提交参数
获取时使用**request.getParameter(“参数”)**获取
<a href="URL?op=xxx&id=xxx">删除</a>
<form action="URL" method="post">
<input type="hidden" name="op" value="xxx">
<input type="text" name="username">
<input type="submit">
</form>
//获取的返回值都是String类型
String str=request.getParameter("xxx");
由servlet发送数据到页面,使用request.setAttribute(String str,Object obj)保存obj到request作用域中, 命名为str。
获取时使用request.getAttribute(String str)获取
@WebServlet("/xxx")
public class XXXServlet extends HttpServlet{
protected void service(HttpServletRequest req,HttpServletResponse resp){
List list = new ArrayList();
req.setAttribute("list",list);
req.getRequestDispatcher("xxx.jsp").forward(req,resp);
}
}
<%
Object obj = request.getAttribute("list");
List list = (List) obj;
%>
9.13
分页查询和条件分页查询
分页原理,核心sql
-- 查询指定表从指定索引起的指定条记录
select * from 表 limit 索引,条数 where 条件
-- 第一页
select * from book_info limit 0,10
-- 第二页
select * from book_info limit 10,10
-- 第三页
select * from book_info limit 20,10
-- 第page页,每页显示size条
select * from 表 limit (page-1)*size,size
-- 查询总记录数,计算最大页数
select count(主键) from book_info where 条件
dao
/*
* 根据条件查询总记录数
* */
public int getAllCount(String keyword) {
try {
conn = DBUtil.getConnection();
pst = conn.prepareStatement(" select count(book_id) from book_info wherebook_name like concat('%',?,'%')");
pst.setString(1,keyword);
res = pst.executeQuery();
if (res.next()) {
return res.getInt(1);
}
} catch (SQLException e) {
System.out.println("查询总数异常" + e);
} finally {
DBUtil.release(conn, pst, res);
}
return 0;
}
/*
* 查询所有
* 返回值为所有BookInfo对象的集合
*/
public List<BookInfo> queryAll(String keyword, int page, int size) {
//创建集合用于最终的返回值
List<BookInfo> list = new ArrayList<>();
try {
//1.加载驱动
//2.连接
conn = DBUtil.getConnection();
//3.构造sql对其预处理
pst = conn.prepareStatement("select * from book_info where book_name likeconcat('%',?,'%') limit ?,?");
pst.setString(1, keyword);
pst.setInt(2, (page - 1) * size);
pst.setInt(3, size);
//4.执行sql
res = pst.executeQuery();
//处理查询到的数据
while (res.next()) {
int id = res.getInt(1);
String name1 = res.getString(2);
String name2 = res.getString(3);
String date = res.getString(4);
double price = res.getDouble(5);
int type = res.getInt(6);
int num = res.getInt(7);
String img = res.getString(8);
//将读取到的数据保存为一个对象
BookInfo bookinfo = new BookInfo(id, name1, name2, date, price, type, num,img);
//将读取到的数据对象保存到集合中
list.add(bookinfo);
}
} catch (SQLException e) {
System.out.println("查询所有异常" + e);
} finally {
//5.关闭
DBUtil.release(conn, pst, res);
}
return list;
}
servlet
第一次访问分页数据页的路径
"/book_shop/book?op=queryAll&page=1&size=10&keyword="
访问servlet,传递page,size和keyword三个参数,默认第一页,显示10条,关键字为空字符串""
//获取请求时的操作
String op = req.getParameter("op");
switch (op) {
case "queryAll":
String keyword = req.getParameter("keyword");
int page=Integer.parseInt(req.getParameter("page"));
int size=Integer.parseInt(req.getParameter("size"));
//计算最大页数
//Math.ceil((double)总数/size)
double allCount = bookInfoDao.getAllCount(keyword);
int maxPage = (int) Math.ceil(allCount / size);
//得到最大页数,保存到request中
req.setAttribute("maxPage",maxPage);
//调用查询,将查询到的结果保存到作用域中,最终在页面中渲染
List<BookInfo> bookInfoList = bookInfoDao.queryAll(keyword,page,size);
//保存bookInfoList到request作用域中,命名为list。
req.setAttribute("list", bookInfoList);
//跳转到bookList.jsp页面中
req.getRequestDispatcher("pages/bookList.jsp").forward(req, resp);
break;
}
页面
jsp中嵌入的java代码,用于获取当前的keyword、page和size,计算翻页时的首页和尾页
<%
//从request作用域中获取集合
ArrayList<BookInfo> list = (ArrayList) request.getAttribute("list");
String keyword = request.getParameter("keyword");
int pno = Integer.parseInt(request.getParameter("page"));
int size = Integer.parseInt(request.getParameter("size"));
//上一页的page值
int prev = pno == 1 ? 1 : pno - 1;
//得到最大页,获取下一页的page值
int maxPage = (int) request.getAttribute("maxPage");
int next = pno == maxPage ? maxPage : pno + 1;
%>
搜索表单
<form class="form-inline" action="http://localhost:8080/book_shop/book">
<input type="hidden" name="op" value="queryAll">
<input type="hidden" name="page" value="1">
<input type="hidden" name="size" value="10">
<div class="form-group">
<div class="input-group">
<input name="keyword" value="<%=keyword%>" type="text" class="form-control"
placeholder="请输入书名关键字">
</div>
</div>
<button type="submit" class="btn btn-primary">查询</button>
</form>
分页组件
<a href="http://localhost:8080/book_shop/book?op=queryAll&keyword=<%=keyword%>&page=
<%=prev%>&size=<%=size%>">上一页</a>
<span>共<%=maxPage%>页/第<%=pno%>页</span>
<a href="http://localhost:8080/book_shop/book?op=queryAll&keyword=<%=keyword%>&page=
<%=next%>&size=<%=size%>">下一页</a>
9.14
web.xml中自定义
自定义初始页面
命名为index,为项目启动的初始化界面;
命名不为index,自定义后再web.xml配置
<!--启动时的欢迎页面welcome-file -->
<welcome-file-list>
<welcome-file>login.html</welcome-file>
</welcome-file-list>
对于HTTP状态码自定义页面
<!--错误页面自定义-->
<error-page>
<error-code>404</error-code>
<location>/404.html</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/500.html</location>
</error-page>
四大作用域对象
作用域:共享数据的区域
如request就是一个作用域对象,request.setAttribute()和getAttribute(),在请求作用域中保存和读取数据
pageContext
当前页对象,共享数据的范围为当前页面,如果不在一个页面,数据无法读取
request
请求对象,共享数据的范围为一次请求
只要请求不改变(不重定向),数据一直保存在请求对象中
session***
会话对象,共享数据的范围为同一个浏览器指定的时间内
默认会话时长为30分钟,表示如果30分钟没有对该站点进行访问,自动销毁会话
application***
应用程序(项目)对象,共享数据的范围为整个项目中
作用域作用范围
最大到最小:application >> session >> request >> pageContext
作用域对象的方法
//向作用域中保存数据
作用域对象.setAttribute(String str,Object obj);
//读取作用域中保存的数据
Object obj = 作用域对象.setAttribute(String str);
//移除作用域中保存的数据
作用域对象.removeAttribute(String str);
作用域对象的使用
在JSP页面中使用
在JSP中,作用域对象为内置对象,无需定义可以直接使用
p1.jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>当前为p1页面</h1>
<%--在页面中,向四个作用域对象中保存数据--%>
<%
pageContext.setAttribute("str","保存在pageContext中的字符串");
request.setAttribute("str","保存在request中的字符串");
session.setAttribute("str","保存在session中的字符串");
application.setAttribute("str","保存在application中的字符串");
%>
<%--当前页中获取数据的情况--%>
<h1><%=pageContext.getAttribute("str")%></h1>
<h1><%=request.getAttribute("str")%></h1>
<h1><%=session.getAttribute("str")%></h1>
<h1><%=application.getAttribute("str")%></h1>
<%--使用a标签跳转,属于重定向,无法获取request中的内容--%>
<a href="p2.jsp">跳转到p2</a>
<%
//手动销毁session,或超时未操作,无法读取其中的数据
//session.invalidate();
//移除指定作用域中保存的数据
application.removeAttribute("str");
//使用请求转发,除pageContext之外的所有作用域中都能获取保存的数据
request.getRequestDispatcher("p2.jsp").forward(request,response);
%>
</body>
</html>
p2.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>当前为p2页面</h1>
<%--当前页中获取数据的情况--%>
<h1><%=pageContext.getAttribute("str")%></h1>
<h1><%=request.getAttribute("str")%></h1>
<h1><%=session.getAttribute("str")%></h1>
<h1><%=application.getAttribute("str")%></h1>
</body>
</html>
在servlet中使用
-
pageContext
servlet中不会使用pageContext,它本身就是一个类,定义成员变量即可
-
request
使用doGet()、doPost()、service()等方法的HttpServletReques参数即可
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //req就是request对象 }
-
session
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //通过req调用getSession()方法获取session对象 HttpSession session = req.getSession(); }
-
application
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //通过getServletContext()方法获取application对象 ServletContext servletContext = getServletContext(); }
总结
- pageContext对象用于jsp页面中,保存的数据只能在当前页面中使用
- request对象常用于servlet中保存查询后的集合,使用请求转发跳转到jsp页面中输出集合
- session对象常用于登录后保存登录的用户,在其他页面中共享用户对象
- application对象保存共享于整个项目中的数据
Ajax
异步的JavaScript和XML
Ajax最大的特点就是在不重新加载整个页面的情况下,可以和服务器交换数据并更新页面部分内容。
任何的浏览器都支持Ajax。通过原生的js使用Ajax很不方便,使用jquery封装的Ajax
使用
1.页面中引入Jquery文件
<script src="jquery文件路径"></script>
2.给某个节点绑定事件后使用ajax提交数据
<script>
//登录按钮单击事件
$("#logBtn").click(() => {
$.ajax({
//访问的地址
url: 'http://localhost:8080/book_shop/user',
//提交的数据,以键值对的形式提交
data: {
op:"login",
phone: $("input[name=phone]").val(),
password: $("input[name=password]").val(),
role: $("input[name=role]:checked").val()
},
//请求方式
type: "post",
//访问成功后的回调函数
success: (res) => {
//res表示函数返回值
if(res=="error"){
alert("用户名或密码错误");
}
if(res=="ok"){
location.href="http://localhost:8080/book_shop/book?op=queryAll&page=1&size=8&keyword=";
}
}
});
})
</script>
<!--servlet需要返回一个结果值-->
switch (op) {
case "login":
String phone = req.getParameter("phone");
String password = req.getParameter("password");
int role = Integer.parseInt(req.getParameter("role"));
Userinfo userinfo = userinfoDao.login(phone, password, role);
if (userinfo != null) {
//将用户信息保存到会话作用域session中
HttpSession session = req.getSession();
session.setAttribute("userinfo", userinfo);
//返回给前端一个结果,可以登录
resp.getWriter().write("ok");
} else {
//返回给前端一个结果,用户名密码错误
resp.getWriter().write("error");
}
break;
}
购物车
购物车需要实现添加新商品时直接添加,添加旧商品时修改数量
使用HashMap作为购物车的核心容器
商品作为键,保证不重复;购买数量作为值
添加新商品时,向HashMap中添加一组键值对,
添加旧商品时,修改HashMap中现有键值对的值
实现过程
1.BookInfo类重写equals和hashcode方法,这里选择除bookNum之外的所有属性
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BookInfo bookInfo = (BookInfo) o;
return bookId == bookInfo.bookId && Double.compare(bookInfo.bookPrice, bookPrice) == 0 && typeId == bookInfo.typeId && Objects.equals(bookName, bookInfo.bookName) && Objects.equals(bookAuthor, bookInfo.bookAuthor) && Objects.equals(publishDate, bookInfo.publishDate) && Objects.equals(bookImg, bookInfo.bookImg);
}
@Override
public int hashCode() {
return Objects.hash(bookId, bookName, bookAuthor, publishDate, bookPrice, typeId, bookImg);
}
2.创建购物车Cart类
package com.hqyj.book_shop.util;
import com.hqyj.book_shop.entity.BookInfo;
import java.util.HashMap;
/*
* 购物车类
* HashMap作为容器
* 图书对象BookInfo作为键
* 购买数量作为值
*
* 添加商品
* 移除商品
* 查看所有
* 清空购物车
* */
public class Cart {
private HashMap<BookInfo, Integer> hm;
public Cart() {
this.hm = new HashMap();
}
/*
* 添加商品
* */
public void addToCart(BookInfo bi, int buyNum) {
//判断图书(键)是否存在
if (hm.containsKey(bi)) {
//得到现有数量
Integer oldNum = hm.get(bi);
//存在修改已有数量
hm.put(bi, oldNum + buyNum);
} else {
//不存在直接添加一组新键值对
hm.put(bi, buyNum);
}
}
/*
* 输出所有商品
* */
public HashMap<BookInfo, Integer> showCart() {
return hm;
}
}
3.登录成功后,创建购物车对象
case "login":
String phone = req.getParameter("phone");
String password = req.getParameter("password");
int role = Integer.parseInt(req.getParameter("role"));
Userinfo userinfo = userinfoDao.login(phone, password, role);
if (userinfo != null) {
//将用户信息保存到会话作用域session中
HttpSession session = req.getSession();
session.setAttribute("userinfo", userinfo);
//创建购物车对象,保存到session中
Cart cart = new Cart();
session.setAttribute("cart",cart);
//resp.sendRedirect("/book_shop/book?op=queryAll&page=1&size=8&keyword=");
resp.getWriter().write("ok");
} else {
//用户名密码错误
resp.getWriter().write("error");
}
break;
4.在图书详情页中,创建按钮或表单提交要购买的商品到购物车中
<script>
$("#addToCart").click(() => {
$.ajax({
url: 'http://localhost:8080/book_shop/book',
data: {
op:"addToCart",
id:<%=book.getBookId()%>,
buyNum:$("input[type=number]").val()
},
type: 'post',
success: (res) => {
if(res=="ok"){
alert("添加成功");
}
}
});
})
</script>
5.在book的servlet中,添加新的op操作,获取参数添加到购物车
case "addToCart":
//查询要购买的图书对象
BookInfo buyBook = bookInfoDao.findById(Integer.parseInt(req.getParameter("id")));
int buyNum = Integer.parseInt(req.getParameter("buyNum"));
//获取登录成功时创建的购物车对象
Cart cart = (Cart) req.getSession().getAttribute("cart");
//添加到购物车
cart.addToCart(buyBook, buyNum);
resp.getWriter().write("ok");
break;
9.15
EL
Expression Language
表达式语言,是为了使JSP中的输出写起来更加便捷;替换JSP中的<%=%>
主要输出保存在某个作用域中的数据
特点
-
减少代码(获取对象、转换对象、获取对象属性)
-
如通过"req.setAttribute(“list”,集合)"保存了一个集合到请求对象中,
在JSP中,如果用原始的表达式,使用以下方式获取
<%List list=(List)request.getAttribute("list");%> <%for(Person p : list){%> <li><%=p.getName()%></li> <%}%>
如果使用EL+JSTL的方式输出
<c:forEach var="p" items="${list}"> <td>${p.name}</td> </c:forEach>
-
-
免去非空判断
- 如果使用${}输出某个保存在作用域中的数据时,未找到也不会报错,会以空字符串的形式输出
使用
1.用于获取保存在某个作用域中的对象
获取使用"作用域对象.setAttribute(“对象名”,对象)"保存的对象:${对象名}
-
会试图从最小的作用域获取到最大的作用域,即pageContext–>request–>session–>application
- 获取到后不再继续获取后续作用域
- 如果任何一个作用域中都没有对应的对象,会输出空字符串""
-
从指定的作用域中获取对象
- 一般直接识别即可,若出现对象名一样的两个作用域才需指定作用域来获取指定
- 一般对象名不重复
作用域单词 | 对应作用域 | 代码 |
---|---|---|
pageScope | pageContext | ${pageScope.对象} |
requestScope | request | ${requestScope.对象} |
sessionScope | session | ${sessionScope.对象} |
applicationScope | application | ${applicationScope.对象} |
- 输出对象的属性
- 要保证该属性有对应的公共的get方法
${对象.属性}
${对象["属性"]}
实例
//在Servlet中,进行查询后,将一个集合保存在请求request中,跳转到某个页面
List<BookInfo> list = dao.queryAll();
req.setAttribute("list",list);
req.getRequestDispatcher("bookList.jsp").forward(req,resp);
<!--如果使用JSP中的表达式-->
<%List<BookInfo> list = (List<BookInfo> request.getAttribute("list"));%>
<table>
<%for(BookInfo bi:list){%>
<tr>
<td><%=bi.getBookName%></td>
<td><%=bi.getBookAuthor%></td>
</tr>
<%}%>
</table>
<!--如果使用EL+JSTL-->
<table>
<c:forEach items="${list}" var="book">
<tr>
<td>${book.bookName}</td>
<td>${book.bookAuthor}</td>
</tr>
</c:forEach>
</table>
2.用于获取当前项目上下文路径(根目录+项目名)
如http://localhost:8080/book_shop
是一个项目上下文路径
如果使用EL,在JSP中获取路径时,使用**${pageContext.request.contextPath}**表示项目上下文路径
<form action="${pageContext.request.contextPath}/book">
</form>
<a href="${pageContext.request.contextPath}/book?id=123"></a>
<img src="${pageContext.request.contextPath}/pages/imgs/xxx.jpg">
$.ajax({
url:"${pageContext.request.contextPath}/book"
})
3.用于获取请求中携带的参数
可以使用EL获取表单或?后提交的数据
${param.参数}表示req.getParameter(“参数”)方法获取
<form action="xxx.jsp">
<input type="text" name="username">
<input type="submit">
</form>
<a href="xxx?id=123"></a>
在xxx.jsp中获取的方式
${param.username}
${param.id}
注意
-
web.xml版本在4.0之后,在JSP中使用EL时,默认可以识别
-
如果JSP无法识别EL,原样输出${}符号时,在指令<%@ %>中加isELIgnored="false"表示不忽略EL
<%@ page contentType="text/html;charset=UTF-8" language="java"
isELIgnored="false"%>
-
如果在使用EL的过程中,出现PropertyNotFoundException异常,表示未找到指定属性,原因有
- 缺少指定属性
- 指定属性没有对应的公共的get方法
JSTL
Java Server Page Standarded Tag Library JSP标准标签库
可以使用JSTL中的特定标签,来替换JSP中常见的Java代码。如循环判断等。减少JSP中的Java代码,提高页面的可读性
使用
导入JSTL依赖
<!-- jstl -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
JSP页面中,加入标签库指令
写标签<c:forEach>
时,会自动导入这句话
<%--导入jstl标签库--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
具体用法
定义变量或给变量赋值
<c:set var="变量名" value="值"></c:set>
<c:set var="name" value="小明"></c:set>
if判断
<c:if test="判断条件">
满足执行
</c:if>
<c:if test="${empty user}">
<span>请登录</span>
</c:if>
<c:if test="${!empty user}">
<span>${user}</span>
</c:if>
遍历List集合
<c:forEach items="要遍历的集合" var="遍历出的每个对象">
</c:forEach>
<%
List list = new ArrayList();
list.add("asdfsd");
list.add(123);
list.add(null);
list.add(true);
request.setAttribute("list",list);
%>
<ul>
<c:forEach items="${list}" var="obj">
<li>${obj}</li>
</c:forEach>
</ul>
遍历Map集合
<c:forEach items="要遍历的Map" var="遍历出的键值对">
</c:forEach>
<%
HashMap hm = new HashMap<>();
hm.put("yyds","永远单身");
hm.put("u1s1","有一说一");
hm.put("dddd","懂的都懂");
request.setAttribute("hm",hm);
%>
<c:forEach items="${hm}" var="kv">
<h3>${kv.key}--${kv.value}</h3>
</c:forEach>
使用JSTL和EL输出购物车中的信息
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--
Created by IntelliJ IDEA.
User: Administrator
Date: 2023/9/15
Time: 11:41
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>
<jsp:include page="nav.jsp"></jsp:include>
<div class="row">
<div class="col-md-2"></div>
<div class="col-md-8">
<table class="table table-striped">
<tr>
<th>图书编号</th>
<th>图书名称</th>
<th>图书图片</th>
<th>图书价格</th>
<th>购买数量</th>
<th>小计</th>
<th>操作</th>
</tr>
<c:if test="${empty hm}">
<tr>
<td colspan="6">当前购物车中无商品</td>
</tr>
</c:if>
<c:if test="${!empty hm}">
<%--定义变量用于计算总和--%>
<c:set var="sum" value="0"></c:set>
<%--遍历保存在session中的hm--%>
<c:forEach items="${hm}" var="kv">
<tr>
<%--键值对中的键key为图书对象,值value为购买数量--%>
<td>${kv.key.bookId}</td>
<td>${kv.key.bookName}</td>
<td><img height="40" src="${pageContext.request.contextPath}/pages/imgs/${kv.key.bookImg}"></td>
<td>${kv.key.bookPrice}</td>
<td><input class="buyNum" type="number" value="${kv.value}" min="1" max="${kv.key.bookNum}">
</td>
<td>¥${kv.value * kv.key.bookPrice}</td>
<%--更新综合--%>
<c:set var="sum" value="${sum + kv.value * kv.key.bookPrice}"></c:set>
<td><a href="${pageContext.request.contextPath}/book?op=remove&id=${kv.key.bookId}"
class="btn btn-danger btn-sm">移除</a></td>
</tr>
</c:forEach>
<tr>
<td colspan="5">总计</td>
<td>¥${sum}</td>
<td><a href="${pageContext.request.contextPath}/book?op=clear"
class="btn btn-danger btn-sm">清空购物车</a></td>
</tr>
</c:if>
</table>
</div>
<div class="col-md-2"></div>
</div>
<script>
/*购买数量改变时*/
$(".buyNum").blur(function () {
if (confirm("确认要修改吗")) {
$.ajax({
url: '${pageContext.request.contextPath}/book',
data: {
op: "updateBuyNum",
// $(this).parent().siblings("td:eq(0)").text()
id: $(this).parent().parent().children("td:eq(0)").text(),
newBuyNum: $(this).val()
},
type: "post",
success: (res) => {
if (res == "ok") {
alert("修改成功");
location.reload();
}
}
});
} else {
location.reload();
}
});
</script>
</body>
</html>
过滤器Filter——特殊的Servlet
一个特殊的Servlet
使用
新建一个类,继承HttpFilter
/*
* 1.继承HttpFilter
* 2.重写受保护的doFilter方法
* 3.配置过滤器
* */
public class MyFilter extends HttpFilter {
@Override
protected void doFilter(HttpServletRequest req, HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
//获取当前访问该程序的uri(地址)
String uri = req.getRequestURI();
System.out.println(uri);
//如果登录成功后,会在session中保存userinfo对象
//过滤器中判断当前session中是否存在userinfo对象
//如果存在,随意访问,不存在则一律跳转到登录页
if (req.getSession().getAttribute("userinfo") != null) {
//存在,放行请求
chain.doFilter(req, res);
return;
}
//放行登录页及其相关的模块、样式文件、js文件等
if (uri.contains("imgs")||uri.contains(".html") || uri.contains("user")||
uri.contains(".css")|| uri.contains(".js")) {
//登录页及其登录功能,放行请求
chain.doFilter(req, res);
}else{
//跳转登录页
res.sendRedirect("http://localhost:8080/book_shop/login.html");
}
}
}
配置过滤器
<!--声明一个过滤器-->
<filter>
<filter-name>myFilter</filter-name>
<!--过滤器类的全限定名-->
<filter-class>com.hqyj.book_shop.filter.MyFilter</filter-class>
</filter>
<!--配置过滤器映射-->
<filter-mapping>
<!--指定某个过滤器-->
<filter-name>myFilter</filter-name>
<!--设置要过滤的请求,这里*表示所有请求-->
<url-pattern>/*</url-pattern>
</filter-mapping>
9.18
带有关键字的外键查询
重新设计实体类
主表实体类
没有变化,只定义属于自身的属性
public class BookType {
private int typeId;
private String typeName;
//省略get/set、构造方法等
}
从表实体类
额外添加一个字段:外键对应的主表对象
public class BookInfo {
private int bookId;
private String bookName;
private String bookAuthor;
private String publishDate;
private double bookPrice;
private int typeId;
private int bookNum;
//额外添加一个字段:外键对应的主表实体对象
private BookType bt;
//省略get/set、构造方法等
}
数据访问类
从表数据访问类BookInfoDao
方式一:连接查询
适合关联的表中字段比较少的情况,使用连接查询的sql语句
-- 根据关键字和类型查询所有图书,显示图书的类型名
select * from book_info bi,book_type bt where bi.type_id=bt.type_id and book_name like '%龙%' and bt.type_id=1
结果
从表数据访问类
/*
* 查询所有
* 返回值为所有BookInfo对象的集合
* 根据书名关键字和类型编号查询图书,显示其类型名称
*/
public List<BookInfo> queryAll(String keyword, int page, int size, int typeId) {
//创建集合用于最终的返回值
List<BookInfo> list = new ArrayList<>();
try {
//1.加载驱动
//2.连接
conn = DBUtil.getConnection();
//3.构造sql对其预处理
//根据关键字查询
String sql1 = "select * from book_info bi,book_type bt " +
"where bi.type_id=bt.type_id " +
"and book_name like concat('%',?,'%') limit ?,?";
//根据关键字和类型查询
String sql2 = "select * from book_info bi,book_type bt " +
"where bi.type_id=bt.type_id " +
"and book_name like concat('%',?,'%') and bt.type_id=? limit ?,?";
if (typeId == 0) {//0表示只根据关键字查询
pst = conn.prepareStatement(sql1);
pst.setString(1, keyword);
pst.setInt(2, (page - 1) * size);
pst.setInt(3, size);
} else {//根据关键字和类型查询
pst = conn.prepareStatement(sql2);
pst.setString(1, keyword);
pst.setInt(2, typeId);
pst.setInt(3, (page - 1) * size);
pst.setInt(4, size);
}
//4.执行sql
res = pst.executeQuery();
//处理查询到的数据
while (res.next()) {
int id = res.getInt(1);
String name1 = res.getString(2);
String name2 = res.getString(3);
String date = res.getString(4);
double price = res.getDouble(5);
int type = res.getInt(6);
int num = res.getInt(7);
String img = res.getString(8);
//得到查询出的类型名
String typeName = res.getString(10);
//创建类型对象
BookType bt = new BookType();
bt.setTypeId(type);
bt.setTypeName(typeName);
//将读取到的数据保存为一个对象
BookInfo bookinfo = new BookInfo(id, name1, name2, date, price, type, num, img, bt);
//将读取到的数据对象保存到集合中
list.add(bookinfo);
}
} catch (SQLException e) {
System.out.println("查询所有异常" + e);
} finally {
//5.关闭
DBUtil.release(conn, pst, res);
}
return list;
}
/*
* 查询总记录数
* */
public int getAllCount(String keyword, int typeId) {
try {
conn = DBUtil.getConnection();
String sql1 = "select count(book_id) from book_info where book_name like concat('%',?,'%')";
String sql2 = "select count(book_id) from book_info where type_id=? and book_name like concat('%',?,'%')";
if (typeId == 0) {
pst = conn.prepareStatement(sql1);
pst.setString(1, keyword);
} else {
pst = conn.prepareStatement(sql2);
pst.setInt(1, typeId);
pst.setString(2, keyword);
}
res = pst.executeQuery();
if (res.next()) {
return res.getInt(1);
}
} catch (SQLException e) {
System.out.println("查询总数异常" + e);
} finally {
DBUtil.release(conn, pst, res);
}
return 0;
}
方式二:子查询
适合外键关联的表的字段比较多的情况
先查询从表,根据从表中的外键字段,作为条件查询主表
-- 查询从表
select * from book_info where book_name like '%龙%';
-- 根据从表中的外键字段,作为条件查询主表
select * from book_type where type_id=1;
select * from book_type where type_id=2;
主表数据访问类
定义一个根据主键查询的方法
public BookType findById(int typeId) {
try {
conn = DBUtil.getConnection();
pst = conn.prepareStatement(" select * from book_type where type_id=?");
pst.setInt(1,typeId);
res = pst.executeQuery();
while (res.next()) {
BookType bt = new BookType(res.getInt(1), res.getString(2));
return bt;
}
} catch (SQLException e) {
System.out.println("查询所有类型异常" + e);
} finally {
DBUtil.release(conn, pst, res);
}
return null;
}
从表数据访问类
查询自身表中,调用主表数据访问类中根据主键查询的方法
//根据外键字段,作为条件查询主表
BookType bt = new BookTypeDao().findById(type);
public List<BookInfo> queryAll2(String keyword,int page,int size) {
//创建集合用于最终的返回值
List<BookInfo> list = new ArrayList<>();
try {
//1.加载驱动
//2.连接
conn = DBUtil.getConnection();
//3.构造sql对其预处理
//查询从表
pst=conn.prepareStatement("select * from book_info where book_name like concat('%',?,'%') limit ?,?");
pst.setString(1, keyword);
pst.setInt(2, (page - 1) * size);
pst.setInt(3, size);
//4.执行sql
res = pst.executeQuery();
//处理查询到的数据
while (res.next()) {
//从表数据
int id = res.getInt(1);
String name1 = res.getString(2);
String name2 = res.getString(3);
String date = res.getString(4);
double price = res.getDouble(5);
int type = res.getInt(6);
int num = res.getInt(7);
String img = res.getString(8);
//根据外键字段,作为条件查询主表
BookType bt = new BookTypeDao().findById(type);
//将读取到的数据保存为一个对象
BookInfo bookinfo = new BookInfo(id, name1, name2, date, price, type, num, img, bt);
//将读取到的数据对象保存到集合中
list.add(bookinfo);
}
} catch (SQLException e) {
System.out.println("查询所有异常" + e);
} finally {
//5.关闭
DBUtil.release(conn, pst, res);
}
return list;
}
JSP内置对象
在JSP中,有一些对象可以不用定义就使用,这些对象称为内置对象
JSP中一共有9个内置对象。4个作用域对象和其他5个内置对象
“rrppsoace”
-
pageContext 当前页作用域
-
request 请求作用域
-
session 会话作用域
-
application 项目作用域
-
response 响应
- 用于页面中处理响应,如自动跳转
-
page 当前页对象
- 表示页面对应的servlet对象,即this对象
-
out 输出对象
- 用于输出信息,效果同<%=%>
-
config 配置
- 得到页面中的配置信息
-
exception 异常
- 页面必须是异常页面才能使用,用于获取该页面中的异常对象
Session和Cookie
session
session称为会话,是一个作用域对象,可以通过它保存对象,共享于各个页面和Servlet之间
默认session的会话有效期为30分钟,可以更改,超时未操作、手动销毁或关闭浏览器会让session失效
session共享数据的原理
-
访问任意jsp页面时,会创建一个JSESSIONID,是一个session编号,保存在一个cookie文件中,不同的浏览器访问,JSESSIONID的值不同
默认使用session <%@ session="true"%> 不使用session <%@ session="false"%>
-
保持浏览器开启的状态下,打开该站点下的其他页面,JSESSIONID的值不变,所以在某个页面中通过session保存的数据,在其他页面也能获取
-
使用session时,需要依赖于cookies文件,如果浏览器关闭了cookie功能,则无法使用session
session对象常用方法
常用方法 | 说明 |
---|---|
getAttribute(String str) | 获取保存在session中的对象,返回值类型为Object |
setAttribute(String str,Object obj) | 将obj保存到session中,命名为str |
removeAttribute(String str) | 移除session中保存的某个对象 |
invalidate() | 销毁session |
getMaxInactiveInterval() | 得到session的有效时长,单位为秒,默认为1800 |
setMaxInactiveInterval(int second) | 设置session的有效时长,单位为秒。 |
getCreationTime() | 得到session创建时间,单位为毫秒 |
getLastAccessedTime() | 得到最后一次访问session的时间,单位为毫秒 |
设置项目全局session有效时长
在web.xml中设置,单位为分钟
<!--session配置-->
<session-config>
<!--session超时时间15分钟-->
<session-timeout>15</session-timeout>
</session-config>
Cookie
cookie是一个用于保存数据的对象,实际是一个保存在浏览器本地的文件。
关闭浏览器,cookie依然存在,在手动清理或超时自动清理后,数据随之消失。
cookie通常用于更久地保存数据,即便关闭浏览器,也能一直存在。如登录信息,购物车信息。
单个cookie中保存的数据有上限(4kb),cookie在浏览器中保存的数量也有上限(30~300根据浏览器不同有变化)。
创建Cookie
//创建cookie,向其中保存一些数据
//Cookie(String name,String value) 创建一个名为name的cookie对象,保存的数据为value
Cookie cookie = new Cookie("xyz","保存在cookie中的数据");
//设置有效时长,单位为秒,如果不设置,关闭浏览器后自动销毁
cookie.setMaxAge(10*24*3600);//10天
保存Cookie
response.addCookie(cookie);
遍历Cookie
Cookie[] cookies = request.getCookies();
for (Cookie c : cookies) {
System.out.println(c.getName());
}
session和cookie的对比
- session中保存的数据可以是任意类型,没有大小限制;cookie中保存的是键值对,单个值大小上限为4kb
- session中保存的数据存在于服务器中;cookie中保存的数据存在于浏览器中。
- session到期或浏览器关闭就会失效;cookie如果设置了有效时长,即使关闭浏览器也会存在,只有到期或手动清理才会失效
监听器Listener
监听一些特殊的事件,当这些事件发生时,监听器中的代码就会自动执行
在使用框架时,可以用于初始化参数
常用的监听器接口
- ServletContextListener application监听器
- HttpSessionListener session监听器
- ServletRequestListener request监听器
以上3个接口中都有两个方法:创建和销毁
如contextInitialized()表示整个项目在启动时自动执行,sessionDestroyed()表示在会话销毁时自动执行
可以通过重写该方法,实现在具体的事件触发时执行对应的操作
实现一个监听器
1.创建一个类,实现某个监听器接口
2.重写实现的监听器接口中的方法
public class MyListener implements ServletContextListener, HttpSessionListener, ServletRequestListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("项目启动成功。。。");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("项目关闭。。。");
}
@Override
public void requestDestroyed(ServletRequestEvent sre) {
System.out.println("请求结束");
}
@Override
public void requestInitialized(ServletRequestEvent sre) {
System.out.println("请求初始化");
}
@Override
public void sessionCreated(HttpSessionEvent se) {
System.out.println("会话session创建");
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
System.out.println("会话session销毁");
}
}
3.配置监听器
<listener>
<listener-class>com.hqyj.book_shop.listener.MyListener</listener-class>
</listener>
web.xml中的常见配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--启动时的欢迎页面,默认为index页面-->
<welcome-file-list>
<welcome-file>login.html</welcome-file>
</welcome-file-list>
<!--配置全局404页面***-->
<error-page>
<error-code>404</error-code>
<location>/404.html</location>
</error-page>
<!--配置空指针异常页面-->
<!--<error-page>
<exception-type>java.lang.NullPointerException</exception-type>
<location>/500.html</location>
</error-page>-->
<error-page>
<error-code>500</error-code>
<location>/500.html</location>
</error-page>
<!--session配置***-->
<session-config>
<!--session超时时间15分钟-->
<session-timeout>15</session-timeout>
</session-config>
<!------------以下内容在框架部分会经常配置--------------->
<!--声明一个过滤器***-->
<filter>
<filter-name>myFilter</filter-name>
<!--过滤器类的全限定名-->
<filter-class>com.hqyj.book_shop.filter.MyFilter</filter-class>
<!--初始化该filter时的参数-->
<init-param>
<param-name>servlet的初始化参数</param-name>
<param-value>值</param-value>
</init-param>
</filter>
<!--配置过滤器映射-->
<filter-mapping>
<!--指定某个过滤器-->
<filter-name>myFilter</filter-name>
<!--设置要过滤的请求,这里*表示所有请求-->
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--配置监听器***-->
<listener>
<listener-class>com.hqyj.book_shop.listener.MyListener</listener-class>
</listener>
<!--定义全局变量***-->
<context-param>
<!--全局变量名-->
<param-name>appVar</param-name>
<!--变量的值-->
<param-value>定义在全局中的一个参数</param-value>
</context-param>
<!--声明servlet***-->
<servlet>
<servlet-name>servlet名</servlet-name>
<servlet-class>servlet全限定名</servlet-class>
<!--初始化该servlet时的参数-->
<init-param>
<param-name>servlet的初始化参数</param-name>
<param-value>值</param-value>
</init-param>
</servlet>
<!--配置servlet请求映射***-->
<servlet-mapping>
<servlet-name>servlet名</servlet-name>
<url-pattern>/请求映射</url-pattern>
</servlet-mapping>
</web-app>
WEB开发模式
Model1模式 传统的以JSP为主的开发方式
JSP+JavaBean的模式称为Model1模式,这种方式适合业务逻辑简单的项目,不适合后期扩展维护。
-
JSP中的内容复杂,既要显示数据,也要处理逻辑。
-
JavaBean是数据的载体
JavaBean是一个基础的类,具备以下几个特点
- 公共的类
- 封装属性,提供get/set方法
- 有无参数的构造方法
这种JavaBean也被称为实体Entity,模型Model,领域模型domain,vo,pojo等,通常对应数据库中的某张表
Model2模式 也称为MVC模式
M:Model 模型 用于封装数据,处理业务
V:View 视图 用于渲染数据
C:Controller 控制器 用于接收请求,响应客户端
MVC模式是当前主流的大型项目使用的模式,做到了界面、数据和逻辑的分离(解耦)
有利于分工协作开发,也使得代码结构更加清晰,便于开发和维护
常见HTTP状态码
- 100类状态码,信息型状态码,表示服务器正在处理请求
- 100,接受的请求正在处理
- 101,切换协议,服务器根据客户端的请求切换协议
- 只能切换到更高级的协议,例如,切换到HTTP的新版本协议
- 200类状态码,成功状态码,表示请求已正常处理完毕
- 200,成功,服务器已成功处理了请求
- 300类状态码,重定向状态码,表示进行额外操作以完成请求
- 301,永久性重定向,表示资源已被分配了新的 URL
- 302,临时性重定向,表示资源临时被分配了新的 URL
- 303,表示资源存在另一个URL,用GET方法获取资源
- 304,(未修改)自从上次请求后,请求网页未修改过。服务器返回此响应时,不会返回网页内容
- 400类状态码,客户端错误状态码,请求可能出错导致服务器无法处理
- 400,错误请求,服务器不理解请求的语法
- 401,表示发送的请求需要有通过HTTP认证的认证信息
- 403,禁止,服务器拒绝请求
- 404,未找到,服务器找不到请求网页
- 500类状态码,服务器错误状态码,表示服务器原因导致请求出错
- 500,服务器内部错误,服务器遇到错误,无法完成请求
- 503,表示服务器处于停机维护或超负载,无法处理请求
- 600类状态码,源站没有返回响应头部,只返回实体内容