JavaWeb 学习

JavaWeb

基本概念

前言

web开发:

  • web,网页的意思,比如:www.baidu.com,我们可以从网页上拿到资源
  • 静态web
    • html,css
    • 提供给所有人看的数据始终不会发生变化
  • 动态web
    • 淘宝,几乎所有的网站
    • 提供给所有人看的数据始终会发生变化,每个人在不同的时间,不同的地点看到的信息各不相同。
    • 技术栈:Servlet/JSP,ASP,PHP

在Java中,动态web资源开发的技术统称为JavaWeb。

Web应用程序

web应用程序:可以提供浏览器访问的程序:

  • a.html、b.html…多个web资源,这些web资源可以被外界访问,对外界提供服务;
  • 我们能访问到的任何一个页面或者资源,都存在于这个世界的某一个角落的计算机上。
  • URL统一资源定位符。
  • 这个统一的web资源会被放在同一个文件夹下,web应用程序 --> Tomcat:服务器
  • 一个web应用由多部分组成(静态web,动态web)
    • html、css、js
    • jsp、servlet
    • Java程序
    • jar包
    • 配置文件(Properties)

web应用程序编写完毕后,若想提供给外界访问,需要一个服务器来统一管理。

静态web

  • *.htm, *.html,这些都是网页的后缀,如果服务器上一直存在这些东西,我们就可以直接通过网络进行读取。

  • 静态web存在的缺点

    • web页面无法动态更新,所有用户看到的都是同一个页面
      • 轮播图,点击特效:伪动态
      • JavaScript(实际开发中用的比较多)
      • VBScript
    • 静态web无法和数据库交互(数据无法持久化,用户无法交互)

动态web

页面会动态展示:web页面的展示效果因人而异。

缺点:

  • 假如服务器的动态web资源出现了错误,需要重新编写后台程序,重新发布:
    • 重新发布意味着停机维护

优点:

  • web页面可以动态更新,所有用户看到的不是同一个页面
  • 动态web可以和数据库交互(数据持久化:注册,商品信息,用户信息等等)

web服务器

技术讲解

ASP:Active Server Pages 动态服务器页面

  • 微软:国内最早流行的就是ASP;

  • 在html中嵌入了VB的脚本,ASP+COM;

  • 在ASP开发中,基本上一个页面要嵌入大量的业务逻辑代码,页面混乱

    <h1>
        <h1>
            <h1>
                <h1>
                    <%
                       System.out.println("Hello")
                     %>           
                </h1>
            </h1>
        </h1>
    </h1>
    
  • 维护成本高!

  • ASP主要使用C#

  • IIS,(Internet Information Services ,互联网信息服务 )

PHP:

  • PHP开发速度很快,功能很强大,跨平台,代码很简单(70%的网站)
  • 无法承载大访问量的情况,有一定的局限性。

JSP/Servlet:

​ B/S:浏览器和服务器

​ C/S:客户端和服务器

  • Sun公司主推的B/S架构
  • 基于Java语言(所有的大公司或者一些开源的组件,都是用Java写的)
  • 可以承载三高(高并发,高可用,高性能)问题带来的影响
  • 语法和ASP很像,方便ASP转JSP,加强市场的强度。(竞争)

web服务器

服务器是一种被动的操作,用来处理用户的一些请求和给用户一些响应信息。

ISS:

  • 微软开发的,ASP,Windows系统中自带。

Tomcat:

Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,最新的Servlet 和JSP 规范总是能在Tomcat 中得到体现。因为Tomcat 技术先进、性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为比较流行的Web 应用服务器。

Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。对于一个Java初学者来说,它是最佳的选择。

Tomcat 实际上运行JSP 页面和Servlet。截止至2023年7月20日,Tomcat最新版本为11.0.0**。**

Tomcat

安装Tomcat

tomcat官网:https://tomcat.apache.org/

这里我安装了10.1.11版本。

Tomcat启动和配置

  • 文件夹的作用

  • 启动和关闭Tomcat

    1. 启动:双击bin目录下的startup.bat,启动之后,浏览器访问localhost:8080,看是否有tomcat界面。
    2. 关闭:双击bin目录下的shutdown.bat;或者直接关掉命令行窗口。

配置

config目录下的server.xml文件为服务器核心配置文件。

在该文件中,我们可以修改启动默认的端口号,可以配置主机的名称,可以配置web存放webapps的目录。

  • 修改端口号:

    Tomcat 的默认端口号:8080

    MySQL的默认端口号:3306

    http的默认端口号:80

    https的默认端口号:443

    <Connector port="8080" protocol="HTTP/1.1"
                   connectionTimeout="20000"
                   redirectPort="8443" />
    
  • 配置主机名称:

    默认主机名为:localhost,相当于电脑ip

    默认网站应用存放的目录为:webapps

    <Host name="localhost"  appBase="webapps"
                unpackWARs="true" autoDeploy="true">
    
  • 面试题:

    请你谈一谈网站是如何进行访问的:

    一位前辈写的很详细的过程可供参考:访问一个网页的全过程(超详细版)_访问原网页_小包同学666的博客-CSDN博客

    1. 输入域名后,发送http请求

    2. 服务器查找域名对应的IP地址

      1. 先检查本地hosts配置文件中或者本机的DNS缓存中有没有这个域名的映射,有则直接返回对应的IP地址,这个地址中,有我们需要访问的web程序,可以直接进行访问。相当于一把钥匙。

      2. 本地没找到的话,浏览器会发出一个DNS请求到本地DNS服务器,本地DNS服务器一般都是我们网络接入服务商提供的,比如中国移动、电信……,请求到达本地DNS服务器后,本地DNS服务器开始查缓存记录(递归查询),查到了,直接返回,没查到,继续向上一级查询,上一级为DNS根服务器,继续查询,DNS根服务器没有记录具体的域名和IP地址的对应关系,于是告诉本地DNS服务器,你可以到域服务器上去继续查询,并给出域服务器的地址(迭代)。

        本地DNS服务器继续向域服务器发出请求,如果请求的对象是.com域服务器。.com域服务器收到请求之后,也不会直接返回域名和IP地址的对应关系,而是告诉本地DNS服务器,你的域名的解析服务器的地址。

        最后,本地DNS服务器向域名的解析服务器发出请求,这时就能收到一个域名和IP地址对应关系,本地DNS服务器不仅要把IP地址返回给用户电脑,还要把这个对应关系保存在缓存中,以备下次别的用户查询时,可以直接返回结果,加快网络访问。如果url里不包含端口号,则会使用该协议的默认端口号。

      3. 浏览器访问这个ip地址

      4. ip地址所在的服务器发送响应页面给客户端

    3. 访问成功

  • 配置一个Tomcat的环境变量(可选)

网站应该有的结构

--webapps:Tomcat服务器的web目录
	-root:网站的目录名
		-WEB-INF
			-classes:java程序
			-lib:web应用所依赖的jar包
			-web.xml:网站的配置文件
		-index.html 默认的首页	
		-static
			-css
			-js
			-img
		-...

HTTP(面试)

什么是HTTP协议

超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在TCP之上。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。请求和响应消息的头以ASCII形式给出;而消息内容则具有一个类似MIME的格式。

  • 文本:数字、字符串…
  • 超文本:图片、音乐、视频…
  • 端口:80
  • HTTPs:s代表SSL协议

两个时代

  • HTTP1.0
    • HTTP/1.0:客户端可以与web服务器连接后,只能获得一个web资源
  • HTTP2.0
    • HTTP/1.1:客户端与web服务器连接后,可以获得多个web资源

HTTP请求

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

  • 以百度主页为例

    Request URL:https://www.baidu.com/		//请求地址
    Request Method:GET						//请求方法:get 或 post
    Status Code:200 OK						//状态码:200
    Remote Address:36.152.44.95:443			//远程地址:ip
    Referrer Policy:unsafe-url
    
    Accept:
    text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
    Accept-Encoding:
    gzip, deflate, br
    Accept-Language:
    zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
    Connection:
    keep-alive
    Cookie:
    BAIDUID=7983DB4511EA3BD8A34711321AD384FB:FG=1; BIDUPSID=7983DB4511EA3BD8A34711321AD384FB; PSTM=1686188034; BAIDUID_BFESS=7983DB4511EA3BD8A34711321AD384FB:FG=1; channel=baidu.yni84.com; COOKIE_SESSION=154_0_2_1_0_11_1_0_2_2_1_6_0_0_0_0_0_0_1686408302%7C2%230_0_1686408371%7C1%7C1; BAIDU_WISE_UID=wapp_1686448323979_511; BDUSS=lvaEN2M0lldFlkdFFxc0o3Q0FTZGRXdFNkfnZOZkJWbzF1NzJEZHNHVXF1YXhrSVFBQUFBJCQAAAAAAQAAAAEAAACeoSEgbHVjazF5NwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACoshWQqLIVkQk; BDUSS_BFESS=lvaEN2M0lldFlkdFFxc0o3Q0FTZGRXdFNkfnZOZkJWbzF1NzJEZHNHVXF1YXhrSVFBQUFBJCQAAAAAAQAAAAEAAACeoSEgbHVjazF5NwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACoshWQqLIVkQk; baikeVisitId=ac871edd-42b2-469b-ab00-d9dc2a5064b0; __bid_n=185f85e53f13290fc64207; FPTOKEN=3EIy0QGw9botoUZIl8A6aTU/WYaM4KfDD8TLNLcHyk0s53EgFCpaLbdeL7gpe9OwcXtiSfjgx4qpWRnC2xRuEqS1WxvoxvanyXTFiNQieYYYZmssKsx9M9cLlr5JxrCF5EuaNrjlfgyPQlcZn/Wl1zRRdBqGXRIvsMty7oqK1FlSTahNoaLzIyT0J7nB+4u5gB0v/DGqq3fKavjtgQyAdQiIxNP4/QsX8yJxU+wnL7twFBskXuA3lyGO2S5ygx6fhX+8LCNHjTMTL8y0uUECPcJXTOPn1Vr0QIjNch3qG10VRkAADXNzPHJHz/jUPA9pOejdWUbVak4IV7fCGJkQDlcSewFrG3qtKrlEwpXL1Kxv5Kh1ZsMUOpuSZqpBOXRs0hSmgXDYL9FN5yJq5iynTQ==|NKdyZ+1W0N6+VylwwZLQNQL3M2oLgR2G7CrVyMJA3q4=|10|1ad2b263ca26ac684ba4962355326c70; BD_UPN=12314753; ZFY=CXvPIk3AByRXnNlRy3JsNyWO:A7v1tOY7obm6IOMGn8A:C; RT="z=1&dm=baidu.com&si=d32fd009-95a5-4b3e-827b-1520f83b64d1&ss=lkala4io&sl=4&tt=2yg&bcn=https%3A%2F%2Ffclog.baidu.com%2Flog%2Fweirwood%3Ftype%3Dperf&ld=kk8&ul=296j&hd=297o"; BDRCVFR[s5kaJWTb083]=OjjlczwSj8nXy4Grjf8mvqV; BD_HOME=1; H_PS_PSSID=26350; BA_HECTOR=0g848g2la4018ha4a5058h8c1ibkagg1o; WWW_ST=1689922231816
    Host:
    www.baidu.com
    Referer:
    https://www.baidu.com/?tn=88093251_87_hao_pg
    Sec-Ch-Ua:
    "Not/A)Brand";v="99", "Google Chrome";v="115", "Chromium";v="115"
    Sec-Ch-Ua-Mobile:
    ?0
    Sec-Ch-Ua-Platform:
    "Windows"
    Sec-Fetch-Dest:
    document
    Sec-Fetch-Mode:
    navigate
    Sec-Fetch-Site:
    same-origin
    Sec-Fetch-User:
    ?1
    Upgrade-Insecure-Requests:
    1
    User-Agent:
    Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36
    
  1. 请求行:

    • 请求行中的请求方式:get/post
      • get:一次请求能够携带的参数比较少,大小有限制,会在浏览器的地址栏中显示数据内容,不安全,但高效。
      • post:请求携带的参数没有限制,大小没有限制,不会在浏览器地址栏显示数据内容,安全,但是不高效。
  2. 消息头:

    Accept:  							 //告诉浏览器,它所支持的数据类型
    Accept-Encoding: gzip, deflate, br   //支持哪种编码格式
    Accept-Language: zh-CN,zh;q=0.9	     //告诉浏览器语言环境
    Cache-Control: max-age=0   			 //缓存控制
    Connection: keep-alive    			 //告诉浏览器请求完成是断开还是保持连接
    HOST:主机
    

