javaweb学习

JavaWeb

基本概念

前言

  • 日常访问的网站,比如百度(www.baidu.com)就是一个web页面。

  • JavaWeb分为两部分:java和web,web用来提供数据的展示,java用来为web提供数据和处理数据。

  • web

    • 静态web

      每个用户访问到的内容都是一样的,页面数据无法动态更新;

    • 动态web

      用户访问到的内容,会根据用户的不同进行动态展示;

  • 动态web资源开发统技术统称为JavaWeb

web应用程序

  • 浏览器可以访问的程序,称为web应用程序;

  • web应用可以访问HTML页面、资源、URL等;

  • 这些资源无法启动,依赖于服务器(比如:Tomcat)。

静态web

1访问静态资源

*.html、*.htm后缀的网页、JSP页面等
  • 缺点:页面内容无法动态更新、无法和数据库进行交互;可以通过JavaScript、轮播图等实现“伪动态”。

动态web

2访问动态资源

  • 优点:可以让页面内容实现动态更新、可以和数据库进行交互;
  • 缺点:当访问web资源报错时,需要修改修改代码并重新部署服务。

web服务

技术讲解

  • APS

    • 微软发布的一种技术;
    • 开发过程中会在HTML中混入java代码,一个页面基本几千行代码;
    • 维护成本高。
  • PHP

    • 优点:开发速度快、功能强大、跨平台、代码简单;
    • 缺点:无法承载大量访问。
  • JSP/servlet

    • B/S架构语言;
    • 基于java语言,很多大公司的开源组件都是java写的;语言生态强大;
    • 可以承载三高(高并发、高可用、高性能)问题;
    • 语法像ASP,转换简单。

Tomcat

下载&安装tomcat

3tomcat下载

  • 安装

​ 下载完成直接解压即可

4tomcat目录

启动&停止

  • 启动:bin目录下的startup.bat,双击即可;

  • 停止:

    • 方式一:直接关闭启动窗口;
    • 方式二:bin目录下的shutdown.bat,双击即可;
  • 注意事项:

    1. java环境变量要配置好;

    2. 闪退问题;

    3. 乱码问题;conf/server.xml

  • 默认访问:

    • 默认访问的是webapps/ROOT/index.jsp页面。
  • 修改访问路径:

    • 修改端口--将端口改为8081

      conf/server.xml

      <Connector port="8081" protocol="HTTP/1.1"
                     connectionTimeout="20000"
                     redirectPort="8443"
                     maxParameterCount="1000"
                     />
      
    • 修改访问路径:将localhost改为www.hahahaha.com

      conf/server.xml

      <Host name="www.hahahaha.com"  appBase="webapps"
                  unpackWARs="true" autoDeploy="true">
      

      直接修改可能无法访问,需要再windows中的host文件先进行以下配置:

      127.0.0.1 www.hahahaha.com
      

5修改为域名进行访问

6域名解析示意图

发布一个网站(模拟)

在webapps,复制ROOT文件夹创建一个副本;删除其他文件,新增一个index.html;就可以访问到其中的内容

7模拟创建网站

网站该有的结构

---webapps :Tomcat服务器的web目录
  --ROOT :默认访问资源 
  --hello :自己的网站目录
    -WEB-INF
      -class :java代码
      -lib:依赖的jar包
	  -web.xml:网站配置文件
    -inde.html(index.jsp) :默认首页
    -static :静态资源
     -css
     -js
     -img
......

HTTP

什么是HTTP

HTTP(超文本传输协议)是一个简单的请求-响应协议,建立在TCP之上。

  • 文本:html、文字等等;
  • 超文本:音频、视频、图片、定位、地图等等;
  • 端口:80

HTTPS:安全的,端口:443

HTTP的两个时代

  • http1.0
    • HTTP/1.0:客户端可以与web服务器建立连接后,一次连接只能获取一个web资源,然后就断开连接。
  • http2.0
    • HTTP/1.1:客户端与web服务器建立连接后,一次连接可以获取多个web资源。

http请求

  • 客户端--发送请求--服务器

以百度为例:

  • general
Request URL:https://www.baidu.com/  请求地址
Request Method:GET  请求方式
Status Code:200 OK  响应状态
Remote Address:180.101.51.73:443 请求地址
  • request headers

    GET / HTTP/1.1  请求方式/协议
    Accept: text/html 接收内容
    Accept-Encoding: gzip, deflate, br, zstd  编码格式
    Accept-Language: zh-CN,zh;q=0.9 语言
    Cache-Control: max-age=0 缓存-控制
    Connection: keep-alive 连接
    Host: www.baidu.com 主机
    

1.请求行

  • 请求行中的请求方式有哪些:get、post、head、delete、put等等
    • get:请求携带的参数较少,有长度和大小限制,会将请求参数显示在浏览器中,不安全,效率较高;
    • post:请求携带的参数较大,有长度和大小无限制,会将请求参数不显示在浏览器中,安全,效率较低

2.消息头

GET / HTTP/1.1  请求方式/协议
Accept: 接收内容
Accept-Encoding:  编码格式
Accept-Language:  语言
Cache-Control: 缓存-控制
Connection: 连接
Host:  主机
......

http响应

  • 服务器--响应--客户端

以百度为例:

  • response headers
HTTP/1.1 200 OK   响应状态
Connection: keep-alive 连接
Content-Encoding: gzip 编码
Content-Type: text/html; charset=utf-8  类型

1.响应体

GET / HTTP/1.1  请求方式/协议
Accept: 接收内容
Accept-Encoding:  编码格式
Accept-Language:  语言
Cache-Control: 缓存-控制
Connection: 连接
Host:  主机
refresh:告诉客户端多久刷新一次
......

2.响应状态码

  • 200:响应成功
  • 3xx:请求重定向
  • 4xx:找不到资源,请求的资源不存在;404
  • 5xx:服务器内部错误,502:网关错误。

常见面试题:
当你的浏览器中地址栏输入地址并回车的一瞬间到页面能够展示回来,经历了什么?

maven

为什么要学习这个技术?

  1. 在javaweb开发中,需要大量的jar包,我们手动导入,管理起来非常麻烦;
  2. 需要一个工具来管理这些jar包,maven诞生了。

maven项目管理工具

maven核心思想:约定大于配置

  • 有约束,就不要去违反!

下载安装maven

  1. 官网:https://maven.apache.org/

  2. 下载后解压

8maven官网

  1. 配置环境变量

    M2_HOME maven所在目录的bin目录

    MAVEN_HOME maven的目录

    在系统中的path变量配置 %MAVEN_HOME%\bin

    配置完成,cmd执行mvn -v 检查是否可以看到maven的版本。

  2. 配置阿里云镜像

    conf下的setting.xml中,mirrors标签下增加以下内容:

    <mirror>
        <id>nexus-aliyun</id>
        <mirrorOf>central</mirrorOf>
        <name>Nexus aliyun</name>
        <url>http://maven.aliyun.com/nexus/content/groups/public</url>
    </mirror>
    
  3. 配置本地仓库

    不是每次都从网上下载jar包,可以将下载过的jar包保存在本地上,下次直接使用,省去下载的过程。

    conf下的setting.xml中,localRepository标签下增加以下内容:

    <localRepository>你的本地仓库路径</localRepository>
    

    idea创建maven工程

