Web总汇

目录

Web

9.9

JavaWeb

JavaWeb,是在B/S模式下,使用Java开发综合性Web服务网站的技术

网站

用户通过浏览器访问某个域名或ip后,浏览到的综合性页面

实际是发布在服务器上的一个应用程序,通过浏览器访问

网页
  • 静态页面:所有人访问的内容都一样
  • 动态页面:不同的人看到页面中的数据不一致

网络服务器

部署web项目的平台

TomCat

由Apecha、Sun公司及其他公司和个人共同开发的网络服务器,免费、开源、轻量级,在中小型系统中普遍使用

下载

tomcat官网Apache Tomcat® - Welcome!

根据Jdk选择合适的版本

image-20230909112621154

使用

下载成功后,无需安装,解压到某个盘下即可

image-20230909112701468

目录说明
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页面,表示资源不存在

image-20230909113123880

修改主机名,即修改本地hosts文件

默认,本机127.0.0.1,对应的域名localhost

将本地hosts文件复制一份到桌面,最后新增自定义host名,而后替换系统中默认的hosts文件

127.0.0.1		xxx

最后启动tomcat后,即可通过xxx:8080代替localhost:8080

image-20230909113516087

Maven

  • 用于管理项目的工具
  • 最主要的作用是管理项目所需的jar文件、打包项目等
  • 通过在Maven项目中加入某个jar文件所需的dependency(依赖),让其自动从Maven仓库下载对应的jar文件
Maven依赖官网

Maven Repository: Search/Browse/Explore (mvnrepository.com),在这个网站查询所需的jar文件的依赖和文件

image-20230909101358291

Maven本地仓库

Maven默认的配置文件会从官网下载jar文件,速度较慢,并且下载的文件保存在c盘

这里修改下载地址和保存路径,即为创建一个本地仓库

image-20230909101843077

本地仓库配置文件
<?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项目配置
设置当前项目的Maven配置

点击顶端File中setting选项,搜索maven后,更改Maven中的User settings file为刚刚自行配置的本地文件

更改User settings file之前,必须先勾选最右边的Override,再进行配置文件更改

image-20230909102256099

设置新项目的Maven配置

先点击顶端File中New Projects Setup选项,再选择Setting for New Projects….选项,而后与之前一致

搜索maven后,更改Maven中的User settings file为刚刚自行配置的本地文件

更改User settings file之前,必须先勾选最右边的Override,再进行配置文件更改

image-20230909103048959

IDEA创建普通Maven项目:easyexcel实现读写excel文件

使用easyexcel实现读写excel文件

创建空Maven项目

image-20230909103601802

项目目录

image-20230909103647938

修改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按钮,然后点击最顶端的刷新按钮

刷新方式一:

image-20230909103940040

刷新方式二:

image-20230909104038545

加入esayexcel依赖,选择版本,复制依赖

与mysql类似操作

image-20230909104623862

<!--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模板

新建webapp模板

在src目录下新建目录用于创建java源文件

image-20230909110148067

修改项目中web.xml版本为4.0
先删除原web.xml版本的文件

删除后,先点击apply,再进行添加

先删除原web.xml版本

再添加web.xml版本为4.0的文件

image-20230908185103443

配置tomcat服务器

点击右端的add configuration,再点击顶端的**+号,在add New configuration找到Tomcat Server并点击Local**

image-20230908185212195

点击左侧Configure…选项,选择tomcat所在的根目录

image-20230908185441726

这些会自动导入,可自行选择修改

image-20230908185558551

部署项目到tomcat中

切换到Deployment下,点击+号,选择Artifact…选项,最后添加第二个带有exploded关键字的项目

image-20230908185727521

添加成功后,在Deployment下的最底部可自定义项目默认的名称

image-20230908190141940

启动项目,即启动Tomcat

启动Tomcat,启动成功后,自动打开指定浏览器并进入当前部署的系统

也可以自行访问http://localhost:8080/web01_war_exploded/

这里看到的界面是web项目中自带的index.jsp 文件

image-20230908190410703

解决tomcat控制台中文乱码