HTTP响应

  • 服务器响应客户端

  • 以百度为例:

    Bdpagetype:
    2
    Bdqid:
    0xb6f1197f00094b34
    Connection:
    keep-alive
    Content-Encoding:
    gzip
    Content-Security-Policy:
    frame-ancestors 'self' https://chat.baidu.com http://mirror-chat.baidu.com https://fj-chat.baidu.com https://hba-chat.baidu.com https://hbe-chat.baidu.com https://njjs-chat.baidu.com https://nj-chat.baidu.com https://hna-chat.baidu.com https://hnb-chat.baidu.com http://debug.baidu-int.com;
    Content-Type:
    text/html; charset=utf-8
    Date:
    Fri, 21 Jul 2023 06:50:32 GMT
    Isprivate:
    1
    Server:
    BWS/1.1
    Set-Cookie:
    BDSVRTM=368; path=/
    Set-Cookie:
    BD_HOME=1; path=/
    Set-Cookie:
    H_PS_PSSID=36555_38642_38831_39026_39024_38943_39007_39114_39120_39087_26350_39094_39100_39043_38953; path=/; domain=.baidu.com
    Strict-Transport-Security:
    max-age=172800
    Traceid:
    1689922232095451905013182345617542302516
    Transfer-Encoding:
    chunked
    X-Ua-Compatible:
    IE=Edge,chrome=1
    
  1. 响应体

    Accept:  //告诉浏览器,它所支持的数据类型
    Accept-Encoding: gzip, deflate, br   //支持哪种编码格式
    Accept-Language: zh-CN,zh;q=0.9	     //告诉浏览器语言环境
    Cache-Control: max-age=0   //缓存控制
    Connection: keep-alive    //告诉浏览器请求完成是断开还是保持连接
    HOST:主机
    
    Refresh:   //告诉客户端,多久刷新一次
    Location:  //让网页重写定位
    
  2. 响应状态码

    200:请求响应成功

    3XX:请求重定向,转到一个新位置

    4XX:找不到资源,资源不存在(404)

    5XX:服务器代码错误(500)

    502:网关错误

常见面试题:

  • 当你的浏览器中地址栏输入地址并回车的一瞬间到页面能够展示出来,经历了什么?(又见面,重点)

Maven

为什么要学习这个技术?

  1. 在JavaWeb中需要使用大量的jar包,这些jar包需要下载来手动导入。
  2. 怎么才能让一个工具来自动帮我导入这些包并且配置它们?

所以,Maven诞生了!

Maven:项目架构管理工具

目前用Maven就是便于导入jar包。

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

  • 有约束,就不要去违反

Maven规定了我们如何按照一定的目录结构来编写我们的Java代码。

环境变量配置

在系统环境变量中,配置如下:

  • M2_HOME :maven目录下的bin目录
  • MAVEN_HOME:maven的目录
  • 在Path中:配置%MAVEN_HOME%\bin

完成后:在控制台输入mvn -version 检查是否安装成功 mvn其实是bin目录中的一个命令

修改配置文件(阿里云镜像)

  • 镜像:mirrors

    • 作用:加速我们的下载
  • 国内建议使用阿里云镜像

    • 在setting.xml文件下的mirrors中粘贴以下内容:

      <mirror>
      		<id>aliyun-public</id>
      		<mirrorOf>*</mirrorOf>
      		<name>aliyun public</name>
      		<url>https://maven.aliyun.com/repository/public</url>
      	</mirror>
      
      	<mirror>
      		<id>aliyun-central</id>
      		<mirrorOf>*</mirrorOf>
      		<name>aliyun central</name>
      		<url>https://maven.aliyun.com/repository/central</url>
      	</mirror>
      
      	<mirror>
      		<id>aliyun-spring</id>
      		<mirrorOf>*</mirrorOf>
      		<name>aliyun spring</name>
      		<url>https://maven.aliyun.com/repository/spring</url>
      	</mirror>
      
      	<mirror>
      		<id>aliyun-spring-plugin</id>
      		<mirrorOf>*</mirrorOf>
      		<name>aliyun spring-plugin</name>
      		<url>https://maven.aliyun.com/repository/spring-plugin</url>
      	</mirror>
      
      	<mirror>
      		<id>aliyun-apache-snapshots</id>
      		<mirrorOf>*</mirrorOf>
      		<name>aliyun apache-snapshots</name>
      		<url>https://maven.aliyun.com/repository/apache-snapshots</url>
      	</mirror>
      
      	<mirror>
      		<id>aliyun-google</id>
      		<mirrorOf>*</mirrorOf>
      		<name>aliyun google</name>
      		<url>https://maven.aliyun.com/repository/google</url>
      	</mirror>
      
      	<mirror>
      		<id>aliyun-gradle-plugin</id>
      		<mirrorOf>*</mirrorOf>
      		<name>aliyun gradle-plugin</name>
      		<url>https://maven.aliyun.com/repository/gradle-plugin</url>
      	</mirror>
      
      	<mirror>
      		<id>aliyun-jcenter</id>
      		<mirrorOf>*</mirrorOf>
      		<name>aliyun jcenter</name>
      		<url>https://maven.aliyun.com/repository/jcenter</url>
      	</mirror>
      
      	<mirror>
      		<id>aliyun-releases</id>
      		<mirrorOf>*</mirrorOf>
      		<name>aliyun releases</name>
      		<url>https://maven.aliyun.com/repository/releases</url>
      	</mirror>
      
      	<mirror>
      		<id>aliyun-snapshots</id>
      		<mirrorOf>*</mirrorOf>
      		<name>aliyun snapshots</name>
      		<url>https://maven.aliyun.com/repository/snapshots</url>
      	</mirror>
      
      	<mirror>
      		<id>aliyun-grails-core</id>
      		<mirrorOf>*</mirrorOf>
      		<name>aliyun grails-core</name>
      		<url>https://maven.aliyun.com/repository/grails-core</url>
      	</mirror>
      
      	<mirror>
      		<id>aliyun-mapr-public</id>
      		<mirrorOf>*</mirrorOf>
      		<name>aliyun mapr-public</name>
      		<url>https://maven.aliyun.com/repository/mapr-public</url>
      	</mirror>
      

建立本地仓库

本地仓库和远程仓库;

建立本地仓库:不用每次都去网上下载各种包

  • 在maven下新建一个文件夹,命名为maven-repo,复制这个地址,在setting.xml文件中进行粘贴

    <localRepository>E:\Java environment\apache-maven-3.9.3\maven-repo</localRepository>
    

在IDEA中创建Maven项目

  • 创建普通maven项目(无模板项目)

  • 注意设置中的maven设置

    每种路径都要设置到自己maven文件夹的路径,否则默认为IDEA自带的maven版本,每次新开的项目要留意看一下。

  • 本地仓库中可以看到多了很多包

  • 一个干净的maven项目

在IDEA中创建带模板Maven Web项目

新建项目时选择左侧Maven Archetype,在Catalog中选择Internal,在Archetype中选择……webapp,我用的是2023版本,新建项目时和前面的版本略有不同。

在IDEA中配置Tomcat

  1. 点击右上角

  2. 找到并添加Tomcat

  3. 配置界面

pom.xml文件

pom.xml是maven的核心文件

<!-- Maven的版本和头文件 -->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <!-- 创建项目时配置的GAV -->
  <groupId>xyz.luck1y</groupId>
  <artifactId>javaweb-01-maven</artifactId>
  <!-- packaging:项目的打包方式
  jar:java应用
  war:JavaWeb应用
  -->
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>javaweb-01-maven Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <!-- 项目依赖 -->
  <dependencies>
    <!-- 具体依赖的jar包配置文件   -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <!--项目构建用的东西-->
  <build>
    <finalName>javaweb-01-maven</finalName>
  </build>
</project>

maven的高级之处在于它会帮我们导入某个jar包所依赖的其他jar包

如果遇到maven项目中的配置文件无法被导出或者生效的问题,在配置文件中加上以下内容:

<!--在build中配置resources,来防止我们资源导出失败的问题-->
<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>false</filtering>
        </resource>
    </resources>
</build>

Maven仓库的使用

maven仓库官网:https://mvnrepository.com/

找到想用的jar包,选好版本后进去就能看见maven的代码配置,复制粘贴到dependencys下就好了,在maven结构目录刷新一下,就能导入jar包,非常方便。

Servlet

Servlet简介

  • Servlet就是Sun公司开发动态web的一门技术
  • Sun公司在这些api中提供了一个接口叫做Servlet,如果想开发一个Servlet程序,只需要完成两个小步骤:
    1. 编写一个类,实现Servlet接口
    2. 把开发好的Java类部署在web服务器中
  • 把实现了Servlet接口的Java程序叫Servlet

HelloServlet

Servlet接口在Sun公司有两个默认的实现类:HttpServlet、GenericServlet

  1. 构建一个普通Maven项目,删掉里面的src目录,以后的学习就在这个项目里简历许多的module;这个空的项目就是Maven的主项目(工程),所以我们把需要的依赖都导入到主工程的pom.xml中

  2. 右键该工程,新建module,勾选Maven自带的webapps模板框架,创建新的Maven子工程。关于Maven父子工程的理解:

    • 父项目的pom.xml中会有以下内容:

          <modules>
              <module>servlet-01</module>
          </modules>
      
    • 子项目的pom.xml中会有以下内容:

          <parent>
              <groupId>xyz.luck1y</groupId>
              <artifactId>javaweb-02-servlet</artifactId>
              <version>1.0-SNAPSHOT</version>
          </parent>
      

      父项目中的Java子项目可以直接使用父项目中的包

  3. Maven环境优化

    1. 将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_3_1.xsd"
               version="3.1"
               metadata-complete="true">
        
      </web-app>
      

      注意pom.xml中的Servlet依赖:

      <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>xyz.luck1y</groupId>
        <artifactId>javaweb-session-cookie</artifactId>
        <packaging>war</packaging>
        <version>1.0-SNAPSHOT</version>
        <name>javaweb-session-cookie Maven Webapp</name>
        <url>http://maven.apache.org</url>
        <dependencies>
          <!-- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-servlet-api -->
          <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-servlet-api</artifactId>
            <version>10.0.4</version>
          </dependency>
      
          <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.3.3</version>
            <scope>provided</scope>
          </dependency>
        </dependencies>
        <build>
          <finalName>javaweb-session-cookie</finalName>
        </build>
      </project>
      

      servlet-api:

      Maven Repository: org.apache.tomcat » tomcat-jsp-api (mvnrepository.com)

      <!-- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-servlet-api -->
      <dependency>
          <groupId>org.apache.tomcat</groupId>
          <artifactId>tomcat-servlet-api</artifactId>
          <version>10.0.4</version>
      </dependency>
      

      学到后面发现还是有些问题,比如在jsp中写Java代码不会自动补充,比如out.println爆红,于是更换依赖就好了,Tomcat10怎么到处都是坑!!!!

          <dependency>
            <groupId>jakarta.servlet</groupId>
            <artifactId>jakarta.servlet-api</artifactId>
            <version>4.0.4</version>
            <scope>provided</scope>
          </dependency>
      
    2. 将Maven项目的目录结构搭建完整,新建java目录(和resource目录)

  4. 编写一个Servlet程序

    1. 编写一个普通类

    2. 实现Servlet接口,这里我们直接继承HttpServlet

      进入源码查看:Servlet --> GenericServlet --> HttpServlet --> 我们自己定义的类,Servlet接口有个service方法,HTTPServlet对其进行了重写,重写后其中包括了很多方法,而我们常用的就是doGet和doPost,所以在自己定义的类中,需要重写这两个方法。

      package xyz.luck1y.servlet;
      
      import jakarta.servlet.ServletException;
      import jakarta.servlet.ServletInputStream;
      import jakarta.servlet.ServletOutputStream;
      import jakarta.servlet.http.HttpServlet;
      import jakarta.servlet.http.HttpServletRequest;
      import jakarta.servlet.http.HttpServletResponse;
      import java.io.IOException;
      import java.io.PrintWriter;
      
      public class HelloServlet extends HttpServlet {
      
          // 由于get或者post只是请求实现的不同方式,可以相互调用,业务逻辑都一样
          @Override
          protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
              // 可以得到输入输出流
      //        ServletInputStream inputStream = req.getInputStream();
      //        ServletOutputStream outputStream = resp.getOutputStream();
              System.out.println("进入doGet方法");
              // 响应流
              PrintWriter writer = resp.getWriter();
              writer.print("Hello,Servlet!");
          }
      
          @Override
          protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
              super.doPost(req, resp);
          }
      }
      
  5. 编写Servlet的映射

    为什么需要映射?我们写的是Java程序,但是要通过浏览器访问,而浏览器需要连接web服务器,所以我们需要在web服务中注册我们写的Servlet,还需要给它一个浏览器能够访问的路径。

    <?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_3_1.xsd"
             version="3.1"
             metadata-complete="true">
        <!-- 注册Servlet -->
        <servlet>
            <servlet-name>hello</servlet-name>
            <servlet-class>xyz.luck1y.servlet.HelloServlet</servlet-class>
        </servlet>
        <!-- 请求路径 -->
        <servlet-mapping>
            <servlet-name>hello</servlet-name>
            <url-pattern>/hello</url-pattern>
        </servlet-mapping>
    </web-app>
    
  6. 配置Tomcat

    注意项目发布的路径

    注意每个子项目添加进artifact的时候,去掉上一个残留的,尽量只存在一个

  7. 启动项目测试