9创建mavenweb工程

工程创建完毕,检查maven配置、编码格式是否是自己想要的结果,配置Tomcat后启动即可。

说明

maven有一个很强大的功能:引入一个jar时,会将这个jar包所依赖的jar包一并引入!!!

maven导出问题

在maven项目中,配置文件可能因为存放路径等问题而无法导出,在pom.xml中加入:

<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
        <includes>
            <include>**/*.properties</include>
            <include>**/*.xml</include>
        </includes>
            <filtering>true</filtering>
        </resource>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
    </resources>
</build>

查看目录树

10查看目录树

maven全局设置

在下图中设置完maven配置后,以后所有的工程都使用此配置

11全局设置

servlet

sun公司写了两个接口实现了servlet:HttpServlet和GenericServlet。

创建一个maven工程后删除src中的内容,创建新的Moudle

maven父子工程的理解

父项目中有:

<modules>
    <module>servlet01</module>
</modules>

子项目中有:

<parent>
    <groupId>com.zzz</groupId>
    <artifactId>java-02-servlet</artifactId>
    <version>1.0-SNAPSHOT</version>
</parent>

父项目中有的依赖子项目可以直接使用。

编写Servlet程序

12servlet

1.编写一个普通类实现HttpServlet

public class HelloServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("请求进入了doGet方法");
        PrintWriter writer = resp.getWriter();
        writer.println("Hell,Servlet");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}

2.编写servlet映射

为什么要编写映射?

因为我们写的是java程序,通过浏览器访问web服务器,web服务器会根据请求找到对应servlet进行处理;所以我们编写的servlet要映射我们的servlet才能被访问到。

<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"
         metadata-complete="true">

  <!--注册servlet-->
  <servlet>
    <!--servlet类的名称-->
    <servlet-name>hello</servlet-name>
    <!--servlet类-->
    <servlet-class>com.zwb.servlet.HelloServlet</servlet-class>
  </servlet>

  <!--servlet的请求路径-->
  <servlet-mapping>
    <servlet-name>hello</servlet-name>
    <!--servlet类的访问路径-->
    <url-pattern>/hello</url-pattern>
  </servlet-mapping>
</web-app>

配置Tomcat

启动

13helloservlet访问成功

Servlet原理

14servlet原理

Mapping映射问题

  1. 一个请求可以指定一个映射路径
<!--注册servlet-->
  <servlet>
    <!--servlet类的名称-->
    <servlet-name>hello</servlet-name>
    <!--servlet类-->
    <servlet-class>com.zwb.servlet.HelloServlet</servlet-class>
  </servlet>

  <!--servlet的请求路径-->
  <servlet-mapping>
    <servlet-name>hello</servlet-name>
    <!--servlet类的访问路径-->
    <url-pattern>/hello</url-pattern>
  </servlet-mapping>
  1. 一个请求可以指定多个映射路径
<servlet>
    <!--servlet类的名称-->
    <servlet-name>hello</servlet-name>
    <!--servlet类-->
    <servlet-class>com.zwb.servlet.HelloServlet</servlet-class>
  </servlet>

  <!--servlet的请求路径-->
  <servlet-mapping>
    <servlet-name>hello</servlet-name>
    <!--servlet类的访问路径-->
    <url-pattern>/hello</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello2</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello3</url-pattern>
  </servlet-mapping>
  1. 一个请求可以指定通用映射路径
<servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>com.zwb.servlet.HelloServlet</servlet-class>
  </servlet>

	<!--默认请求路径,默认不再访问inex.jsp-->
  <servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>

  1. 指定后缀

    <!--注册servlet-->
      <servlet>
        <!--servlet类的名称-->
        <servlet-name>hello</servlet-name>
        <!--servlet类-->
        <servlet-class>com.zwb.servlet.HelloServlet</servlet-class>
      </servlet>
    
      <!--servlet的请求路径-->
      <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <!--只能写成 *.xxx这种格式,否则启动报错-->
        <url-pattern>*.do</url-pattern>
      </servlet-mapping>
    

配置404页面

public class ErrorServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html");
        PrintWriter writer = resp.getWriter();
        writer.println("<h1>404</h1>");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}
<!--404处理-->
  <servlet>
    <servlet-name>error</servlet-name>
    <servlet-class>com.zwb.servlet.ErrorServlet</servlet-class>
  </servlet>

  <!--404处理-->
  <servlet-mapping>
    <servlet-name>error</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>

ServletContext

共享数据

ServletContext用于解决同一个web程序中多个servlet之间信息共享的问题:将共享的信息放入ServletContext中即可实现信息的共享。

public class HelloServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext servletContext = this.getServletContext();
        servletContext.setAttribute("username","我是亿万富,我摊牌了");

        resp.getWriter().println("hello");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req, resp);
    }
}
public class GetServletContext extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext servletContext = this.getServletContext();
        String username = (String)servletContext.getAttribute("username");

//        resp.setContentType("text/html");//告诉浏览器这是 HTML
//        resp.setCharacterEncoding("utf-8");//防止中文乱码  单独使用还是会乱码
        resp.setContentType("text/html;charset=UTF-8");
        resp.getWriter().println("名字:"+username);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req, resp);
    }
}
<servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>com.zwb.servlet.HelloServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
</servlet-mapping>

<servlet>
    <servlet-name>getc</servlet-name>
    <servlet-class>com.zwb.servlet.GetServletContext</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>getc</servlet-name>
    <url-pattern>/getc</url-pattern>
</servlet-mapping>

说明

  1. 必须先访问HelloServlet,再访问GetServletContext才能获取到username信息;

  2. 没有以下配置会中文乱码(以下两种方式二选一即可,但是必须放在writer之前):

    resp.setContentType("text/html");//告诉浏览器这是 HTML
    resp.setCharacterEncoding("utf-8");//防止中文乱码  单独使用还是会乱码
    
    resp.setContentType("text/html;charset=UTF-8");
    

获取初始化参数

<!--web应用初始化参数-->
<context-param>
    <param-name>url</param-name>
    <param-value>https://www.baidu.com/</param-value>
</context-param>
public class GetContextParam extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext servletContext = this.getServletContext();
        //获取web.xml中的context-param
        String url = servletContext.getInitParameter("url");
        resp.getWriter().println(url);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req, resp);
    }
}

请求转发

15请求转发

16重定向

演示:发给"dis"的请求实际是交给"getcp"处理的。