点击顶端help中的Edit Custom VM Options…,再最后添加下面的语句

-Dfile.encoding=utf-8

必须确保该文件下没有中文存在,否则会出错

image-20230909110759299

web项目目录结构

webapp下如果有index文件,访问项目后会自动访问index文件

如果没有index文件,会出现404,表示index 页面不存在

项目上下文路径

域名+端口号+项目名,称为项目上下文路径;如:localhost:8080/web01 就是项目上下文路径,可以理解为项目根目录

webapp目录中的内容直接通过项目上下文路径进行访问

image-20230908190703220

更新了项目中的内容后,根据需求更新资源、重启或重新部署

image-20230908190740653

Servlet

Servlet表示Server+Applet ,表示运行在服务器上的程序,是一个被动程序,每次请求时都会执行

编写Servlet的步骤
在项目中导入Servlet所需的依赖

image-20230909111549011

粘贴到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>
复制某个类的全限定名

image-20230909112201979

解决控制台输出中文时的乱码

打开刚刚部署的tomcat服务器,在VM option处添加-Dfile.encoding=utf-8

image-20230909111356476

9.11

Web项目的访问流程

下图整体,表示Web项目的访问流程

绿色部分,属于"三层架构"

浏览器发送的请求,称为request,得到的响应,称为response

image-20230911182613116

三层架构——“高内聚,低耦合”

在软件开发中,并不是将所有的功能集成到一个类或文件中实现,而是要将其分层处理

满足三层架构,就是为了满足**“高内聚,低耦合”**的目的

  • 高内聚是指,各个模块的功能不可再分
  • 低耦合是指,降低各个模块之间的关联程度,便于开发和维护,各个模块各司其职

通常所说的三层架构中的三层,指"视图表现层"、“业务逻辑层"和"数据访问层

  • 视图表现层:用于展示和提供用户输入数据的渠道

    • 在适当的情况下,输出业务逻辑层中的内容

    • 如 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页面
  • 执行增删改后,不建议使用请求转发,因为刷新页面会重复提交

image-20230918183041286

重定向
response.sendRedirect("目的路径");
  • 使用重定向跳转到目的地后,浏览器的地址栏是最终访问的目的地址
  • 如果在重定向时使用request保存数据,后续无法获取保存的数据
  • 在执行增删改后使用重定向跳转,防止表单重复提交

image-20230918183126875

跳转过程中传递参数

由页面发送数据到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 * fromlimit 索引,条数 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 * fromlimit (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

    • 获取到后不再继续获取后续作用域
    • 如果任何一个作用域中都没有对应的对象,会输出空字符串""
  • 从指定的作用域中获取对象

    • 一般直接识别即可,若出现对象名一样的两个作用域才需指定作用域来获取指定
    • 一般对象名不重复
作用域单词对应作用域代码
pageScopepageContext${pageScope.对象}
requestScoperequest${requestScope.对象}
sessionScopesession${sessionScope.对象}
applicationScopeapplication${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

image-20230918184210420

使用
新建一个类,继承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

带有关键字的外键查询

image-20230918184328945

重新设计实体类
主表实体类

没有变化,只定义属于自身的属性

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

结果

image-20230918184531711

从表数据访问类
/*
     * 查询所有
     * 返回值为所有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 '%龙%';

image-20230918184702384

-- 根据从表中的外键字段,作为条件查询主表
select * from book_type where type_id=1;
select * from book_type where type_id=2;

image-20230918184752403

主表数据访问类

定义一个根据主键查询的方法

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等,通常对应数据库中的某张表

image-20230918162910028

Model2模式 也称为MVC模式

M:Model 模型 用于封装数据,处理业务

V:View 视图 用于渲染数据

C:Controller 控制器 用于接收请求,响应客户端

MVC模式是当前主流的大型项目使用的模式,做到了界面、数据和逻辑的分离(解耦)

有利于分工协作开发,也使得代码结构更加清晰,便于开发和维护

image-20230918163415144

常见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类状态码,源站没有返回响应头部,只返回实体内容
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值