Servlet原理

Servlet是由web服务器调用,web服务器在收到浏览器请求之后的原理图如下,

Mapping问题

  1. 一个Servlet可以指定一个映射路径

    <!-- 访问路径:localhost:8080/servlet01/hello-->
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
    
  2. 一个Servlet可以指定多个映射路径

    <!-- 访问路径:localhost:8080/servlet01/hello(或者hello2或者hello3...)-->
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello1</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello2</url-pattern>
    </servlet-mapping>
    
  3. 一个Servlet可以指定通用映射路径

    <!-- 访问路径:localhost:8080/servlet01/hellocdjsnhvbh-->
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello/*</url-pattern>
    </servlet-mapping>
    
    <!--默认请求路径,会替代index.jsp-->
    <!-- 访问路径:localhost:8080/servlet01-->
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
    
  4. 指定一些后缀等等…

    注意:*前面不能加项目映射的路径

    <!--默认请求路径,会干掉index.jsp-->
    <!-- 访问路径:localhost:8080/servlet01/abc.do-->
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
    
  5. 扩展:如果出错,网页404跳转error界面

    package xyz.luck1y.servlet;
    
    import jakarta.servlet.ServletException;
    import jakarta.servlet.http.HttpServlet;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    
    public class ErrorServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            resp.setContentType("text/html");
            resp.setCharacterEncoding("utf-8");
    
            PrintWriter writer = resp.getWriter();
            writer.print("<h1>404 网页找不到咯</h1>");
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            super.doPost(req, resp);
        }
    }
    
        <servlet>
            <servlet-name>error</servlet-name>
            <servlet-class>xyz.luck1y.servlet.ErrorServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>error</servlet-name>
            <url-pattern>/*</url-pattern>
        </servlet-mapping>
    
  6. 优先级问题:

    指定了固有的映射路径:越准确优先级越高,如果找不到就会走默认的处理请求

    精准匹配>扩展匹配>模糊匹配>通配匹配

ServletContent

web容器在启动的时候,它会为每个web程序都创建一个对应的ServletContext对象,它代表了当前的web应用。

全局唯一,只有一个,所有servlet公用。

应用:

  • 共享数据

    在这个Servlet中保存的数据,可以在另一个Servlet中拿到。

    创建数据的类:

    package xyz.luck1y.servlet;
    
    import jakarta.servlet.ServletContext;
    import jakarta.servlet.ServletException;
    import jakarta.servlet.http.HttpServlet;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    
    import java.io.IOException;
    
    public class HelloServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //        this.getInitParameter();     初始化参数
    //        this.getServletConfig();     Servlet配置
    //        this.getServletContext();    上下文,第三方
            resp.setContentType("text/html;charset=utf-8");
            ServletContext context = this.getServletContext();
            // 数据
            String username = "Luck1y";
            // 将一个数据保存到了ServletContext中。名字为:username,值为:username
            context.setAttribute("username", username);
            System.out.println("存放值");
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            super.doPost(req, resp);
        }
    }
    

    读取数据的类:

    package xyz.luck1y.servlet;
    
    import jakarta.servlet.ServletContext;
    import jakarta.servlet.ServletException;
    import jakarta.servlet.http.HttpServlet;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    
    import java.io.IOException;
    
    public class GetServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            ServletContext context = this.getServletContext();
            String username = (String) context.getAttribute("username");
            resp.setContentType("text/html;charset=utf-8");
            resp.getWriter().print("名字:" + username);
            System.out.println("取值");
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            super.doPost(req, resp);
        }
    }
    

    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_3_1.xsd"
             version="3.1"
             metadata-complete="true">
      <servlet>
        <servlet-name>hello</servlet-name>
        <servlet-class>xyz.luck1y.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>xyz.luck1y.servlet.GetServlet</servlet-class>
      </servlet>
      <servlet-mapping>
        <servlet-name>getc</servlet-name>
        <url-pattern>/getc</url-pattern>
      </servlet-mapping>
    </web-app>
    

    运行结果:

    没存值进去的时候(没有进入hello),null

    存了值进去(进入hello)之后

  • 获取初始化参数

    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_3_1.xsd"
             version="3.1"
             metadata-complete="true">
        <!--配置一些web应用初始化参数-->
        <context-param>
            <param-name>url</param-name>
            <param-value>jdbc:mysql://localhost:3066/mybatis</param-value>
        </context-param>
        <servlet>
            <servlet-name>hello</servlet-name>
            <servlet-class>xyz.luck1y.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>xyz.luck1y.servlet.GetServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>getc</servlet-name>
            <url-pattern>/getc</url-pattern>
        </servlet-mapping>
    
        <servlet>
            <servlet-name>gp</servlet-name>
            <servlet-class>xyz.luck1y.servlet.ServletDemo03</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>gp</servlet-name>
            <url-pattern>/gp</url-pattern>
        </servlet-mapping>
    
    </web-app>
    

    获取类:

    package xyz.luck1y.servlet;
    
    import jakarta.servlet.ServletContext;
    import jakarta.servlet.ServletException;
    import jakarta.servlet.http.HttpServlet;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    
    import java.io.IOException;
    
    public class ServletDemo03 extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            ServletContext context = this.getServletContext();
            String url = context.getInitParameter("url");
            resp.getWriter().print(url);
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            super.doPost(req, resp);
        }
    }
    

    运行结果:

  • 请求转发

    转发不是重定向,路径不会变,但是页面内容会改变

    web.xml

        <servlet>
            <servlet-name>sd4</servlet-name>
            <servlet-class>xyz.luck1y.servlet.ServletDemo04</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>sd4</servlet-name>
            <url-pattern>/sd4</url-pattern>
        </servlet-mapping>
    

    转发类(将ServletDemo03页面的内容转发到ServletDemo04)

    package xyz.luck1y.servlet;
    
    import jakarta.servlet.ServletContext;
    import jakarta.servlet.ServletException;
    import jakarta.servlet.http.HttpServlet;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    
    import java.io.IOException;
    
    public class ServletDemo03 extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            ServletContext context = this.getServletContext();
            String url = context.getInitParameter("url");
            resp.getWriter().print(url);
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            super.doPost(req, resp);
        }
    }
    

    运行结果:

    可以看出,路径是/sd4但是打开的内容还是/gp的内容,其结构原理类似下图:

    A想连接到C的资源,要通过B来实现,所以A向B发送请求,要C的资源,B将C的资源转发给A。在这个例子中,A就是ServletDemo04,B就是ServletContext,C就是ServeltDemo03。

    重定向的话是这样的:

    A需要发送两次请求,过程为:A告诉B我要C的资源,B回答说:你去找C要,然后A向C发送请求。即重定向。

  • 读取资源文件

    Properties

    新建一个db.properties文件,放在resources目录下,运行服务器,查看target目录,发现生成了对应的db.properties文件

    新建一个aa.properties文件,放在java目录下,运行服务器,查看target目录,发现不没有生成对应的aa.properties文件,这就是Maven中提到的资源导出问题

    我们在该module下的配置文件中的build部分加入以下内容:

    <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>false</filtering>
            </resource>
          </resources>
    </build>
    

    对比打包的项目target和原本的main,我们可以发现一条java和resources目录的内容都被打包到了classes,我们俗称这个路径为类路径,classpath。

    db.properties:

    username=root
    password=123456
    

    获取类:

    package xyz.luck1y.servlet;
    
    import jakarta.servlet.ServletException;
    import jakarta.servlet.http.HttpServlet;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Properties;
    
    public class ServletDemo05 extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            InputStream is = this.getServletContext().getResourceAsStream("WEB-INF/classes/db.properties");
            Properties prop = new Properties();
            prop.load(is);
            String user = prop.getProperty("username");
            String pwd = prop.getProperty("password");
    
            resp.getWriter().print(user + ":" + pwd);
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
    

    web.xml:

        <servlet>
            <servlet-name>sd5</servlet-name>
            <servlet-class>xyz.luck1y.servlet.ServletDemo05</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>sd5</servlet-name>
            <url-pattern>/sd5</url-pattern>
        </servlet-mapping>
    

    运行结果:

HttpServletResponse

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

  • 我们如果要获取我们客户端请求过来的参数:找HttpServletRequest
  • 如果要给客户端响应一些信息:就找HttpServletResponse
简单分类
  • 负责向浏览器发送数据的方法:

    ServletOutputStream getOutputStream() throws IOException;
    PrintWriter getWriter() throws IOException;
    
  • 负责向浏览器发送响应头的方法:

    void setCharacterEncoding(String var1);
    
    void setContentLength(int var1);
    
    void setContentLengthLong(long var1);
    
    void setContentType(String var1);
    
    void setBufferSize(int var1);
    
  • 响应的状态码:

    int SC_CONTINUE = 100;
    int SC_SWITCHING_PROTOCOLS = 101;
    int SC_OK = 200;
    int SC_CREATED = 201;
    int SC_ACCEPTED = 202;
    int SC_NON_AUTHORITATIVE_INFORMATION = 203;
    int SC_NO_CONTENT = 204;
    int SC_RESET_CONTENT = 205;
    int SC_PARTIAL_CONTENT = 206;
    int SC_MULTIPLE_CHOICES = 300;
    int SC_MOVED_PERMANENTLY = 301;
    int SC_MOVED_TEMPORARILY = 302;
    int SC_FOUND = 302;
    int SC_SEE_OTHER = 303;
    int SC_NOT_MODIFIED = 304;
    int SC_USE_PROXY = 305;
    int SC_TEMPORARY_REDIRECT = 307;
    int SC_BAD_REQUEST = 400;
    int SC_UNAUTHORIZED = 401;
    int SC_PAYMENT_REQUIRED = 402;
    int SC_FORBIDDEN = 403;
    int SC_NOT_FOUND = 404;
    int SC_METHOD_NOT_ALLOWED = 405;
    int SC_NOT_ACCEPTABLE = 406;
    int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
    int SC_REQUEST_TIMEOUT = 408;
    int SC_CONFLICT = 409;
    int SC_GONE = 410;
    int SC_LENGTH_REQUIRED = 411;
    int SC_PRECONDITION_FAILED = 412;
    int SC_REQUEST_ENTITY_TOO_LARGE = 413;
    int SC_REQUEST_URI_TOO_LONG = 414;
    int SC_UNSUPPORTED_MEDIA_TYPE = 415;
    int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
    int SC_EXPECTATION_FAILED = 417;
    int SC_INTERNAL_SERVER_ERROR = 500;
    int SC_NOT_IMPLEMENTED = 501;
    int SC_BAD_GATEWAY = 502;
    int SC_SERVICE_UNAVAILABLE = 503;
    int SC_GATEWAY_TIMEOUT = 504;
    int SC_HTTP_VERSION_NOT_SUPPORTED = 505;
    
常见应用
  1. 向浏览器输出消息(前面已使用过)

  2. 下载文件

    • 要获取下载文件的路径
    • 下载的文件名
    • 想办法让浏览器能够支持下载我们需要的东西
    • 获取下载文件的输入流
    • 创建缓冲区
    • 获取OutputStream对象
    • 将FileOutputStream流写入到buffer缓冲区
    • 使用OutputStream将缓冲区中的数据输出到客户端

    下载类:

    package xyz.luck1y.servlet;
    
    import jakarta.servlet.ServletException;
    import jakarta.servlet.ServletOutputStream;
    import jakarta.servlet.http.HttpServlet;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.net.URLEncoder;
    
    public class FileServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // 1.要获取下载文件的路径
    //        String realPath = this.getServletContext().getRealPath("/1.png");
            String realPath = "E:\\Java\\学习记录\\JavaWeb\\图片.png";
            System.out.println("下载文件的路径" + realPath);
            // 2.下载的文件名
            String filename = realPath.substring(realPath.lastIndexOf("\\") + 1);
            // 3.想办法让浏览器能够支持下载我们需要的东西
            // URLEncoder.encode(filename,"UTF-8") 使用中文文件名也不会出现乱码问题
            resp.setHeader("Content-Disposition", "attachment;filename=" + filename + URLEncoder.encode(filename,"UTF-8"));
            // 4.获取下载文件的输入流
            FileInputStream in = new FileInputStream(realPath);
            // 5.创建缓冲区
            int len = 0;
            byte[] buffer = new byte[1024];
            // 6.获取OutputStream对象
            ServletOutputStream out = resp.getOutputStream();
            // 7.将FileOutputStream流写入到buffer缓冲区,使用OutputStream将缓冲区中的数据输出到客户端
            while ((in.read(buffer) != -1)) {
                out.write(buffer, 0, len);
            }
            // 8.关闭资源
            out.close();
            in.close();
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
        }
    }
    

    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_3_1.xsd"
             version="3.1"
             metadata-complete="true">
      <servlet>
        <servlet-name>filedownload</servlet-name>
        <servlet-class>xyz.luck1y.servlet.FileServlet</servlet-class>
      </servlet>
      <servlet-mapping>
        <servlet-name>filedownload</servlet-name>
        <url-pattern>/down</url-pattern>
      </servlet-mapping>
    </web-app>
    
  3. 验证码功能

    验证码是怎么来的?

    • 前端实现

    • 后端实现,需要用到java的图片类,生成一个图片

      package xyz.luck1y.servlet;
      
      import jakarta.servlet.ServletException;
      import jakarta.servlet.http.HttpServlet;
      import jakarta.servlet.http.HttpServletRequest;
      import jakarta.servlet.http.HttpServletResponse;
      
      import javax.imageio.ImageIO;
      import java.awt.*;
      import java.awt.image.BufferedImage;
      import java.io.IOException;
      import java.util.Random;
      
      public class ImageServlet extends HttpServlet {
          @Override
          protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
              // 如何让浏览器3秒自动刷新一次
              resp.setHeader("refresh", "3");
              // 在内存中创建一个图片
              BufferedImage bufferedImage = new BufferedImage(60,30,BufferedImage.TYPE_3BYTE_BGR);
              // 得到图片
              Graphics2D graphics = (Graphics2D) bufferedImage.getGraphics();
              // 设置图片的背景颜色
              graphics.setColor(Color.white);
              graphics.fillRect(0,0,60,30);
              // 给图片写数据
              graphics.setColor(Color.blue);
              graphics.setFont(new Font(null, Font.BOLD, 20));
              graphics.drawString(makeNum(), 0, 20);
      
              // 告诉浏览器这个请求用图片的方式打开
              resp.setContentType("image/png");
              // 网站存在缓存,不让浏览器缓存
              resp.setDateHeader("expires", -1);
              resp.setHeader("Cache-Control", "no-cache");
              resp.setHeader("Prpgram", "no-cache");
              // 把图片写给浏览器
              ImageIO.write(bufferedImage, "png", resp.getOutputStream());
          }
      
          // 生成随机数
          private String makeNum(){
              Random random = new Random();
              String num = random.nextInt(99999) + "";
              StringBuffer sb = new StringBuffer();
              for (int i = 0; i < 5 - num.length(); i++) {
                  sb.append("0");
              }
              num = sb + num;
              return num;
          }
      
          @Override
          protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
              doGet(req, resp);
          }
      }
      

      web.xml:

        <servlet>
          <servlet-name>image</servlet-name>
          <servlet-class>xyz.luck1y.servlet.ImageServlet</servlet-class>
        </servlet>
        <servlet-mapping>
          <servlet-name>image</servlet-name>
          <url-pattern>/img</url-pattern>
        </servlet-mapping>
      

      实现效果:

  4. 实现重定向

    一个Web资源收到客户端请求后,通知他去访问另一个web资源,这个过程叫做重定向。

    常见场景:

    • 用户登录

      void sendRedirect(String var1) throws IOException;
      
      package xyz.luck1y.servlet;
      
      import jakarta.servlet.ServletException;
      import jakarta.servlet.http.HttpServlet;
      import jakarta.servlet.http.HttpServletRequest;
      import jakarta.servlet.http.HttpServletResponse;
      
      import java.io.IOException;
      
      public class RedirectServlet extends HttpServlet {
          @Override
          protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
              resp.sendRedirect("img");
              // 等价于302
              // resp.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
          }
      
          @Override
          protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
              doGet(req, resp);
          }
      }
      

      web.xml

          <servlet>
              <servlet-name>RedirectServlet</servlet-name>
              <servlet-class>xyz.luck1y.servlet.RedirectServlet</servlet-class>
          </servlet>
          <servlet-mapping>
              <servlet-name>RedirectServlet</servlet-name>
              <url-pattern>/red</url-pattern>
          </servlet-mapping>
      
  5. 聊一聊重定向和转发的区别(面试题)

    相同点:

    • 页面都会实现跳转

    不同点:

    • 请求转发的时候,URL不会产生变化;重定向的时候,URL会发生变化(地址栏会变化)
    • 转发是一次请求重定向是两次请求
    • 请求转发是在服务器内部完成,重定向是在客户端完成的
    • 转发的url必须是当前web工程内部的地址,重定向可以是任意地址
    • 转发的状态码是307,重定向是302
简单的登陆小案例(获取jsp中提交的参数)

去maven仓库导入一个jsp包

<dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>javax.servlet.jsp-api</artifactId>
    <version>2.3.3</version>
</dependency>
package xyz.luck1y.servlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

public class RequestTest extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("进入这个请求了");

        // 处理请求
        String username = req.getParameter("username");
        String password = req.getParameter("password");

        System.out.println(username + ":" + password);

        resp.sendRedirect("success.jsp");
    }

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

web.xml

    <servlet>
        <servlet-name>request</servlet-name>
        <servlet-class>xyz.luck1y.servlet.RequestTest</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>request</servlet-name>
        <url-pattern>/login</url-pattern>
    </servlet-mapping>

index.jsp

<html>
<body>
<h2>Hello World!</h2>
<%--设置中文编码--%>
<%@page pageEncoding="utf-8"%>
<%--这里提交的路径,需要寻找到项目的路径--%>
<%--${pageContext.request.contextPath} 代表当前的项目--%>
<form action="${pageContext.request.contextPath}/login" method="get">
    用户名:<input type="text" name="username"><br>
    密码:<input type="password" name="password"><br>
    <input type="submit">
</form>
</body>
</html>

success.jsp(点击提交后跳转界面)

<%--
  Created by IntelliJ IDEA.
  User: 刘嘉奇
  Date: 2023/7/24
  Time: 19:24
  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>

<h1>Success!</h1>

</body>
</html>

运行效果:

HttpServletRequest

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

  1. 获取前段传递的参数

    // 返回String
    requset.getParameter(String s);
    // 返回数组String[]
    request.getParameterValues(String s);
    
    package xyz.luck1y.servlet;
    
    import jakarta.servlet.ServletException;
    import jakarta.servlet.http.HttpServlet;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    
    import java.io.IOException;
    import java.util.Arrays;
    
    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("===========================");
            System.out.println(username);
            System.out.println(password);
            System.out.println(Arrays.toString(hobbys));
            System.out.println("===========================");
            // 通过请求转发或重定向跳转到其他页面
            req.getRequestDispatcher("success.jsp").forward(req, resp);
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
    

    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_3_1.xsd"
             version="3.1"
             metadata-complete="true">
      <servlet>
        <servlet-name>LoginServlet</servlet-name>
        <servlet-class>xyz.luck1y.servlet.LoginServlet</servlet-class>
      </servlet>
      <servlet-mapping>
        <servlet-name>LoginServlet</servlet-name>
        <url-pattern>/login</url-pattern>
      </servlet-mapping>
    </web-app>
    

    index.jsp:

    <%--
      Created by IntelliJ IDEA.
      User: 刘嘉奇
      Date: 2023/7/24
      Time: 20:19
      To change this template use File | Settings | File Templates.
    --%>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>登陆页</title>
    </head>
    <body>
    
    <h1 align="center">登录</h1>
    
    <div style="text-align: center">
        <%--表单表示的意思是以post方式提交表单,提交到我们的login请求--%>
        <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="女孩">女孩
            <input type="checkbox" name="hobbys" value="代码">代码
            <input type="checkbox" name="hobbys" value="唱歌">唱歌
            <input type="checkbox" name="hobbys" value="电影">电影
            <input type="submit">
        </form>
    </div>
    </body>
    </html>
    

    success.jsp:

    <%--
      Created by IntelliJ IDEA.
      User: 刘嘉奇
      Date: 2023/7/24
      Time: 20:26
      To change this template use File | Settings | File Templates.
    --%>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>成功!</title>
    </head>
    <body>
    
    <h1>登陆成功!</h1>
    
    </body>
    </html>
    

    运行效果:

Cookie、Session

会话

会话:用户打开一个浏览器,点击一个或多个超链接,访问多个web资源,关闭浏览器,这个过程称之为会话

有状态会话:一个同学来过教室,下次再来教室,我们会知道这个同学曾经来过,称之有状态会话。

  • 证明自己是一个学生:
    • 学生证
    • 毕业证书
  • 两个对象:
    • 学生证:学校发给你的学生证
    • 毕业证书:学校标记你读过大学了
  • 那么一个网站怎么证明你来过:
    • 两个对象:客户端、服务端
    • 怎么让服务端证明客户端来过?
      1. 服务端给客户端一个信件,客户端下次访问带上信件就可以了。(Cookie)
      2. 服务器登记你来过了,下次你来的时候直接匹配你。(Session)

无状态会话:访问一次后就断开连接,没有留下信息

保存会话的两种技术

Cookie:

  • 客户端技术,通过响应发给客户端,通过请求给服务端查验

Session:

  • 服务器技术,利用这个技术,可以保存用户的会话信息,可以把信息或数据放在Session中

常见场景:网站登陆一次后,下次不用再次登录。

Cookie

  1. 从请求中拿到cookie信息
  2. 服务器响应给客户端cookie
// 获得Cookie
Cookie[] cookies = req.getCookies();
// 获得Cookie中的key
cookie.getName();
// 获得Cookie中的值
cookie.getValue();
// 新建一个Cookie
Cookie cookie = new Cookie("lastLoginTime",System.currentTimeMillis() + "");
// 设置Cookie的有效期 (24*60*60 有效期为一天)
cookie.setMaxAge(24*60*60);
// 响应给客户端一个Cookie
resp.addCookie(cookie);
package xyz.luck1y.servlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;

public class CookieDemo01 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //解决中文乱码
        req.setCharacterEncoding("utf-16");
        resp.setCharacterEncoding("utf-16");

        PrintWriter out = resp.getWriter();

        // Cookie,服务器端从客户端获取
        // 返回数组,说明Cookie可能存在多个
        Cookie[] cookies = req.getCookies();

        // 判断Cookie是否存在
        if (cookies != null) {
            // 如果存在
            out.write("您上一次访问的时间是:");
            for (int i = 0; i < cookies.length; i++) {
                Cookie cookie = cookies[i];
                // 获取cookie的名字
                if (cookie.getName().equals("lastLoginTime")) {
                    // 获取cookie中的值
                    long lastLoginTime = Long.parseLong(cookie.getValue());
                    Date date = new Date(lastLoginTime);
                    out.write(date.toLocaleString());
                }
            }
        } else {
            out.write("这是您第一次访问本站");
        }

        // 服务器给客户端响应一个Cookie
        Cookie cookie = new Cookie("lastLoginTime", System.currentTimeMillis() + "");
        resp.addCookie(cookie);

        // 设置有效期 (24*60*60 有效期为一天)
        cookie.setMaxAge(24*60*60);
        resp.addCookie(cookie);
    }

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

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_3_1.xsd"
         version="3.1"
         metadata-complete="true">
    <servlet>
        <servlet-name>CookieDemo01</servlet-name>
        <servlet-class>xyz.luck1y.servlet.CookieDemo01</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>CookieDemo01</servlet-name>
        <url-pattern>/c1</url-pattern>
    </servlet-mapping>
</web-app>

Cookie一般会保存在本地的用户目录下 appdata;

一个网站的Cookie是否存在上限?存在!

  • 一个Cookie只能保存一对信息,键和值
  • 一个web站点可以给浏览器发送多个Cookie,有上限,每个站点最多存放20个cookie。
  • cookie的大小有限制,4kb
  • 浏览器的cookie的上限是300个

删除cookie:

  • 不设置有效期,关闭浏览器,自动失效;

  • 设置有效期时间为0

    package xyz.luck1y.servlet;
    
    import jakarta.servlet.ServletException;
    import jakarta.servlet.http.Cookie;
    import jakarta.servlet.http.HttpServlet;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.util.Date;
    
    public class CookieDemo02 extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //解决中文乱码
            req.setCharacterEncoding("utf-16");
            resp.setCharacterEncoding("utf-16");
    
            // 创建一个cookie,,名字必须要和要删除的名字一致
            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 {
            doGet(req, resp);
        }
    }
    

    web.xml

        <servlet>
            <servlet-name>CookieDemo02</servlet-name>
            <servlet-class>xyz.luck1y.servlet.CookieDemo02</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>CookieDemo02</servlet-name>
            <url-pattern>/c2</url-pattern>
        </servlet-mapping>
    
  • cookie中文乱码问题(解码和编码)

    package xyz.luck1y.servlet;
    
    import jakarta.servlet.ServletException;
    import jakarta.servlet.http.Cookie;
    import jakarta.servlet.http.HttpServlet;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.net.URLDecoder;
    import java.net.URLEncoder;
    
    /**
     * 中文数据传递
     */
    public class CookieDemo03 extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //解决中文乱码
            req.setCharacterEncoding("utf-16");
            resp.setCharacterEncoding("utf-16");
    
            // Cookie,服务器端从客户端获取
            // 返回数组,说明Cookie可能存在多个
            Cookie[] cookies = req.getCookies();
    
            PrintWriter out = resp.getWriter();
    
            // 判断Cookie是否存在
            if (cookies != null) {
                // 如果存在
                out.write("您上一次访问的时间是:");
                for (int i = 0; i < cookies.length; i++) {
                    Cookie cookie = cookies[i];
                    // 获取cookie的名字
                    if (cookie.getName().equals("name")) {
    //                    System.out.println(cookie.getValue());
                        // 解码
                        URLDecoder.decode(cookie.getValue(), "UTF-8");
                    }
                }
            } else {
                out.write("这是您第一次访问本站");
            }
            // 编码
            Cookie cookie = new Cookie("name", URLEncoder.encode("刘子", "utf-8"));
            resp.addCookie(cookie);
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
    

    web.xml

        <servlet>
            <servlet-name>CookieDemo03</servlet-name>
            <servlet-class>xyz.luck1y.servlet.CookieDemo03</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>CookieDemo03</servlet-name>
            <url-pattern>/c3</url-pattern>
        </servlet-mapping>
    