public class ServletDispatchDemo extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("dis收到请求");
        ServletContext servletContext = this.getServletContext();
        //请求转发,将请求转发给getcp
        servletContext.getRequestDispatcher("/getcp").forward(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req, resp);
    }
}
<servlet>
    <servlet-name>dis</servlet-name>
    <servlet-class>com.zwb.servlet.ServletDispatchDemo</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>dis</servlet-name>
    <url-pattern>/dis</url-pattern>
</servlet-mapping>

读取资源文件

  1. 新建一个Propertiees文件

    • 在java目录下建(如果没有导出在classes文件中,参考3.4.2.2)
    • 在resources下建(默认是会导出在classes文件中)

    打包后的结论:发现都打在classes目录下,俗称这个路径为classPath。

17读取资源文件1

18读取资源文件2

/WEB-INF/classes/db.properties

/WEB-INF/classes/com/zwb/servlet/aa.properties

第一个"/"表示当前工程

public class GetPropertiesServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext servletContext = this.getServletContext();
//        InputStream is = servletContext.getResourceAsStream("/WEB-INF/classes/db.properties");
        InputStream is = servletContext.getResourceAsStream("/WEB-INF/classes/com/zwb/servlet/aa.properties");
        Properties properties = new Properties();
        properties.load(is);
        String username = (String)properties.get("username");
        String password = (String)properties.get("password");
        resp.getWriter().println(username+":"+password);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req, resp);
    }
}
<servlet>
    <servlet-name>getProp</servlet-name>
    <servlet-class>com.zwb.servlet.GetPropertiesServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>getProp</servlet-name>
    <url-pattern>/getProp</url-pattern>
</servlet-mapping>

HttpServletResponse

下载文件

web服务器接收到客户端的http请求,针对这个请求,分别创建一个代表请求的HttpServletRequest对象,代表响应的一个HttpServletResponse;

  • 如果要获取客户端请求过来的参数:找HttpServletRequest
  • 如果要给客户端响应一些信息:找HttpServletResponse

实际应用

  • 向浏览器输出消息(上面有例子)
  • 下载文件(可能会出现访问指定下载路径后图片无法下载,并且地址栏跳转至默认地址,浏览器和idea控制台都无报错的问题;谷歌和edge需开启无痕模式,QQ浏览器可以正常下载,具体原因可能因为浏览器缓存。
    1. 获取下载文件的路径
    2. 获取文件名
    3. 设置浏览器支持下载的内容
    4. 读取文件
    5. 输出文件给浏览器
    6. 关闭流
public class DownServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println("========== DownServlet doGet 进来了 ==========");

            // 1. 读取文件信息
            File file = new File("E:\\文档\\AI\\2026.png");
            if (!file.exists()) {
                System.out.println("文件不存在");
                // ✅ 用 OutputStream 返回错误(不要用 Writer!)
                resp.setContentType("text/plain;charset=UTF-8");
                resp.getOutputStream().write("文件不存在".getBytes("UTF-8"));
                return; // ✅ 必须 return,避免继续执行下面的下载逻辑
            }

            String fileName = file.getName();
            // 2. 设置返回数据的响应类型为(二进制)流类型
            resp.setContentType("application/octet-stream");
            // 3. 设置响应头信息 告诉浏览器以何种方式处理后台返回的数据;attachment 表示浏览器以下载的形式打开文件
            // 文件名有中文需要单独编码
            resp.setHeader("Content-Disposition",
                    "attachment; filename=\"" + fileName + "\"; filename*=UTF-8''" + URLEncoder.encode(fileName, "UTF-8"));
            resp.setContentLengthLong(file.length()); // 告诉浏览器文件总大小

            // 4. 获取文件输入流和响应对象输出流 使用try()的语法不用手动关闭输入输出流
            // 使用添加了 Buffered 的文件处理类 效率会更高一些
            try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
                 BufferedOutputStream bos = new BufferedOutputStream(resp.getOutputStream())) {

                // 5. 将输入流写入到响应输出流中
                int len;
                byte[] bytes = new byte[1024];
                while ((len = bis.read(bytes)) != -1) {
                    bos.write(bytes, 0, len);
                }
                // 6. 刷新缓冲区数据(try-with-resources 会自动关闭,但显式 flush 更安全)
                bos.flush();
            } catch (Exception e) {
                e.printStackTrace();
            }

            System.out.println("文件下载完成!");
        }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req, resp);
    }
}
<servlet>
    <servlet-name>d</servlet-name>
    <servlet-class>com.zwb.servlet.DownServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>d</servlet-name>
    <url-pattern>/d</url-pattern>
</servlet-mapping>

验证码

public class ImageServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //设置浏览器三秒刷新一次
        resp.setHeader("refresh","3");
        //在内存中创建一个图片
        BufferedImage image = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB);
        //获取图片
        Graphics2D graphics = (Graphics2D)image.getGraphics();
        //设置图片颜色背景
        graphics.setBackground(Color.WHITE);
        graphics.fillRect(0,0,80,20);
        //给图片数据
        graphics.setColor(Color.black);
        graphics.setFont(new Font(null,Font.BOLD,20));
        graphics.drawString(getNum(),0,20);
        //告诉浏览器这个响应用图片打开
        resp.setContentType("image/jpeg");
        //不让浏览器进行缓存
        resp.setDateHeader("expires",-1);
        resp.setHeader("Cache-Control","no-cache");
        resp.setHeader("Pragma","no-cache");

        //将图片返回给浏览器
        ImageIO.write(image,"jpeg",resp.getOutputStream());
    }

    private String getNum(){
        Random random = new Random();
        String num = random.nextInt(999999) + "";
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < 6-num.length(); i++) {
            sb.append("0");
        }
        num = sb.toString() + num;
        return num;
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req, resp);
    }
}
<servlet>
    <servlet-name>ImageServlet</servlet-name>
    <servlet-class>com.zwb.servlet.ImageServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>ImageServlet</servlet-name>
    <url-pattern>/img</url-pattern>
</servlet-mapping>

重定向

16重定向

  • 示例一
public class RedirectServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.sendRedirect("/servlet/img");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req, resp);
    }
}
<servlet>
    <servlet-name>RedirectServlet</servlet-name>
    <servlet-class>com.zwb.servlet.RedirectServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>RedirectServlet</servlet-name>
    <url-pattern>/redirect</url-pattern>
</servlet-mapping>
  • 示例二
public class RequestTest extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        System.out.println(username+":"+password);

        resp.sendRedirect("/servlet/success.html");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req, resp);
    }
}
<!DOCTYPE html>
<html>
<body>
<h2>Hello World!</h2>

<%--这里提交的路径需要为当前项目的路径--%>
<%--${pageContext.request.contextPath}表示当前项目的路径--%>
<form action="${pageContext.request.contextPath}/login" method="get">
    username<input type="text" name="username"> <br/>
    password<input type="password" name="password"> <br/>
    <input type="submit">
</form>
</body>
</html>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
  <h1>success</h1>
</body>
</html>
<servlet>
        <servlet-name>RequestTest</servlet-name>
        <servlet-class>com.zwb.servlet.RequestTest</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>RequestTest</servlet-name>
        <url-pattern>/login</url-pattern>
    </servlet-mapping>

记得导入包:

<dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>javax.servlet.jsp-api</artifactId>
    <version>2.3.3</version>
</dependency>

面试题:重定向和请求转发有什么区别?

  • 相同点:都实现了页面跳转
  • 不同点:
重定向 请求转发
请求次数 两次 一次
地址栏变化 变成新地址 不变
数据共享 不共享 共享
跳转范围 任意网站 内部资源

HttpServletRequest

HttpServletRequest代表客户端的请求,用户通过Http协议访问服务器,HTP请求中的所有信息会被封装到HttpServletRequest,通过这个HttpServletRequest的方法,获得客户端的所有信息;

获取前段传递参数常用方法:

req.getParameter() //获取单个参数,返回值是一个String
req.getParameterValues()//获取多个参数,返回值是一个数组

示例

注意

//这里/success.jsp中的/表示当前项目,即 /r;/success.jsp会跳转至 /r/success.jsp
req.getRequestDispatcher("/success.jsp").forward(req,resp);

代码

---index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录</title>
</head>
<body>
<h1>登录</h1>
<div>
    <%--这里表单表示的意思:以post方式提交表单,提交到--%>
    <form action="${pageContext.request.contextPath}/login" method="post">
    用户名: <input type="text" name="username"> <br>
    密码:<input type="password" name="password"><br>爱好:
    <input type="checkbox" name="hobbys" value="money">money
    <input type="checkbox" name="hobbys" value="girl">girl
    <input type="checkbox" name="hobbys" value="代码">代码
    <br>
    <input type="submit"></form>
</div>


</body>
</html>
---success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>成功</title>
</head>
<body>
    <h1>success</h1>
</body>
</html>
public class LoginServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");

        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String[] hobbys = req.getParameterValues("hobbys");
        System.out.println(username);
        System.out.println(password);
        System.out.println(Arrays.toString(hobbys));
//        resp.sendRedirect("/r/success.jsp");
        System.out.println(req.getContextPath());
        //这里/success.jsp中的/表示当前项目,即 /r;/success.jsp会跳转至 /r/success.jsp
        req.getRequestDispatcher("/success.jsp").forward(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req, resp);
    }
}
<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"
         metadata-complete="true">

  <servlet>
    <servlet-name>LoginServlet</servlet-name>
    <servlet-class>com.zwb.servlet.LoginServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>LoginServlet</servlet-name>
    <url-pattern>/login</url-pattern>
  </servlet-mapping>

</web-app>

Cookie、Session

会话

用户打开浏览器访问web资源,一直到浏览器关闭,称之为一次会话。

保存会话的两种技术

  • cookie

​ 客户端技术,保存在浏览器中。

  • session

​ 服务器技术,可以保存用户会话信息;可以把信息或者数据存在session中。

常见:网站登录之后,可以一段时间之内不再登录,仍然可以正常访问。

1.从请求中获取cookie信息;

2.将响应信息放在cookie中

public class CookieDemo01 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {

        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=utf-8");

        PrintWriter out = resp.getWriter();
        Cookie[] cookies = req.getCookies();

        if (cookies != null) {
            for (Cookie c : cookies) {
                if ("lastLoginTime".equals(c.getName())) {
                    long time = Long.parseLong(c.getValue());
                    String fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
                            .format(new Date(time));
                    out.write("你上一次访问本站的时间为:" + fmt);
                }
            }
        } else {
            out.write("这是您第一次访问本站");
        }

        Cookie cookie = new Cookie("lastLoginTime", System.currentTimeMillis() + "");
        //设置cookie有效期为1天
        cookie.setMaxAge(24*60*60);
        // 记录本次访问时间
        resp.addCookie(cookie);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req, resp);
    }
}

cookie一般存在本地用户目录下的appdata中

一个网站cookie是否存在上限!聊聊细节问题。

  • 一个Cookie只能保存一个信息;

  • 一个web站点可以给浏览器发送多个cookie,最多存放20个cookie;.Cookie大小有限制4kb;

  • 300个cookie浏览器上限

删除Cookie:不设置有效期,关闭浏览器,自动失效。

public class CookieDemo02 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        Cookie cookie = new Cookie("lastLoginTime", System.currentTimeMillis() + "");
        //设置cookie有效期为0,立刻过期
        cookie.setMaxAge(0);
        resp.addCookie(cookie);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req, resp);
    }
}

编码/解码

URLDecoder.decode()//解码
URLEncoder.encode()//编码

Sessioon

  • 什么是session

    • 服务器会为每个用户创建一个session对象;

    • 相同的网站,在同一个浏览器中一次访问只能存在一个session;

    • 用户登录后可以访问网站中的信息,这些信息有一部分只有属于自己;比如:购物车、个人信息、订单信息等等。

    • HttpSession常用方法

19HttpSessioon常用方法

  • cookie和session的区别

    • cookie是存储在客户端(浏览器)的,证明当前登录人信息的技术;
    • session是存在服务端,保存登录人信息的技术;由服务器创建;保存重要的信息(避免服务器资源的浪费);

session的使用实例

使用session存储信息
public class SessionDemo01 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=utf-8");

        //获取session
        HttpSession session = req.getSession();
        //给session中存东西
        session.setAttribute("person",new Person("亿万富翁",35));
        //获取sessionId
        String sessionId = session.getId();
        //判断session刚创建
        if(session.isNew()){
            resp.getWriter().write("session创建成功,ID:"+sessionId);
        }else{
            resp.getWriter().write("session已经在服务器中存在,ID:"+sessionId);
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req, resp);
    }
}
获取session存储的信息
public class SessionDemo02 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=utf-8");

        //获取session
        HttpSession session = req.getSession();
        Person person = (Person)session.getAttribute("person");
        System.out.println(person.toString());
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req, resp);
    }
}
session的注销
public class SessionDemo03 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=utf-8");

        //获取session
        HttpSession session = req.getSession();
        //删除存在session中的属性
        session.removeAttribute("person");
        //注销session,注销后服务器会立刻生成一个新的session信息
        session.invalidate();
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req, resp);
    }
}
通过配置文件设置session的失效时间
<!--设置session自动失效-->
<session-config>
    <!--设置session失效的时间,单位是分钟;指的是对当前页面没有操作后多少分钟后注销-->
    <session-timeout>15</session-timeout>
</session-config>

JSP

什么是JSP

  • java server page :java服务端页面,和servlet一样,用于动态web技术。

  • 最大的特点:

    • 写jap就像在写html代码
    • 与html的区别:
      • html只给用户提供静态数据;
      • jsp中可以写java代码,为用户提供动态数据。
  • 早期使用servlet怎么输出页面:

    //使用这种方式输出页面
    PrintWriter out = resp.getWriter();
    out.write("<h1>");
    out.write("你好");
    out.write("</h1>");
    