Session(重点)

什么是Session:

  • 服务器会给每一个用户(浏览器)创建一个Session对象;Session在创建的时候,会通过cookie给客户端发送属于它的SessionID
  • 一个Session独占一个浏览器,只要浏览器没有关闭,这个Session就存在;
    • 用户登录后:整个网站的所有页面都可以访问,保存用户的信息;亦或者保存购物车的信息。都是Session在起作用。

Session和Cookie的区别:

  • Cookie把用户的数据写给用户的浏览器,浏览器保存(可以保存多个)
  • Session把用户的数据写到用户独占的Session中,服务器端保存,(可以保存多个,但最好保存重要的信息,减少服务器资源的浪费)

在网站中经常使用的信息,保存在session中,方便使用。

Session存放普通键值对

package xyz.luck1y.servlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;

import java.io.IOException;

public class SessionDemo01 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 解决乱码问题
        resp.setCharacterEncoding("UTF-8");
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html; charset=utf-8");

        // 得到Session
        HttpSession session = req.getSession();
        // 给Session中存东西
        session.setAttribute("name", "刘子");

        // 获取Session的ID
        String id = session.getId();

        // 判断是不是新的Session
        if (session.isNew()) {
            resp.getWriter().write("Session创建成功,ID:" + id);
        } else {
            resp.getWriter().write("Session已经在服务器中存在了,ID:" + id);
        }

    }

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