JSP原理

  • jsp源码:通过类的继承,发现index_jsp就是一个Servlet。

    public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
        implements org.apache.jasper.runtime.JspSourceDependent,
                     org.apache.jasper.runtime.JspSourceImports{}
    
    //HttpJspBase
    public abstract class HttpJspBase extends HttpServlet implements HttpJspPage{}
    
  • index_jsp中的代码简单分析

    //主要方法
    //初始化
    public void _jspInit() {
    }
    //销毁
    public void _jspDestroy() {
    }
    //处理
    public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
      throws java.io.IOException, javax.servlet.ServletException {}
    
    //主要内置对象
    final javax.servlet.jsp.PageContext pageContext; 	//页面上下文
    javax.servlet.http.HttpSession session = null;		//session
    final javax.servlet.ServletContext application;		//applicationCoontext
    final javax.servlet.ServletConfig config;			//配置
    javax.servlet.jsp.JspWriter out = null;				//输出
    final java.lang.Object page = this;					//当前页面
    
  • JSP是怎么执行的?

    • 在idea的临时数据中,存在一个tomcat目录(不同版本的idea这个目录存在差异);会将jsp页面编译成xxx.jsp.class;

20idea中的tomcat目录

21idea目录中编译的jsp路径

java C:\Users\Zhang\AppData\Local\JetBrains\IntelliJIdea2023.2\tomcat\fdc59a2b-eaac-473b-960e-b330b37331dc\work\Catalina\localhost\ROOT\org\apache\jsp

  • index.jsp源码与编译后的代码核心部分的对比:jsp实际就是将写在jsp中的html标签转换成servlet输出的格式,简化了程序员的开发。

22index编译前后对比

  • jsp页面中java代码编译前后对比

23jsp中java代码编译前后对比

JSP工作原理示意图

重点:是每次清求的时候才会将jsp转换为 xxx_jsp.java文件并编译!!!

24JSP原理图

JSP基础语法

任何语言都有自己的语法,java有属于自己的语法;jsp也有属于自己的语法。jsp除了包含java的所有语法外,还有一些字节特定的语法。

jsp表达式

<%--jsp表达式
作用:用来将程序的输出,输出到客户端
<%= 表达式%>
--%>
<%= new java.util.Date()%>

25jsp表达式

jsp脚本片段 & jsp脚本片段再实现

<%--jsp脚本片段|--%>
<%
    int sum = 0;
    for (int i = 0; i <= 100; i++) {
        sum = sum + 1;
    }
    out.print("<h1>"+sum+"</h1>");
%>

<%--jsp脚本片段再实现--%>
<%
    int num1 = 1;
    out.print(num1);
%>
<p>这是一个JSP文本</p>
<%
    int num2 = 2;//不能再定义为num1
    out.print(num2);
%>

<%
    for (int i = 0; i < 5; i++) {
%>
<h1>hello,world <%=i%></h1>
<%}%>

26jsp脚本片段

jsp声明

<%--jsp声明--%>
<%!
    static {
        System.out.println("loading...");
    }

    private int gloablVar = 0;

    public void methodTest(){

    }
%>
  • jsp声明与jsp表达式和jsp脚本片段的区别

    • jsp声明会被放在java类中
    • 其他会放在_jspService()方法中

27jsp声明

28定义在方法中华

注释

<!--这是一个HTML注释-->
<%--这是一个jsp注释--%>

HTML注释和jsp注释的区别:HTML的注释可以客户端查看到,jsp的注释不会。

29jsp和html注释的区别

自定义错误页面

方式一:使用jsp标签
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--会在出现500错误时跳转至指定的500页面--%>
<%@ page errorPage="error/500.jsp" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <% int num = 1/0;%>
</body>
</html>
方式二:在web.xml中配置
<error-page>
    <!--出现404错误跳转至指定页面-->
    <error-code>404</error-code>
    <location>/error/404.jsp</location>
</error-page>

JSP指令

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <%-- <%@include> 标签会将页面合并为一个--%>
    <%@include file="/common/header.jsp"%>
    <h1>html体</h1>
    <%@include file="/common/footer.jsp"%>
    <hr/>

    <%-- <jsp:include/> 标签本质还是多个页面--%>
    <jsp:include page="/common/header.jsp"/>
    <h1>html体</h1>
    <jsp:include page="/common/footer.jsp"/>
</body>
</html>

30-2JSP指令

  • 证明

    header.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <h1>这是一个Header</h1>
    <%
        int i = 10;
    %>
    

    jsp01.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%--会在出现500错误时跳转至指定的500页面--%>
    <%@ page errorPage="error/500.jsp" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
        <%-- <%@include> 标签会将页面合并为一个--%>
        <%@include file="/common/header.jsp"%>
        <h1>html体</h1>
        <%
            int i = 10;//直接在编译时就报错,以为header中已经定义了这个变量
        %>
        <%@include file="/common/footer.jsp"%>
    </body>
    </html>
    
    

    jsp02.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%--会在出现500错误时跳转至指定的500页面--%>
    <%@ page errorPage="error/500.jsp" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
        <%-- <jsp:include/> 标签本质还是多个页面--%>
        <jsp:include page="/common/header.jsp"/>
        <h1>html体</h1>
        <%
            int i = 10;//可以正常编译和运行,因为header是不同的页面
        %>
        <jsp:include page="/common/footer.jsp"/>
    </body>
    </html>
    

JSP的九大内置对象

  • request //存东西,作用域:一次请求
  • pageContext //存东西 ,作用域:当前页面
  • session //存东西 ,作用域:一次会话内
  • application 【servletContext】 //存东西,作用域:从服务器开启到关闭
  • response //响应用户请求
  • config 【servletConfig】
  • out //输出内容到页面
  • page //当前页面,类似于java中的this;几乎不用
  • exception //和Java中的exception相同

存取值实例

页面中取值的顺序(从底层向高层找,jvm的双亲委派机制):pageContext→request→session→application;如果都找不到就是null。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%--内置对象--%>
<%
    request.setAttribute("name1","发财一号");
    pageContext.setAttribute("name2","发财二号");
    session.setAttribute("name3","发财三号");
    application.setAttribute("name4","发财四号");
%>

<%
    String name1 = (String)pageContext.findAttribute("name1");
    String name2 = (String)pageContext.findAttribute("name2");
    String name3 = (String)pageContext.findAttribute("name3");
    String name4 = (String)pageContext.findAttribute("name4");
    String name5 = (String)pageContext.findAttribute("name5");
%>

<h1>取出的值为:</h1>
<h3>${name1}</h3>
<h3>${name2}</h3>
<h3>${name3}</h3>
<h3>${name4}</h3>
<h3>${name5}</h3>
<h3><%=name5%></h3>

</body>
</html>

输出结果

31-jsp四大作用域1

  • 注意事项:
${name5}   这种写法,找不到值,在页面中默认是不展示的;√使用这种写法
<%=name5%>  这种写法,找不到值,会展示null;×不要使用这种写法

验证四大作用域的作用范围

pageContextDemo01.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%--内置对象--%>
<%
    request.setAttribute("name1","发财一号");
    pageContext.setAttribute("name2","发财二号");
    session.setAttribute("name3","发财三号");
    application.setAttribute("name4","发财四号");
%>

<%
    String name1 = (String)pageContext.findAttribute("name1");
    String name2 = (String)pageContext.findAttribute("name2");
    String name3 = (String)pageContext.findAttribute("name3");
    String name4 = (String)pageContext.findAttribute("name4");
    String name5 = (String)pageContext.findAttribute("name5");
%>

<h1>取出的值为:</h1>
<h3>${name1}</h3>
<h3>${name2}</h3>
<h3>${name3}</h3>
<h3>${name4}</h3>
<h3>${name5}</h3>
<h3><%=name5%></h3>

</body>
</html>

pageContextDemo02.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%
    String name1 = (String)pageContext.findAttribute("name1");
    String name2 = (String)pageContext.findAttribute("name2");
    String name3 = (String)pageContext.findAttribute("name3");
    String name4 = (String)pageContext.findAttribute("name4");
    String name5 = (String)pageContext.findAttribute("name5");
%>

<h1>取出的值为:</h1>
<h3>${name1}</h3>
<h3>${name2}</h3>
<h3>${name3}</h3>
<h3>${name4}</h3>
<h3>${name5}</h3>
<h3><%=name5%></h3>

</body>
</html>

输出结果:

32-jsp四大作用域2

pageContextDemo01.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%--内置对象--%>
<%
    request.setAttribute("name1","发财一号");
    pageContext.setAttribute("name2","发财二号");
    session.setAttribute("name3","发财三号");
    application.setAttribute("name4","发财四号");
%>

<%
    String name1 = (String)pageContext.findAttribute("name1");
    String name2 = (String)pageContext.findAttribute("name2");
    String name3 = (String)pageContext.findAttribute("name3");
    String name4 = (String)pageContext.findAttribute("name4");
    String name5 = (String)pageContext.findAttribute("name5");

    pageContext.forward("pageContextDemo02.jsp");
%>

<h1>取出的值为:</h1>
<h3>${name1}</h3>
<h3>${name2}</h3>
<h3>${name3}</h3>
<h3>${name4}</h3>
<h3>${name5}</h3>
<h3><%=name5%></h3>

</body>
</html>

pageContextDemo02.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%
    String name1 = (String)pageContext.findAttribute("name1");
    String name2 = (String)pageContext.findAttribute("name2");
    String name3 = (String)pageContext.findAttribute("name3");
    String name4 = (String)pageContext.findAttribute("name4");
    String name5 = (String)pageContext.findAttribute("name5");
%>

<h1>取出的值为:</h1>
<h3>${name1}</h3>
<h3>${name2}</h3>
<h3>${name3}</h3>
<h3>${name4}</h3>
<h3>${name5}</h3>
<h3><%=name5%></h3>

</body>
</html>

33-jsp四大作用域3

拓展一:请求转发的方式

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%
    //请求转发的两种方式
    pageContext.forward("/jsp1.jsp");
   // request.getRequestDispatcher("/jsp2.jsp").forward(request,response);
%>
</body>
</html>

拓展二:pageContext.setAttribute

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%
    pageContext.setAttribute("key1","name1",PageContext.SESSION_SCOPE);//等价于session.setAttribute
    //session.setAttribute("key1","name1");
%>
</body>
</html>

34-jsp九大内置对象扩展1

35-拓展2

36拓展3

JSP标签、JSTL标签、EL表达式

首先,需要引入依赖

<!--jstl表达式-->
<dependency>
    <groupId>javax.servlet.jsp.jstl</groupId>
    <artifactId>jstl-api</artifactId>
    <version>1.2</version>
</dependency>
<!--standard标签库-->
<dependency>
    <groupId>taglibs</groupId>
    <artifactId>standard</artifactId>
    <version>1.1.2</version>
</dependency>

EL表达式:

  • 获取数据
  • 执行运算
  • 获取web开发的常用对象

JSP标签

<jsp:forward page="" />  //请求转发
<jsp:include page="" />  //与其他页面进行拼接
<jsp:param name="=" value=""/> //传递参数

jsptag1.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>jsptag1</h1>
    <hr/>
    <%--相当于 jsptag2.jsp?name=DaiV&age=18 跳转--%>
    <jsp:forward page="jsptag2.jsp">
        <jsp:param name="name" value="DaiV"/>
        <jsp:param name="age" value="18"/>
    </jsp:forward>
</body>
</html>

jsptag2.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>jsptag2</h1>

    <jsp:include page="common/footer.jsp" />
    名字:<%= request.getParameter("name")%>
    年龄:<%= request.getParameter("age")%>
</body>
</html>

效果:

37JSP标签

JSTL表达式

jstl标签库是为了弥补html标签的不足;它自身定义了许多标签供我们使用,标签的功能和java代码一样,是为了让jsp页面的代码看起来更像是一个html(更优雅)。

学习网站:https://www.runoob.com/jsp/jsp-jstl.html

核心标签(掌握部分即可)
  • 引入核心类库

    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    
  • 在tomcat中也需要引入jstl的包(可能会引起报错)

core-if

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--引入jstl核心库--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>jstl表达式的if判断</h1>
    <hr/>

    <form action="coreif.jsp" method="get">
        <%--
        EL表达式获取表单中的数据${param.参数舒}
        --%>
        <input type="text" name="username" value="${param.username}">
        <input type="submit" value="登录">
    </form>

    <%--判断登录用户为管理员时,显示登录成功--%>
    <c:if test="${param.username == 'admin'}" var="isAdmin">
        <c:out value="欢迎管理员登录" />
    </c:if>
    <c:out value="${isAdmin}" />

</body>
</html>

38-coreif标签

core-when

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--引入jstl核心库--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <c:set var="score" value="85" />

    <%--
        if(){
        }else if(){
        }if(){
        }
    --%>
    <c:choose>
        <c:when test="${score >= 90}">
            你的成绩为优秀
        </c:when>
        <c:when test="${score >= 80}">
            你的成绩为中等
        </c:when>
        <c:when test="${score >= 70}">
            你的成绩为一般
        </c:when>
        <c:when test="${score >= 60}">
            你的成绩需要努力了
        </c:when>
        <c:when test="${score <60}">
            你的成绩为不及格
        </c:when>

    </c:choose>

</body>
</html>

39-corewhen标签

core-foreach