web.xml:

    <servlet>
        <servlet-name>SessionDemo01</servlet-name>
        <servlet-class>xyz.luck1y.servlet.SessionDemo01</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>SessionDemo01</servlet-name>
        <url-pattern>/s1</url-pattern>
    </servlet-mapping>

运行结果:

session存放对象

package xyz.luck1y.servlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.*;
import xyz.luck1y.servlet.pojo.Person;

import java.io.IOException;

public class SessionDemo02 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 解决乱码问题
        resp.setCharacterEncoding("UTF-8");
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html; charset=utf-8");

        // 得到Session
        HttpSession session = req.getSession();

        Person name = (Person) session.getAttribute("name");

        System.out.println(name);
    }

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

可以看到控制台可以获取到session的值:

清除session的两种方法:

手动清除

package xyz.luck1y.servlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;

import java.io.IOException;

public class SessionDemo03 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession();
        session.removeAttribute("name");
        // 手动注销session
        session.invalidate();
    }

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

web.xml中清除session(会话自动过期)

    <!--设置session默认的失效时间-->
    <session-config>
        <!--15分钟后session自动失效,以分钟为单位-->
        <session-timeout>15</session-timeout>
    </session-config>

JSP

什么是JSP

Java Server Pages:Java服务器页面,和Servlet一样,用于开发动态web技术,假如用Servlet类实现JSP功能(心态大崩)很麻烦!

于是JSP出现,其最大的特点是:

  • 写JSP就像在写HTML
  • 区别:
    • HTML只给用户提供静态的数据;
    • JSP页面中可以嵌入Java代码,为用户提供动态数据;

JSP原理

思路:JSP到底怎么执行的?

  • 代码层面没有任何不同

  • 服务器内部:

    • Tomcat中有一个work目录,在IDEA中使用Tomcat会在IDEA的Tomcat中产生一个work目录,进去可以发现.java文件和.class文件。查找方法(IDEA导航栏–>help–>Show Log In Explorer,这个路径的上级路径中有个tomcat目录)

      也就是说:页面转换为了java程序

    • 浏览器向服务器发送请求,不管访问什么资源,其实都是在访问Servlet!

      即JSP最终会被转换为一个Java类,所以JSP本质上还是一个Servlet!

      在源码中可以看到这样的三个方法:

      //初始化
      public void _jspINit(){
          
      }
      
      //销毁
      public void _jspDestroy(){
          
      }
      
      //JSPService
      public void _jspService(HttpServletRequest request,HttpServletResponse response)
      
      1. 判断请求属于哪种类型

      2. 内置了一些对象

        final javax.servlet.jsp.PageContext pageContext;  // 页面上下文
        javax.servlet.http.HttpSession session = null;    // session
        final javax.servlet.ServletContext application;   // application
        final javax.servlet.ServletConfig config;         // 配置
        javax.servlet.jsp.JspWriter out = null;           // 输出
        final java.lang.Object page = this;               // page 当前页
        HttpServletRequest request;                       // 请求
        HttpServletResponse response;                     // 响应
        
      3. 输出页面前增加的代码

        response.setContentType("text/html");    // 设置响应的页面类型
        pageContext = _jspxFactory.getPageContext(this, request, response,
                                                  null, true, 8192, true);
        _jspx_page_context = pageContext;
        application = pageContext.getServletContext();
        config = pageContext.getServletConfig();
        session = pageContext.getSession();
        out = pageContext.getOut();
        _jspx_out = out;
        
      4. 以上的这些对象,我们可以直接在jsp页面中使用

      5. jsp中还可以直接写Java代码

        使用<% %>就可以在里面写java代码

        使用<%= %>就可以获取某个字段的值

      6. 在jsp页面中,只要是Java代码,就会原封不动的输出;

        如果是HTML代码,就会转换为以下格式输出到前端:

        out.write("<html>\r\n")
        

JSP基础语法

导包:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>xyz.luck1y</groupId>
    <artifactId>javaweb-jsp</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!--Servlet 依赖-->
    <dependency>
      <groupId>jakarta.servlet</groupId>
      <artifactId>jakarta.servlet-api</artifactId>
      <version>4.0.4</version>
      <scope>provided</scope>
    </dependency>

        <!--JSP 依赖-->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.3.3</version>
        </dependency>

        <!--JSTL表达式依赖-->
        <!-- https://mvnrepository.com/artifact/javax.servlet.jsp.jstl/jstl-api -->
        <dependency>
            <groupId>javax.servlet.jsp.jstl</groupId>
            <artifactId>jstl-api</artifactId>
            <version>1.2</version>
        </dependency>

        <!--Standard标签库依赖-->
        <!-- https://mvnrepository.com/artifact/org.apache.taglibs/taglibs-standard-impl -->
        <dependency>
            <groupId>org.apache.taglibs</groupId>
            <artifactId>taglibs-standard-impl</artifactId>
            <version>1.2.5</version>
            <scope>runtime</scope>
        </dependency>

    </dependencies>

</project>

JSP基础语法

任何语言都有自己的语法,Java有,JSP也有,作为Java技术的一种应用,它拥有一些自己扩充的语法(了解)Java中的所有语法JSP都支持。

JSP表达式

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

JSP脚本片段:

<%--JSP脚本片段--%>
<%
    int sum = 0;
    for (int i = 1; i <= 100; i++) {
        sum += i;
    }
    out.println("<h1>SUM = " + sum + "<h1>");