<%@ page import="java.util.List" %>
<%@ page import="java.util.ArrayList" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--引入jstl核心库--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <%
        List<String> person = new ArrayList<>();
        person.add(0,"A1");//下标从0开始
        person.add(1,"B2");
        person.add(2,"C3");
        person.add(3,"D4");
        person.add(4,"E5");
        person.add(5,"F6");
        person.add(6,"G7");
        request.setAttribute("person",person);
    %>

    <%--
    var 每一次遍历出的变量名称
    items 要遍历的对象
    begin 开始下标
    end   结束下标
    step 步长
    --%>
    <c:forEach var="people" items="${person}">
        <c:out value="${people}" /> <br/>
    </c:forEach>

    <hr>

    <c:forEach var="people" items="${person}" begin="0" end="6" step="3">
        <c:out value="${people}" /> <br/>
    </c:forEach>

</body>
</html>

40-corewhen标签

格式化标签

了解

SQL标签

了解

xml标签

了解

javabean

一搬用来和数据库表相关联的实体类,有以下特点:

  • 属性私有化
  • 必须有无参构造
  • 属性必须有对应的get/set方法。

jsp页面中使用javabean

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
  <%--声明实体类--%>
  <jsp:useBean id="people" class="com.zwb.po.People" scope="page" />

  <%--为实体类设置属性--%>
  <jsp:setProperty name="people" property="id" value="1001" />
  <jsp:setProperty name="people" property="name" value="暴富" />
  <jsp:setProperty name="people" property="age" value="18" />
  <jsp:setProperty name="people" property="address" value="上海" />

  <%--获取实体类中的值--%>
id:<jsp:getProperty name="people" property="id"/>
姓名:<jsp:getProperty name="people" property="name"/>
年龄:<jsp:getProperty name="people" property="age"/>
地址:<jsp:getProperty name="people" property="address"/>

</body>
</html>

MVC(模型视图控制器)三层架构

  • M:model 模型 (实体类与数据库之间的映射)
  • V:view 视图 (前端展示)
  • C:controller 控制器 (处理请求内容)

MVC之前早期架构示意图

41mvc之前的架构

缺点:servlet会处理业务逻辑、页面的跳转、和数据库的交互等等,十分臃肿,不利于维护。

解决方法:加一层。

架构界的定律:没有什么是加一层解决不了的。

经典实例:JDBC,市面上有多种数据库,如果系统更换数据库,对代码的改动会比较大;为了解决这一问题,JDBC提供了一个完整的接口,其他数据库厂商只需要实现这些接口接口,在更换数据库的时候,程序调用的是JDBC,可以实现无缝更换数据库。

MVC架构

42MVC三层架构示意图

Model

  • 业务逻辑处理:service
  • 数据持久层:dao

View

  • 展示数据
  • 提供链接发起servlet

Coontroller

  • 接收用户请求
  • 将请求交给业务层处理
  • 控制视图的跳转

业务举例描述:

客户发起登录→接收用户的请求(Controller)→传递参数给业务层处理(Controller)→业务层查询用户名和密码是否正确(Model)。

Filter-过滤器

本质是一个servlet,在请求发送在servlet之前,进行一些过滤处理(字符编码设置、登录认证等等)。

43-filter原理图

Filter的使用

示例:统一设置响应返回的字符集,保证中文不乱码

public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        resp.setCharacterEncoding("utf-8");
        resp.getWriter().write("你好,世界!");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req, resp);
    }
}
//注意导入的Filter是javax.servlet.下的
public class CharacterEncodingFilter implements Filter {

    //初始化:服务启动时就执行,随时等待过滤请求
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("CharacterEncodingFilter初始化。。。");
    }

    /*
    1.过滤所有代码,在过滤特定请求时执行(web.xml配置的过滤路径)
    2.将过滤交给其他过滤器继续执行--filterChain.doFilter(servletRequest,servletResponse);//固定代码
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        servletRequest.setCharacterEncoding("utf-8");
        servletResponse.setCharacterEncoding("utf-8");
        servletResponse.setContentType("text/html; charset=UTF-8");

        System.out.println("CharacterEncodingFilter执行前。。。");
        //固定代码,没有这行代码,程序不会往下执行
        //可能存在其他过滤器,将请求交给其他过滤器进行处理
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println("CharacterEncodingFilter执行后。。。");
    }

    //服务关闭时执行
    @Override
    public void destroy() {
        System.out.println("CharacterEncodingFilter销毁。。。");
    }
}
<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"
         metadata-complete="true">

    <servlet>
      <servlet-name>helloServlet</servlet-name>
      <servlet-class>com.zwb.servlet.HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/servlet/hello</url-pattern>
    </servlet-mapping>

    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>com.zwb.filter.CharacterEncodingFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/servlet/*</url-pattern>
    </filter-mapping>


</web-app>
<url-pattern>/servlet/*</url-pattern>

以上filter的配置会让/servlet/hello的请求编码正常;而/hello的请求乱码,并且这两个请求都是同一个servlet处理的;更能直观看出“过滤特定请求”的含义。

45-filter访问乱码

46-filter访问正常

filterChain.doFilter(servletRequest,servletResponse);//可能存在多个servlet,将本次过滤完整后交给filter继续处理

44多个filter示意图

Listener--监听器

实现一个监听器的接口(监听器有N种)→重写方法→配置xml

示例:

1.实现监听器

public class OnlineCounterListener implements HttpSessionListener {

    /**
     * 创建session时监听,session一旦创建,就会执行
     * @param httpSessionEvent
     */
    @Override
    public void sessionCreated(HttpSessionEvent httpSessionEvent) {
        System.out.println(httpSessionEvent.getSession().getId());

        ServletContext servletContext = httpSessionEvent.getSession().getServletContext();
        Integer onlineCounter = (Integer)servletContext.getAttribute("onlineCounter");//在线人数

        if(onlineCounter == null){//没有在线人数
            onlineCounter = new Integer(1);
        }else{
            int count = onlineCounter.intValue();
            onlineCounter = new Integer(count + 1);
        }
        servletContext.setAttribute("onlineCounter",onlineCounter);
    }


    /**
     * 销毁session监听,一旦session销毁就会触发这个方法
     * @param httpSessionEvent
     */
    @Override
    public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
        ServletContext servletContext = httpSessionEvent.getSession().getServletContext();
        Integer onlineCounter = (Integer)servletContext.getAttribute("onlineCounter");//在线人数

        if(onlineCounter == null){//没有在线人数
            onlineCounter = new Integer(0);
        }else{
            int count = onlineCounter.intValue();
            onlineCounter = new Integer(count - 1);
        }
        servletContext.setAttribute("onlineCounter",onlineCounter);
    }

}

2.配置

<!--配置监听器-->
<listener>
    <listener-class>com.zwb.listener.OnlineCounterListener</listener-class>
</listener>

3.查看效果

注意:第一次访问的时候在线人数可能不是1,因为tomcat在启动时会进行访问,导致在线人数不为1。

session失效的方式:

  • xml中配置失效时间;

    <session-config>
        <session-timeout>1</session-timeout>
    </session-config>
    
  • 手动销毁

    session().invalidate();
    
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<body>
<h2>Hello World!</h2>
<hr>