%>

脚本片段的再实现

<%--
  Created by IntelliJ IDEA.
  User: 刘嘉奇
  Date: 2023/7/27
  Time: 21:01
  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表达式
作用:用来将程序的输出,输出到客户端
<%= 变量或者表达式%>
--%>
<%= new java.util.Date()%>

<hr>

<%--JSP脚本片段--%>
<%
    int sum = 0;
    for (int i = 1; i <= 100; i++) {
        sum += i;
    }
    out.println("<h1>SUM = " + sum + "<h1>");
%>

<%
    int x = 10;
    out.println(x);
%>
<p>
    这是一个JSP文档
</p>
<%
    int y = 2;
    out.println(y);
%>

<%--在代码中嵌入HTML元素--%>
<%
    for (int i = 0; i < 5; i++) {
%>
    <h1>Hello,刘子  <%=i%> </h1>
<%
    }
%>
</body>
</html>

效果:

查看转换后的Java文件代码可以发现,这些所有的标签等等都被转换在_jspService这个方法中

JSP声明

<%!
    static {
    System.out.println("Loading Servlet!");
    }
    private int globalVar = 0;

    public void Liu(){
        System.out.println("进入了刘子写的方法!");
    }
%>

jsp声明:会被编译到JSP生成的Java文件的类中,而其他的(表达式、脚本片段)会被生成到_jspService方法中!

在JSP中嵌入Java代码即可

如何使JSP变得简洁?—> EL表达式

<%--使用EL表达式对上面代码进行简化--%>
<%--注意跟JQuery进行区分:
      JQuery:$()
      EL表达式:${}
--%>
  <%
    for (int i = 0; i < 5; i++) {
  %>
  <h1>Hello,World! ${pageContext.request} </h1>
  <%
    }
  %>

JSP语法总结:

<% 脚本片段 %>
<%!  JSP声明   %>
<%=   JSP表达式   %>
<%-- 注释 --%>

补充:HTML的注释:<!---->

通过在浏览器中查看源代码,可以发现HTML的注释依旧会显示出来,但是JSP的就不会被显示,提高了网页的安全性。

JSP指令

错误页面的定制

<%--
  Created by IntelliJ IDEA.
  User: 刘嘉奇
  Date: 2023/7/27
  Time: 21:42
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<%--导包--%>
<%@ page import="java.util.*" %>
<%--定制错误页面--%>
<%@ page errorPage="error/500.jsp" %>


<html>
<head>
    <title>Title</title>
</head>
<body>


<%
    int x = 1 / 0;

%>

<%
    new Date();
%>
</body>
</html>

500错误页面:

<%--
  Created by IntelliJ IDEA.
  User: 刘嘉奇
  Date: 2023/7/27
  Time: 21:43
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>500错误页面</title>
</head>
<body>
<img src="../img/500.png" alt="500" width=100% height=100%>
</body>
</html>

404错误页面:

<%--
  Created by IntelliJ IDEA.
  User: 刘嘉奇
  Date: 2023/7/27
  Time: 21:51
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>404错误页面</title>
</head>
<body>
<img src="../img/404.jpg" alt="404" width=100% height=100%>
</body>
</html>

在错误页面中使用了<img>标签来加载图片到网页上。

在web.xml文件中配置错误页面,需要重启Tomcat。

<?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_3_1.xsd"
         version="3.1"
         metadata-complete="true">
<error-page>
  <error-code>404</error-code>
  <location>/error/404.jsp</location>
</error-page>
  <error-page>
    <error-code>500</error-code>
    <location>/error/500.jsp</location>
  </error-page>
</web-app>

网页公共部分的提取

common包:一般用来存放相同的界面,比如一个网站不同界面的头部导航栏是一样的

<%--
  Created by IntelliJ IDEA.
  User: 刘嘉奇
  Date: 2023/7/27
  Time: 22:22
  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>

<%--@include指令:会将三个页面合为一,都使用out.write()转成html代码--%>
<%@include file="common/header.jsp"%>
<h1>网页主体</h1>
<%@include file="common/footer.jsp"%>
<hr>

<%--JSP标签:会将公共页面提取出来拼接起来,本质还是三个页面--%>
<jsp:include page="common/header.jsp"/>
<h1>网页主体</h1>
<jsp:include page="common/footer.jsp"/>
<%--建议使用后者,代码互不影响,比如说在公共页面有个变量i,那么使用jsp标签是不会报错的,但是使用@include会报错500,说已经定义过了--%>

</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<h1>我是header</h1>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<h1>我是footer</h1>

关于项目目录

  • main目录下只放java和resources文件夹
  • web目录下存放common(网页相同的部分)error(错误页面)img(图片文件夹)static(静态资源)
  • static目录下会有css、img、js、plugins邓哥文件夹
  • WEB-INF目录存放对用户不可见的文件,属于机密文件

Maven是约定大于配置的,所以文件目录不要乱搞,会出现问题。

JSP九大内置对象和四大域

  • PageContext :用来存东西
  • Request :用来存东西
  • Response
  • Session :用来存东西
  • Application(ServletContext):用来存东西
  • config(ServletConfig)
  • out (resp.getWrite):用来输出
  • page:几乎不用
  • Exception
<%--
  Created by IntelliJ IDEA.
  User: 刘嘉奇
  Date: 2023/7/29
  Time: 9:35
  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>

<%--内置对象--%>

<%
    // 保存的数据只在一个页面中有效
    pageContext.setAttribute("name1", "1号");
    // 保存的数据只在一次请求中有效,请求转发会携带这个数据
    request.setAttribute("name2", "2号");
    // 保存的数据只在一次会话中有效,从打开浏览器到关闭浏览器
    session.setAttribute("name3", "3号");
    // 保存的数据在服务器中有效,从打开服务器到关闭服务器
    application.setAttribute("name4", "4号");
%>

<%--脚本片段中的代码,会被原封不动的生成到 jsp.java中国
要求:这里的代码,必须保证Java语法的正确性
--%>
<%
    // 通过pageContext取出我们保存的值,我们通过寻找的方式来
    // 从底层到高层(作用域)
    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");
%>

<%--使用EL表达式输出 ${} --%>
<h1>取出的值为:</h1>
<h3>${name1}</h3>
<h3>${name2}</h3>
<h3>${name3}</h3>
<h3>${name4}</h3>
<%--使用EL表达式会自动过滤掉不存在的--%>
<h3>${name5}</h3>
<%--使用jsp表达式会把不存在的显示为null--%>
<h3><%=name5%></h3>

</body>
</html>

在另一个页面中获取同样的内容,作用域不同导致获取不到

<%--
  Created by IntelliJ IDEA.
  User: 刘嘉奇
  Date: 2023/7/29
  Time: 9:35
  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.java中国
要求:这里的代码,必须保证Java语法的正确性
--%>
<%
    // 通过pageContext取出我们保存的值,我们通过寻找的方式来
    // 从底层到高层(作用域)
    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");
%>

<%--使用EL表达式输出 ${} --%>
<h1>取出的值为:</h1>
<h3>${name1}</h3>
<h3>${name2}</h3>
<h3>${name3}</h3>
<h3>${name4}</h3>
<%--使用EL表达式会自动过滤掉不存在的--%>
<h3>${name5}</h3>
<%--使用jsp表达式会把不存在的显示为null--%>
<h3><%=name5%></h3>

</body>
</html>

部分源码分析

<%--
  Created by IntelliJ IDEA.
  User: 刘嘉奇
  Date: 2023/7/29
  Time: 9:58
  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>
<%--
//scope:作用域

public static final int PAGE_SCOPE    = 1;
public static final int REQUEST_SCOPE  = 2;
public static final int SESSION_SCOPE  = 3;
public static final int APPLICATION_SCOPE  = 4;


public void setAttribute(String name, Object attribute, int scope) {
        switch(scope) {
        case 1:
            this.mPage.put(name, attribute);
            break;
        case 2:
            this.mRequest.put(name, attribute);
            break;
        case 3:
            this.mSession.put(name, attribute);
            break;
        case 4:
            this.mApp.put(name, attribute);
            break;
        default:
            throw new IllegalArgumentException("Bad scope " + scope);
        }
    }
--%>

<%
    // 可以手动设置作用域等价于:session.setAttribute();
    pageContext.setAttribute("hello1","hello1",PageContext.SESSION_SCOPE);
%>

</body>
</html>

以下这个查找过程类似于:双亲委派机制中类的加载过程

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

<%
    // 访问该页面地址栏不会有变化(转发)
    pageContext.forward("/index.jsp");
    // 后端实现
//    request.getRequestDispatcher("/index.jsp").forward(request, response);
%>

</body>
</html>

如果在存放数据的页面,存完数据后转发到其他取数据的页面,那么request存的数据也能取到,因为转发是一次请求

分场景理解

  • request:客户端向服务器发送请求,产生的数据,用户看完就没用了。
  • session:客户端向服务器发送请求,产生的数据,用户用完一会可能还会用。
  • application:客户端向服务器发送请求,产生的数据,一个用户用完了,其他用户还可以使用。

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

EL表达式${},记得导包

  • 获取数据
  • 执行运算
  • 获取web开发的常用对象
  • 调用Java方法
      <!--JSTL表达式依赖-->
      <!-- https://mvnrepository.com/artifact/javax.servlet.jsp.jstl/jstl-api -->
      <dependency>
        <groupId>javax.servlet.jsp.jstl</groupId>
        <artifactId>jstl-api</artifactId>
        <version>1.2</version>
      </dependency>

      <!--Standard标签库依赖-->
      <!-- https://mvnrepository.com/artifact/org.apache.taglibs/taglibs-standard-impl -->
      <dependency>
        <groupId>org.apache.taglibs</groupId>
        <artifactId>taglibs-standard-impl</artifactId>
        <version>1.2.5</version>
        <scope>runtime</scope>
    </dependency>

JSP标签

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

<jsp:forward page="/jspTag2.jsp">
  <jsp:param name="name" value="liu"/>
  <jsp:param name="age" value="12"/>
</jsp:forward>

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

<h1>2</h1>

<%--取出参数--%>
名字:<%=request.getParameter("name")%>
年龄:<%=request.getParameter("age")%>

</body>
</html>

效果:

JSTL表达式:

JSTL标签库的使用就是为了弥补HTML标签的不足;它自定义了许多的标签,可以供我们使用,标签的功能和Java代码一样~

核心标签:

  • 引入核心标签库

    <%--引入核心标签库才能够使用核心标签 core--%>
    <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    

    引入库后还要在pom.xml中导入依赖

          <dependency>
              <groupId>org.glassfish.web</groupId>
              <artifactId>jakarta.servlet.jsp.jstl</artifactId>
              <version>2.0.0</version>
          </dependency>
          <!--Standard标签库依赖-->
          <!-- https://mvnrepository.com/artifact/org.apache.taglibs/taglibs-standard-impl -->
          <dependency>
            <groupId>org.apache.taglibs</groupId>
            <artifactId>taglibs-standard-impl</artifactId>
            <version>1.2.5</version>
            <scope>runtime</scope>
        </dependency>
    

    经测试后,发现jakarta.servlet.jsp.jstl依赖用这个版本才行,maven仓库下的最新版本不行,可能是因为我用的Tomcat10,从一开始的Servlet到现在一直有各种各样的问题,Tomcat10一生之敌。

    这两个依赖被我注掉了,不注掉就跑不出来!

    测试:

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%--引入核心标签库才能够使用核心标签 core--%>
    <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
    
    
    <%--if测试--%>
    <hr>
    <h1>哼哼终于跑出来了</h1>
    <form action="coreif.jsp" method="get">
        <%--
        EL表达式获取表单中的数据
        ${param,参数名}
        --%>
        <input type="text" name="username" value="${param.username}">
        <input type="submit" value="登录">
    </form>
    
    
    </body>
    </html>
    

    结果:

  • 核心标签:

  • 分别测试

    • if标签

      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <%--引入核心标签库才能够使用核心标签 core--%>
      <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
      <html>
      <head>
          <title>Title</title>
      </head>
      <body>
      
      
      <%--if测试--%>
      <hr>
      <h1>哼哼终于跑出来了</h1>
      <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}">
      
      </c:out>
      
      </body>
      </html>
      

      测试结果:

      输入 admin 登录后:

    • when标签:

      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
      <html>
      <head>
          <title>Title</title>
      </head>
      <body>
      
      <%--定义一个变量score,值为85--%>
      <c:set var="score" value="85"/>
      <%--按照先后顺序判断,满足就会跳出--%>
      <c:choose>
        <c:when test="${score>=90}">
          你的成绩为:优秀
        </c:when>
        <c:when test="${score>=80}">
          你的成绩为:良好
        </c:when>
        <c:when test="${score>=70}">
          你的成绩为:一般
        </c:when>
      </c:choose>
      
      </body>
      </html>
      

      测试结果:

    • for-each标签:

      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
      <%@ page import="java.util.ArrayList" %>
      <html>
      <head>
          <title>Title</title>
      </head>
      <body>
      
      <%--要遍历的对象:items--%>
      <%--遍历出来的对象:var--%>
      <%--从哪里开始:begin--%>
      <%--到哪里结束:end--%>
      <%--步长:step--%>
      
      <%
          ArrayList<String> people = new ArrayList<>();
          people.add(0, "张三");
          people.add(1, "李四");
          people.add(2, "王五");
          people.add(3, "赵六");
          people.add(4, "田七");
          request.setAttribute("list", people);
      %>
      <c:forEach var="people" items="${list}">
          <c:out value="${people}"/> <br>
      </c:forEach>
      
      <hr/>
      
      <c:forEach var="people" items="${list}" begin="1" end="3" step="2">
          <c:out value="${people}"/> <br>
      </c:forEach>
      
      </body>
      </html>
      

      测试结果:

    • 格式化标签:了解

    • SQL标签:了解

    • XML标签:了解

JavaBean

通常称JavaBean为实体类

JavaBean有特定的写法:

  • 必须有一个无参构造
  • 属性必须私有化
  • 必须有对应的get/set方法
  • 一般用来和数据库的字段做映射 ORM

ORM:对象关系映射

  • 表 — > 类
  • 字段 — > 属性
  • 行记录 — > 对象
idnameageaddress
1张三15南京
2李四18上海
3王五21广州
class People{
    private int id;
    private String name;
    private int age;
    private String address;
}
class A{
    new People(1,"张三",15,"北京");
    ...
}

javabean演示:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="xyz.luck1y.pojo.People" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

<%
    // 和下面的写法是等价的
//    People people = new People();
//    people.setAddress();
//    people.setId();
//    people.setAge();
//    people.setName();
%>

<jsp:useBean id="people" class="xyz.luck1y.pojo.People" scope="page"/>

<jsp:setProperty name="people" property="address" value="南京"/>
<jsp:setProperty name="people" property="id" value="1"/>
<jsp:setProperty name="people" property="age" value="21"/>
<jsp:setProperty name="people" property="name" value="刘子"/>

<jsp:getProperty name="people" property="name"/>
<jsp:getProperty name="people" property="id"/>
<jsp:getProperty name="people" property="age"/>
<jsp:getProperty name="people" property="address"/>

</body>
</html>

结果:

这里有个小插曲,运行Tomcat时突然又给我报错:

不再支持源选项6。请使用7或更高版本

在网上冲浪一番后,得到解决方案:

  • 先检查Project Structures中,JDK版本是否是自己的JDK版本

  • 然后看Settings中JDK版本

  • 还是不行就将以下内容添加到pom.xml文件中properties标签内,我的JDK版本是17.0.6,所以根据自己JDK的版本进行相应的更改。

        <properties>
            <!--   以下内容添加至properties标签内    -->
    
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
            <java.version>17.0.6</java.version>
            <maven.compiler.source>17.0.6</maven.compiler.source>
            <maven.compiler.target>17.0.6</maven.compiler.target>
        </properties>
    
  • 还有一种就是修改maven配置文件setting.xml,在setting.xml中添加以下信息到profiles标签中:

    也是根据自己JDK版本进行相应的更改。

    <profile>  
         <id>jdk-17.0.6</id>  
         <activation>  
             <activeByDefault>true</activeByDefault>  
             <jdk>17.0.6</jdk>  
         </activation>
         <properties>
             <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
             <maven.compiler.source>17.0.6</maven.compiler.source>  
             <maven.compiler.target>17.0.6</maven.compiler.target>   
         </properties>   
    </profile>
    
  • 顺便把setting.xml里面这个部分也改一下吧,感觉统一版本应该没什么问题

        <profile>
          <id>jdk-17.0.6</id>
    
          <activation>
            <jdk>17.0.6</jdk>
          </activation>
    
          <repositories>
            <repository>
              <id>jdk14</id>
              <name>Repository for JDK 17.0.6 builds</name>
              <url>http://www.myhost.com/maven/jdk14</url>
              <layout>default</layout>
              <snapshotPolicy>always</snapshotPolicy>
            </repository>
          </repositories>
        </profile>
    

MVC三层架构

什么是MVC:Model,View,Controller;模型,视图,控制器。

早些年的架构

用户直接访问控制层,控制层直接操作数据库;

Servlet–>CRUD–>数据库

这样程序很臃肿,不利于维护

  • Servlet的代码中,处理请求,响应,视图跳转,处理JDBC,处理业务代码,处理逻辑代码

在架构的思想中,没有什么是多加一层解决不了的一层不够再加一层JDBC也是如此!

MVC三层架构

Model:

  • 业务处理:业务逻辑(Service)
  • 数据持久层:CRUD(Dao)

View:

  • 展示数据
  • 提供链接发起Servlet请求(a,form,img…赋值一些herf属性就能跳转啦)

Controller(Servlet):

  • 接受用户的请求:reques,请求参数,session信息
  • 交给业务层处理对应的代码
  • 控制视图的跳转

登陆 --> 接收用户的登陆请求 --> 处理用户的请求(获取用户登陆的参数,username、password) --> 交给业务层处理登陆业务(判断用户名和密码是否正确)–> Dao层查询用户名和密码信息是否正确 --> 数据库

Filter过滤器(重点)

Filter:过滤器,用来过滤网站的数据,后续的一些框架都会用到

  • 处理中文乱码
  • 登录验证

Filter开发步骤

  1. 导入相关依赖包

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>xyz.luck1y</groupId>
        <artifactId>javaweb-filter</artifactId>
        <packaging>war</packaging>
        <version>1.0-SNAPSHOT</version>
        <name>javaweb-filter Maven Webapp</name>
        <url>http://maven.apache.org</url>
        <dependencies>
            <dependency>
                <groupId>jakarta.servlet</groupId>
                <artifactId>jakarta.servlet-api</artifactId>
                <version>6.0.0</version>
            </dependency>
            <dependency>
                <groupId>javax.servlet.jsp.jstl</groupId>
                <artifactId>jstl-api</artifactId>
                <version>1.2</version>
            </dependency>
            <dependency>
                <groupId>org.glassfish.web</groupId>
                <artifactId>jakarta.servlet.jsp.jstl</artifactId>
                <version>2.0.0</version>
            </dependency>
            <!--Standard标签库依赖-->
            <!-- https://mvnrepository.com/artifact/org.apache.taglibs/taglibs-standard-impl -->
            <dependency>
                <groupId>org.apache.taglibs</groupId>
                <artifactId>taglibs-standard-impl</artifactId>
                <version>1.2.5</version>
                <scope>runtime</scope>
            </dependency>
            <!--连接数据库-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.47</version>
            </dependency>
    
        </dependencies>
        <properties>
            <!--   以下内容添加至properties标签内    -->
    
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
            <java.version>17.0.6</java.version>
            <maven.compiler.source>17.0.6</maven.compiler.source>
            <maven.compiler.target>17.0.6</maven.compiler.target>
        </properties>
        <build>
            <finalName>javaweb-filter</finalName>
        </build>
    </project>
    
  2. 编写过滤器,实现Filter接口,重写方法。

    package xyz.luck1y.filter;
    
    import jakarta.servlet.*;
    import java.io.IOException;
    
    public class CharacterEncodingFilter implements Filter {
        // 初始化
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            System.out.println("过滤器初始化:");
        }
    
        /*
        chain:链
        1.过滤器中的所有代码,在过滤特定请求的时候都会执行
        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执行后......");
        }
    
        // 销毁: web服务关闭的时候,过滤会销毁
        @Override
        public void destroy() {
            System.out.println("CharacterEncodingFilter已销毁");
        }
    }
    
  3. 配置web.xml中的filter

    <?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_3_1.xsd"
             version="3.1"
             metadata-complete="true">
        <servlet>
            <servlet-name>ShowServlet</servlet-name>
            <servlet-class>xyz.luck1y.servlet.ShowServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>ShowServlet</servlet-name>
            <url-pattern>/show</url-pattern>
        </servlet-mapping>
    
        <filter>
            <filter-name>CharacterEncodingFilter</filter-name>
            <filter-class>xyz.luck1y.filter.CharacterEncodingFilter</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>CharacterEncodingFilter</filter-name>
            <!--只要是 /servlet 的任何请求,都会经过这个过滤器-->
            <url-pattern>/servlet/*</url-pattern>
        </filter-mapping>
    </web-app>
    
  4. 测试结果:

    • 不经过过滤器:

    • 经过过滤器:

      过滤路径每请求一次,都会执行一次过滤器中的代码!

    • 销毁:

      web服务器关闭的时候,过滤会销毁

LIstener监听器

实现一个监听器的接口,有N种,很多~

  1. 编写一个监听器,实现监听器接口

    package xyz.luck1y.listener;
    
    import jakarta.servlet.ServletContext;
    import jakarta.servlet.http.HttpSessionEvent;
    import jakarta.servlet.http.HttpSessionListener;
    
    /**
     * 统计网站在线人数:统计session
     */
    public class OnlineCountListener implements HttpSessionListener {
        // 创建session监听,一举一动
        // 一旦创建一个session,就会触发一次这个事件
        @Override
        public void sessionCreated(HttpSessionEvent se) {
            ServletContext ctx = se.getSession().getServletContext();
            System.out.println(se.getSession().getId());
            Integer onlineCount = (Integer) ctx.getAttribute("OnlineCount");
            if (onlineCount == null) {
                onlineCount = new Integer(1);
            } else {
                int count = onlineCount.intValue();
                onlineCount = new Integer(count + 1);
            }
            ctx.setAttribute("OnlineCount", onlineCount);
        }
    
        // 销毁session监听
        // 一旦销毁session,就会触发一次这个事件
        @Override
        public void sessionDestroyed(HttpSessionEvent se) {
            ServletContext ctx = se.getSession().getServletContext();
            System.out.println(se.getSession().getId());
            Integer onlineCount = (Integer) ctx.getAttribute("OnlineCount");
            if (onlineCount == null) {
                onlineCount = new Integer(0);
            } else {
                int count = onlineCount.intValue();
                onlineCount = new Integer(count - 1);
            }
            ctx.setAttribute("OnlineCount", onlineCount);
        }
    
        /*
        Session销毁:
        1.手动销毁
        se.getSession().invalidate();
        2.自动销毁
            <!--自动销毁-->
        <session-config>
            <session-timeout>1</session-timeout>
        </session-config>
         */
    }
    
  2. 配置监听器,在web.xml中

        <!--注册监听器-->
        <listener>
            <listener-class>xyz.luck1y.listener.OnlineCountListener</listener-class>
        </listener>
    
        <!--自动销毁-->
        <session-config>
            <session-timeout>1</session-timeout>
        </session-config>
    

    可以配置自动销毁

  3. 看情况是否使用,一般不怎么用。