<%
    Integer onlineCounter = (Integer)session.getServletContext().getAttribute("onlineCounter");
%>
<h3>
    <p color="blue">当前在线人数为${onlineCounter}</p>
</h3>
</body>
</html>

过滤器和监听器的常见应用

监听器常见应用

在线人数、GUI编程的事件监听

监听器常见应用

用户登录后才能进入主页!用户注销后就不能进入主页了!

  • 没有过滤器的情况:每个需要登录的页面都需要做登录判断;
  • 油过滤器的情况:在过滤器中配置需要检查登录的页面路径即可。

jsp页面

login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>登录页面</h1>
    <form action="/servlet/login" method="post">
        <input type="text" name="username">
        <input type="submit">
    </form>

</body>
</html>

error.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>错误</h1>
    <h2>用户名或密码错误、没有登录权限</h2>

    <span>
        <a href="/login.jsp">返回主页</a>
    </span>
</body>
</html>

home.jsp 在sys文件夹下

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>主页</h1>

    <span>
        <a href="/servlet/loginOut">注销登录</a>
    </span>
</body>
</html>

servlet

LoginServlet.java

public class LoginServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("username");

        if(username != null && "admin".equals(username)){//登录成功
            //将登录信息设置在sessioon中
            req.getSession().setAttribute(Constant.USER_SESSION,username);
            //跳转至主页
            resp.sendRedirect("/sys/home.jsp");
        }else{
            //登录失败,跳转至失败页面
            resp.sendRedirect("/error.jsp");
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req, resp);
    }
}

LoginOut.java

public class LoginOut extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String attribute = (String)req.getSession().getAttribute(Constant.USER_SESSION);
        if(attribute != null){
            //移除session信息
            req.getSession().removeAttribute(Constant.USER_SESSION);
        }
        resp.sendRedirect("/login.jsp");

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req, resp);
    }
}

SessioninCheckFilter

public class SessioinCheckFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        Object attribute = request.getSession().getAttribute(Constant.USER_SESSION);
        //判断用户是否登录
        if(attribute == null){//未登录
            response.sendRedirect("/error.jsp");
        }

        filterChain.doFilter(request,response);
    }

    @Override
    public void destroy() {

    }
}
 <!--登录-->
<servlet>
    <servlet-name>LoginServlet</servlet-name>
    <servlet-class>com.zwb.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>LoginServlet</servlet-name>
    <url-pattern>/servlet/login</url-pattern>
</servlet-mapping>
<!--注销登录-->
<servlet>
    <servlet-name>LoginOut</servlet-name>
    <servlet-class>com.zwb.servlet.LoginOut</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>LoginOut</servlet-name>
    <url-pattern>/servlet/loginOut</url-pattern>
</servlet-mapping>

<!--登录检查-->
<filter>
    <filter-name>SessioinCheckFilter</filter-name>
    <filter-class>com.zwb.filter.SessioinCheckFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>SessioinCheckFilter</filter-name>
    <url-pattern>/sys/*</url-pattern>
</filter-mapping>

JDBC

固定步骤:

  1. 引入依赖
  2. 获取配置
  3. 加载驱动
  4. 获取连接
  5. 向数据库发送SQL的对象statement(使用预编译SQL时要先编写SQL)
  6. 编写SQL
  7. 执行
  8. 获取执行结果
  9. 关闭连接
<!-- MySQL驱动 -->
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>5.1.49</version>
</dependency>

非预编译SQL

public static void main(String[] args) throws ClassNotFoundException, SQLException {
    //配置信息
    String username = "username";
    String password = "password";
    String url = "jdbc:mysql://localhost:3306/jdbc?characterEncoding=UTF-8";

    //1.加载驱动
    Class.forName("com.mysql.jdbc.Driver");
    //2.获取连接
    Connection connection = DriverManager.getConnection(url, username, password);
    //3.向数据库发送SQL的对象statement
    Statement statement = connection.createStatement();
    //4.编写SQL
    //select * from users; 和 select * from users 这两种写法都可以
    String sql = "select * from users";
    //5.执行SQL
    ResultSet rs = statement.executeQuery(sql);
    //获取结结果集
    while(rs.next()){
        System.out.print("id="+rs.getObject("id"));
        System.out.print(",name="+rs.getObject("name"));
        System.out.print(",password="+rs.getObject("password"));
        System.out.print(",email="+rs.getObject("email"));
        System.out.println(",birthday="+rs.getObject("birthday"));
    }

    //6.关闭连接
    rs.close();
    statement.close();
    connection.close();

}

预编译执行SQL

public static void main(String[] args) throws ClassNotFoundException, SQLException {
    //配置信息
    String username = "username";
    String password = "password";
    String url = "jdbc:mysql://localhost:3306/jdbc?characterEncoding=UTF-8";

    //1.加载驱动
    Class.forName("com.mysql.jdbc.Driver");
    //2.获取连接
    Connection connection = DriverManager.getConnection(url, username, password);
    //3.编写SQL
    String sql = "insert into users values(?,?,?,?,?);";
    //4.向数据库发送SQL的对象statement
    PreparedStatement preparedStatement = connection.prepareStatement(sql);
    preparedStatement.setInt(1,6);
    preparedStatement.setString(2,"哈基米");
    preparedStatement.setString(3,"12345678");
    preparedStatement.setString(4,"123456@qq.com");
    preparedStatement.setDate(5,new Date(new java.util.Date().getTime()));
    //5.执行SQL
    int i = preparedStatement.executeUpdate();
    if(i == 1){
        System.out.println("执行成功");
    }else{
        System.out.println("执行失败");
    }
    //6.关闭连接
    preparedStatement.close();
    connection.close();

}

问题总结

请求与响应路径问题

请求路径

action="${pageContext.request.contextPath }/jsp/user.do"

响应路径

req.getRequestDispatcher("pwdmodify.jsp").forward(req,resp);

此时,响应路径为:

${pageContext.request.contextPath}/jsp/pwdmodify.jsp;//如果pwdmodify.jsp在jsp目录下就会404

如果

action="${pageContext.request.contextPath }/user.do" //请求
响应:${pageContext.request.contextPath}/pwdmodify.jsp

一次HTTP请求只能响应一次

forward、sendRedirect 都会完成响应提交,同一请求内第二次调用跳转 API,容器直接抛出响应已提交非法状态异常。

以下代码在执行时会抛出异常(java.lang.IllegalStateException)示例:

request.getRequestDispatcher("/target.jsp").forward(request, response);
response.sendRedirect("/other.jsp"); // 这里抛异常
或者
request.getRequestDispatcher("/target.jsp").forward(request, response);
request.getRequestDispatcher("/ssss.jsp").forward(request, response);

原因:在第一次响应后,已经commit;再次响应,响应已经关闭,就会报错。

posted @ 2026-07-15 22:38  出去透透气吧  阅读(4)  评论(0)    收藏  举报