过滤器、监听器常见应用

监听器:GUI编程中经常使用

package xyz.luck1y.listener;

import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;

public class TestPane1 {
    public static void main(String[] args) {
        // 新建一个窗体
        Frame frame = new Frame("为爱发电!");
        // 面板
        Panel panel = new Panel(null);
        // 设置窗体的布局
        frame.setLayout(null);

        frame.setBounds(300, 300, 500, 500);
        // 设置背景颜色
        frame.setBackground(new Color(0, 0, 255));

        panel.setBounds(50, 50, 300, 300);
        panel.setBackground(new Color(0, 255, 0));

        frame.add(panel);
        frame.setVisible(true);

        // 监听事件,监听关闭事件
        frame.addWindowListener(new WindowListener() {
            @Override
            public void windowOpened(WindowEvent e) {
                System.out.println("打开");
            }

            @Override
            public void windowClosing(WindowEvent e) {
                System.out.println("关闭中");
                // 0和其他非0的int参数的区别:0是正常终止,其他为异常终止,根据情况使用,结合 try catch
                System.exit(1);
            }

            @Override
            public void windowClosed(WindowEvent e) {
                System.out.println("已关闭");
            }

            @Override
            public void windowIconified(WindowEvent e) {

            }

            @Override
            public void windowDeiconified(WindowEvent e) {

            }

            @Override
            public void windowActivated(WindowEvent e) {
                System.out.println("已激活");
            }

            @Override
            public void windowDeactivated(WindowEvent e) {
                System.out.println("未激活");
            }
        });

        // 适配器模式:只需要重写一个子类即可
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
    }
}

实现登录监听

场景:用户登录之后才能进入主页!用户注销之后就不能进入主页了。

  1. 登录页面

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>登陆页</title>
    </head>
    <body>
    
    <h1>登陆</h1>
    <form action="/servlet/login" method="post">
        <input type="text" name="username">
        <input type="submit">
    </form>
    
    
    </body>
    </html>
    
  2. 主页(登录成功):

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>主页</title>
    </head>
    <body>
    
    <%-- 就在jsp这实现了,使用过滤器
    <%
        Object userSession = request.getSession().getAttribute("USER_SESSION");
        if (userSession == null) {
            response.sendRedirect("/login.jsp");
        }
    %>
    --%>
    
    <h1>主页</h1>
    
    <p>
        <a href="/servlet/logout">注销</a>
    </p>
    </body>
    </html>
    
  3. 错误页面:

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>错误</title>
    </head>
    <body>
    
    <h1>错误</h1>
    <h3>没有权限,用户名错误</h3>
    
    <a href="login.jsp">返回登录页面</a>
    </body>
    </html>
    
  4. 登录Servlet:

    package xyz.luck1y.servlet;
    
    import jakarta.servlet.ServletException;
    import jakarta.servlet.http.HttpServlet;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    
    import java.io.IOException;
    
    public class LoginServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // 获取前端请求的参数
            String username = req.getParameter("username");
            if (username.equals("admin")) {
                // 登陆成功
                req.getSession().setAttribute("USER_SESSION", req.getSession().getId());
                resp.sendRedirect("/sys/success.jsp");
            } else {
                // 登陆失败
                resp.sendRedirect("/error.jsp");
            }
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
    
  5. 注销Servlet:

    package xyz.luck1y.servlet;
    
    import jakarta.servlet.ServletException;
    import jakarta.servlet.http.HttpServlet;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    
    import java.io.IOException;
    
    public class LogoutServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
            Object user_session = req.getSession().getAttribute("USER_SESSION");
    
            if (user_session != null) {
                req.getSession().removeAttribute("USER_SESSION");
                resp.sendRedirect("/login.jsp");
            } else {
                resp.sendRedirect("/login.jsp");
            }
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
    
  6. 过滤器:

    package xyz.luck1y.filter;
    
    import jakarta.servlet.*;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    
    import java.io.IOException;
    
    public class SysFilter 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;
    
            if (request.getSession().getAttribute("USER_SESSION") == null) {
                response.sendRedirect("/login.jsp");
            }
    
            filterChain.doFilter(request, response);
        }
    
        @Override
        public void destroy() {
    
        }
    }
    
  7. 常量类:

    package xyz.luck1y.util;
    
    public class Constant {
        // 这样写后面需要改动时直接到这改,一次即可全部修改
        public static final String USER_SESSION = "USER_SESSION";
    }
    
  8. web.xml:

        <servlet>
            <servlet-name>LoginServlet</servlet-name>
            <servlet-class>xyz.luck1y.servlet.LoginServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>LoginServlet</servlet-name>
            <url-pattern>/servlet/login</url-pattern>
        </servlet-mapping>
        
        <servlet>
            <servlet-name>LogoutServlet</servlet-name>
            <servlet-class>xyz.luck1y.servlet.LogoutServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>LogoutServlet</servlet-name>
            <url-pattern>/servlet/logout</url-pattern>
        </servlet-mapping>
        
        <filter>
            <filter-name>SysFilter</filter-name>
            <filter-class>xyz.luck1y.filter.SysFilter</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>SysFilter</filter-name>
            <url-pattern>/sys/*</url-pattern>
        </filter-mapping>
    

总结:

  1. 当一个或多个常量要多次用到的时候,可以建立一个常量类,方便后期修改,不然还要一次次修改。

JDBC的复习与回顾

具体可以回顾MySQL笔记

JDBC:Java DataBase Connection;Java连接数据库

需要jar包的支持:

  • java.sql
  • javax.sql
  • mysql-connecter-java, (必须要导入)

环境搭建:

  1. 创建数据库表

    CREATE TABLE users(
    id INT(10) PRIMARY KEY NOT NULL AUTO_INCREMENT,
    `name` VARCHAR(40) NOT NULL,
    `password` VARCHAR(40) NOT NULL,
    email VARCHAR(60),
    birthday DATE
    )ENGINE=INNODB CHARSET=utf8;
    
    INSERT INTO users(id, `name`, `password`, email, birthday)
    VALUES (1,'张三','123456','zs@123','2001-10-5');
    
    INSERT INTO users(id, `name`, `password`, email, birthday)
    VALUES (2,'李四','123456','ls@123','2001-01-5');
    
    INSERT INTO users(id, `name`, `password`, email, birthday)
    VALUES (3,'王五','123456','ww@123','2001-02-15');
    
    SELECT * FROM users;
    
  2. 导入包

    1. 连接数据库

    2. 勾选数据库

      看见表了说明连接成功

    3. web.xml导入依赖包

      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
          <modelVersion>4.0.0</modelVersion>
      
          <groupId>xyz.luck1y</groupId>
          <artifactId>javaweb-jdbc</artifactId>
          <version>1.0-SNAPSHOT</version>
      
          <dependencies>
              <!--mysql的驱动-->
              <dependency>
                  <groupId>mysql</groupId>
                  <artifactId>mysql-connector-java</artifactId>
                  <version>5.1.47</version>
              </dependency>
          </dependencies>
      
          <properties>
              <maven.compiler.source>17</maven.compiler.source>
              <maven.compiler.target>17</maven.compiler.target>
              <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
          </properties>
      
      </project>
      
  3. 编写JDBC程序

    普通Statement对象:

    package xyz.luck1y.test;
    
    import java.sql.*;
    
    public class TestJdbc {
        public static void main(String[] args) throws ClassNotFoundException, SQLException {
            // 配置信息
            // useUnicode=true&characterEncoding=utf8 解决中文乱码
            String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC";
            String username = "root";
            String password = "123456";
    
            // 1.加载驱动
            Class.forName("com.mysql.jdbc.Driver");
            // 2.连接数据库,conn可以理解为数据库;conn代表数据库
            Connection conn = DriverManager.getConnection(url, username, password);
            // 3.向数据库发送sql的对象
            Statement statement = conn.createStatement();
            // 4.编写SQL
            String sql = "select * from users;";
    //        String sql = "delete from users where id = 4;";
    
            // 受影响的行数,增删改都使用executeUpdate即可
    //        int i = statement.executeUpdate(sql);
    
            // 5.执行查询SQL,返回结果集
            ResultSet resultSet = statement.executeQuery(sql);
            while (resultSet.next()) {
                System.out.println("id:" + resultSet.getObject("id"));
                System.out.println("name:" + resultSet.getObject("name"));
                System.out.println("password:" + resultSet.getObject("password"));
                System.out.println("email:" + resultSet.getObject("email"));
                System.out.println("birthday:" + resultSet.getObject("birthday"));
            }
    
            // 6.关闭连接,释放资源,先开后关
            resultSet.close();
            statement.close();
            conn.close();
        }
    }
    
    

    JDBC固定步骤:

    1. 加载驱动
    2. 连接数据库
    3. 向数据库发送SQL的对象Statement:CRUD
    4. 编写SQL(根据业务,编写不同的SQL)
    5. 执行SQL
    6. 关闭连接

    预编译PrepareStatement对象(防止SQL注入):

    package xyz.luck1y.test;
    
    import java.sql.*;
    
    public class TestJdbc2 {
        public static void main(String[] args) throws Exception{
            // 配置信息
            // useUnicode=true&characterEncoding=utf8 解决中文乱码
            String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC";
            String username = "root";
            String password = "123456";
    
            // 1.加载驱动
            Class.forName("com.mysql.jdbc.Driver");
            // 2.连接数据库,conn可以理解为数据库;conn代表数据库
            Connection conn = DriverManager.getConnection(url, username, password);
            // 3.编写SQL
            String sql = "insert into users(id, name, password, email, birthday) values (?,?,?,?,?);";
            // 4.预编译
            PreparedStatement preparedStatement = conn.prepareStatement(sql);
            // 给第一个占位符 ? 的值赋值为 1
            preparedStatement.setInt(1, 4);
            // 给第二个占位符 ? 的值赋值为 1
            preparedStatement.setString(2, "刘子");
            // 给第三个占位符 ? 的值赋值为 1
            preparedStatement.setString(3, "123456");
            // 给第四个占位符 ? 的值赋值为 1
            preparedStatement.setString(4, "1231231@qq.com");
            // 给第五个占位符 ? 的值赋值为 1
            preparedStatement.setDate(5, new Date(new java.util.Date().getTime()));
    
            // 5.执行SQL
            int i = preparedStatement.executeUpdate();
            if (i > 0) {
                System.out.println("插入成功!");
            }
    
            // 6.关闭连接,释放资源,先开后关
            preparedStatement.close();
            conn.close();
        }
    }
    

事务

要么都成功,要么都失败~

ACID原则:保证数据的安全

  • 开启事务
  • 事务提交
  • 事务回滚
  • 关闭事务

经典转账例子,查看MySQL事务笔记即可~

事务、索引、权限管理和备份_Luck1y的博客-CSDN博客

单元测试Junit

如果需要测试一个方法,我们一般都要在main方法里新建这个方法的类的一个对象,然后用这个对象调用要测试的方法。

  1. 导入junit依赖:

    <!-- junit -->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.13.1</version>
            </dependency>
    
  2. 单元测试:

    package xyz.luck1y.test;
    
    import org.junit.Test;
    
    public class TestJdbc3 {
        // 不需要写main方法就可以执行测试结果
        @Test
        public void test() {
            // 测试内容
            System.out.println("Hello");
        }
    }
    
  3. 注解内容:

    package org.junit;
    
    import org.junit.function.ThrowingRunnable;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    // 声明运行时有效
    @Retention(RetentionPolicy.RUNTIME)
    // 声明该注解只在方法上有效
    @Target({ElementType.METHOD})
    public @interface Test {
    
        /**
         * Default empty exception.
         */
        static class None extends Throwable {
            private static final long serialVersionUID = 1L;
    
            private None() {
            }
        }
    
        Class<? extends Throwable> expected() default None.class;
       
        long timeout() default 0L;
    }
    
  4. 失败时状态:

  5. 成功时状态:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Luck1y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值