22:JavaWeb 详解 || 是值得翻越的高山也是即将扬起的帆
JavaWeb详解
一、基本概念
1. 前言
web开发:
- web:网页的意思,www.baidu.com,可以从网页上拿到资源
- 静态web
- html,css
- 提供给所有人看的数据始终不会发生变化
- 动态web
- 淘宝 几乎所有的网站
- 提供给所有人看的数据始终会发生变化,每个人在不同的时间,不同的地点看到的信息各不相同。
- 技术栈:Servlet/JSP,ASP,PHP
在Java中,动态web资源开发的技术统称为JavaWeb。
2. Web应用程序
web应用程序:可以提供浏览器访问的程序;
- a.html、b.html......多个web资源,这些web资源可以被外界访问,对外界提供服务;
- 能访问到的任何一个页面或者资源,都存在于这个世界的某一个角落的计算机上。
- URL
- 这个统一的web资源会被放在同一个文件夹下,web应用程序 --> Tomcat:服务器
- 一个web应用由多部分组成(静态web,动态web)
- html,css,js
- jsp,servlet
- Java程序
- jar包
- 配置文件(Properties)
web应用程序编写完毕之后,若想提供给外界访问:需要一个服务器来统一管理;
3. 静态web
-
*.htm,*.html,这些都是网页的后缀,如果服务器上一直存在这些东西,我们就可以直接通过网络进行读取。

-
静态web存在的缺点
- web页面无法动态更新,所有用户看到的都是同一个页面
- 轮播图,点击特效:伪动态
- JavaScript【实际开发中,用的比较多】
- VBScript
- 它无法和数据库交互(数据无法持久化,用户无法交互)
- web页面无法动态更新,所有用户看到的都是同一个页面
4. 动态web
页面会动态展示:web页面的展示效果因人而异;

缺点:
- 假如服务器的动态web资源出现了错误,需要重新编写后台程序,重新发布:
- 重新发布意味着停机维护
优点:
-
web页面可以动态更新,所有用户看到的都不是同一个页面
-
它可以和数据库交互(数据持久化:注册,商品信息,用户信息等等)

新手村 --> 魔鬼训练(分析原理,看源码)--> PK场
二、web服务器
1. 技术讲解
ASP:
-
微软:国内最早流行的就是ASP;
-
在html中嵌入了VB的脚本,ASP+COM;
-
在ASP开发中,基本上一个页面要嵌入大量的业务逻辑代码,页面混乱
<h1> <h1> <h1> <h1> <% System.out.println("Hello") %> </h1> </h1> </h1> -
维护成本高!
-
C#
-
IIS
PHP:
- PHP开发速度很快,功能很强大。跨平台,代码很简单(70%的网站)
- 无法承载大访问量的情况,有局限性
JSP/Servlet:
B/S:浏览器和服务器
C/S:客户端和服务器
-
sun公司主推的B/S架构
-
基于Java语言的(所有的大公司或者一些开源的组件,都是Java写的)
-
可以承载三高(高并发,高可用,高性能)问题带来的影响
-
语法像ASP,方便ASP -->转为 JSP ,加强市场强度
2. web服务器
服务器是一种被动的操作,用来处理用户的一些请求和给用户一些响应信息;
ISS:
- 微软的,ASP,Windows中自带
Tomcat:

-
Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,最新的Servlet 和JSP 规范总是能在Tomcat 中得到体现。因为Tomcat 技术先进、性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为比较流行的Web 应用服务器。
Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。对于一个初学者来说,它是最佳选择。
Tomcat 实际上运行JSP 页面和Servlet。Tomcat最新版本为10.0.5。
......
工作3-5年之后,可以尝试手写Tomcat服务器;
下载Tomcat:
- 安装or解压
- 了解配置文件及目录结构
- 这个东西的作用
三、Tomcat
1. 安装Tomcat
tomcat官网:https://tomcat.apache.org/
-
下载:

-
解压:

2. Tomcat启动和配置
-
文件夹的作用

-
启动和关闭Tomcat
-
启动:双击bin目录下的startup.bat

启动之后,浏览器访问localhost:8080,看是否有tomcat界面

-
关闭:双击bin目录下的shutdown.bat;或者直接关掉刚才启动时候打开的黑窗口
-
-
可能遇到的问题:
- Java环境变量没有配置(Tomcat依赖于Java)
- 闪退问题:需要配置兼容性
- 乱码问题,修改配置文件
3. 配置

在这个文件里,可以配置启动的端口号,可以配置主机的名称,可以配置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"> -
面试题:
请你谈一谈网站是如何进行访问的:
超详细过程描述:点击访问
-
输入一个域名,点击回车,发送http请求
-
查找域名对应的IP地址
-
检查本机C:\Windows\System32\drivers\etc\hosts配置文件下或者本机的DNS(Domain Name System,域名系统)缓存中找有没有这个域名的映射
有的话:直接返回对应的ip地址,相当于拿到了访问web应用程序的钥匙

-
如果在本地的 hosts 文件没有能够找到对应的 ip 地址,浏览器会发出一个 DNS请求到本地DNS服务器 。本地DNS服务器一般都是你的网络接入服务器商提供,比如中国电信,中国移动。
查询你输入的网址的DNS请求到达本地DNS服务器之后,本地DNS服务器会首先查询它的缓存记录,如果缓存中有此条记录,就可以直接返回结果,此过程是递归的方式进行查询。如果没有,本地DNS服务器还要向DNS根服务器进行查询。
DNS根服务器没有记录具体的域名和IP地址的对应关系,于是告诉本地DNS服务器,你可以到域服务器上去继续查询,并给出域服务器的地址。这种过程是迭代的过程。
本地DNS服务器继续向域服务器发出请求,如果请求的对象是.com域服务器。.com域服务器收到请求之后,也不会直接返回域名和IP地址的对应关系,而是告诉本地DNS服务器,你的域名的解析服务器的地址。
最后,本地DNS服务器向域名的解析服务器发出请求,这时就能收到一个域名和IP地址对应关系,本地DNS服务器不仅要把IP地址返回给用户电脑,还要把这个对应关系保存在缓存中,以备下次别的用户查询时,可以直接返回结果,加快网络访问。如果url里不包含端口号,则会使用该协议的默认端口号。
-
-
浏览器访问这个ip地址
-
ip地址所在的服务器发送响应页面给客户端
-
-
可选项(可以配置一个tomcat的环境变量)
4. 发布一个web网站
先简单模仿一下,在webapps里复制一下ROOT文件,修改为项目名:Laxsilence
将不需要的东西都删掉,只保留WEB-INF文件夹
修改WEB-INF文件夹中的web.xml文件
-
内容
<?xml version="1.0" encoding="UTF-8"?> <!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <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>
回到webapps目录下,打开复制的项目文件夹,新建index.html
浏览器输入:localhost:8080/项目名/index.html 就可以开始访问了(index.html)可以省略,默认就会走这个页面
补充:tomcat自带的几个web应用也可以去访问

网站应该有的结构:
--webapps:Tomcat服务器的web目录
-Laxsilence:网站的目录名
-WEB-INF
-classes:java程序
-lib:web应用所依赖的jar包
-web.xml:网站的配置文件
-index.html 默认的首页
-static
-css
-js
-img
-...
Http协议:面试
Maven:构建工具
- Maven安装包
Servlet入门
- Hello World
- Servlet配置
- 原理
四、HTTP
1. 什么是http协议
超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在TCP之上。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。请求和响应消息的头以ASCII形式给出;而消息内容则具有一个类似MIME的格式。
- 文本:数字,字符串,~...
- 超文本:图片、音乐、视频...
- 端口:80
- Https:s代表安全的,默认端口443
2. 两个时代
- http1.0
- http/1.0:客户端可以与web服务器连接后,只能获得一个web资源
- http2.0
- http/1.1:客户端与web服务器连接后,可以获得多个web资源
3. Http请求
-
客户端 发请求 服务器
-
以百度为例子:
Request URL: https://www.baidu.com/ //请求地址 Request Method: GET //get 或 post Status Code: 200 OK //状态码:200 Remote Address: 180.101.49.11:443 //远程地址:ip Referrer Policy: strict-origin-when-cross-originAccept: 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.9 Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 Cache-Control: max-age=0 Connection: keep-alive Cookie: PSTM=1615797992; BIDUPSID=A177B44B5EBFF19354E57917AFCCD76D; BD_UPN=12314753; __yjs_duid=1_de0756165ddbe7438e37630d7ede61611618398954422; MCITY=-%3A; BDUSS=wxWGR0WHdjZmhGYXFhNEUzTW8tSzB2d0k1dkhmb250dERvLXgtMFFJRX54bEppSVFBQUFBJCQAAAAAAAAAAAEAAACG1aNn0~nB-r6yzOzQpgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD85K2I~OStiWW; BDUSS_BFESS=wxWGR0WHdjZmhGYXFhNEUzTW8tSzB2d0k1dkhmb250dERvLXgtMFFJRX54bEppSVFBQUFBJCQAAAAAAAAAAAEAAACG1aNn0~nB-r6yzOzQpgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD85K2I~OStiWW; BAIDUID=6234E70DA140AB06AA1F6D52E85F4C6E:SL=0:NR=10:FG=1; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; B64_BOT=1; channel=baidusearch; Hm_lvt_aec699bb6442ba076c8981c6dc490771=1649314183,1649330646,1649665945,1649828528; BD_HOME=1; delPer=0; BD_CK_SAM=1; PSINO=5; BAIDUID_BFESS=784E60AB1618F7FBE037C86620D059CA:FG=1; H_PS_PSSID=36070_31254_36005_35913_36167_34584_36121_36126_36233_26350_36100_36061; COOKIE_SESSION=562_5_4_4_11_2_1_0_4_2_0_4_2126_0_5_0_1649838848_1649828520_1649838843%7C9%23159753_17_1649827993%7C9; H_PS_645EC=29bepimyDEPGvQbgYTWR%2BJMxhCdjRXmlcBSGFIFFGprPnBJgXfSxa0kSV5I; baikeVisitId=35d6c75c-0711-449a-9b86-62fac16d67c1; ab_sr=1.0.1_YTE2NDVmZDdhYTYwNDVkZjJhYTBiOTI1Y2ZkMjgwY2UwY2JiNTI1MDkxMTNjNjE3Yzc4Y2QxZWU2OTI0OTg5NDkxYmRlN2RmMjU4ZDBiYzAyZTllZDJlYWEwYWExMTI3NmIyYzMwOTgwMWZiMmFmN2RiNWQ1OTQ5NzhlNWRmODc5YWQ5N2FmMTQyOGU0MjVmNzA1Nzg4MjFkYzQ2OTdkMzMyNTM2NmY1MjVkNWM0MTExYWNhYzJhYjUyMWMwOTk1; RT="z=1&dm=baidu.com&si=6ocfo720f3b&ss=l1xbq3e7&sl=2&tt=7cv&bcn=https%3A%2F%2Ffclog.baidu.com%2Flog%2Fweirwood%3Ftype%3Dperf&ld=7ts"; sug=3; sugstore=0; ORIGIN=0; bdime=0; BA_HECTOR=a00l258l202484algt1h5d3qj0q Host: www.baidu.com sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="100", "Google Chrome";v="100" sec-ch-ua-mobile: ?0 sec-ch-ua-platform: "Windows" Sec-Fetch-Dest: document Sec-Fetch-Mode: navigate Sec-Fetch-Site: none Sec-Fetch-User: ?1 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.60 Safari/537.36
-
请求行
- 请求行中的请求方式:get post
- get:一次请求能够携带的参数比较少,大小有限制,会在浏览器的地址栏中显示数据内容,不安全,但高效
- post:请求携带的参数没有限制,大小没有限制,不会在浏览器地址栏显示数据内容,安全,但是不高效
- 请求行中的请求方式:get post
-
消息头
Accept: //告诉浏览器,它所支持的数据类型 Accept-Encoding: gzip, deflate, br //支持哪种编码格式 Accept-Language: zh-CN,zh;q=0.9 //告诉浏览器语言环境 Cache-Control: max-age=0 //缓存控制 Connection: keep-alive //告诉浏览器请求完成是断开还是保持连接 HOST:主机
4. Http响应
-
服务器 响应 客户端
-
以百度为例:
Bdpagetype: 2 Bdqid: 0xa3a0deab00048ae3 Cache-Control: private //缓存控制 Connection: keep-alive //连接:保持连接 Content-Encoding: gzip Content-Type: text/html;charset=utf-8 Date: Wed, 13 Apr 2022 08:52:43 GMT Expires: Wed, 13 Apr 2022 08:52:42 GMT Server: BWS/1.1 Set-Cookie: BDSVRTM=236; path=/ Set-Cookie: BD_HOME=1; path=/ Set-Cookie: H_PS_PSSID=36070_31254_36005_35913_36167_34584_36121_36126_36233_26350_36100_36061; path=/; domain=.baidu.com Strict-Transport-Security: max-age=172800 Traceid: 1649839963279410689011790668650477030115 Transfer-Encoding: chunked //编码类型 X-Frame-Options: sameorigin X-Ua-Compatible: IE=Edge,chrome=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: //让网页重写定位 -
响应状态码
200:请求响应成功
3XX:请求重定向,转到一个新位置
4XX:找不到资源(404)
5XX:服务器代码错误(500)
502:网关错误
面试题:
- 当你的浏览器中地址栏输入地址并回车的一瞬间到页面能够展示出来,经历了什么?
五、Maven
我为什么要学习这个技术?
- 在JavaWeb中需要使用大量的jar包,这些jar包需要下载下来手动导入
- 如何能够让一个东西帮我们自动导入和配置jar包
由此,Maven诞生了!
1. Maven:项目架构管理工具
我们目前用Maven就是方便导入jar包的!
Maven的核心思想:约定大于配置
- 有约束,不要去违反
Maven会规定好你该如何取编写我们的Java代码(目录结构),必须要按照这个规范。
2. 下载安装Maven
Maven官网:https://maven.apache.org/index.html

解压压缩包

建议:最好所有相关环境都放在一个文件夹里,方便管理
文件目录跟tomcat相差不多,主要是conf目录下有个setting.xml文件,即配置文件
3. 环境变量配置
在系统环境变量中,配置如下:
- M2_HOME :maven目录下的bin目录
- MAVEN_HOME:maven的目录
- 在Path中:配置%MAVEN_HOME%\bin
完成后:在控制台输入mvn -version 检查是否安装成功 mvn其实是bin目录中的一个命令
4. 修改配置文件(阿里云镜像)
-
镜像: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>
-
5. 建立本地仓库
本地仓库和远程仓库;
建立本地仓库:不需要每次都去网上下载
-
在maven下新建一个文件夹,命名为maven-repo,复制这个地址,在setting.xml文件中进行粘贴
<localRepository>D:\Maven\repo</localRepository> -
其实这个文件的仓库里面都是各种各样的jar包。
6.在IDEA中创建MavenWeb项目
-
启动IDEA
-
创建一个MavenWeb项目

-
Next
-
旧版本IDEA

点击next

补充:

点击next,填写项目名和位置

-
新版本IDEA:


-
-
创建成功


-
观察本地仓库中多了什么东西

-
IDEA中的Maven设置
IDEA项目创建成功后,多留意一眼Maven的设置!

-
永久配置新项目Maven

-
到这里,Maven在IDEA中简单的配置和使用就结束了,但是这是因为我们创建的是MavenWeb项目
-
看下IDEA中的目录结构:

可以发现跟tomcat中的webapp结构相似
7. 在IDEA中创建普通的Maven项目
不勾选任何模板,直接next

会发现,不需要选择maven仓库那一步了,而且创建出来的项目是十分干净的,速度也是十分快的。

8. 在MavenWeb项目中标记文件夹
-
新建两个文件夹

发现此时java文件夹并没有像普通maven中的一样变成蓝色,也不能在这个文件夹new class出来
-
解决文件夹标记问题
-
方法一:鼠标右键文件夹

-
方法二:


-
9. 在IDEA中配置Tomcat
-
点击右上角

-
选择tomcat

-
选择tomcat配置

-
解决警告信息
为什么会有这个问题:
我们访问一个网站,需要指定一个文件夹的名字,我们就需要手动配置

-
虚拟路径映射

-
启动tomcat

-
运行成功

-
运行成功后,会自动在目录结构生成target

-
这个时候再回到tomcat的webapps目录下就会发现很有意思的事情

所以说,虚拟路径映射之后,写的什么就会在tomcat的webapps下出现什么,然后通过这个文件名访问页面。
10. pom.xml文件
-
了解maven的目录结构

-
pom.xml的内容
<?xml version="1.0" encoding="UTF-8"?> <!--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/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <!--这里就是我们刚才配置的GAV--> <groupId>xyz.Laxsilence</groupId> <artifactId>javaweb-01-maven</artifactId> <version>1.0-SNAPSHOT</version> <!--package:项目的打包方式 1. jar:java应用 2. war:javaweb应用 --> <packaging>war</packaging> <name>javaweb-01-maven Maven Webapp</name> <!-- FIXME change it to the project's website --> <url>http://www.example.com</url> <!--配置--> <properties> <!--项目的默认构建编码--> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <!--编码版本--> <maven.compiler.source>1.7</maven.compiler.source> <maven.compiler.target>1.7</maven.compiler.target> </properties> <!--项目的依赖--> <dependencies> <!--具体依赖的jar包配置文件--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <!--Maven的高级之处在于,他会帮你导入这个Jar包所依赖的其他jar--> </dependencies> <!--项目构建用的东西--> <build> <finalName>javaweb-01-maven</finalName> <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --> <plugins> <plugin> <artifactId>maven-clean-plugin</artifactId> <version>3.1.0</version> </plugin> <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging --> <plugin> <artifactId>maven-resources-plugin</artifactId> <version>3.0.2</version> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>2.22.1</version> </plugin> <plugin> <artifactId>maven-war-plugin</artifactId> <version>3.2.2</version> </plugin> <plugin> <artifactId>maven-install-plugin</artifactId> <version>2.5.2</version> </plugin> <plugin> <artifactId>maven-deploy-plugin</artifactId> <version>2.8.2</version> </plugin> </plugins> </pluginManagement> </build> </project> -
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> -
pom.xml 添加依赖
maven仓库官网:https://mvnrepository.com/
在里面搜索想用的jar包


然后点击刷新按钮:

11. maven窗口介绍
目录树(快捷键 ctrl + shift+ alt + u)
Maven中jar包的联系关联图

新版本图标:


12. 解决一些遇到的问题
-
Maven 3.6.2版本

解决方法:将Maven降级3.6.1
-
Tomcat闪退
-
IDEA每次都要重复配置Maven

-
Maven项目中默认的web.xml版本问题
解决方法:找到tomcat的webapps中,找到root目录,复制其中web.xml的内容即可,即替换为4.0版本,和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"> </web-app> -
Maven仓库的使用
maven仓库官网:https://mvnrepository.com/
在里面搜索想用的jar包


然后点击刷新按钮:

13. 初识Servlet
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @ClassName HelloServlet
* @Description TODO
* @Author 刘英俊
* @Date 2022/4/16 14:18
* @Version 1.0
**/
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException {
//响应的类型
response.setContentType("text/html");
//设置网页响应的字符编码
response.setCharacterEncoding("utf-8");
//获取响应的输出流
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title>HelloWorld!</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>Hello,世界!</h1>");
out.println("</body>");
out.println("</html>");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
web.xml:
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!-- web.xml中是配置我们的web的核心应用-->
<!-- 注册Servlet-->
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>HelloServlet</servlet-class>
</servlet>
<!-- 一个servlet对应一个mapping-->
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<!-- 映射的请求路径-->
<url-pattern>/DearLiu</url-pattern>
</servlet-mapping>
</web-app>
Tomcat服务过程:

- 先在地址栏输入请求,返回默认的index.jsp界面

- 在地址栏输入项目下的静态页面,返回静态页面的内容

- 在状态栏输入web.xml中动态注册的请求映射路径名,然后这个路径会去寻找对应的servlet名,根据这个名字会寻找对应的java类,输出对应内容

六、Servlet
1. Servlet简介
- Servlet就是Sun公司开发动态web的一门技术
- Sun公司在这些api中提供了一个接口叫Servlet,如果想开发一个Servlet程序,只需要完成两个小步骤:
- 编写一个类,实现Servlet接口
- 把开发好的java类部署在web服务器中
- 把实现了Servlet接口的java程序叫Servlet
2. HelloServlet
Servlet接口在Sun公司有两个默认的实现类:HttpServlet,GenericServlet
-
构建一个普通的Maven项目,删掉里面的src目录,以后我们的学习就在这个项目里面建立module;这个空的项目就是maven的主项目(工程),所以我们把需要的依赖都导入到主工程的pom.xml中
-
右键该工程,新建module,勾选maven自带的webapps框架
关于maven父子工程的理解
父项目的pom.xml会有以下东西:
<modules> <module>servlet-01</module> </modules>子项目的pom.xml会有以下东西:
<parent> <artifactId>javaweb-02-servlet</artifactId> <groupId>com.laxsilence</groupId> <version>1.0-SNAPSHOT</version> </parent>父项目中的java子项目可以直接使用父项目中的包
-
Maven环境优化
-
将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> -
将Maven的结构搭建完整,新建java和resource目录
-
-
编写一个servlet程序
-
编写一个普通类
-
实现Servlet接口,这里我们直接继承HttpServlet
进入源码查看:Servlet --> GenericServlet --> HttpServlet --> 我们自己定义的类,Servlet接口有个service方法,HTTPServlet对齐进行了重写,重写后其中包括了很多方法,而我们常用的就是doGet和doPost,所以在自己定义的类中,需要重写这两个方法。
package com.laxsilence.servlet; import javax.servlet.ServletException; import javax.servlet.ServletInputStream; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; /** * @ClassName HelloServlet * @Description TODO * @Author 刘英俊 * @Date 2022/4/16 15:19 * @Version 1.0 **/ 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(); PrintWriter writer = resp.getWriter();//响应流 writer.print("Hello,Servlet"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
-
-
编写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>com.laxsilence.servlet.HelloServlet</servlet-class> </servlet> <!-- 请求路径--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> </web-app> -
配置Tomcat
注意项目发布的路径
注意每个子项目添加进artifact的时候,去掉上一个残留的,尽量只存在一个
-
启动项目测试


3. Servlet原理
Servlet是由web服务器调用,web服务器在收到浏览器请求之后的原理图如下(三张图的含义是一样的)



4. Mapping问题
-
一个Servlet可以指定一个映射路径
<!-- 访问路径:localhost:8080/servlet01/hello--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> -
一个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> -
一个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> -
指定一些后缀等等...
注意:*前面不能加项目映射的路径
<!--默认请求路径,会干掉index.jsp--> <!-- 访问路径:localhost:8080/servlet01/abc.do--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> -
扩展:如果出错,网页404跳转error界面
<servlet> <servlet-name>error</servlet-name> <servlet-class>com.laxsilence.servlet.ErrorServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>error</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> -
优先级问题:
指定了固有的映射路径:越准确优先级越高,如果找不到就会走默认的处理请求。
精准匹配>扩展匹配>模糊匹配>通配匹配
5. ServletContext
web容器在启动的时候,它会为每个web程序都创建一个对应的ServletContext对象,它代表了当前的web应用。
全局唯一,只有一个,所有servlet公用
-
共享数据
在这个Servlet中保存的数据,可以在另外一个servlet中拿到;
创建数据的类:
package com.laxsilence.servlet; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @ClassName HelloServlet * @Description TODO * @Author 刘英俊 * @Date 2022/4/16 19:08 * @Version 1.0 **/ 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 = "DearLiu";//数据 context.setAttribute("username",username);//将一个数据保存到了ServletContext中。名字为:username,值为:username System.out.println("存放值"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }读取数据的类:
package com.laxsilence.servlet; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @ClassName GetServlet * @Description TODO * @Author 刘英俊 * @Date 2022/4/16 19:27 * @Version 1.0 **/ 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 { 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> <servlet-name>hello</servlet-name> <servlet-class>com.laxsilence.servlet.HelloServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> <servlet> <servlet-name>getc</servlet-name> <servlet-class>com.laxsilence.servlet.GetServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>getc</servlet-name> <url-pattern>/getc</url-pattern> </servlet-mapping> </web-app>运行结果:
在没有存值进去的时候:

存了值进去之后:

-
获取初始化参数
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"> <!-- 配置一些初始化参数,配置在这里的参数,在类中可以获取到--> <context-param> <param-name>url</param-name> <param-value>jdbc:mysql://localhost:3306/mybatis</param-value> </context-param> <!-- 注册servlet--> <servlet> <servlet-name>gp</servlet-name> <servlet-class>com.laxsilence.servlet.ServletDemo03</servlet-class> </servlet> <servlet-mapping> <servlet-name>gp</servlet-name> <url-pattern>/gp</url-pattern> </servlet-mapping> </web-app>获取类:
package com.laxsilence.servlet; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @ClassName ServletDemo03 * @Description TODO * @Author 刘英俊 * @Date 2022/4/16 19:43 * @Version 1.0 **/ 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 { 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"> <!-- 配置一些初始化参数,配置在这里的参数,在类中可以获取到--> <context-param> <param-name>url</param-name> <param-value>jdbc:mysql://localhost:3306/mybatis</param-value> </context-param> <!-- 注册servlet--> <servlet> <servlet-name>gp</servlet-name> <servlet-class>com.laxsilence.servlet.ServletDemo03</servlet-class> </servlet> <servlet-mapping> <servlet-name>gp</servlet-name> <url-pattern>/gp</url-pattern> </servlet-mapping> <servlet> <servlet-name>sd4</servlet-name> <servlet-class>com.laxsilence.servlet.ServletDemo04</servlet-class> </servlet> <servlet-mapping> <servlet-name>sd4</servlet-name> <url-pattern>/sd4</url-pattern> </servlet-mapping> </web-app>转发类(将ServletDemo03页面的内容转发到ServletDemo04)
package com.laxsilence.servlet; import javax.servlet.RequestDispatcher; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @ClassName ServletDemo04 * @Description TODO * @Author 刘英俊 * @Date 2022/4/16 19:43 * @Version 1.0 **/ public class ServletDemo04 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext context = this.getServletContext(); // RequestDispatcher requestDispatcher = context.getRequestDispatcher("/gp");//转发的请求路径 // requestDispatcher.forward(req,resp);//调用forward实现请求转发 context.getRequestDispatcher("/gp").forward(req,resp); System.out.println("进入了ServletDemo04"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }运行结果:


可以看出路径不同,内容相同,类似下图

A想拿到C的资源,但是只能连接到B。过程为:A向B发送一次请求,想要C的资源,B收到后,发送请求到C,得到C的资源,然后转发给A。
在这个例子中,A就是ServletDemo04,B就是ServletContext,C就是ServeltDemo03
简单说下重定向:(后续会讲)

A需要发送两次请求,过程为:A告诉B我要C的资源,B回答说:你去找C要,然后A向C发送请求。
-
读取资源文件
新建一个db.properties文件,放在resources目录下,查看target目录,发现确实存在
新建一个aa.properties文件,放在java目录下,查看target目录,发现不存在了,这就是Maven中提到的资源导出问题

补充:如果没有出现的,可以试下maven的clean命令,然后重新执行,如果clean命令报错(Maven执行clean失败 Process terminatedl),可以检查下pom文件,可能是找不到父文件的原因,新版的IDEA新建Module可能不会在pom中引入父文件,需要手动引入。pom.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> <parent> <artifactId>javaweb-02-servlet</artifactId> <groupId>com.laxsilence</groupId> <version>1.0-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent> <groupId>com.laxsilence</groupId> <artifactId>servlet-002</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <name>servlet-002 Maven Webapp</name> <!-- FIXME change it to the project's website --> <url>http://www.example.com</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.7</maven.compiler.source> <maven.compiler.target>1.7</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> </dependencies> <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> </project>对比打包的项目target和原本的main,我们可以发现一条java和resources目录的内容都被打包到了classes,我们俗称这个路径为类路径,classpath。


db.properties:
username=root password=123456获取类:
package com.laxsilence.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.util.Properties; /** * @ClassName ServletDemo05 * @Description TODO * @Author 刘英俊 * @Date 2022/4/16 20:32 * @Version 1.0 **/ 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:
<?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"> <!-- 配置一些初始化参数,配置在这里的参数,在类中可以获取到--> <context-param> <param-name>url</param-name> <param-value>jdbc:mysql://localhost:3306/mybatis</param-value> </context-param> <!-- 注册servlet--> <servlet> <servlet-name>gprop</servlet-name> <servlet-class>com.laxsilence.servlet.ServletDemo05</servlet-class> </servlet> <servlet-mapping> <servlet-name>gprop</servlet-name> <url-pattern>/gprop</url-pattern> </servlet-mapping> </web-app>结果:

6. HttpServletResponse
web服务器接收到客户端的Http请求,会针对这个请求,分别创建一个代表请求的HttpServletRequest对象,代表响应的一个HttpServletResponse对象。
- 我们如果要获取我们客户端请求过来的参数:找HttpServletResquest
- 如果要给客户端响应一些信息:就找HttpServletResponse
1. 简单分类
负责向浏览器发送数据的方法:
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;
2. 下载文件
-
向浏览器输出消息(前面已经使用过了)
-
下载文件
- 要获取下载文件的路径
- 下载文件的名字
- 想办法让浏览器能够支持下载我们需要的东西
- 获取下载文件的输入流
- 创建缓冲区
- 获取OutputStream对象
- 将FileOutputStream流写入到buffer缓冲区
- 使用OutputStream将缓冲区中的数据输出到客户端
java:
package com.laxsilence.servlet; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.FileInputStream; import java.io.IOException; import java.net.URLEncoder; /** * @ClassName FileServlet * @Description TODO * @Author 刘英俊 * @Date 2022/4/19 16:57 * @Version 1.0 **/ 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 = "D:\\旧桌面\\学习\\JavaStudy\\JavaWeb\\javaweb-02-servlet\\response\\src\\main\\resources\\1.png"; System.out.println("下载文件的路径:"+realPath); // 2. 下载的文件名是啥 String fileName = realPath.substring(realPath.lastIndexOf("\\" + 1)); // 3. 设置想办法让浏览器能够支持下载我们需要的东西 resp.setHeader("Content-Disposition","attachment;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 ((len=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 { 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>filedown</servlet-name> <servlet-class>com.laxsilence.servlet.FileServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>filedown</servlet-name> <url-pattern>/down</url-pattern> </servlet-mapping> </web-app>运行效果:(注意地址栏中/后面还有down)

3. 验证码功能
验证码怎么来的?
-
前端实现
-
后端实现,需要用到java的图片类,生成一个图片
Java
package com.laxsilence.servlet; import javax.imageio.ImageIO; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.awt.*; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.Random; /** * @ClassName ImageServlet * @Description TODO * @Author 刘英俊 * @Date 2022/4/20 14:04 * @Version 1.0 **/ public class ImageServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //如何让浏览器五秒自动刷新一次 resp.setHeader("refresh","3"); //在内存中创建一个图片 BufferedImage bufferedImage = new BufferedImage(50,25,BufferedImage.TYPE_3BYTE_BGR); //得到图片 Graphics2D g = (Graphics2D) bufferedImage.getGraphics();//笔 //设置图片的背景颜色 g.setColor(Color.white); g.fillRect(0,0,50,25); //给图片写数据 g.setColor(Color.blue); g.setFont(new Font(null,Font.BOLD,20)); g.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(9999)+""; StringBuffer sb = new StringBuffer(); for (int i = 0;i < 4 - num.length();i++){ sb.append("0"); } num = sb.toString() + num; return num; } @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>filedown</servlet-name> <servlet-class>com.laxsilence.servlet.FileServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>filedown</servlet-name> <url-pattern>/down</url-pattern> </servlet-mapping> <servlet> <servlet-name>image</servlet-name> <servlet-class>com.laxsilence.servlet.ImageServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>image</servlet-name> <url-pattern>/img</url-pattern> </servlet-mapping> </web-app>实现效果:
4. 实现重定向

一个Web资源收到客户端请求后,通知他去访问另外一个web资源,这个过程叫重定向
常见场景:
- 用户登录
void sendRedirect(String var1) throws IOException;
package com.laxsilence.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @ClassName RedirectServlet
* @Description TODO
* @Author 刘英俊
* @Date 2022/4/20 15:06
* @Version 1.0
**/
public class RedirectServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.sendRedirect("/resp/img");//或者直接写(“img”)
// 原理
// resp.setHeader("Location","/resp/img");
// resp.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);//等价于302
}
@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>filedown</servlet-name>
<servlet-class>com.laxsilence.servlet.FileServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>filedown</servlet-name>
<url-pattern>/down</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>image</servlet-name>
<servlet-class>com.laxsilence.servlet.ImageServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>image</servlet-name>
<url-pattern>/img</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>Redi</servlet-name>
<servlet-class>com.laxsilence.servlet.RedirectServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Redi</servlet-name>
<url-pattern>/redi</url-pattern>
</servlet-mapping>
</web-app>

5. 聊一聊重定向和转发的区别:
相同点:
- 页面都会实现跳转
不同点:
- 请求转发的时候,URL不会产生变化;重定向的时候,URL会发生变化
- 转发是一次请求,重定向是两次
- 请求转发是在服务器内部完成的,重定向是在客户端完成的
- 转发的url必须是当前web工程内部的地址,重定向可以是任意地址
- 转发的编码是307,重定向是302
路径问题:
注意:转发和重定向的URLString前有加 / 为绝对路径 反之为相对路径
-
绝对路径
重定向的 / 表示:http://服务器ip:端口/
response.sendRedirect("/Manager/index.jsp");
生成的地址:http://localhost:8080/Manager/index.jsp
请求转发的 / 表示:http://服务器ip:端口/项目名
request.getRequestDispatcher("/index.jsp").forward(request, response); -
相对路径
通过表单请求指定的url资源 action=“LoginServlet”
则表单生成的请求地址为: http://localhost:8080/项目名/LoginServlet请求重定向:response.sendRedirect(“Manager/index.jsp”)
生成相对路径:http://localhost:8080/项目名/Manager/index.jsp
请求转发:相对路径情况下生成完整URL与重定向方法相同
5. 简单的登陆小案例(获取jsp中提交的参数)
需要导入maven依赖
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
</dependency>
java类:
package com.laxsilence.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.PageContext;
import java.io.IOException;
/**
* @ClassName RequestTest
* @Description TODO
* @Author 刘英俊
* @Date 2022/4/20 15:22
* @Version 1.0
**/
public class RequestTest extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("进入这个请求了");
System.out.println(req.getContextPath());
// 处理请求
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
<?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>filedown</servlet-name>
<servlet-class>com.laxsilence.servlet.FileServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>filedown</servlet-name>
<url-pattern>/down</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>image</servlet-name>
<servlet-class>com.laxsilence.servlet.ImageServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>image</servlet-name>
<url-pattern>/img</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>Redi</servlet-name>
<servlet-class>com.laxsilence.servlet.RedirectServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Redi</servlet-name>
<url-pattern>/redi</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>request</servlet-name>
<servlet-class>com.laxsilence.servlet.RequestTest</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>request</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
</web-app>
index.jsp
<html>
<body>
<%--这里提交的路径需要寻找到项目的路径--%>
<%--${pageContext.request.contextPath}代表当前的项目--%>
<form action="${pageContext.request.contextPath}/login" method="get">
用户名:<input type="text" name="username">
<br>
密码:<input type="password" name="password">
<input type="submit" value="提交">
</form>
</body>
</html>
7. HttpServletRequest
HttpServletRequest代表客户端的请求,用户通过Http协议访问服务器,Http请求中的所有信息会被封装到HttpServletRequest,通过这个HttpServletRequest的方法,可以获得客户端的所有信息。


获取前端传递的参数
requset.getParameter(String s);//返回String
request.getParameterValues(String s);//返回数组String[]
package com.laxsilence.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
/**
* @ClassName LoginServlet
* @Description TODO
* @Author 刘英俊
* @Date 2022/4/20 15:52
* @Version 1.0
**/
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("hobby");
System.out.println("==========================================");
// 后天接收中文乱码问题
System.out.println(username);
System.out.println(password);
System.out.println(Arrays.toString(hobbys));
//通过请求转发或重定向跳转到其他页面
req.getRequestDispatcher("success.jsp").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
<%--
Created by IntelliJ IDEA.
User: 刘英俊
Date: 2022/4/20
Time: 15:51
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>
<div>
<%-- 以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="hobby" value="女孩">女孩
<input type="checkbox" name="hobby" value="代码">代码
<input type="checkbox" name="hobby" value="唱歌">唱歌
<input type="checkbox" name="hobby" value="电影">电影
<br>
<input type="submit" value="登录">
</form>
</div>
</body>
</html>
<?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>com.laxsilence.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
</web-app>
8. Servlet的生命周期
servlet的生命周期顾名思义就是从servlet出现到消亡(销毁)的全过程。
主要分为以下几个阶段:
加载类—>实例化(为对象分配空间)—>初始化(为对象的属性赋值)—>请求响应(服务阶段)—>销毁
(1)Servlet生命周期的三个方法
init()初始化阶段
service()处理客户端请求阶段
destroy()终止阶段
容器(tomcat等)装载servlet
(2)实例化阶段
- 当客户端首次发送第一次请求后,由Servlet容器去解析请求,根据请求找到是否有对应的servlet。
- 判断是否有Servlet实现类的对象存在?存在则直接使用,不存在则先创建一个servlet实现类的对象。
(3)初始化阶段
Servlet 初始化是其生命周期的第一个阶段,也是其他阶段的基础。只有完成了初始化,Servlet 才能处理来自客户端的请求。
Servlet 初始化阶段分为 2 步:
- 加载和实例化 Servlet;
- 调用 init() 方法进行初始化
加载和实例化:
Servlet 容器负责加载和实例化 Servlet。
当容器启动或首次请求某个 Servlet 时,容器会读取 web.xml (配置load-on-startup=1,默认为0)或 @WebServlet 中的配置信息,对指定的 Servlet 进行加载。加载成功后,容器会通过反射对 Servlet 进行实例化。
调用 init() 方法进行初始化
加载和实例化完成后,Servlet 容器会创建一个servlet对象并调用servlet的init方法(在servlet生命周期内只能调用一次init方法)去初始化 Servlet 实例。
(4)请求响应阶段
初始化完成后调取service()方法,由service()判断客户端的请求方式。
- 如果是get请求,则执行doGet()方法。
- 如果是post请求,则执行doPost()。
- 处理方法完成后会作出相应的结果返回给客户端,单次请求处理完毕。
当用户发送第二次以后的请求时,会判断对象是否存在,但是不再执行init(),而直接执行service方法调取doGet() / doPost()方法。
(5)服务终止阶段
当服务器关闭,重启或移除 Servlet 实例时Servlet调取destroy()方法进行销毁,宣告生命周期的结束。
public class EmpServlet extends HttpServlet{
//初始化servlet,调用init方法
public void init() throws ServletException {
System.out.println("初始化时调用");
}
//开启服务
protected void service(HttpServletRequest arg0, HttpServletResponse arg1) throws ServletException, IOException {
System.out.println("开启服务时调用");
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
//销毁时调用destory
public void destroy() {
System.out.println("销毁时调用");
}
}
七、Cookie、Session
1. 会话
会话:用户打开一个浏览器,(点击一个超链接),访问多个web资源,关闭浏览器,这个过程称之为会话
有状态会话:一个同学来过教室,下次再来教室,我们会知道这个同学曾经来过,称之为有状态会话
- 你能怎么证明自己是一个学生?
- 学生证
- 登记册
- 可以发现有两个对象
- 学生证:学校给你学生证
- 登记册:学校标记你来过了
- 那么一个网站怎么证明你来过?
- 两个对象 客户端 服务端
- 怎么让服务端证明客户端来过?
- 服务端给客户端一个 信件 ,客户端下次访问带上 信件 就可以了;(Cookie)
- 服务器登记你来过了,下次你来的时候我来匹配你。(Session)
无状态会话:访问一次后就断开连接,没有留下信息
2. 保存会话的两种技术
Cookie和Session:
- 存储位置不同:cookie是保存在客户端,session是保存在服务器端
- 存储数据量不同:cookie存储有限,不超过4kb,session是无限的
Cookie:
- 客户端技术,通过响应发给客户端,通过请求给服务端查验
Session:
- 服务器技术,利用这个技术,可以保存用户的会话信息,我们可以把信息或数据放在Session中。
常见场景:网站登陆之后,下次不用再登陆
3. Cookie
- 从请求中拿到cookie信息
- 服务器响应给客户端cookie

package com.laxsilence.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
/**
* @ClassName CookieDemo01
* @Description TODO
* @Author 刘英俊
* @Date 2022/4/20 16:57
* @Version 1.0
**/
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[] cookies = req.getCookies();//返回数组,说明Cookie可能存在多个(学生证,校服等等)
//判断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 lastTime = Long.parseLong(cookie.getValue());
Date date = new Date(lastTime);
out.write(date.toLocaleString());
}
}
}else {
out.write("这是您第一次访问本站");
}
//服务器给客户端响应一个cookie
Cookie cookie = new Cookie("lastLoginTime",System.currentTimeMillis()+"");
//设置有效期
cookie.setMaxAge(24*60*60);
resp.addCookie(cookie);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
<?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>com.laxsilence.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,但是这个cookie是有上限的,每个站点最多存放20个cookie
- Cookie的大小有限制,4kb
- 浏览器的cookie上限是300个
删除Cookie:
-
不设置有效期,关闭浏览器,自动失效;
-
设置有效期为0
package com.laxsilence.servlet; import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.util.Date; /** * @ClassName CookieDemo01 * @Description TODO * @Author 刘英俊 * @Date 2022/4/20 16:57 * @Version 1.0 **/ public class CookieDemo02 extends HttpServlet { // 删除cookie @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); } }<?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>com.laxsilence.servlet.CookieDemo01</servlet-class> </servlet> <servlet-mapping> <servlet-name>CookieDemo01</servlet-name> <url-pattern>/c1</url-pattern> </servlet-mapping> <servlet> <servlet-name>CookieDemo02</servlet-name> <servlet-class>com.laxsilence.servlet.CookieDemo02</servlet-class> </servlet> <servlet-mapping> <servlet-name>CookieDemo02</servlet-name> <url-pattern>/c2</url-pattern> </servlet-mapping> <servlet> <servlet-name>CookieDemo03</servlet-name> <servlet-class>com.laxsilence.servlet.CookieDemo03</servlet-class> </servlet> <servlet-mapping> <servlet-name>CookieDemo03</servlet-name> <url-pattern>/c3</url-pattern> </servlet-mapping> </web-app> -
cookie中文乱码问题(解码和编码)
package com.laxsilence.servlet; import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.net.URLDecoder; import java.net.URLEncoder; import java.util.Date; /** * @ClassName CookieDemo01 * @Description TODO * @Author 刘英俊 * @Date 2022/4/20 16:57 * @Version 1.0 **/ public class CookieDemo03 extends HttpServlet { // cookie存中文 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 设置编码 Cookie cookie1 = new Cookie("name", URLEncoder.encode("刘英俊","utf-8")); resp.addCookie(cookie1); req.setCharacterEncoding("utf-16"); resp.setCharacterEncoding("utf-16"); PrintWriter out = resp.getWriter(); //Cookie,服务器端从客户端获取 Cookie[] cookies = req.getCookies();//返回数组,说明Cookie可能存在多个(学生证,校服等等) //判断Cookie是否存在 if (cookies != null){ //如果存在取出 out.write("你上一次访问的时间是:"); for (int i = 0; i < cookies.length; i++) { Cookie cookie = cookies[i]; //获取cookie的名字 if (cookie.getName().equals("name")){ // 解码 String decode = URLDecoder.decode(cookie.getValue(),"utf-8"); out.write(decode); } } } } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }<?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>com.laxsilence.servlet.CookieDemo01</servlet-class> </servlet> <servlet-mapping> <servlet-name>CookieDemo01</servlet-name> <url-pattern>/c1</url-pattern> </servlet-mapping> <servlet> <servlet-name>CookieDemo02</servlet-name> <servlet-class>com.laxsilence.servlet.CookieDemo02</servlet-class> </servlet> <servlet-mapping> <servlet-name>CookieDemo02</servlet-name> <url-pattern>/c2</url-pattern> </servlet-mapping> <servlet> <servlet-name>CookieDemo03</servlet-name> <servlet-class>com.laxsilence.servlet.CookieDemo03</servlet-class> </servlet> <servlet-mapping> <servlet-name>CookieDemo03</servlet-name> <url-pattern>/c3</url-pattern> </servlet-mapping> </web-app>
4. Session(重点)
什么是Session:
- 服务器会给每一个用户(浏览器)创建一个Session对象;Session在创建的时候,会通过Cookie给客户端发送属于它的SessionID
- 一个Session独占一个浏览器,只要浏览器没有关闭,这个Session就存在;
- 用户登录之后:整个网站的所有页面都可以访问,保存用户的信息;保存购物车的信息

需要注意的是:session是针对用户的,同一个用户可以访问到所有关于他的请求,那要是有第二个用户也想访问一些资源呢,就是说有一些资源是公共的,那么就会用到前面我们提过的ServletContext,它存放的资源是不同用户都能够访问到的,后面会有另外一种写法,叫applicationContext

给Session中存普通键值对或对象
package com.laxsilence.servlet;
import com.laxsilence.pojo.Person;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URLDecoder;
import java.net.URLEncoder;
/**
* @ClassName CookieDemo01
* @Description TODO
* @Author 刘英俊
* @Date 2022/4/20 16:57
* @Version 1.0
**/
public class SessionDemo01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 解决乱码问题
req.setCharacterEncoding("utf-16");
resp.setCharacterEncoding("utf-16");
resp.setContentType("text/html;charset=UTF-8");
// 得到Session
HttpSession session = req.getSession();
// 给Session中存东西
session.setAttribute("name",new Person("刘英俊",18));
// 获取session的id
String id = session.getId();
// 判断session是不是新创建的
boolean aNew = session.isNew();
if (aNew){
resp.getWriter().write("seesion创建成功,ID为:"+id);
}else {
resp.getWriter().write("session已经存在,ID为:"+id);
}
// session创建的时候做了什么
// Cookie cookie = new Cookie("JSESSIONID",id);
// resp.addCookie(cookie);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
获取session的值
package com.laxsilence.servlet;
import com.laxsilence.pojo.Person;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* @ClassName CookieDemo01
* @Description TODO
* @Author 刘英俊
* @Date 2022/4/20 16:57
* @Version 1.0
**/
public class SessionDemo02 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 解决乱码问题
req.setCharacterEncoding("utf-16");
resp.setCharacterEncoding("utf-16");
resp.setContentType("text/html;charset=UTF-8");
// 得到Session
HttpSession session = req.getSession();
// 获取session
Person person = (Person)session.getAttribute("name");
resp.getWriter().write(person.toString());
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
清除session的两种方法:
手动清除
package com.laxsilence.servlet;
import com.laxsilence.pojo.Person;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* @ClassName CookieDemo01
* @Description TODO
* @Author 刘英俊
* @Date 2022/4/20 16:57
* @Version 1.0
**/
public class SessionDemo03 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 注销
HttpSession session = req.getSession();
session.removeAttribute("name");
// 手动注销名为name的session
session.invalidate();
}
@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>com.laxsilence.servlet.CookieDemo01</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CookieDemo01</servlet-name>
<url-pattern>/c1</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>CookieDemo02</servlet-name>
<servlet-class>com.laxsilence.servlet.CookieDemo02</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CookieDemo02</servlet-name>
<url-pattern>/c2</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>CookieDemo03</servlet-name>
<servlet-class>com.laxsilence.servlet.CookieDemo03</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CookieDemo03</servlet-name>
<url-pattern>/c3</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>SessionDemo01</servlet-name>
<servlet-class>com.laxsilence.servlet.SessionDemo01</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>SessionDemo01</servlet-name>
<url-pattern>/s1</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>SessionDemo02</servlet-name>
<servlet-class>com.laxsilence.servlet.SessionDemo02</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>SessionDemo02</servlet-name>
<url-pattern>/s2</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>SessionDemo03</servlet-name>
<servlet-class>com.laxsilence.servlet.SessionDemo03</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>SessionDemo03</servlet-name>
<url-pattern>/s3</url-pattern>
</servlet-mapping>
<!-- 设置session默认的失效时间-->
<session-config>
<!-- 15分钟后session自动失效,以分钟为单位-->
<session-timeout>
15
</session-timeout>
</session-config>
</web-app>
Session和Cookie的区别:
- Cookie把用户的数据写给用户的浏览器,浏览器保存
- Session把用户的数据写到用户独占的Session中,服务器端保存,(可以保存多个,但最好保存重要的信息,减少服务器资源的浪费)
在网站中经常使用的信息,保存在session中,方便使用
八、JSP
1. 什么是JSP
Java Server Pages:Java服务器端页面,也和Servlet一样,用于开发动态web技术
假如用Servlet类实现JSP功能(心态可能会崩):
package com.laxsilence.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class JspTest extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
PrintWriter writer = resp.getWriter();
writer.write("<html>");
writer.write("<head>");
writer.write("<title>Title</title>");
writer.write("</head>");
writer.write("<body>");
writer.write("<h1>Hello,World</h1>");
writer.write("</body>");
writer.write("</html>");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
所以出现了JSP,JSP最大的特点:
- 写JSP就像在写HTML
- 区别:
- HTML只给用户提供静态的数据;
- JSP页面中可以嵌入java代码,为用户提供动态数据;
2. JSP原理
思路:JSP到底怎么执行的?
-
代码层面没有任何不同,打包后的target目录下,仍然是一个html页面
-
服务器内部工作
Tomcat的work目录;
IDEA使用tomcat的话,会在IDEA的tomcat中产生一个work目录:查找方法(IDEA导航栏-->help-->Show Log In Explorer,这个路径的上级路径中有个tomcat目录)



一直点下去,就会发现java文件和class文件:

也就是说:页面转换为了java程序
查看java文件,可以发下一些有趣的代码:

不错,这些代码就是我们在上边用Servlet类写的代码!!!
-
浏览器向服务器发送请求,不管访问什么资源,其实都是在访问Servlet!
即JSP最终会被转换为Java类!所以JSP本质上就是一个Servlet!
在源码中可以看到这样的三个方法:
//初始化 public void _jspINit(){ } //销毁 public void _jspDestroy(){ } //JSPService public void _jspService(HttpServletRequest request,HttpServletResponse response)-
判断请求属于哪种类型
-
内置了一些对象
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; // 响应 -
输出页面前增加的代码
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; -
以上的这些对象,我们可以在jsp页面中直接使用
-
jsp中还可以直接写java代码
使用<% %>就可以在里面写java代码了
使用<%= %>就可以获取某个字段的值了
例子:
<% String name = "DearLiu"; %> <!--最后在界面上会输出DearLiu--> name:<%=name%> -
每次编译执行的时候,在文件管理器中可以看到项目中的work目录消失,然后编译结束之后,才会重新出现,但这个时候新建的jsp页面,仍然没有被转换为java文件,直到在浏览器中访问该jsp页面,文件管理器中会瞬间出现新的jsp页面转换后的java文件
-
JSP页面的源码很大一部分是重复的,只有中间输出的一些会根据你jsp写的html代码发生改变。
-

在JSP页面中:
只要是Java代码,就会被原封不动的转换为java语句;
如果是HTML代码,就会转换为下面这样的格式输出到前端:
out.write("<html>\r\n")
3. JSP基础语法
补充:在新建Maven的时候不用自带的webapp框架,是不支持web项目的,那么如何让它支持web项目呢


但是这样web的路径还是稍微有点区别的,最好按照以前那种方式。
导入包,pom.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>com.laxsilence</groupId>
<artifactId>javaweb-jsp</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- Servlet的依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<!-- JSP的依赖-->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
</dependency>
<!-- jstl表达式的依赖-->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- standard表达式-->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
</project>
配置Tomcat,完成环境搭建。

JSP基础语法
任何语言都有自己的语法,Java中有,JSP作为java技术的一种应用,它拥有一些自己扩充的语法(了解,知道即可!)Java所有语法都支持!
JSP表达式:
<%-- JSP表达式
作用:用来将程序的输出,输出到客户端 <%= %>
--%>
<%= new java.util.Date()%>
JSP脚本片段
<%-- jsp脚本片段--%>
<%
int sum = 0;
for (int i = 0; i <= 100; i++) {
sum+=1;
}
out.println("<h1>Sum:"+sum+"</h1>");
%>
脚本片段的再实现
<%--
Created by IntelliJ IDEA.
User: 刘英俊
Date: 2022/4/20
Time: 20:18
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 = 0; i <= 100; i++) {
sum+=1;
}
out.println("<h1>Sum:"+sum+"</h1>");
%>
<%
int x = 10;
out.println(x);
%>
<p>这是一个JSP文档</p>
<%
// int x = 10; 报错,在同一个作用域,x只能有一个
out.println(x);
%>
<hr>
<%-- 在代码中嵌入HTML元素--%>
<%
for (int i = 0; i < 5; i++) {
%>
<h1>Hello,World! <%=i%> </h1>
<%
}
%>
</body>
</html>
查看转换后的java文件代码可以发现,这些所有的标签等等都被转换在_jspService这个方法中,那我们可不可以在这个方法外面创建变量呢,JSP支持这样的想法:
JSP声明
<%!
static {
System.out.println("Loading Servlet!");
}
private int globalVar = 0;
public void Liu(){
System.out.println("进入了Liu写的方法!");
}
%>
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表达式 %>
<%-- 注释 --%>
补充:HTML的注释:<!---->
通过在浏览器中查看源代码,可以发现HTML的注释仍然会出现在网页源代码中,但是JSP的就不会,提高了网页的安全性。
4. JSP指令
1. 错误界面的定制
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--一般page指令会放在最上面--%>
<%--定制错误界面--%>
<%@page errorPage="error/500.jsp" %>
<%--导包--%>
<%@ page import="java.util.*" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
int x = 1/0; //网页报错 500
%>
<%
new Date();
%>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--显示的声明这是一个错误页面--%>
<%@page isErrorPage="true" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<img src="/jsp/img/img.png" alt="500" width=100% height=100%>
</body>
</html>
补充:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- 也可以在这里定制错误页面,就不需要在jsp中声明指令了,但这里必须要重启tomcat-->
<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>
2. 网页公共部分的提取

common包:用来存放相同的界面,比如一个网站不同界面的头部导航栏是一样的
<%@ 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"></jsp:include>
<h1>网页主体</h1>
<jsp:include page="common/header.jsp"></jsp:include>
<%--建议使用后者,代码互补影响,比如说在公共页面有个变量i,那么使用jsp标签是不会报错的,但是使用@include会报错500,说已经定义过了--%>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<h1>我是footer</h1>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<h1>我是Header</h1>
5. 一些关于项目目录的问题

Maven是约定大于配置的,所以不要乱放,容易出问题!
6. JSP九大内置对象
- PageContext :用来存东西
- Request :用来存东西
- Response
- Session :用来存东西
- Application(ServletContext):用来存东西
- config(ServletConfig)
- out (resp.getWrite,用来输出)
- page
- Exception
演示:
<%@ 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取出,我们通过寻找的方式来
// pageContext.getAttribute();不用这种方式
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>
在不同的页面中获取:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
//取东西
// pageContext.getAttribute();不用这种方式
// 从pageContext取出,我们通过寻找的方式来 :
// 会从底层到高层(作用域):page-->request-->session-->application
// 类似于双亲委派机制;类加载器,一直找一个类,在反射那里学习过
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>
可以发现只能取到name3和name4,这里就体现了作用域的不同:
<%@ 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);
}
}
--%>
<%
pageContext.setAttribute("hello1","hello1",PageContext.SESSION_SCOPE);//可以手动设置作用域等价于:session.setAttribute();
%>
</body>
</html>
补充:查询方式取出
pageContext.findAttribute("name1");
// 从pageContext取出,我们通过寻找的方式来 :
// 会从底层到高层(作用域):page-->request-->session-->application
这个查找过程类似于:双亲委派机制中类加载器的过程。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--转发--%>
<%
pageContext.forward("/index.jsp");//访问该页面会跳转到index页面,但是地址栏不会发生改变
// 后端实现
// request.getRequestDispatcher("/index.jsp").forward(request,response);
%>
</body>
</html>
补充:如果在存放数据的页面,存完数据后转发到其他取数据的页面,那么request存的数据也能取到,因为转发是一次请求。
场景理解:
- request:客户端向服务器发送请求,产生的数据,用户看完就没用了,比如:新闻,用户看完就不管了!
- session:客户端向服务器发送请求,产生的数据,用户用完一会可能还会用,比如:购物车
- application:客户端向服务器发送请求,产生的数据,一个用户用完了,其他用户还可能使用,比如一个网站的访问量。
7. JSP标签、JSTL标签、EL表达式
EL表达式:${},需要导入包才能使用
<!-- jstl表达式的依赖-->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- standard表达式-->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
作用:
-
获取数据
-
执行运算
-
获取web 开发的常用对象
-
调用java方法 -
注意
<%--注意跟JQuery进行区分: JQuery:$() EL表达式:${} --%>
JSP标签:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>1</h1>
<%--拼接--%>
<%--jsp:include--%>
<%--转发--%>
<%--http://localhost:8080/jsp/jsptag.jsp?name=liu&age=12--%>
<jsp:forward page="/jsptag2.jsp">
<jsp:param name="name" value="liu"></jsp:param>
<jsp:param name="age" value="12"></jsp:param>
</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代码一样!
导入库之后500错误解决:复制仓库中的jstl包,到tomcat的lib目录下!
核心标签(掌握部分):
-
需要引入核心标签库
<%--引入核心标签库才能够使用核心标签--%> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> -
标签内容
-
测试:
if标签:
<%@ 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> <h4>If测试</h4> <hr> <form action="coreif.jsp" method="get"> <%-- 使用el表达式获取表单中的数据:value="${param.参数名}--%> <input type="text" name="username" value="${param.username}"> <input type="submit" value="登陆"> </form> <%--判断如果提交的用户是管理员,则登陆成功--%> <%--<%--%> <%-- if (request.getParameter("username").equals("admin")){--%> <%-- out.print("登陆成功!");--%> <%-- }--%> <%--%>--%> <c:if test="${param.username == 'admin'}" var="isAdmin"> <c:out value="管理员,欢迎您!"></c:out> </c:if> <c:out value="${isAdmin}"></c:out> </body> </html>when标签:
<%--引入核心标签库才能够使用核心标签--%> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <%--定义一个变量score,值为85--%> <c:set var="score" value="85"></c:set> <%--会按照先后顺序判断,满足就会跳出--%> <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 import="java.util.ArrayList" %><%--引入核心标签库才能够使用核心标签--%> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <% ArrayList<String> people = new ArrayList<>(); people.add(0,"张三"); people.add(1,"李四"); people.add(2,"王五"); people.add(3,"赵六"); people.add(4,"田七"); request.setAttribute("list",people); %> <%--要遍历的对象:items--%> <%--遍历出来的对象:var--%> <%--从哪里开始:begin--%> <%--到哪里结束:end--%> <%--步长:step--%> <c:forEach var="people" items="${list}"> <c:out value="${people}"></c:out> <br> </c:forEach> <hr> <c:forEach var="people" items="${list}" begin="1" end="3" step="2"> <c:out value="${people}"></c:out> <br> </c:forEach> </body> </html>
格式化标签:了解
SQL标签:了解
XML标签:了解
九、JavaBean
实体类
JavaBean有特定的写法:
- 必须有一个无参构造
- 属性必须私有化
- 必须有对应的get/set方法
- 一般用来和数据库的字段做映射 ORM;
ORM:对象关系映射
- 表-->类
- 字段-->属性
- 行记录-->对象
| id | name | age | address |
|---|---|---|---|
| 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,"北京");
...
}
实例:
-
表:
/* Navicat MySQL Data Transfer Source Server : localhost_3306 Source Server Version : 50555 Source Host : 127.0.0.1:3306 Source Database : smbms Target Server Type : MYSQL Target Server Version : 50555 File Encoding : 65001 Date: 2019-04-19 17:54:33 */ SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for smbms_address -- ---------------------------- DROP TABLE IF EXISTS `smbms_address`; CREATE TABLE `smbms_address` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `contact` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '联系人姓名', `addressDesc` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '收货地址明细', `postCode` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '邮编', `tel` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '联系人电话', `createdBy` bigint(20) DEFAULT NULL COMMENT '创建者', `creationDate` datetime DEFAULT NULL COMMENT '创建时间', `modifyBy` bigint(20) DEFAULT NULL COMMENT '修改者', `modifyDate` datetime DEFAULT NULL COMMENT '修改时间', `userId` bigint(20) DEFAULT NULL COMMENT '用户ID', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- ---------------------------- -- Records of smbms_address -- ---------------------------- INSERT INTO `smbms_address` VALUES ('1', '王丽', '北京市东城区东交民巷44号', '100010', '13678789999', '1', '2016-04-13 00:00:00', null, null, '1'); INSERT INTO `smbms_address` VALUES ('2', '张红丽', '北京市海淀区丹棱街3号', '100000', '18567672312', '1', '2016-04-13 00:00:00', null, null, '1'); INSERT INTO `smbms_address` VALUES ('3', '任志强', '北京市东城区美术馆后街23号', '100021', '13387906742', '1', '2016-04-13 00:00:00', null, null, '1'); INSERT INTO `smbms_address` VALUES ('4', '曹颖', '北京市朝阳区朝阳门南大街14号', '100053', '13568902323', '1', '2016-04-13 00:00:00', null, null, '2'); INSERT INTO `smbms_address` VALUES ('5', '李慧', '北京市西城区三里河路南三巷3号', '100032', '18032356666', '1', '2016-04-13 00:00:00', null, null, '3'); INSERT INTO `smbms_address` VALUES ('6', '王国强', '北京市顺义区高丽营镇金马工业区18号', '100061', '13787882222', '1', '2016-04-13 00:00:00', null, null, '3'); -- ---------------------------- -- Table structure for smbms_bill -- ---------------------------- DROP TABLE IF EXISTS `smbms_bill`; CREATE TABLE `smbms_bill` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `billCode` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '账单编码', `productName` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '商品名称', `productDesc` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '商品描述', `productUnit` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '商品单位', `productCount` decimal(20,2) DEFAULT NULL COMMENT '商品数量', `totalPrice` decimal(20,2) DEFAULT NULL COMMENT '商品总额', `isPayment` int(10) DEFAULT NULL COMMENT '是否支付(1:未支付 2:已支付)', `createdBy` bigint(20) DEFAULT NULL COMMENT '创建者(userId)', `creationDate` datetime DEFAULT NULL COMMENT '创建时间', `modifyBy` bigint(20) DEFAULT NULL COMMENT '更新者(userId)', `modifyDate` datetime DEFAULT NULL COMMENT '更新时间', `providerId` int(20) DEFAULT NULL COMMENT '供应商ID', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- ---------------------------- -- Records of smbms_bill -- ---------------------------- INSERT INTO `smbms_bill` VALUES ('1', 'BILL2016_001', '洗发水、护发素', '日用品-洗发、护发', '瓶', '500.00', '25000.00', '2', '1', '2014-12-14 13:02:03', '15', '2019-04-16 21:43:12', '13'); INSERT INTO `smbms_bill` VALUES ('2', 'BILL2016_002', '香皂、肥皂、药皂', '日用品-皂类', '块', '1000.00', '10000.00', '2', '1', '2016-03-23 04:20:40', null, null, '13'); INSERT INTO `smbms_bill` VALUES ('3', 'BILL2016_003', '大豆油', '食品-食用油', '斤', '300.00', '5890.00', '2', '1', '2014-12-14 13:02:03', null, null, '6'); INSERT INTO `smbms_bill` VALUES ('4', 'BILL2016_004', '橄榄油', '食品-进口食用油', '斤', '200.00', '9800.00', '2', '1', '2013-10-10 03:12:13', null, null, '7'); INSERT INTO `smbms_bill` VALUES ('5', 'BILL2016_005', '洗洁精', '日用品-厨房清洁', '瓶', '500.00', '7000.00', '2', '1', '2014-12-14 13:02:03', null, null, '9'); INSERT INTO `smbms_bill` VALUES ('6', 'BILL2016_006', '美国大杏仁', '食品-坚果', '袋', '300.00', '5000.00', '2', '1', '2016-04-14 06:08:09', null, null, '4'); INSERT INTO `smbms_bill` VALUES ('7', 'BILL2016_007', '沐浴液、精油', '日用品-沐浴类', '瓶', '500.00', '23000.00', '1', '1', '2016-07-22 10:10:22', null, null, '14'); INSERT INTO `smbms_bill` VALUES ('8', 'BILL2016_008', '不锈钢盘碗', '日用品-厨房用具', '个', '600.00', '6000.00', '2', '1', '2016-04-14 05:12:13', null, null, '14'); INSERT INTO `smbms_bill` VALUES ('9', 'BILL2016_009', '塑料杯', '日用品-杯子', '个', '350.00', '1750.00', '2', '1', '2016-02-04 11:40:20', null, null, '14'); INSERT INTO `smbms_bill` VALUES ('10', 'BILL2016_010', '豆瓣酱', '食品-调料', '瓶', '200.00', '2000.00', '2', '1', '2013-10-29 05:07:03', null, null, '8'); INSERT INTO `smbms_bill` VALUES ('11', 'BILL2016_011', '海之蓝', '饮料-国酒', '瓶', '50.00', '10000.00', '1', '1', '2016-04-14 16:16:00', null, null, '1'); INSERT INTO `smbms_bill` VALUES ('12', 'BILL2016_012', '芝华士', '饮料-洋酒', '瓶', '20.00', '6000.00', '1', '1', '2016-09-09 17:00:00', null, null, '1'); INSERT INTO `smbms_bill` VALUES ('13', 'BILL2016_013', '长城红葡萄酒', '饮料-红酒', '瓶', '60.00', '800.00', '2', '1', '2016-11-14 15:23:00', null, null, '1'); INSERT INTO `smbms_bill` VALUES ('14', 'BILL2016_014', '泰国香米', '食品-大米', '斤', '400.00', '5000.00', '2', '1', '2016-10-09 15:20:00', null, null, '3'); INSERT INTO `smbms_bill` VALUES ('15', 'BILL2016_015', '东北大米', '食品-大米', '斤', '600.00', '4000.00', '2', '1', '2016-11-14 14:00:00', null, null, '3'); INSERT INTO `smbms_bill` VALUES ('16', 'BILL2016_016', '可口可乐', '饮料', '瓶', '2000.00', '6000.00', '2', '1', '2012-03-27 13:03:01', null, null, '2'); INSERT INTO `smbms_bill` VALUES ('17', 'BILL2016_017', '脉动', '饮料', '瓶', '1500.00', '4500.00', '2', '1', '2016-05-10 12:00:00', null, null, '2'); -- ---------------------------- -- Table structure for smbms_provider -- ---------------------------- DROP TABLE IF EXISTS `smbms_provider`; CREATE TABLE `smbms_provider` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `proCode` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '供应商编码', `proName` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '供应商名称', `proDesc` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '供应商详细描述', `proContact` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '供应商联系人', `proPhone` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '联系电话', `proAddress` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '地址', `proFax` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '传真', `createdBy` bigint(20) DEFAULT NULL COMMENT '创建者(userId)', `creationDate` datetime DEFAULT NULL COMMENT '创建时间', `modifyDate` datetime DEFAULT NULL COMMENT '更新时间', `modifyBy` bigint(20) DEFAULT NULL COMMENT '更新者(userId)', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- ---------------------------- -- Records of smbms_provider -- ---------------------------- INSERT INTO `smbms_provider` VALUES ('1', 'BJ_GYS001', '北京三木堂商贸有限公司', '长期合作伙伴,主营产品:茅台、五粮液、郎酒、酒鬼酒、泸州老窖、赖茅酒、法国红酒等', '张国强', '13566669999', '北京市丰台区育芳园北路', '010-58858787', '1', '2013-03-21 16:52:07', '2019-04-12 16:44:03', '10'); INSERT INTO `smbms_provider` VALUES ('2', 'HB_GYS001', '石家庄帅益食品贸易有限公司', '长期合作伙伴,主营产品:饮料、水饮料、植物蛋白饮料、休闲食品、果汁饮料、功能饮料等', '王军', '13309094212', '河北省石家庄新华区', '0311-67738876', '1', '2016-04-13 04:20:40', null, null); INSERT INTO `smbms_provider` VALUES ('3', 'GZ_GYS001', '深圳市泰香米业有限公司', '初次合作伙伴,主营产品:良记金轮米,龙轮香米等', '郑程瀚', '13402013312', '广东省深圳市福田区深南大道6006华丰大厦', '0755-67776212', '1', '2014-03-21 16:56:07', null, null); INSERT INTO `smbms_provider` VALUES ('4', 'GZ_GYS002', '深圳市喜来客商贸有限公司', '长期合作伙伴,主营产品:坚果炒货.果脯蜜饯.天然花茶.营养豆豆.特色美食.进口食品.海味零食.肉脯肉', '林妮', '18599897645', '广东省深圳市福龙工业区B2栋3楼西', '0755-67772341', '1', '2013-03-22 16:52:07', null, null); INSERT INTO `smbms_provider` VALUES ('5', 'JS_GYS001', '兴化佳美调味品厂', '长期合作伙伴,主营产品:天然香辛料、鸡精、复合调味料', '徐国洋', '13754444221', '江苏省兴化市林湖工业区', '0523-21299098', '1', '2015-11-22 16:52:07', null, null); INSERT INTO `smbms_provider` VALUES ('6', 'BJ_GYS002', '北京纳福尔食用油有限公司', '长期合作伙伴,主营产品:山茶油、大豆油、花生油、橄榄油等', '马莺', '13422235678', '北京市朝阳区珠江帝景1号楼', '010-588634233', '1', '2012-03-21 17:52:07', null, null); INSERT INTO `smbms_provider` VALUES ('7', 'BJ_GYS003', '北京国粮食用油有限公司', '初次合作伙伴,主营产品:花生油、大豆油、小磨油等', '王驰', '13344441135', '北京大兴青云店开发区', '010-588134111', '1', '2016-04-13 00:00:00', null, null); INSERT INTO `smbms_provider` VALUES ('8', 'ZJ_GYS001', '慈溪市广和绿色食品厂', '长期合作伙伴,主营产品:豆瓣酱、黄豆酱、甜面酱,辣椒,大蒜等农产品', '薛圣丹', '18099953223', '浙江省宁波市慈溪周巷小安村', '0574-34449090', '1', '2013-11-21 06:02:07', null, null); INSERT INTO `smbms_provider` VALUES ('9', 'GX_GYS001', '优百商贸有限公司', '长期合作伙伴,主营产品:日化产品', '李立国', '13323566543', '广西南宁市秀厢大道42-1号', '0771-98861134', '1', '2013-03-21 19:52:07', null, null); INSERT INTO `smbms_provider` VALUES ('10', 'JS_GYS002', '南京火头军信息技术有限公司', '长期合作伙伴,主营产品:不锈钢厨具等', '陈女士', '13098992113', '江苏省南京市浦口区浦口大道1号新城总部大厦A座903室', '025-86223345', '1', '2013-03-25 16:52:07', null, null); INSERT INTO `smbms_provider` VALUES ('11', 'GZ_GYS003', '广州市白云区美星五金制品厂', '长期合作伙伴,主营产品:海绵床垫、坐垫、靠垫、海绵枕头、头枕等', '梁天', '13562276775', '广州市白云区钟落潭镇福龙路20号', '020-85542231', '1', '2016-12-21 06:12:17', null, null); INSERT INTO `smbms_provider` VALUES ('12', 'BJ_GYS004', '北京隆盛日化科技', '长期合作伙伴,主营产品:日化环保清洗剂,家居洗涤专卖、洗涤用品网、墙体除霉剂、墙面霉菌清除剂等', '孙欣', '13689865678', '北京市大兴区旧宫', '010-35576786', '1', '2014-11-21 12:51:11', null, null); INSERT INTO `smbms_provider` VALUES ('13', 'SD_GYS001', '山东豪克华光联合发展有限公司', '长期合作伙伴,主营产品:洗衣皂、洗衣粉、洗衣液、洗洁精、消杀类、香皂等', '吴洪转', '13245468787', '山东济阳济北工业区仁和街21号', '0531-53362445', '1', '2015-01-28 10:52:07', null, null); -- ---------------------------- -- Table structure for smbms_role -- ---------------------------- DROP TABLE IF EXISTS `smbms_role`; CREATE TABLE `smbms_role` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `roleCode` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '角色编码', `roleName` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '角色名称', `createdBy` bigint(20) DEFAULT NULL COMMENT '创建者', `creationDate` datetime DEFAULT NULL COMMENT '创建时间', `modifyBy` bigint(20) DEFAULT NULL COMMENT '修改者', `modifyDate` datetime DEFAULT NULL COMMENT '修改时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- ---------------------------- -- Records of smbms_role -- ---------------------------- INSERT INTO `smbms_role` VALUES ('1', 'SMBMS_ADMIN', '系统管理员', '1', '2016-04-13 00:00:00', null, null); INSERT INTO `smbms_role` VALUES ('2', 'SMBMS_MANAGER', '经理', '1', '2016-04-13 00:00:00', null, null); INSERT INTO `smbms_role` VALUES ('3', 'SMBMS_EMPLOYEE', '普通员工', '1', '2016-04-13 00:00:00', null, null); -- ---------------------------- -- Table structure for smbms_user -- ---------------------------- DROP TABLE IF EXISTS `smbms_user`; CREATE TABLE `smbms_user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `userCode` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '用户编码', `userName` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '用户名称', `userPassword` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '用户密码', `gender` int(10) DEFAULT NULL COMMENT '性别(1:女、 2:男)', `birthday` date DEFAULT NULL COMMENT '出生日期', `phone` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '手机', `address` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '地址', `userRole` int(10) DEFAULT NULL COMMENT '用户角色(取自角色表-角色id)', `createdBy` bigint(20) DEFAULT NULL COMMENT '创建者(userId)', `creationDate` datetime DEFAULT NULL COMMENT '创建时间', `modifyBy` bigint(20) DEFAULT NULL COMMENT '更新者(userId)', `modifyDate` datetime DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- ---------------------------- -- Records of smbms_user -- ---------------------------- INSERT INTO `smbms_user` VALUES ('1', 'wen', '系统管理员', '123', '1', '1997-01-01', '15200981234', '湖南省衡阳市蒸湘区南华大学', '1', '1', '2019-04-07 10:15:55', null, null); INSERT INTO `smbms_user` VALUES ('5', 'hanlubiao', '韩路彪', '0000000', '2', '1984-06-05', '18567542321', '北京市朝阳区北辰中心12号', '2', '1', '2014-12-31 19:52:09', null, null); INSERT INTO `smbms_user` VALUES ('6', 'zhanghua', '张华', '0000000', '1', '1983-06-15', '13544561111', '北京市海淀区学院路61号', '3', '1', '2013-02-11 10:51:17', null, null); INSERT INTO `smbms_user` VALUES ('7', 'wangyang', '王洋', '0000000', '2', '1982-12-31', '13444561124', '北京市海淀区西二旗辉煌国际16层', '3', '1', '2014-06-11 19:09:07', null, null); INSERT INTO `smbms_user` VALUES ('8', 'zhaoyan', '赵燕', '0000000', '1', '1986-03-07', '18098764545', '北京市海淀区回龙观小区10号楼', '3', '1', '2016-04-21 13:54:07', null, null); INSERT INTO `smbms_user` VALUES ('10', 'sunlei', '孙磊', '0000000', '2', '1981-01-04', '13387676765', '北京市朝阳区管庄新月小区12楼', '3', '1', '2015-05-06 10:52:07', null, null); INSERT INTO `smbms_user` VALUES ('11', 'sunxing', '孙兴', '0000000', '2', '1978-03-12', '13367890900', '北京市朝阳区建国门南大街10号', '3', '1', '2016-11-09 16:51:17', null, null); INSERT INTO `smbms_user` VALUES ('12', 'zhangchen', '张晨', '0000000', '1', '1986-03-28', '18098765434', '朝阳区管庄路口北柏林爱乐三期13号楼', '3', '1', '2016-08-09 05:52:37', '1', '2016-04-14 14:15:36'); INSERT INTO `smbms_user` VALUES ('13', 'dengchao', '邓超', '0000000', '2', '1981-11-04', '13689674534', '北京市海淀区北航家属院10号楼', '3', '1', '2016-07-11 08:02:47', null, null); INSERT INTO `smbms_user` VALUES ('14', 'yangguo', '杨过', '0000000', '2', '1980-01-01', '13388886623', '北京市朝阳区北苑家园茉莉园20号楼', '3', '1', '2015-02-01 03:52:07', null, null); INSERT INTO `smbms_user` VALUES ('15', 'test', 'test', '111', '1', '2019-04-16', '123456789', '南华大学', '1', '1', '2019-04-16 19:52:37', null, null); -
JavaBean 类,就是一一对应,就不详细写出来了,遵循上边的原则就可以

十、MVC三层架构
什么是MVC:model view controller 模型、视图、控制器
1. 早些年的架构
用户直接访问控制层,控制层就可以直接操作数据库;
Servlet-->CRUD-->数据库
程序十分臃肿,不利于维护
- Servlet代码中,处理请求,响应,视图跳转,处理JDBC,处理业务代码,处理逻辑代码
在架构中:没有什么是加一层解决不了的!JDBC也是如此!

2. MVC三层架构

Model:
- 业务处理:业务逻辑(Service)
- 数据持久层:CRUD(Dao)
View:
- 展示数据
- 提供链接发起Servlet请求
Controller(Servlet):
- 接受用户的请求:request,请求参数,session信息
- 交给业务层处理对应的代码
- 控制视图的跳转
登陆 --> 接收用户的登陆请求 --> 处理用户的请求(获取用户登陆的参数,username,password) --> 交给业务层处理登陆业务(判断用户名和密码是否正确)--> Dao层查询用户名和密码信息是否正确 --> 数据库
十一、Filter 过滤器(重点)
Filter:过滤器,用来过滤网站的数据,后续的一些框架都会用到
- 处理中文乱码
- 登陆验证
- ...

Filter开发步骤:
准备阶段:新建一个会乱码的servlet,并配置两个不同路径的访问请求,方便对比
package com.laxsilence.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ShowServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("您好,这里测试下乱码问题!");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- 配置Servlet-->
<servlet>
<servlet-name>ShowServlet</servlet-name>
<servlet-class>com.laxsilence.servlet.ShowServlet</servlet-class>
</servlet>
<!-- 可以通过两个路径来访问-->
<servlet-mapping>
<servlet-name>ShowServlet</servlet-name>
<url-pattern>/show</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ShowServlet</servlet-name>
<url-pattern>/servlet/show</url-pattern>
</servlet-mapping>
</web-app>
运行结果:

比如说这个页面,会有乱码,在servlet中也可以解决,但是如果servlet太多的话,就是一个很重复的工作,所以我们使用过滤器!
-
导包
<?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>com.laxsilence</groupId> <artifactId>javaweb-filter</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!-- servlet依赖--> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> </dependency> <!-- jsp依赖--> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.3</version> </dependency> <!-- jstl表达式依赖--> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>javax.servlet.jsp.jstl</groupId> <artifactId>jstl-api</artifactId> <version>1.2</version> </dependency> <!-- standard标签依赖--> <dependency> <groupId>taglibs</groupId> <artifactId>standard</artifactId> <version>1.1.2</version> </dependency> <!-- JDBC依赖--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> </dependencies> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> </project> -
编写过滤器
-
import 包的时候
import javax.servlet.Filter; -
过滤器代码:
package com.laxsilence.filter; import javax.servlet.*; import java.io.IOException; public class CharacterEncodingFilter implements Filter { // 初始化 // web服务器启动就已经初始化了,因为要随时等待监听 @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("过滤器初始化:"); } // 过滤器 // chain:链 // 1. 过滤器中的所有代码,在过滤特定请求(需要配置)的时候都会执行 // 2. 必须要让过滤器继续同行(必须写!) @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { response.setCharacterEncoding("utf-8"); request.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=UTF-8"); System.out.println("CharacterEncodingFilter执行前:"); chain.doFilter(request, response);//让我们的请求继续走,如果不写那么程序到这里就被拦截停止了!就是说过滤了这一层后,请求继续向下交接,可以有多个过滤器! System.out.println("CharacterEncodingFilter执行后:"); } // 销毁 // 服务器关闭的时候,过滤器会销毁 @Override public void destroy() { System.out.println("过滤器销毁!"); } }
-
-
配置xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!-- 配置Servlet--> <servlet> <servlet-name>ShowServlet</servlet-name> <servlet-class>com.laxsilence.servlet.ShowServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>ShowServlet</servlet-name> <url-pattern>/show</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>ShowServlet</servlet-name> <url-pattern>/servlet/show</url-pattern> </servlet-mapping> <!-- 配置过滤器--> <!-- 如果有多个过滤器,那么按照配置顺序--> <filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>com.laxsilence.filter.CharacterEncodingFilter</filter-class> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <!-- 只要是/servlet这个访问路径下的任何请求,都会经过这个过滤器--> <url-pattern>/servlet/*</url-pattern> </filter-mapping> </web-app> -
运行程序
-
不走过滤路径:

-
走过滤路径:

-
过滤路径每请求一次,都会执行一次过滤器中的代码!
十二、Listener 监听器
实现一个监听器的接口,有N种,太多太多了,但是一通百通

步骤:
-
实现监听器的接口
package com.laxsilence.listener; import javax.servlet.ServletContext; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; //统计网站在线人数:统计session public class OnLineCountListener implements HttpSessionListener { // 创建Session监听:看你的一举一动 // 一旦创建一个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(); se.getSession().invalidate(); 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. 自动销毁 // 在web.xml中配置 // <session-config> // <!-- 分钟为单位--> // <session-timeout>15</session-timeout> // </session-config> } -
注册监听器
<!-- 注册监听器--> <listener> <listener-class>com.laxsilence.listener.OnLineCountListener</listener-class> </listener> -
现在监听器用到的很少,看具体情况是否使用
十三、过滤器、监听器常见应用
监听器:GUI编程笔记中查阅,GUI编程中经常使用
package com.laxsilence.listener;
import javafx.scene.layout.Pane;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
//参考GUI编程笔记
public class TestPanel {
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(188, 188, 245));
panel.setBounds(50,50,300,300);
panel.setBackground(new Color(255, 154, 154));
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(0);
}
@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);
}
});
}
}
实现登录监听
用户登录之后才能进入主页!用户注销之后就不能进入主页了。
-
登录界面:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>登陆</title> </head> <body> <form action="${pageContext.request.contextPath}/servlet/login" method="post"> <input type="text" name="username"> <input type="submit" value="登陆"> </form> </body> </html> -
主页:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>主页</title> </head> <body> <% Object user_session = request.getSession().getAttribute("USER_SESSION"); if (user_session == null){ response.sendRedirect("/login.jsp"); } %> <h1>主页</h1> <p><a href="${pageContext.request.contextPath}/servlet/logout">注销</a></p> </body> </html> -
错误界面
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h1>错误</h1> <h3>没有权限,用户名错误</h3> <a href="login.jsp">返回登陆页面</a> </body> </html> -
登录Servlet
package com.laxsilence.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.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("/filterTest/sys/success.jsp"); }else{ //登陆失败 resp.sendRedirect("/filterTest/error.jsp"); } } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } } -
注销Servlet
package com.laxsilence.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.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("/filterTest/login.jsp"); }else { resp.sendRedirect("/filterTest/login.jsp"); } } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } } -
过滤器:
package com.laxsilence.filter; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.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 request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest request1 = (HttpServletRequest) request; HttpServletResponse response1 = (HttpServletResponse) response; if (request1.getSession().getAttribute("USER_SESSION")==null){ response1.sendRedirect("/filterTest/login.jsp"); } chain.doFilter(request1, response1); } @Override public void destroy() { } } -
常量类:
package com.laxsilence.util; public class Constant { // 这样后边使用这个常量的地方只需要在这里修改一次就可以了,不需要到处寻找修改 public static final String USER_SESSION = "USER_SESSION"; } -
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!-- 配置Servlet--> <servlet> <servlet-name>LoginServlet</servlet-name> <servlet-class>com.laxsilence.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>com.laxsilence.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>com.laxsilence.filter.SysFilter</filter-class> </filter> <filter-mapping> <filter-name>sysFilter</filter-name> <url-pattern>/sys/*</url-pattern> </filter-mapping> <!-- 注册监听器--> <listener> <listener-class>com.laxsilence.listener.OnLineCountListener</listener-class> </listener> <!-- session 销毁--> <session-config> <!-- 分钟为单位--> <session-timeout>15</session-timeout> </session-config> </web-app>
总结:
- 当有常量多次要用到的时候,可以建立一个常量类,方便后期修改,不然需要导出寻找这一个常量
- 比如以前对vip功能进行划分的时候就是用过滤器,在session里面存放vip的等级,如果等级匹配,就开放相对应的权限
十四、JDBC的复习与回顾
详情:查看MySQL详解笔记内容
什么是JDBC:Java DataBase Connection,Java 连接数据库

需要jar包的支持:
- java.sql
- javax.sql
- mysql-connecter-java...(必须要导入)
环境搭建:
-
创建数据库表
use javawebjdbc; create table users( id int(10) primary key not null auto_increment, `name` varchar(50) not null , `password` varchar(40) not null , email varchar(60) not null , 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; -
导入包
<?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>com.laxsilence</groupId> <artifactId>javaweb-jdbc</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!-- mySql驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.15</version> </dependency> </dependencies> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> </project> -
编写JDBC程序
普通Statement对象:
package com.laxsilence.test; import java.sql.*; public class TestJDBC { public static void main(String[] args) throws ClassNotFoundException, SQLException { // 配置信息 String url="jdbc:mysql://localhost:3306/javawebjdbc?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC"; String username = "root"; String password = "123456"; // 1. 加载驱动 Class.forName("com.mysql.jdbc.Driver"); // 2. 连接数据库,conn代表数据库 Connection conn = DriverManager.getConnection(url, username, password); // 3. 向数据库发送sql的对象 Statement statement = conn.createStatement(); // 4. 编写sql String sql = "select * from users;"; // 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(); } }预编译PrepareStatement对象(防止SQL注入):
package com.laxsilence.test; import java.sql.*; /** * @ClassName TestJDBC2 * @Description TODO * @Author 刘英俊 * @Date 2022/4/22 17:24 * @Version 1.0 **/ public class TestJDBC2 { public static void main(String[] args) throws ClassNotFoundException, SQLException { // 配置信息 String url="jdbc:mysql://localhost:3306/javawebjdbc?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC"; String username = "root"; String password = "19142001lj"; // 1. 加载驱动 Class.forName("com.mysql.jdbc.Driver"); // 2. 连接数据库,conn代表数据库 Connection conn = DriverManager.getConnection(url, username, password); // 3. 编写sql String sql = "insert into users (id, name, password, email, birthday)\n" + "values (?,?,?,?,?); "; // 4. 预编译 赋值 PreparedStatement ps = conn.prepareStatement(sql); ps.setInt(1,4); ps.setString(2,"赵六"); ps.setString(3,"123456"); ps.setString(4,"zl@123"); ps.setDate(5,new Date(System.currentTimeMillis())); // 5. 执行SQL int i = ps.executeUpdate(); if (i>0){ System.out.println("插入成功!"); } // 6. 关闭连接,释放资源 ps.close(); conn.close(); } }
补充:单元测试Junit
如果需要测试一个方法,我们一般都要在main方法里新建这个方法的类的一个对象,然后用这个对象调用方法
导入Junit依赖后,通过注解方式用来测试:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
</dependency>
package com.laxsilence.test;
import org.junit.Test;
public class TestJDBC3 {
//不需要写main方法也可以执行
@Test
public void test(){
//测试内容
}
}
点进注解:
package org.junit;
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 {
Class<? extends Throwable> expected() default Test.None.class;
long timeout() default 0L;
public static class None extends Throwable {
private static final long serialVersionUID = 1L;
private None() {
}
}
}
运行(点击小箭头就可以运行):

测试报错:

测试成功:

没有进行测试的图标:

测试完成之后的图标:

事务:
要么都成功,要么都失败!ACID原则:保证数据的安全
开启事务
事务提交
事务回滚
关闭事务
经典转账例子,查看前一篇笔记即可,点击查看:MySQL笔记。
十五、SMBMS 超市订单管理系统 实战
完整源码链接: https://pan.baidu.com/s/1pkNE_-qWjxeZBP7S2q_JTw
提取码:31cu
功能架构:

1. 数据库
(1)数据库结构

(2)建表语句
CREATE DATABASE `smbms`;
USE `smbms`;
DROP TABLE IF EXISTS `smbms_address`;
CREATE TABLE `smbms_address` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`contact` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '联系人姓名',
`addressDesc` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '收货地址明细',
`postCode` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '邮编',
`tel` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '联系人电话',
`createdBy` bigint(20) DEFAULT NULL COMMENT '创建者',
`creationDate` datetime DEFAULT NULL COMMENT '创建时间',
`modifyBy` bigint(20) DEFAULT NULL COMMENT '修改者',
`modifyDate` datetime DEFAULT NULL COMMENT '修改时间',
`userId` bigint(20) DEFAULT NULL COMMENT '用户ID',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
insert into `smbms_address`(`id`,`contact`,`addressDesc`,`postCode`,`tel`,`createdBy`,`creationDate`,`modifyBy`,`modifyDate`,`userId`) values (1,'王丽','北京市东城区东交民巷44号','100010','13678789999',1,'2016-04-13 00:00:00',NULL,NULL,1),(2,'张红丽','北京市海淀区丹棱街3号','100000','18567672312',1,'2016-04-13 00:00:00',NULL,NULL,1),(3,'任志强','北京市东城区美术馆后街23号','100021','13387906742',1,'2016-04-13 00:00:00',NULL,NULL,1),(4,'曹颖','北京市朝阳区朝阳门南大街14号','100053','13568902323',1,'2016-04-13 00:00:00',NULL,NULL,2),(5,'李慧','北京市西城区三里河路南三巷3号','100032','18032356666',1,'2016-04-13 00:00:00',NULL,NULL,3),(6,'王国强','北京市顺义区高丽营镇金马工业区18号','100061','13787882222',1,'2016-04-13 00:00:00',NULL,NULL,3);
DROP TABLE IF EXISTS `smbms_bill`;
CREATE TABLE `smbms_bill` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`billCode` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '账单编码',
`productName` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '商品名称',
`productDesc` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '商品描述',
`productUnit` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '商品单位',
`productCount` decimal(20,2) DEFAULT NULL COMMENT '商品数量',
`totalPrice` decimal(20,2) DEFAULT NULL COMMENT '商品总额',
`isPayment` int(10) DEFAULT NULL COMMENT '是否支付(1:未支付 2:已支付)',
`createdBy` bigint(20) DEFAULT NULL COMMENT '创建者(userId)',
`creationDate` datetime DEFAULT NULL COMMENT '创建时间',
`modifyBy` bigint(20) DEFAULT NULL COMMENT '更新者(userId)',
`modifyDate` datetime DEFAULT NULL COMMENT '更新时间',
`providerId` bigint(20) DEFAULT NULL COMMENT '供应商ID',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
insert into `smbms_bill`(`id`,`billCode`,`productName`,`productDesc`,`productUnit`,`productCount`,`totalPrice`,`isPayment`,`createdBy`,`creationDate`,`modifyBy`,`modifyDate`,`providerId`) values (2,'BILL2016_002','香皂、肥皂、药皂','日用品-皂类','块','1000.00','10000.00',2,1,'2016-03-23 04:20:40',NULL,NULL,13),(3,'BILL2016_003','大豆油','食品-食用油','斤','300.00','5890.00',2,1,'2014-12-14 13:02:03',NULL,NULL,6),(4,'BILL2016_004','橄榄油','食品-进口食用油','斤','200.00','9800.00',2,1,'2013-10-10 03:12:13',NULL,NULL,7),(5,'BILL2016_005','洗洁精','日用品-厨房清洁','瓶','500.00','7000.00',2,1,'2014-12-14 13:02:03',NULL,NULL,9),(6,'BILL2016_006','美国大杏仁','食品-坚果','袋','300.00','5000.00',2,1,'2016-04-14 06:08:09',NULL,NULL,4),(7,'BILL2016_007','沐浴液、精油','日用品-沐浴类','瓶','500.00','23000.00',1,1,'2016-07-22 10:10:22',NULL,NULL,14),(8,'BILL2016_008','不锈钢盘碗','日用品-厨房用具','个','600.00','6000.00',2,1,'2016-04-14 05:12:13',NULL,NULL,14),(9,'BILL2016_009','塑料杯','日用品-杯子','个','350.00','1750.00',2,1,'2016-02-04 11:40:20',NULL,NULL,14),(10,'BILL2016_010','豆瓣酱','食品-调料','瓶','200.00','2000.00',2,1,'2013-10-29 05:07:03',NULL,NULL,8),(11,'BILL2016_011','海之蓝','饮料-国酒','瓶','50.00','10000.00',1,1,'2016-04-14 16:16:00',NULL,NULL,1),(12,'BILL2016_012','芝华士','饮料-洋酒','瓶','20.00','6000.00',1,1,'2016-09-09 17:00:00',NULL,NULL,1),(13,'BILL2016_013','长城红葡萄酒','饮料-红酒','瓶','60.00','800.00',2,1,'2016-11-14 15:23:00',NULL,NULL,1),(14,'BILL2016_014','泰国香米','食品-大米','斤','400.00','5000.00',2,1,'2016-10-09 15:20:00',NULL,NULL,3),(15,'BILL2016_015','东北大米','食品-大米','斤','600.00','4000.00',2,1,'2016-11-14 14:00:00',NULL,NULL,3),(16,'BILL2016_016','可口可乐','饮料','瓶','2000.00','6000.00',2,1,'2012-03-27 13:03:01',NULL,NULL,2),(17,'BILL2016_017','脉动','饮料','瓶','1500.00','4500.00',2,1,'2016-05-10 12:00:00',NULL,NULL,2),(18,'BILL2016_018','哇哈哈','饮料','瓶','2000.00','4000.00',2,1,'2015-11-24 15:12:03',NULL,NULL,2);
DROP TABLE IF EXISTS `smbms_provider`;
CREATE TABLE `smbms_provider` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`proCode` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '供应商编码',
`proName` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '供应商名称',
`proDesc` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '供应商详细描述',
`proContact` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '供应商联系人',
`proPhone` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '联系电话',
`proAddress` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '地址',
`proFax` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '传真',
`createdBy` bigint(20) DEFAULT NULL COMMENT '创建者(userId)',
`creationDate` datetime DEFAULT NULL COMMENT '创建时间',
`modifyDate` datetime DEFAULT NULL COMMENT '更新时间',
`modifyBy` bigint(20) DEFAULT NULL COMMENT '更新者(userId)',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
insert into `smbms_provider`(`id`,`proCode`,`proName`,`proDesc`,`proContact`,`proPhone`,`proAddress`,`proFax`,`createdBy`,`creationDate`,`modifyDate`,`modifyBy`) values (1,'BJ_GYS001','北京三木堂商贸有限公司','长期合作伙伴,主营产品:茅台、五粮液、郎酒、酒鬼酒、泸州老窖、赖茅酒、法国红酒等','张国强','13566667777','北京市丰台区育芳园北路','010-58858787',1,'2013-03-21 16:52:07',NULL,NULL),(2,'HB_GYS001','石家庄帅益食品贸易有限公司','长期合作伙伴,主营产品:饮料、水饮料、植物蛋白饮料、休闲食品、果汁饮料、功能饮料等','王军','13309094212','河北省石家庄新华区','0311-67738876',1,'2016-04-13 04:20:40',NULL,NULL),(3,'GZ_GYS001','深圳市泰香米业有限公司','初次合作伙伴,主营产品:良记金轮米,龙轮香米等','郑程瀚','13402013312','广东省深圳市福田区深南大道6006华丰大厦','0755-67776212',1,'2014-03-21 16:56:07',NULL,NULL),(4,'GZ_GYS002','深圳市喜来客商贸有限公司','长期合作伙伴,主营产品:坚果炒货.果脯蜜饯.天然花茶.营养豆豆.特色美食.进口食品.海味零食.肉脯肉','林妮','18599897645','广东省深圳市福龙工业区B2栋3楼西','0755-67772341',1,'2013-03-22 16:52:07',NULL,NULL),(5,'JS_GYS001','兴化佳美调味品厂','长期合作伙伴,主营产品:天然香辛料、鸡精、复合调味料','徐国洋','13754444221','江苏省兴化市林湖工业区','0523-21299098',1,'2015-11-22 16:52:07',NULL,NULL),(6,'BJ_GYS002','北京纳福尔食用油有限公司','长期合作伙伴,主营产品:山茶油、大豆油、花生油、橄榄油等','马莺','13422235678','北京市朝阳区珠江帝景1号楼','010-588634233',1,'2012-03-21 17:52:07',NULL,NULL),(7,'BJ_GYS003','北京国粮食用油有限公司','初次合作伙伴,主营产品:花生油、大豆油、小磨油等','王驰','13344441135','北京大兴青云店开发区','010-588134111',1,'2016-04-13 00:00:00',NULL,NULL),(8,'ZJ_GYS001','慈溪市广和绿色食品厂','长期合作伙伴,主营产品:豆瓣酱、黄豆酱、甜面酱,辣椒,大蒜等农产品','薛圣丹','18099953223','浙江省宁波市慈溪周巷小安村','0574-34449090',1,'2013-11-21 06:02:07',NULL,NULL),(9,'GX_GYS001','优百商贸有限公司','长期合作伙伴,主营产品:日化产品','李立国','13323566543','广西南宁市秀厢大道42-1号','0771-98861134',1,'2013-03-21 19:52:07',NULL,NULL),(10,'JS_GYS002','南京火头军信息技术有限公司','长期合作伙伴,主营产品:不锈钢厨具等','陈女士','13098992113','江苏省南京市浦口区浦口大道1号新城总部大厦A座903室','025-86223345',1,'2013-03-25 16:52:07',NULL,NULL),(11,'GZ_GYS003','广州市白云区美星五金制品厂','长期合作伙伴,主营产品:海绵床垫、坐垫、靠垫、海绵枕头、头枕等','梁天','13562276775','广州市白云区钟落潭镇福龙路20号','020-85542231',1,'2016-12-21 06:12:17',NULL,NULL),(12,'BJ_GYS004','北京隆盛日化科技','长期合作伙伴,主营产品:日化环保清洗剂,家居洗涤专卖、洗涤用品网、墙体除霉剂、墙面霉菌清除剂等','孙欣','13689865678','北京市大兴区旧宫','010-35576786',1,'2014-11-21 12:51:11',NULL,NULL),(13,'SD_GYS001','山东豪克华光联合发展有限公司','长期合作伙伴,主营产品:洗衣皂、洗衣粉、洗衣液、洗洁精、消杀类、香皂等','吴洪转','13245468787','山东济阳济北工业区仁和街21号','0531-53362445',1,'2015-01-28 10:52:07',NULL,NULL),(14,'JS_GYS003','无锡喜源坤商行','长期合作伙伴,主营产品:日化品批销','周一清','18567674532','江苏无锡盛岸西路','0510-32274422',1,'2016-04-23 11:11:11',NULL,NULL),(15,'ZJ_GYS002','乐摆日用品厂','长期合作伙伴,主营产品:各种中、高档塑料杯,塑料乐扣水杯(密封杯)、保鲜杯(保鲜盒)、广告杯、礼品杯','王世杰','13212331567','浙江省金华市义乌市义东路','0579-34452321',1,'2016-08-22 10:01:30',NULL,NULL);
DROP TABLE IF EXISTS `smbms_role`;
CREATE TABLE `smbms_role` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`roleCode` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '角色编码',
`roleName` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '角色名称',
`createdBy` bigint(20) DEFAULT NULL COMMENT '创建者',
`creationDate` datetime DEFAULT NULL COMMENT '创建时间',
`modifyBy` bigint(20) DEFAULT NULL COMMENT '修改者',
`modifyDate` datetime DEFAULT NULL COMMENT '修改时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
insert into `smbms_role`(`id`,`roleCode`,`roleName`,`createdBy`,`creationDate`,`modifyBy`,`modifyDate`) values (1,'SMBMS_ADMIN','系统管理员',1,'2016-04-13 00:00:00',NULL,NULL),(2,'SMBMS_MANAGER','经理',1,'2016-04-13 00:00:00',NULL,NULL),(3,'SMBMS_EMPLOYEE','普通员工',1,'2016-04-13 00:00:00',NULL,NULL);
DROP TABLE IF EXISTS `smbms_user`;
CREATE TABLE `smbms_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`userCode` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '用户编码',
`userName` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '用户名称',
`userPassword` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '用户密码',
`gender` int(10) DEFAULT NULL COMMENT '性别(1:女、 2:男)',
`birthday` date DEFAULT NULL COMMENT '出生日期',
`phone` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '手机',
`address` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '地址',
`userRole` bigint(20) DEFAULT NULL COMMENT '用户角色(取自角色表-角色id)',
`createdBy` bigint(20) DEFAULT NULL COMMENT '创建者(userId)',
`creationDate` datetime DEFAULT NULL COMMENT '创建时间',
`modifyBy` bigint(20) DEFAULT NULL COMMENT '更新者(userId)',
`modifyDate` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
insert into `smbms_user`(`id`,`userCode`,`userName`,`userPassword`,`gender`,`birthday`,`phone`,`address`,`userRole`,`createdBy`,`creationDate`,`modifyBy`,`modifyDate`) values (1,'admin','系统管理员','1234567',1,'1983-10-10','13688889999','北京市海淀区成府路207号',1,1,'2013-03-21 16:52:07',NULL,NULL),(2,'liming','李明','0000000',2,'1983-12-10','13688884457','北京市东城区前门东大街9号',2,1,'2014-12-31 19:52:09',NULL,NULL),(5,'hanlubiao','韩路彪','0000000',2,'1984-06-05','18567542321','北京市朝阳区北辰中心12号',2,1,'2014-12-31 19:52:09',NULL,NULL),(6,'zhanghua','张华','0000000',1,'1983-06-15','13544561111','北京市海淀区学院路61号',3,1,'2013-02-11 10:51:17',NULL,NULL),(7,'wangyang','王洋','0000000',2,'1982-12-31','13444561124','北京市海淀区西二旗辉煌国际16层',3,1,'2014-06-11 19:09:07',NULL,NULL),(8,'zhaoyan','赵燕','0000000',1,'1986-03-07','18098764545','北京市海淀区回龙观小区10号楼',3,1,'2016-04-21 13:54:07',NULL,NULL),(10,'sunlei','孙磊','0000000',2,'1981-01-04','13387676765','北京市朝阳区管庄新月小区12楼',3,1,'2015-05-06 10:52:07',NULL,NULL),(11,'sunxing','孙兴','0000000',2,'1978-03-12','13367890900','北京市朝阳区建国门南大街10号',3,1,'2016-11-09 16:51:17',NULL,NULL),(12,'zhangchen','张晨','0000000',1,'1986-03-28','18098765434','朝阳区管庄路口北柏林爱乐三期13号楼',3,1,'2016-08-09 05:52:37',1,'2016-04-14 14:15:36'),(13,'dengchao','邓超','0000000',2,'1981-11-04','13689674534','北京市海淀区北航家属院10号楼',3,1,'2016-07-11 08:02:47',NULL,NULL),(14,'yangguo','杨过','0000000',2,'1980-01-01','13388886623','北京市朝阳区北苑家园茉莉园20号楼',3,1,'2015-02-01 03:52:07',NULL,NULL),(15,'zhaomin','赵敏','0000000',1,'1987-12-04','18099897657','北京市昌平区天通苑3区12号楼',2,1,'2015-09-12 12:02:12',NULL,NULL);
2. 项目搭建准备阶段
考虑使用不使用Maven?或者导入jar包
(1)搭建一个mavenweb项目
(2)配置tomcat
(3)测试项目是否能够跑起来
(4)导入项目需要的依赖
- javax.servlet.jsp-api
- servlet-api
- mysql-connector-java
- jstl-api
- standard
- junit
- fastjson
(5)创建项目包结构

(6)编写实体类
ORM映射:表-类映射

Bill:
package com.laxsilence.pojo;
import java.math.BigDecimal;
import java.util.Date;
/**
* 订单
*/
public class Bill {
private Integer id; //id
private String billCode; //账单编码
private String productName; //商品名称
private String productDesc; //商品描述
private String productUnit; //商品单位
private BigDecimal productCount; //商品数量
private BigDecimal totalPrice; //总金额
private Integer isPayment; //是否支付
private Integer providerId; //供应商ID
private Integer createdBy; //创建者
private Date creationDate; //创建时间
private Integer modifyBy; //更新者
private Date modifyDate;//更新时间
private String providerName;//供应商名称
@Override
public String toString() {
return "Bill{" +
"id=" + id +
", billCode='" + billCode + '\'' +
", productName='" + productName + '\'' +
", productDesc='" + productDesc + '\'' +
", productUnit='" + productUnit + '\'' +
", productCount=" + productCount +
", totalPrice=" + totalPrice +
", isPayment=" + isPayment +
", providerId=" + providerId +
", createdBy=" + createdBy +
", creationDate=" + creationDate +
", modifyBy=" + modifyBy +
", modifyDate=" + modifyDate +
", providerName='" + providerName + '\'' +
'}';
}
public String getProviderName() {
return providerName;
}
public void setProviderName(String providerName) {
this.providerName = providerName;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getBillCode() {
return billCode;
}
public void setBillCode(String billCode) {
this.billCode = billCode;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public String getProductDesc() {
return productDesc;
}
public void setProductDesc(String productDesc) {
this.productDesc = productDesc;
}
public String getProductUnit() {
return productUnit;
}
public void setProductUnit(String productUnit) {
this.productUnit = productUnit;
}
public BigDecimal getProductCount() {
return productCount;
}
public void setProductCount(BigDecimal productCount) {
this.productCount = productCount;
}
public BigDecimal getTotalPrice() {
return totalPrice;
}
public void setTotalPrice(BigDecimal totalPrice) {
this.totalPrice = totalPrice;
}
public Integer getIsPayment() {
return isPayment;
}
public void setIsPayment(Integer isPayment) {
this.isPayment = isPayment;
}
public Integer getProviderId() {
return providerId;
}
public void setProviderId(Integer providerId) {
this.providerId = providerId;
}
public Integer getCreatedBy() {
return createdBy;
}
public void setCreatedBy(Integer createdBy) {
this.createdBy = createdBy;
}
public Date getCreationDate() {
return creationDate;
}
public void setCreationDate(Date creationDate) {
this.creationDate = creationDate;
}
public Integer getModifyBy() {
return modifyBy;
}
public void setModifyBy(Integer modifyBy) {
this.modifyBy = modifyBy;
}
public Date getModifyDate() {
return modifyDate;
}
public void setModifyDate(Date modifyDate) {
this.modifyDate = modifyDate;
}
}
Provider:
package com.laxsilence.pojo;
import java.util.Date;
public class Provider {
private Integer id; //id
private String proCode; //供应商编码
private String proName; //供应商名称
private String proDesc; //供应商描述
private String proContact; //供应商联系人
private String proPhone; //供应商电话
private String proAddress; //供应商地址
private String proFax; //供应商传真
private Integer createdBy; //创建者
private Date creationDate; //创建时间
private Integer modifyBy; //更新者
private Date modifyDate;//更新时间
@Override
public String toString() {
return "Provider{" +
"id=" + id +
", proCode='" + proCode + '\'' +
", proName='" + proName + '\'' +
", proDesc='" + proDesc + '\'' +
", proContact='" + proContact + '\'' +
", proPhone='" + proPhone + '\'' +
", proAddress='" + proAddress + '\'' +
", proFax='" + proFax + '\'' +
", createdBy=" + createdBy +
", creationDate=" + creationDate +
", modifyBy=" + modifyBy +
", modifyDate=" + modifyDate +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getProCode() {
return proCode;
}
public void setProCode(String proCode) {
this.proCode = proCode;
}
public String getProName() {
return proName;
}
public void setProName(String proName) {
this.proName = proName;
}
public String getProDesc() {
return proDesc;
}
public void setProDesc(String proDesc) {
this.proDesc = proDesc;
}
public String getProContact() {
return proContact;
}
public void setProContact(String proContact) {
this.proContact = proContact;
}
public String getProPhone() {
return proPhone;
}
public void setProPhone(String proPhone) {
this.proPhone = proPhone;
}
public String getProAddress() {
return proAddress;
}
public void setProAddress(String proAddress) {
this.proAddress = proAddress;
}
public String getProFax() {
return proFax;
}
public void setProFax(String proFax) {
this.proFax = proFax;
}
public Integer getCreatedBy() {
return createdBy;
}
public void setCreatedBy(Integer createdBy) {
this.createdBy = createdBy;
}
public Date getCreationDate() {
return creationDate;
}
public void setCreationDate(Date creationDate) {
this.creationDate = creationDate;
}
public Integer getModifyBy() {
return modifyBy;
}
public void setModifyBy(Integer modifyBy) {
this.modifyBy = modifyBy;
}
public Date getModifyDate() {
return modifyDate;
}
public void setModifyDate(Date modifyDate) {
this.modifyDate = modifyDate;
}
}
Role:
package com.laxsilence.pojo;
import java.util.Date;
public class Role {
private Integer id; //id
private String roleCode; //角色编码
private String roleName; //角色名称
private Integer createdBy; //创建者
private Date creationDate; //创建时间
private Integer modifyBy; //更新者
private Date modifyDate;//更新时间
@Override
public String toString() {
return "Role{" +
"id=" + id +
", roleCode='" + roleCode + '\'' +
", roleName='" + roleName + '\'' +
", createdBy=" + createdBy +
", creationDate=" + creationDate +
", modifyBy=" + modifyBy +
", modifyDate=" + modifyDate +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getRoleCode() {
return roleCode;
}
public void setRoleCode(String roleCode) {
this.roleCode = roleCode;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public Integer getCreatedBy() {
return createdBy;
}
public void setCreatedBy(Integer createdBy) {
this.createdBy = createdBy;
}
public Date getCreationDate() {
return creationDate;
}
public void setCreationDate(Date creationDate) {
this.creationDate = creationDate;
}
public Integer getModifyBy() {
return modifyBy;
}
public void setModifyBy(Integer modifyBy) {
this.modifyBy = modifyBy;
}
public Date getModifyDate() {
return modifyDate;
}
public void setModifyDate(Date modifyDate) {
this.modifyDate = modifyDate;
}
}
User:
package com.laxsilence.pojo;
import java.util.Date;
public class User {
private Integer id; //id
private String userCode; //用户编码
private String userName; //用户名称
private String userPassword; //用户密码
private Integer gender; //性别
private Date birthday; //出生日期
private String phone; //电话
private String address; //地址
private Integer userRole; //用户角色
private Integer createdBy; //创建者
private Date creationDate; //创建时间
private Integer modifyBy; //更新者
private Date modifyDate; //更新时间
private Integer age;//年龄
private String userRoleName; //用户角色名称
public String getUserRoleName() {
return userRoleName;
}
public void setUserRoleName(String userRoleName) {
this.userRoleName = userRoleName;
}
public Integer getAge() {
/*long time = System.currentTimeMillis()-birthday.getTime();
Integer age = Long.valueOf(time/365/24/60/60/1000).IntegerValue();*/
Date date = new Date();
Integer age = date.getYear() - birthday.getYear();
return age;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", userCode='" + userCode + '\'' +
", userName='" + userName + '\'' +
", userPassword='" + userPassword + '\'' +
", gender=" + gender +
", birthday=" + birthday +
", phone='" + phone + '\'' +
", address='" + address + '\'' +
", userRole=" + userRole +
", createdBy=" + createdBy +
", creationDate=" + creationDate +
", modifyBy=" + modifyBy +
", modifyDate=" + modifyDate +
", age=" + age +
", userRoleName='" + userRoleName + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserCode() {
return userCode;
}
public void setUserCode(String userCode) {
this.userCode = userCode;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserPassword() {
return userPassword;
}
public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Integer getUserRole() {
return userRole;
}
public void setUserRole(Integer userRole) {
this.userRole = userRole;
}
public Integer getCreatedBy() {
return createdBy;
}
public void setCreatedBy(Integer createdBy) {
this.createdBy = createdBy;
}
public Date getCreationDate() {
return creationDate;
}
public void setCreationDate(Date creationDate) {
this.creationDate = creationDate;
}
public Integer getModifyBy() {
return modifyBy;
}
public void setModifyBy(Integer modifyBy) {
this.modifyBy = modifyBy;
}
public Date getModifyDate() {
return modifyDate;
}
public void setModifyDate(Date modifyDate) {
this.modifyDate = modifyDate;
}
}
(7)编写基础公共类
-
数据库配置文件

driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/smbms1?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC username=root password=123456 -
编写数据库的公共类

package com.laxsilence.dao; import java.io.IOException; import java.io.InputStream; import java.sql.*; import java.util.Properties; /** * @ClassName BaseDao * @Description TODO 操作数据库的公共类 * @Author 刘英俊 * @Date 2022/4/26 14:01 * @Version 1.0 **/ public class BaseDao { private static String driver; private static String url; private static String username; private static String password; //静态代码块,类加载的时候就会初始化 static { Properties properties = new Properties(); //通过类加载器读取对应的资源 InputStream is = BaseDao.class.getClassLoader().getResourceAsStream("db.properties"); try { properties.load(is); } catch (IOException e) { e.printStackTrace(); } driver = properties.getProperty("driver"); url = properties.getProperty("url"); username = properties.getProperty("username"); password = properties.getProperty("password"); } //获取数据库的连接 public static Connection getConnection(){ Connection connection = null; try { Class.forName(driver); connection = DriverManager.getConnection(url, username, password); } catch ( Exception e) { e.printStackTrace(); } return connection; } //编写查询公共类 public static ResultSet execute(Connection connection,String sql,Object[] params,ResultSet resultSet,PreparedStatement preparedStatement) throws SQLException { //预编译的sql,在后面执行就可以,不用再传进去 preparedStatement = connection.prepareStatement(sql); for (int i = 0; i < params.length; i++) { //setObject,占位符从1开始,数组从0开始 preparedStatement.setObject(i+1,params[i]); } resultSet = preparedStatement.executeQuery(); return resultSet; } //编写增删改公共方法 public static int execute(Connection connection,String sql,Object[] params,PreparedStatement preparedStatement) throws SQLException { preparedStatement = connection.prepareStatement(sql); for (int i = 0; i < params.length; i++) { //setObject,占位符从1开始,数组从0开始 preparedStatement.setObject(i+1,params[i]); } int updateRows = preparedStatement.executeUpdate(); return updateRows; } //释放资源 public static boolean closeResource(Connection connection,PreparedStatement preparedStatement,ResultSet resultSet){ boolean flag = true; if (resultSet != null){ try { resultSet.close(); //gc回收 resultSet = null; } catch (SQLException e) { e.printStackTrace(); flag = false; } } if (resultSet != null){ try { preparedStatement.close(); //gc回收 preparedStatement = null; } catch (SQLException e) { e.printStackTrace(); flag = false; } } if (resultSet != null){ try { connection.close(); //gc回收 connection = null; } catch (SQLException e) { e.printStackTrace(); flag = false; } } return flag; } } -
编写字符过滤器
package com.laxsilence.filter; import javax.servlet.*; import java.io.IOException; /** * @ClassName CharacterEncodingFilter * @Description TODO * @Author 刘英俊 * @Date 2022/4/26 14:29 * @Version 1.0 **/ public class CharacterEncodingFilter implements Filter { public void init(FilterConfig filterConfig) throws ServletException { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setCharacterEncoding("utf-8"); response.setCharacterEncoding("utf-8"); chain.doFilter(request,response); } public void destroy() { } }在web.xml中进行配置
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0" metadata-complete="false"> <!-- 字符编码过滤器--> <filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>com.laxsilence.filter.CharacterEncodingFilter</filter-class> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
(8)导入静态资源

3. 登录功能实现

(1)编写登录前端界面
login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>系统登录 - 超市订单管理系统</title>
<link type="text/css" rel="stylesheet" href="${pageContext.request.contextPath}/css/style.css"/>
</head>
<body class="login_bg">
<section class="loginBox">
<header class="loginHeader">
<h1>超市订单管理系统</h1>
</header>
<section class="loginCont">
<form class="loginForm" action="${pageContext.request.contextPath}/login.do" method="post" name="actionForm"
id="actionForm">
<div class="info">${error}</div>
<div class="inputbox">
<label for="userCode">用户名:</label>
<input type="text" class="input-text" id="userCode" name="userCode" placeholder="请输入用户名" required/>
</div>
<div class="inputbox">
<label for="userPassword">密码:</label>
<input type="password" id="userPassword" name="userPassword" placeholder="请输入密码" required/>
</div>
<div class="subBtn">
<input type="submit" value="登录"/>
<input type="reset" value="重置"/>
</div>
</form>
</section>
</section>
</body>
</html>
(2)配置欢迎首页
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0"
metadata-complete="false">
<!-- 字符编码过滤器-->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>com.laxsilence.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--设置欢迎界面-->
<welcome-file-list>
<welcome-file>login.jsp</welcome-file>
</welcome-file-list>
</web-app>
(3)编写Dao层用户登录的接口

package com.laxsilence.dao.user;
import com.laxsilence.pojo.User;
import java.sql.Connection;
import java.sql.SQLException;
public interface UserDao {
//得到登录的用户
public User getLoginUser(Connection connection,String userCode) throws SQLException;
}
(4)编写Dao接口的实现类

package com.laxsilence.dao.user;
import com.laxsilence.dao.BaseDao;
import com.laxsilence.pojo.User;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* @ClassName UserDaoImpl
* @Description TODO
* @Author 刘英俊
* @Date 2022/4/26 14:51
* @Version 1.0
**/
public class UserDaoImpl implements UserDao {
public User getLoginUser(Connection connection, String userCode) throws SQLException {
PreparedStatement pstm = null;
ResultSet rs = null;
User user = null;
if (connection != null) {
String sql = "select * from smbms_user where userCode=?;";
Object[] params = {userCode};
rs = BaseDao.execute(connection, pstm, rs, sql, params);
if (rs.next()) {
user = new User();
user = new User();
user.setId(rs.getInt("id"));
user.setUserCode(rs.getString("userCode"));
user.setUserName(rs.getString("userName"));
user.setUserPassword(rs.getString("userPassword"));
user.setGender(rs.getInt("gender"));
user.setBirthday(rs.getDate("birthday"));
user.setPhone(rs.getString("phone"));
user.setAddress(rs.getString("address"));
user.setUserRole(rs.getInt("userRole"));
user.setCreatedBy(rs.getInt("createdBy"));
user.setCreationDate(rs.getTimestamp("creationDate"));
user.setModifyBy(rs.getInt("modifyBy"));
user.setModifyDate(rs.getTimestamp("modifyDate"));
}
BaseDao.closeResource(null, pstm, rs);
}
return user;
}
}
(5)业务层接口

package com.laxsilence.service.user;
import com.laxsilence.pojo.User;
public interface UserService {
//用户登陆
public User login(String userCode, String password);
}
(6)业务层实现

package com.laxsilence.service.user;
import com.laxsilence.dao.BaseDao;
import com.laxsilence.dao.user.UserDao;
import com.laxsilence.dao.user.UserDaoImpl;
import com.laxsilence.pojo.User;
import org.junit.jupiter.api.Test;
import java.sql.Connection;
import java.sql.SQLException;
/**
* @ClassName UserServiceImpl
* @Description TODO
* @Author 刘英俊
* @Date 2022/4/26 15:05
* @Version 1.0
**/
public class UserServiceImpl implements UserService{
//业务层都会调用dao层,所以要引入Dao层
private UserDao userDao;
public UserServiceImpl(){
userDao = new UserDaoImpl();
}
public User login(String userCode, String password) {
Connection connection = null;
User user = null;
try {
connection = BaseDao.getConnection();
//通过业务层调用对应的数据库操作
user = userDao.getLoginUser(connection,userCode);
} catch (SQLException e) {
e.printStackTrace();
}finally {
BaseDao.closeResource(connection,null,null);
}
//匹配密码
if (null != user) {
if (!user.getUserPassword().equals(password)) {
user = null;
}
}
return user;
}
@Test
public void test(){
UserServiceImpl userService = new UserServiceImpl();
User admin = userService.login("admin", "sfsrfr");
System.out.println(admin.getUserPassword());
}
}
(7)编写Servlet

package com.laxsilence.servlet.user;
import com.laxsilence.pojo.User;
import com.laxsilence.service.user.UserService;
import com.laxsilence.service.user.UserServiceImpl;
import com.laxsilence.util.Constants;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @ClassName LoginServlet
* @Description TODO
* @Author 刘英俊
* @Date 2022/4/26 15:21
* @Version 1.0
**/
public class LoginServlet extends HttpServlet {
//servlet:控制层,要调用业务层代码
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("进入LoginServlet。。。");
//获取用户名和密码
String userCode = req.getParameter("userCode");
String userPassword = req.getParameter("userPassword");
//和数据库中的密码进行对比,调用业务层
UserService userService = new UserServiceImpl();
User user = userService.login(userCode, userPassword);//查询到登陆的人
if (user!=null){//查有此人可以登陆
//将用户的信息放到Session中;
req.getSession().setAttribute(Constants.USER_SESSION,user);
//跳转到主页
resp.sendRedirect("jsp/frame.jsp");
}else {
//查无此人 无法登陆,顺带提示,有用户名或密码错误
req.setAttribute("error","用户名或密码不正确");//前端使用EL表达式获取
req.getRequestDispatcher("login.jsp").forward(req,resp);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
(8)注册Servlet
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0"
metadata-complete="false">
<!-- Servlet-->
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>com.laxsilence.servlet.user.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/login.do</url-pattern>
</servlet-mapping>
<!-- 字符编码过滤器-->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>com.laxsilence.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--设置欢迎界面-->
<welcome-file-list>
<welcome-file>login.jsp</welcome-file>
</welcome-file-list>
</web-app>
(9)编写首页界面

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8" %>
<%@include file="/jsp/common/head.jsp" %>
<div class="right">
<img class="wColck" src="${pageContext.request.contextPath }/images/clock.jpg" alt=""/>
<div class="wFont">
<h2>${userSession.userName }</h2>
<p>欢迎来到超市订单管理系统!</p>
</div>
</div>
</section>
<%@include file="/jsp/common/foot.jsp" %>
4. 登录功能优化(注销)
注销功能:移除Session,返回登陆界面
(1)编写Servlet

package com.laxsilence.servlet.user;
import com.laxsilence.util.Constants;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @ClassName LoginOut
* @Description TODO
* @Author 刘英俊
* @Date 2022/4/26 15:44
* @Version 1.0
**/
public class LoginOutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//移除用户的Session
req.getSession().removeAttribute(Constants.USER_SESSION);
resp.sendRedirect(req.getContextPath()+"/login.jsp");//返回登陆界面
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
(2)注册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_4_0.xsd"
version="4.0"
metadata-complete="false">
<!-- Servlet-->
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>com.laxsilence.servlet.user.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/login.do</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>LogOutServlet</servlet-name>
<servlet-class>com.laxsilence.servlet.user.LoginOutServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LogOutServlet</servlet-name>
<url-pattern>/jsp/logout.do</url-pattern>
</servlet-mapping>
<!-- 字符编码过滤器-->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>com.laxsilence.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--设置欢迎界面-->
<welcome-file-list>
<welcome-file>login.jsp</welcome-file>
</welcome-file-list>
</web-app>
(3)登陆拦截优化

package com.laxsilence.filter;
import com.laxsilence.pojo.User;
import com.laxsilence.util.Constants;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @ClassName SysFilter
* @Description TODO
* @Author 刘英俊
* @Date 2022/4/26 15:53
* @Version 1.0
**/
public class SysFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
//过滤器,从Session中获取用户
User user = (User) request.getSession().getAttribute(Constants.USER_SESSION);
if (user == null){
//已经被移除或者注销了
response.sendRedirect("/smbms/error.jsp");
}else {
chain.doFilter(req,resp);
}
}
public void destroy() {
}
}
(4)注册登陆拦截
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0"
metadata-complete="false">
<!-- Servlet-->
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>com.laxsilence.servlet.user.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/login.do</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>LogOutServlet</servlet-name>
<servlet-class>com.laxsilence.servlet.user.LoginOutServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LogOutServlet</servlet-name>
<url-pattern>/jsp/logout.do</url-pattern>
</servlet-mapping>
<!-- 字符编码过滤器-->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>com.laxsilence.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 用户登陆过滤器-->
<filter>
<filter-name>SysFilter</filter-name>
<filter-class>com.laxsilence.filter.SysFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SysFilter</filter-name>
<url-pattern>/jsp/*</url-pattern>
</filter-mapping>
<!--设置欢迎界面-->
<welcome-file-list>
<welcome-file>login.jsp</welcome-file>
</welcome-file-list>
</web-app>
(5)登陆拦截页面

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>无权限</title>
</head>
<body>
<h1>请登录后再访问该页面!</h1>
<a href="login.jsp">返回</a>
</body>
</html>
(6)测试目前所有功能
包括注销之后,能否不通过登陆,通过URL进入首页
5. 密码修改(修改新密码)

(1)编写密码修改界面

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %>
<%@include file="/jsp/common/head.jsp" %>
<div class="right">
<div class="location">
<strong>你现在所在的位置是:</strong>
<span>密码修改页面</span>
</div>
<div class="providerAdd">
<form id="userForm" name="userForm" method="post" action="${pageContext.request.contextPath }/jsp/user.do">
<input type="hidden" name="method" value="savepwd">
<!--div的class 为error是验证错误,ok是验证成功-->
<div class="info">${message}</div>
<div class="">
<label for="oldPassword">旧密码:</label>
<input type="password" name="oldpassword" id="oldpassword" value="">
<font color="red"></font>
</div>
<div>
<label for="newPassword">新密码:</label>
<input type="password" name="newpassword" id="newpassword" value="">
<font color="red"></font>
</div>
<div>
<label for="rnewpassword">确认新密码:</label>
<input type="password" name="rnewpassword" id="rnewpassword" value="">
<font color="red"></font>
</div>
<div class="providerAddBtn">
<!--<a href="#">保存</a>-->
<input type="button" name="save" id="save" value="保存" class="input-button">
</div>
</form>
</div>
</div>
</section>
<%@include file="/jsp/common/foot.jsp" %>
<script type="text/javascript" src="${pageContext.request.contextPath }/js/pwdmodify.js"></script>
(2)编写密码修改UserDao接口
//修改当前用户密码
public int updatePwd(Connection connection,int id,int password) throws SQLException;
(3)编写UserDao实现类
//修改当前用户密码
public int updatePwd(Connection connection, int id, int password) throws SQLException {
PreparedStatement pstm = null;
int execute = 0;
if (connection!=null){
String sql = "update smbms_user set userPassword = ? where id = ?";
Object params[] = {password,id};
execute = BaseDao.execute(connection, pstm, sql, params);
BaseDao.closeResource(null,pstm,null);
}
return execute;
}
(4)编写UserService接口
//修改密码
public boolean updatePwd(int id, int password);
(5)编写UserService实现类
public boolean updatePwd(int id, int password) {
Connection connection = null;
boolean flag = false;
//修改密码
try {
connection = BaseDao.getConnection();
if (userDao.updatePwd(connection,id,password)>0){
flag = true;
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
BaseDao.closeResource(connection,null,null);
}
return flag;
}
(6)编写UserServlet类
注意提取出方法,实现类的复用

package com.laxsilence.servlet.user;
import com.laxsilence.pojo.User;
import com.laxsilence.service.user.UserService;
import com.laxsilence.service.user.UserServiceImpl;
import com.laxsilence.util.Constants;
import com.mysql.cj.util.StringUtils;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @ClassName LoginServlet
* @Description TODO
* @Author 刘英俊
* @Date 2022/4/26 15:21
* @Version 1.0
**/
//实现Servlet复用
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getParameter("method");
if (method.equals("savepwd")&&method!=null){
this.updatePwd(req,resp);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
public void updatePwd(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//从Session里面拿id
Object o = req.getSession().getAttribute(Constants.USER_SESSION);
String newpassword = req.getParameter("newpassword");
boolean flag = false;
if (o!=null && !StringUtils.isNullOrEmpty(newpassword)){
UserService userService = new UserServiceImpl();
flag = userService.updatePwd(((User) o).getId(), newpassword);
if (flag){
req.setAttribute("message","修改密码成功,请退出使用新密码登陆!");
//密码修改成功,移除当前session
req.getSession().removeAttribute(Constants.USER_SESSION);
}else {
req.setAttribute("message","密码修改失败!");
}
}else {
req.setAttribute("message","新密码有问题!");
}
req.getRequestDispatcher("pwdmodify.jsp").forward(req,resp);
}
}
(7)注册UserServlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0"
metadata-complete="false">
<!-- Servlet-->
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>com.laxsilence.servlet.user.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/login.do</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>LogOutServlet</servlet-name>
<servlet-class>com.laxsilence.servlet.user.LoginOutServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LogOutServlet</servlet-name>
<url-pattern>/jsp/logout.do</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>UserServlet</servlet-name>
<servlet-class>com.laxsilence.servlet.user.UserServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UserServlet</servlet-name>
<url-pattern>/jsp/user.do</url-pattern>
</servlet-mapping>
<!-- 字符编码过滤器-->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>com.laxsilence.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 用户登陆过滤器-->
<filter>
<filter-name>SysFilter</filter-name>
<filter-class>com.laxsilence.filter.SysFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SysFilter</filter-name>
<url-pattern>/jsp/*</url-pattern>
</filter-mapping>
<!--设置欢迎界面-->
<welcome-file-list>
<welcome-file>login.jsp</welcome-file>
</welcome-file-list>
</web-app>
6. 优化密码修改,验证旧密码
使用Ajax,Json
阿里巴巴的fastjson
js中实现ajax内容,后续详细讲解ajax和json
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.80</version>
</dependency>
从session中获取旧密码
(1)设置session过期时间
模拟实际开发
<!-- 默认session过期时间-->
<session-config>
<session-timeout>30</session-timeout>
</session-config>
(2)原密码验证UserServlet
package com.laxsilence.servlet.user;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.laxsilence.pojo.User;
import com.laxsilence.service.user.UserService;
import com.laxsilence.service.user.UserServiceImpl;
import com.laxsilence.util.Constants;
import com.mysql.cj.util.StringUtils;
import com.mysql.cj.xdevapi.JsonArray;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
/**
* @ClassName LoginServlet
* @Description TODO
* @Author 刘英俊
* @Date 2022/4/26 15:21
* @Version 1.0
**/
//实现Servlet复用
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getParameter("method");
if (method.equals("savepwd")&&method!=null){
this.updatePwd(req,resp);
}else if (method.equals("pwdmodify")&&method!=null){
this.pwdModify(req,resp);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
//验证旧密码,session中有用户的密码
public void pwdModify(HttpServletRequest req, HttpServletResponse resp){
//从Session里面拿password
Object o = req.getSession().getAttribute(Constants.USER_SESSION);
String oldpassword = req.getParameter("oldpassword");
//万能的map:结果集
HashMap<String, String> resultMap = new HashMap<String, String>();
if (o==null){//session失效了
resultMap.put("result","sessionerror");
}else if (StringUtils.isNullOrEmpty(oldpassword)){//输入的密码为空
resultMap.put("result","error");
}else{
String userPassword = ((User) o).getUserPassword();//session中用户的密码
if(oldpassword.equals(userPassword)){
resultMap.put("result","true");
}else {//密码输入不正确
resultMap.put("result","false");
}
}
PrintWriter writer = null;
try {
resp.setContentType("application/json");
writer = resp.getWriter();
// 阿里巴巴Json工具类,用来转换格式,需要将Map转为JSON
writer.write(JSONArray.toJSONString(resultMap));
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
可以发下上边的密码修改,是直接换了个界面,从主页跳转到了密码修改界面:

那么如何不跳转见面,而是通过请求访问呢,比如这样:

7. 用户管理实现
展示角色,展示用户,要分页...
所以说,一个页面可以实现多个查询 ,不同的表,不同的电脑,不同的数据库等等

(1)导入编写的分页工具类

package com.laxsilence.util;
public class PageSupport {
//当前页码-来自于用户输入
private int currentPageNo = 1;
//总数量(表)
private int totalCount = 0;
//页面容量
private int pageSize = 0;
//总页数-totalCount/pageSize(+1)
private int totalPageCount = 1;
public int getCurrentPageNo() {
return currentPageNo;
}
public void setCurrentPageNo(int currentPageNo) {
if (currentPageNo > 0) {
this.currentPageNo = currentPageNo;
}
}
public int getTotalCount() {
return totalCount;
}
public void setTotalCount(int totalCount) {
if (totalCount > 0) {
this.totalCount = totalCount;
//设置总页数
this.setTotalPageCountByRs();
}
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
if (pageSize > 0) {
this.pageSize = pageSize;
}
}
public int getTotalPageCount() {
return totalPageCount;
}
public void setTotalPageCount(int totalPageCount) {
this.totalPageCount = totalPageCount;
}
public void setTotalPageCountByRs() {
if (this.totalCount % this.pageSize == 0) {
this.totalPageCount = this.totalCount / this.pageSize;
} else if (this.totalCount % this.pageSize > 0) {
this.totalPageCount = this.totalCount / this.pageSize + 1;
} else {
this.totalPageCount = 0;
}
}
}
(2)用户列表页面和分页页面编写
userlist.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %>
<%@include file="/jsp/common/head.jsp" %>
<div class="right">
<div class="location">
<strong>你现在所在的位置是:</strong>
<span>用户管理页面</span>
</div>
<div class="search">
<form method="get" action="${pageContext.request.contextPath }/jsp/user.do">
<input name="method" value="query" class="input-text" type="hidden">
<span>用户名:</span>
<input name="queryname" class="input-text" type="text" value="${queryUserName }">
<span>用户角色:</span>
<select name="queryUserRole">
<c:if test="${roleList != null }">
<option value="0">--请选择--</option>
<c:forEach var="role" items="${roleList}">
<option
<c:if test="${role.id == queryUserRole }">selected="selected"</c:if>
value="${role.id}">${role.roleName}</option>
</c:forEach>
</c:if>
</select>
<input type="hidden" name="pageIndex" value="1"/>
<input value="查 询" type="submit" id="searchbutton">
<a href="${pageContext.request.contextPath}/jsp/useradd.jsp">添加用户</a>
</form>
</div>
<!--用户-->
<table class="providerTable" cellpadding="0" cellspacing="0">
<tr class="firstTr">
<th width="10%">用户编码</th>
<th width="20%">用户名称</th>
<th width="10%">性别</th>
<th width="10%">年龄</th>
<th width="10%">电话</th>
<th width="10%">用户角色</th>
<th width="30%">操作</th>
</tr>
<c:forEach var="user" items="${userList }" varStatus="status">
<tr>
<td>
<span>${user.userCode }</span>
</td>
<td>
<span>${user.userName }</span>
</td>
<td>
<span>
<c:if test="${user.gender==1}">男</c:if>
<c:if test="${user.gender==2}">女</c:if>
</span>
</td>
<td>
<span>${user.age}</span>
</td>
<td>
<span>${user.phone}</span>
</td>
<td>
<span>${user.userRoleName}</span>
</td>
<td>
<span><a class="viewUser" href="javascript:;" userid=${user.id } username=${user.userName }><img
src="${pageContext.request.contextPath }/images/read.png" alt="查看" title="查看"/></a></span>
<span><a class="modifyUser" href="javascript:;" userid=${user.id } username=${user.userName }><img
src="${pageContext.request.contextPath }/images/xiugai.png" alt="修改" title="修改"/></a></span>
<span><a class="deleteUser" href="javascript:;" userid=${user.id } username=${user.userName }><img
src="${pageContext.request.contextPath }/images/schu.png" alt="删除" title="删除"/></a></span>
</td>
</tr>
</c:forEach>
</table>
<input type="hidden" id="totalPageCount" value="${totalPageCount}"/>
<c:import url="rollpage.jsp">
<c:param name="totalCount" value="${totalCount}"/>
<c:param name="currentPageNo" value="${currentPageNo}"/>
<c:param name="totalPageCount" value="${totalPageCount}"/>
</c:import>
</div>
</section>
<!--点击删除按钮后弹出的页面-->
<div class="zhezhao"></div>
<div class="remove" id="removeUse">
<div class="removerChid">
<h2>提示</h2>
<div class="removeMain">
<p>你确定要删除该用户吗?</p>
<a href="#" id="yes">确定</a>
<a href="#" id="no">取消</a>
</div>
</div>
</div>
<%@include file="/jsp/common/foot.jsp" %>
<script type="text/javascript" src="${pageContext.request.contextPath }/js/userlist.js"></script>
rollpage.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
</script>
</head>
<body>
<div class="page-bar">
<ul class="page-num-ul clearfix">
<li>共${param.totalCount }条记录 ${param.currentPageNo }/${param.totalPageCount }页</li>
<c:if test="${param.currentPageNo > 1}">
<a href="javascript:page_nav(document.forms[0],1);">首页</a>
<a href="javascript:page_nav(document.forms[0],${param.currentPageNo-1});">上一页</a>
</c:if>
<c:if test="${param.currentPageNo < param.totalPageCount }">
<a href="javascript:page_nav(document.forms[0],${param.currentPageNo+1 });">下一页</a>
<a href="javascript:page_nav(document.forms[0],${param.totalPageCount });">最后一页</a>
</c:if>
</ul>
<span class="page-go-form"><label>跳转至</label>
<input type="text" name="inputPage" id="inputPage" class="page-key" />页
<button type="button" class="page-btn" onClick='jump_to(document.forms[0],document.getElementById("inputPage").value)'>GO</button>
</span>
</div>
</body>
<script type="text/javascript" src="${pageContext.request.contextPath }/js/rollpage.js"></script>
</html>
(3)获取用户数量
-
UserDao
//根据用户名或者角色查询用户总数 public int getUserCount(Connection connection,String username,String userRole); -
UserDaoImpl
package com.laxsilence.dao.user; import com.laxsilence.dao.BaseDao; import com.laxsilence.pojo.User; import com.mysql.cj.util.StringUtils; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; /** * @ClassName UserDaoImpl * @Description TODO * @Author 刘英俊 * @Date 2022/4/26 14:51 * @Version 1.0 **/ public class UserDaoImpl implements UserDao { //根据用户名或者角色查询用户总数 public int getUserCount(Connection connection, String username, int userRole) throws SQLException{ PreparedStatement pstm = null; ResultSet rs = null; int count = 0; if (connection!=null){ StringBuffer sql = new StringBuffer(); sql.append("select count(1) as count from smbms_user u,smbms_role r where u.userRole = r.id"); ArrayList<Object> list = new ArrayList<Object>();//存放我们的参数 if (!StringUtils.isNullOrEmpty(username)){ sql.append(" and u.username like ?"); list.add("%"+username+"%");//index 0 } if (userRole>0){ sql.append(" and u.userRole = ?"); list.add(userRole);//index 1 } //把list转换为数组 Object[] params = list.toArray(); System.out.println("UserDaoImpl->getUserCount:"+sql.toString()); rs = BaseDao.execute(connection, pstm, rs, sql.toString(), params); if (rs.next()){ count = rs.getInt("count");//从结果集获取最终的数量 } BaseDao.closeResource(null,pstm,rs); } return count; } } -
UserService
//获取用户记录数 public int getUserCount(String username,int userRole); -
UserServiceImpl
package com.laxsilence.service.user; import com.laxsilence.dao.BaseDao; import com.laxsilence.dao.user.UserDao; import com.laxsilence.dao.user.UserDaoImpl; import com.laxsilence.pojo.User; import org.junit.jupiter.api.Test; import java.sql.Connection; import java.sql.SQLException; /** * @ClassName UserServiceImpl * @Description TODO * @Author 刘英俊 * @Date 2022/4/26 15:05 * @Version 1.0 **/ public class UserServiceImpl implements UserService{ //业务层都会调用dao层,所以要引入Dao层 private UserDao userDao; public UserServiceImpl(){ userDao = new UserDaoImpl(); } //获取用户记录数 public int getUserCount(String username, int userRole) { Connection connection = null; int count = 0; try { connection = BaseDao.getConnection(); count = userDao.getUserCount(connection,username,userRole); } catch (SQLException e) { e.printStackTrace(); }finally { BaseDao.closeResource(connection,null,null); } return count; } @Test public void testUserCount(){ UserServiceImpl userService = new UserServiceImpl(); int userCount = userService.getUserCount(null,0); System.out.println(userCount); } }
(4)获取用户列表
-
UserDao
//通过查询获取用户列表 public List<User> getUserList(Connection connection, String userName, int userRole, int currentPageNo, int pageSize)throws Exception; -
UserDaoImpl
//通过查询获取用户列表 public List<User> getUserList(Connection connection, String userName, int userRole, int currentPageNo, int pageSize) throws Exception { PreparedStatement pstm = null; ResultSet rs = null; List<User> userList = new ArrayList<User>(); if (connection != null) { StringBuffer sql = new StringBuffer(); sql.append("select u.*,r.roleName as userRoleName from smbms_user u,smbms_role r where u.userRole = r.id"); List<Object> list = new ArrayList<Object>(); if (!StringUtils.isNullOrEmpty(userName)) { sql.append(" and u.userName like ?"); list.add("%" + userName + "%"); } if (userRole > 0) { sql.append(" and u.userRole = ?"); list.add(userRole); } //在mysql数据库中,分页使用 limit startIndex,pageSize ; 总数 sql.append(" order by creationDate DESC limit ?,?"); currentPageNo = (currentPageNo - 1) * pageSize; list.add(currentPageNo); list.add(pageSize); Object[] params = list.toArray(); System.out.println("sql ----> " + sql.toString()); rs = BaseDao.execute(connection, pstm, rs, sql.toString(), params); while (rs.next()) { User _user = new User(); _user.setId(rs.getInt("id")); _user.setUserCode(rs.getString("userCode")); _user.setUserName(rs.getString("userName")); _user.setGender(rs.getInt("gender")); _user.setBirthday(rs.getDate("birthday")); _user.setPhone(rs.getString("phone")); _user.setUserRole(rs.getInt("userRole")); _user.setUserRoleName(rs.getString("userRoleName")); userList.add(_user); } BaseDao.closeResource(null, pstm, rs); } return userList; } -
UserService
//获取用户列表 public List<User> getUserList(String queryUserName, int queryUserRole, int currentPageNo, int pageSize); -
UserServiceImpl
//获取用户列表 public List<User> getUserList(String queryUserName, int queryUserRole, int currentPageNo, int pageSize) { Connection connection = null; List<User> userList = null; try { connection = BaseDao.getConnection(); userList = userDao.getUserList(connection, queryUserName, queryUserRole, currentPageNo, pageSize); } catch (Exception e) { e.printStackTrace(); } finally { BaseDao.closeResource(connection, null, null); } return userList; }
(5)获取角色列表
放在角色包下!职责分明
-
RoleDao
package com.laxsilence.dao.role; import com.laxsilence.pojo.Role; import java.sql.Connection; import java.util.List; public interface RoleDao { //获取角色列表 public List<Role> getRoleList(Connection connection)throws Exception; } -
RoleDaoImpl
package com.laxsilence.dao.role; import com.laxsilence.dao.BaseDao; import com.laxsilence.pojo.Role; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.ArrayList; import java.util.List; /** * @ClassName RoleDao * @Description TODO * @Author 刘英俊 * @Date 2022/4/26 19:54 * @Version 1.0 **/ public class RoleDaoImpl implements RoleDao{ //获取角色列表 public List<Role> getRoleList(Connection connection) throws Exception { PreparedStatement pstm = null; ResultSet resultSet = null; ArrayList<Role> roleList = new ArrayList<Role>(); if (connection!=null){ String sql = "select * from smbms_role"; Object[] params = {}; resultSet = BaseDao.execute(connection, pstm, resultSet, sql, params); while (resultSet.next()){ Role _role = new Role(); _role.setId(resultSet.getInt("id")); _role.setRoleCode(resultSet.getString("roleCode")); _role.setRoleName(resultSet.getString("roleName")); roleList.add(_role); } BaseDao.closeResource(null,pstm,resultSet); } return roleList; } } -
RoleService
package com.laxsilence.service.role; import com.laxsilence.pojo.Role; import java.util.List; public interface RoleService { //获取角色列表 public List<Role> getRoleList(); } -
RoleServiceImpl
package com.laxsilence.service.role; import com.laxsilence.dao.BaseDao; import com.laxsilence.dao.role.RoleDao; import com.laxsilence.dao.role.RoleDaoImpl; import com.laxsilence.pojo.Role; import java.sql.Connection; import java.util.List; /** * @ClassName RoleServiceImpl * @Description TODO * @Author 刘英俊 * @Date 2022/4/26 19:56 * @Version 1.0 **/ public class RoleServiceImpl implements RoleService{ private RoleDao roleDao; public RoleServiceImpl(){ roleDao = new RoleDaoImpl(); } // 获取角色列表 public List<Role> getRoleList() { Connection connection = null; List<Role> roleList = null; try { connection = BaseDao.getConnection(); roleList = roleDao.getRoleList(connection); } catch (Exception e) { e.printStackTrace(); } finally { BaseDao.closeResource(connection, null, null); } return roleList; } }
(6)UserServlet进行整合
//用户界面查询
public void query(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//查询用户列表
//从前端获取数据
String queryUserName = request.getParameter("queryname");
String temp = request.getParameter("queryUserRole");
String pageIndex = request.getParameter("pageIndex");
int queryUserRole = 0;
UserService userService = new UserServiceImpl();
List<User> userList = null;
//第一次走这个请求,一定是第一页,页面大小是固定的
//设置页面容量
int pageSize = Constants.pageSize;
//默认当前页码
int currentPageNo = 1;
if (queryUserName == null) {
queryUserName = "";
}
if (temp != null && !temp.equals("")) {
queryUserRole = Integer.parseInt(temp);
}
if (pageIndex != null) {
try {
currentPageNo = Integer.valueOf(pageIndex);
} catch (NumberFormatException e) {
response.sendRedirect("error.jsp");
}
}
//总数量(表)
int totalCount = userService.getUserCount(queryUserName, queryUserRole);
//总页数
PageSupport pages = new PageSupport();
pages.setCurrentPageNo(currentPageNo); //当前页
pages.setPageSize(pageSize); //页面大小
pages.setTotalCount(totalCount); //总数量
int totalPageCount = pages.getTotalPageCount(); //总页面数量
//控制首页和尾页
//如果页面小于第一页,就显示第一页
if (currentPageNo < 1) {
currentPageNo = 1;
//如果当前页面大于最后一页,当前页等于最后一页即可
} else if (currentPageNo > totalPageCount) {
currentPageNo = totalPageCount;
}
//获取用户列表展示
userList = userService.getUserList(queryUserName, queryUserRole, currentPageNo, pageSize);
request.setAttribute("userList", userList);
//获取角色列表
List<Role> roleList = null;
RoleService roleService = new RoleServiceImpl();
roleList = roleService.getRoleList();
request.setAttribute("roleList", roleList);
request.setAttribute("queryUserName", queryUserName);
request.setAttribute("queryUserRole", queryUserRole);
request.setAttribute("totalPageCount", totalPageCount);
request.setAttribute("totalCount", totalCount);
request.setAttribute("currentPageNo", currentPageNo);
request.getRequestDispatcher("userlist.jsp").forward(request, response);
}
8. 架构分析
其他几个界面跟用户管理界面相差不大,所以不再一一细述,项目源码已在网盘保存。

9. war包反编译
存源代码的是zip压缩文件,可以直接跑的是war包,将这个war包放在tomcat的webapps下等自动解压后就可以运行了,直接在浏览器输入url即可(记得修改数据库的密码)。
其实war包就是IDEA中的target目录,或者在out目录。
直接将包中的文件夹拖到IDEA中即可反编译,但是反编译后的东西跟源码还是有一些区别的,需要注意!
其实反编译就是将.class文件转为.java文件!
十六、拓展:文件的上传和下载
在Web应用中,文件上传和下载功能是非常常用的功能,今天来实现一下JavaWeb中的文件上传和下载功能。
文件下载前面Servlet已经讲过了!
1. 准备工作
对于文件上传,浏览器在上传的过程中是将文件以流的形式提交到服务器端的。
一般选择采用apache的开源工具common-fileupload这个文件上传组件。
common-fileupload是依赖于common-io这个包的,所以还需要下载这个包。
下载最新的jar包
https://mvnrepository.com/artifact/commons-io/commons-io
https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload
使用web项目导入包:需要将包加入到lib目录中,将lib目录允许打包到artifacts中

使用Maven导入依赖:
pom.xml:
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
2. 使用类介绍
【文件上传的注意事项】
- 为保证服务器安全,上传文件应该放在外界无法直接访问的目录下,比如放于WEB-INF目录下。
- 为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名。
- 要限制上传文件的最大值。
- 可以限制上传文件的类型,在收到上传文件名时,判断后缀名是否合法。
【需要用到的类详解】
ServletFileUpload负责处理上传的文件数据,并将表单中每个输入项封装成一个FileItem对象, 在使用ServletFileUpload对象解析请求时需要DiskFileItemFactory对象。所以,我们需要在进行解析工作前构造好DiskFileItemFactory对象,通过ServletFileUpload对象的构造方法或setFileItemFactory()方法设置ServletFileUpload对象的fileItemFactory属性。
-
FileItem类
在HTML页面input 必须有 name
表单如果包含一个文件上传输入项的话,这个表单的enctype属性就必须设置为multipart/form-data
<%-- Created by IntelliJ IDEA. User: 刘英俊 Date: 2022/4/27 Time: 15:34 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <%--通过表单上传文件--%> <%--get:上传文件大小有限制--%> <%--post:上传文件大小没有限制--%> <body> <form action="${pageContext.request.contextPath}/upload.do" enctype="multipart/form-data" method="post"> 上传用户:<input type="text" name="username"><br/> <p><input type="file" name="file1"></p> <p><input type="file" name="file2"></p> <p><input type="submit" value="提交"> | <input type="reset" value="重置"></p> </form> </body> </html>浏览器表单的类型如果为multipart/form-data , 在服务器端想获取数据就要通过流。
常用方法:
//isFormField方法用于判断FileItem类对象封装的数据是一个普通文本表单 //还是一个文件表单,如果是普通表单字段则返回true,否则返回false boolean isFormField(); //getFieldName方法用于返回表单标签name属性的值。 String getFieldName(); //getString方法用于将FileItem对象中保存的数据流内容以一个字符串返回 String getString(); //getName方法用于获得文件上传字段中的文件名。 String getName(); //以流的形式返回上传文件的数据内容。 InputStream getInputStream() //delete方法用来清空FileItem类对象中存放的主体内容 //如果主体内容被保存在临时文件中,delete方法将删除该临时文件。 void delete() -
ServletFileUpload类
ServletFileUpload负责处理上传的文件数据,并将表单中每个输入项封装成一个FileItem对象中。使用其parseRequest(HttpServletRequest) 方法可以将通过表单中每一个HTML标签提交的数据封装成一个FileItem对象,然后以List列表的形式返回。使用该方法处理上传文件简单易用。
3. 代码实现
pom.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>com.laxsilence</groupId>
<artifactId>file</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
</dependencies>
</project>
web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>FileServlet</servlet-name>
<servlet-class>com.laxsilence.servlet.UploadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FileServlet</servlet-name>
<url-pattern>/upload.do</url-pattern>
</servlet-mapping>
</web-app>
全部写在一个方法中:(注释中含部分其他知识点)
package com.laxsilence.servlet;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;
public class FileServlet extends HttpServlet {
//将所有功能写在一个类里
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//判断前端传递的表单是普通表单还是带文件上传的表单
try {
if (!ServletFileUpload.isMultipartContent(request)) {
return;//终止方法运行,说明是一个普通的表单,直接返回
}//如果通过了这个if,说明表单是带文件上传的
//创建上传文件的保存路径,建议在web-inf路径下,安全,用户无法直接访问上传的文件
String uploadPath = this.getServletContext().getRealPath("/WEB-INF/upload");
File uploadFile = new File(uploadPath);
if (uploadFile.exists()) {
uploadFile.mkdirs();//创建这个目录
}
//缓存 临时文件
//临时路径,假如文件超过了预期的大小,我们就把他放到一个临时文件中,过几天自动删除,或者提醒用户转存为永久
String tmpPath = this.getServletContext().getRealPath("/WEB-INF/tmp");
File file = new File(tmpPath);
if (file.exists()) {
file.mkdirs();//创建这个临时目录
}
//处理上传的文件,一般都需要通过流来获取,我们可以使用request.getInputStream(),原生态的文件上传流获取,十分麻烦
//但是我们都建议使用 Apache的文件上传组件来实现,common-fileupload,它需要依赖于 commons-io 组件;
/*
*ServletFileUpload 负责处理上传的文件数据,并将表单中每个输入项封装成一个FileItem对象,在使用ServletFileUpload对象解析请求时需要
DiskFileItemFactory对象,通过ServletFileUpload对象的构造方法或setFileItemFactory()方法
设置ServletFileUpload对象的FileItemFactory属性
* **/
//1.创建DiskFileItemFactory对象,处理文件上传路径或者大小限制的;
DiskFileItemFactory factory = new DiskFileItemFactory();
//通过这个工厂设置一个缓冲区,当上传的文件大于这个缓冲区的时候,将他放到临时文件中
// factory.setSizeThreshold(1024 * 1024);//缓冲区大小为1M,可以选择手动设置,也可以不设置,有默认的
factory.setRepository(file);//临时目录的保存目录,需要一个file
//2. 获取ServletFileUpload
ServletFileUpload upload = new ServletFileUpload(factory);
// 以下部分默认有设置,可以选择手动设置,也可以不设置
// //监听文件上传进度:
// upload.setProgressListener(new ProgressListener() {
// @Override
// public void update(long pBytesRead, long pContentLength, int pItems) {
// System.out.println("总大小:"+pContentLength+"已上传:"+pBytesRead);
// }
// });
//
// //处理乱码问题
// upload.setHeaderEncoding("UTF-8");
// //设置单个文件的最大值
// upload.setFileSizeMax(1024 * 1024 * 10);
// //设置总共能够上传文件的大小
// //1024 = 1kb * 1024 = 1M *10 = 10M
// upload.setSizeMax(1024 * 1024 *10);
//3. 处理上传文件,把前端请求解析,封装成一个FileItem对象
//把前端请求解析,封装成一个FileItem对象,需要从ServletFileUpload对象中获取
List<FileItem> fileItems = upload.parseRequest(request);
//fileItem 每一个表单对象
for (FileItem fileItem : fileItems) {//判断上传的文件是普通的表单还是带文件的表单
if (fileItem.isFormField()) {
//getFileName指的是前端表单控件的name
String name = fileItem.getFieldName();
String value = fileItem.getString("UTF-8");
System.out.println(name + ":" + value);
} else {//文件
//=================处理文件=================//
//拿到文件上传的名字
String uploadFileName = fileItem.getName();
System.out.println("上传的文件名:" + uploadFileName);
//可能存在文件名不合法的情况
if (uploadFileName.trim().equals("") || uploadFileName == null) {
continue;
}
//获得上传的文件名 /images/girl/paojie.png
String fileName = uploadFileName.substring(uploadFileName.lastIndexOf("/") + 1);
//获得文件的后缀名
String fileExtName = uploadFileName.substring(uploadFileName.lastIndexOf("." + 1));
/*
如果文件后缀名 fileExtName 不是我们所需要的
就直接return,不处理,告诉用户文件类型不对。
*/
System.out.println("文件信息 [件名:" + fileName + "---文件类型" + fileExtName + "]");
//可以使用UUID(唯一识别的通用码),保证文件名唯一;
//UUID.randomUUID(),随机生一个唯一识别的通用码;
//网络传输中的东西,都需要序列化
//pojo,实体类,如果想要在多个电脑上使用, 传输==>需要把对象都序列化
//序列化的类都要实现接口:implement Serializable :标记式接口,里面没有方法(如果有方法的叫做 函数式接口)
//JVM-->本地方法栈 和 Java栈 平时我们写的在Java栈;而 本地方法栈用C++写的 native --> C++
//jdk中include文件夹中JNI文件:Java Native Interface java本地化接口,java的JVM不跟操作系统打交道,所以需要通过JNI操作本机
//比如多线程中的Thread中有一个方法的定义,本来类中是不能出现方法的定义的,但是这个方法有个关键字叫native
//方法为 private native void start0(); 就是通知C++调用操作系统分配进程
String uuidPath = UUID.randomUUID().toString();
//=================处理文件=================//
//存到哪? uploadPath
//文件真实存在的路径 realPath
String realPath = uploadPath + "\\" + uuidPath;
//给每个文件创建一个对应的文件夹
File realPathFile = new File(realPath);
if (!realPathFile.exists()) {
realPathFile.mkdir();
}
//=================文件传输=================//
// 获得文件上传的流
InputStream inputStream = fileItem.getInputStream();
//创建一个文件输出流
//realPath = 真实的文件夹;
//差了一个文件; 加上输出文件的名字+"/"+uuidFileName
FileOutputStream fos = new FileOutputStream(realPath + "\\" + fileName);
//创建一个缓冲区
byte[] buffer = new byte[1024 * 1024];
//判断是否读取完毕
int len = 0;
//如果大于0说明还存在数据;
while ((len = inputStream.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
//关闭流
fos.close();
inputStream.close();
String msg = "";
msg = "文件上传成功!";
fileItem.delete(); //上传成功,清除临时文件
//=======================文件传输完毕===============================//
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException {
doGet(request, response);
}
}
写在三个方法中:
package com.laxsilence.servlet;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;
/**
* @ClassName FileServlet2
* @Description TODO
* @Author 刘英俊
* @Date 2022/4/27 17:03
* @Version 1.0
**/
public class FileServlet2 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
//判断上传的文件是普通的表单还是带文件的表单
if (!ServletFileUpload.isMultipartContent(request)) {
return;//如果是普通文件,我们可以直接返回
} //如果通过了这个if,说明我们的表单是带文件上传的;
//创建上传文件的保存路径,建议在WEB-INF路径下,安全,用户无法直接访问上传的文件;
String uploadPath = this.getServletContext().getRealPath("/WEB-INF/upload");
File uploadFile = new File(uploadPath);
if (!uploadFile.exists()) {//如果目录不存在,创建这样一个目录;
uploadFile.mkdir();
}
//临时路径,假如文件超过了预期的大小,我们就把他放到一个临时文件中,过几天自动删除,或者提醒用户转存为永久
String tmpPath = this.getServletContext().getRealPath("/WEB-INF/tmp");
File file = new File(tmpPath);
if (!file.exists()) {//如果目录不存在,创建这样一个目录;
file.mkdir();
}
//处理上传的文件,一般都需要通过流来获取,我们可以使用request.getInputStream(),原生态的文件上传流获取,十分麻烦
//但是我们都建议使用 Apache的文件上传组件来实现,common-fileupload,它需要依赖于 commons-io组件;
//1.创建DiskFileItemFactory对象,处理文件上传路径或者大小限制的;
DiskFileItemFactory factory = getDiskFileItemFactory(file);
//2.获取ServletFileUpload
ServletFileUpload upload = getServletFileUpload(factory);
//3.处理上传的文件
String msg = uploadParseRequest(upload, request, uploadPath);
//servlet请求转发消息
request.setAttribute("msg",msg);
request.getRequestDispatcher("msg.jsp").forward(request,response);
} catch (FileUploadException e) {
e.printStackTrace();
}
}
public static DiskFileItemFactory getDiskFileItemFactory(File file) {
DiskFileItemFactory factory = new DiskFileItemFactory();
//通过这个工厂设置一个缓冲区,当上传的文件大于这个缓冲区的时候,将他放到临时文件中;
factory.setSizeThreshold(1024 * 1024); //缓存区大小为1M
factory.setRepository(file);//临时目录的保存目录,需要一个File
return factory;
}
public static ServletFileUpload getServletFileUpload(DiskFileItemFactory factory) {
ServletFileUpload upload = new ServletFileUpload(factory);
//监听文件上传进度;
upload.setProgressListener(new ProgressListener() {
@Override
//pBytesRead:已经读取到的文件大小
//pContentLength : 文件大小
public void update(long pBytesRead, long pContentLength, int pItems) {
System.out.println("总大小:" + pContentLength + "已上传:" + pBytesRead);
}
});
//处理乱码问题
upload.setHeaderEncoding("UTF-8");
//设置单个文件的最大值
upload.setFileSizeMax(1024 * 1024 * 10);
//设置总共能够上传文件的大小
//1024 = 1kb * 1024 = 1M * 10 = 10M
upload.setSizeMax(1024 * 1024 * 10);
return upload;
}
public static String uploadParseRequest(ServletFileUpload upload,HttpServletRequest request,String uploadPath)
throws FileUploadException, IOException {
String msg = "";
//3.把前端请求解析,封装成一个FileItem对象
List<FileItem> fileItems = upload.parseRequest(request);
for (FileItem fileItem : fileItems) {
if (fileItem.isFormField()){ //判断上传的文件是普通的表单还是带文件的表单
//getFieldName指的是前端表单控件的name;
String name = fileItem.getFieldName();
String value = fileItem.getString("UTF-8"); //处理乱码
System.out.println(name+":"+value);
}else { //判断它是上传的文件
//=======================处理文件===============================//
//拿到文件名字
String uploadFileName = fileItem.getName();
System.out.println("上传的文件名:"+uploadFileName);
if (uploadFileName.trim().equals("")||uploadFileName==null){
continue;
}
//获得上传的文件名 /images/girl/paojie.png
String fileName = uploadFileName.substring(uploadFileName.lastIndexOf("\\") + 1);
//获得文件的后缀名
String fileExtName = uploadFileName.substring(uploadFileName.lastIndexOf(".") + 1);
/*
如果文件后缀名 fileExtName 不是我们所需要的
就直接return,不处理,告诉用户文件类型不对。
*/
System.out.println("文件信息 [件名:"+fileName+"---文件类型"+fileExtName+"]");
//可以使用UUID(唯一识别的通用码),保证文件名唯一;
//UUID.randomUUID(),随机生一个唯一识别的通用码;
String uuidPath = UUID.randomUUID().toString();
//=======================处理文件完毕===============================//
//存到哪? uploadPath
//文件真实存在的路径 realPath
String realPath = uploadPath+"\\"+uuidPath;
//给每个文件创建一个对应的文件夹
File realPathFile = new File(realPath);
if (!realPathFile.exists()){
realPathFile.mkdir();
}
//=======================存放地址完毕===============================//
//获得文件上传的流
InputStream inputStream = fileItem.getInputStream();
//创建一个文件输出流
//realPath = 真实的文件夹;
//差了一个文件; 加上输出文件的名字+"/"+uuidFileName
FileOutputStream fos = new FileOutputStream(realPath+"\\"+fileName);
//创建一个缓冲区
byte[] buffer = new byte[1024*1024];
//判断是否读取完毕
int len = 0;
//如果大于0说明还存在数据;
while ((len=inputStream.read(buffer))>0){
fos.write(buffer,0,len);
}
//关闭流
fos.close();
inputStream.close();
msg = "文件上传成功!";
fileItem.delete(); //上传成功,清除临时文件
//=======================文件传输完毕===============================//
}
}
return msg;
}
}
msg.jsp:
<%--
Created by IntelliJ IDEA.
User: 刘英俊
Date: 2022/4/27
Time: 17:07
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>
${msg}
</body>
</html>
如果出现EL表达式不生效的情况,需要同步web.xml和Tomcat的版本,去tomcat中查看web.xml,然后将配置复制一份。
上传后的结果,需要再打包目录下去找,根据tomcat部署的不一样,位置不一样,如果是war,需要再tomcat的webapps下去找:

如果是war exploded,就是下图所在位置:

4. UUID补充
原文链接:https://blog.csdn.net/u012760435/article/details/122304214
UUID会重复吗
有的人说一定不会重复,也有的人说很小几率会重复,其实都有可能,取决于代码中使用的是哪种UUID生成方式,或者说,使用的是哪种UUID算法。
UUID Version 1:基于时间的UUID
基于时间的UUID通过计算当前时间戳、随机数和机器MAC地址得到。由于在算法中使用了MAC地址,这个版本的UUID可以保证在全球范围的唯一性。但与此同时,使用MAC地址会带来安全性问题,这就是这个版本UUID受到批评的地方。如果应用只是在局域网中使用,也可以使用退化的算法,以IP地址来代替MAC地址(不考虑手动更改MAC地址的情况)。
UUID Version 2:DCE安全的UUID
DCE(Distributed Computing Environment)安全的UUID和基于时间的UUID算法相同,但会把时间戳的前4位置换为POSIX的UID或GID。这个版本的UUID在实际中较少用到。
UUID Version 3:基于名字的UUID(MD5)
基于名字的UUID通过计算名字和名字空间的MD5散列值得到。这个版本的UUID保证了:相同命名空间中不同名字生成的UUID的唯一性;不同命名空间的UUID的唯一性;相同名字空间中相同名字的UUID重复生成是相同的。
UUID Version 4:随机UUID
根据随机数,或者伪随机数生成UUID。这种UUID产生重复的概率是可以计算出来的,但随机的东西就像是买彩票:你指望它发财是不可能的,但狗屎运通常会在不经意中到来。
UUID Version 5:基于名字的UUID(SHA1)
和版本3的UUID算法类似,只是散列值计算使用SHA1(Secure Hash Algorithm 1)算法。
应该用哪种
从UUID的不同版本中可以看出
Version 1.2 适合应用于分布式计算环境下,具有高度唯一性
Version 3.5 适合于一定范围内名字唯一,且有需要生成重复UUID的场景下
Version 4 最为简单和方便,适合数据量不是特别大的场景下,Java默认的uuid工具便是此种
狗屎运总在不经意中到来
老生常谈,目前所有的随机数发生器都是伪随机数,JVM的随机数找到低也是采集于操作系统中一个特殊的设备:熵池。在linux系统中这个设备位于/dev/random(他是一个阻塞性的),当熵池噪声不够的时候随机数生成能力会下降。然后呢,就看运气了,我在之前的一个项目的开发过程中遇到过一次uuid重复,以后就再也没出现过。
综合来看,一般情况下使用Ver.4的算法是没什么太大的问题的,如果追求高并发或者大数据量处理可以使用Ver.1,毕竟再趋近于0的概率也是会有一个1存在。
十七、拓展:邮件发送原理及实现
邮件接发流程:


要在网络上实现邮件功能,必须要有专门的邮件服务器。
这些邮件服务器类似于现实生活中的邮局,它主要负责接收用户投递过来的邮件,并把邮件投递到邮件接收者的电子邮箱中。
对应:接收和发送
- 接收:POP3协议
- 发送:SMTP协议
SMTP服务器地址:一般是smtp.xxx.com,每一个网站都有自己的服务器地址(固定的)
比如163邮箱是smtp.163.com,qq邮箱是smtp.qq.com。
电子邮箱(E-Mail地址)的获得需要在邮件服务器上进行申请。
比如我们要使用QQ邮箱,就需要开通邮箱功能。一般都要遵循SMTP和POP3协议
例如:QQ邮箱 ---> 设置 - - ->账号:

1. 传输协议
SMTP协议
发送邮件:
我们通常把处理用户smtp请求(邮件发送请求)的服务器称之为SMTP服务器(邮件发送服务器)。
POP3协议
接收邮件:
我们通常把处理用户pop3请求(邮件接收请求)的服务器称之为POP3服务器(邮件接收服务器)。

(1)张三通过smtp协议连接到Smtp服务器,然后发送一封邮件给网易的邮件服务器
(2)网易分析发现需要去QQ的邮件服务器,通过smtp协议将邮件转投给QQ的Smtp服务器
(3)QQ将接收到的邮件存储在lisi@qq.com这个邮件账号的空间中
(4)李四通过Pop3协议连接到Pop3服务器收取邮件
(5)从lisi@qq.com这个邮件账号的空间中取出邮件
(6)Pop3服务器将取出来的邮件送到李四手中
【注意】:有可能你收件人地址,发件人地址等信息都正确了,控制台也打印了正确的信息,但是在收件箱就是收不到信息。这是因为可能收件箱服务器拒收了你发的邮件(比如认为你的邮件是广告),这时候可能在垃圾箱里能找到,可能找不到。解决办法是重复的邮件内容不要多次发送,或者更换收件箱试试。
2. 概述
我们将用代码完成邮件的发送,这在实际项目中应用的非常广泛。
- 比如注册需要发送邮件进行账号激活,
- 再比如OA项目中利用邮件进行任务提醒等等。
使用Java发送 E-mail 十分简单,但是首先你应该准备
-
JavaMail API
-
Java Activation Framework 。(发送邮件的支持包)
得到两个jar包:网络协议的
-
mail.jar
-
activation.jar
JavaMail 是sun公司(现以被甲骨文收购)为方便Java开发人员在应用程序中实现邮件发送和接收功能而提供的一套标准开发包,它支持一些常用的邮件协议,如前面所讲的SMTP,POP3,IMAP,还有MIME(附件和图片)等。
我们在使用JavaMail API 编写邮件时,无须考虑邮件的底层实现细节,只要调用JavaMail 开发包中相应的API类就可以了。
我们可以先尝试发送一封简单的邮件,确保电脑可以连接网络。
-
创建包含邮件服务器的网络连接信息的Session对象。
-
创建代表邮件内容的Message对象
-
创建Transport对象,连接服务器,发送Message,关闭连接
主要有四个核心类,我们在编写程序时,记住这四个核心类,就很容易编写出Java邮件处理程序。

3. 纯文本邮件
1. 导包
同文件的上传一样,将下载的包导入lib,记得添加为库
maven:
<!-- https://mvnrepository.com/artifact/javax.activation/activation -->
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.mail/mail -->
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.7</version>
</dependency>
2. 权限
权限
QQ邮箱中获取对应的权限,其他邮箱默认开启
QQ邮箱需要安全验证,我们需要获取他对应的权限;
QQ邮箱–>邮箱设置–>账户

已经开启的点击下方生成授权码
没有开启的,点击开启,获得16位的授权码。

3. 代码实现
package com.laxsilence;
import com.sun.mail.util.MailSSLSocketFactory;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.Properties;
/**
* @ClassName laxsilence
* @Description TODO
* @Author 刘英俊
* @Date 2022/4/27 21:22
* @Version 1.0
**/
public class test {
//简单邮件:没有附件和图片,纯文本邮件
//复杂邮件:包括附件
//要发送邮件,需要获得协议和支持!需要开启pop3和smtp服务
//其他邮件默认可以,QQ邮箱不可以,需要开启授权码
public static void main(String[] args) throws Exception {
//Properties中 设置属性
Properties prop=new Properties();
prop.setProperty("mail.host","smtp.qq.com");///设置QQ邮件服务器
prop.setProperty("mail.transport.protocol","smtp");///邮件发送协议
prop.setProperty("mail.smtp.auth","true");//需要验证用户密码
//QQ邮箱需要设置SSL加密,原因:大厂。其他邮箱不需要
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
prop.put("mail.smtp.ssl.enable","true");//使用安全的连接为true
prop.put("mail.smtp.ssl.socketFactory",sf);//socket工厂,使用自己的socket工厂
//使用javaMail发送邮件的5个步骤
//1.创建定义整个应用程序所需要的环境信息的session对象
//QQ才有!其他邮箱就不用
Session session= Session.getDefaultInstance(prop, new Authenticator() {//获取默认的实例
@Override
protected PasswordAuthentication getPasswordAuthentication() {
//发件人邮箱、授权码
return new PasswordAuthentication("邮箱@qq.com","授权码");
}
});
//开启session的debug模式,这样可以查看到程序发送Email的运行状态
session.setDebug(true);
//2.通过session得到transport对象
Transport ts = session.getTransport();
//3.使用邮箱的用户名和授权码连上邮件服务器
ts.connect("smtp.qq.com","邮箱@qq.com","授权码");
//4.创建邮件:写邮件
//需要传递session
MimeMessage message = new MimeMessage(session);
//指明邮件的发件人
message.setFrom(new InternetAddress("邮箱@qq.com"));
//指明邮件的收件人,现在发件人和收件人是一样的,那就是自己给自己发
message.setRecipient(Message.RecipientType.TO, new InternetAddress("邮箱@qq.com"));
//邮件的标题 只包含文本的简单邮件
message.setSubject("发送的标题");
//邮件的文本内容
message.setContent("你好","text/html;charset=UTF-8");
//5.发送邮件
ts.sendMessage(message,message.getAllRecipients());
//6.关闭连接,一切网络都需要关闭
ts.close();
}
}
点击运行:发送成功!且样式为HTMl样式

4. 带图片和附件的复杂邮件
1. 两个类
先认识两个类一个名词:
-
名词:MIME(多用途互联网邮件扩展类型)
-
MimeBodyPart类 (内容的主体)
- javax.mail.internet.MimeBodyPart类
- 表示的是一个MIME消息,它和MimeMessage类一样都是从Part接口继承过来。
-
MimeMultipart类 (内容的封装)
- javax.mail.internet.MimeMultipart是抽象类
- Multipart的实现子类,它用来组合多个MIME消息。
- 一个MimeMultipart对象可以包含多个代表MIME消息的MimeBodyPart对象。

2. 在MimeBodyPart中有三个属性
- mixed:附件(包括所有的类型,设置这个绝对不会出错)
- related:内嵌资源(图片什么的)
- alternative:超文本正文

3. 代码实现
package com.laxsilence;
import com.sun.mail.util.MailSSLSocketFactory;
import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import java.util.Properties;
/**
* @ClassName MainDemo01
* @Description TODO
* @Author 刘英俊
* @Date 2022/4/27 21:58
* @Version 1.0
**/
public class MainDemo01 {
public static void main(String[] args) throws Exception {
//Properties中 设置属性
Properties prop=new Properties();
prop.setProperty("mail.host","smtp.qq.com");///设置QQ邮件服务器
prop.setProperty("mail.transport.protocol","smtp");///邮件发送协议
prop.setProperty("mail.smtp.auth","true");//需要验证用户密码
//QQ邮箱需要设置SSL加密,原因:大厂。其他邮箱不需要
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
prop.put("mail.smtp.ssl.enable","true");//使用安全的连接为true
prop.put("mail.smtp.ssl.socketFactory",sf);//socket工厂,使用自己的socket工厂
//使用javaMail发送邮件的5个步骤
//1.创建定义整个应用程序所需要的环境信息的session对象
//QQ才有!其他邮箱就不用
Session session= Session.getDefaultInstance(prop, new Authenticator() {//获取默认的实例
@Override
protected PasswordAuthentication getPasswordAuthentication() {
//发件人邮箱、授权码
return new PasswordAuthentication("laxsilence@vip.qq.com","授权码");
}
});
//开启session的debug模式,这样可以查看到程序发送Email的运行状态
session.setDebug(true);
//2.通过session得到transport对象
Transport ts = session.getTransport();
//3.使用邮箱的用户名和授权码连上邮件服务器
ts.connect("smtp.qq.com","laxsilence@vip.qq.com","授权码");
//4.创建邮件:写邮件
//需要传递session
MimeMessage message = new MimeMessage(session);
//指明邮件的发件人
message.setFrom(new InternetAddress("laxsilence@vip.qq.com"));
//指明邮件的收件人,现在发件人和收件人是一样的,那就是自己给自己发
message.setRecipient(Message.RecipientType.TO, new InternetAddress("laxsilence@gmail.com"));
//邮件的标题 只包含文本的简单邮件
message.setSubject("测试复杂邮件");
//邮件的文本内容
message.setContent("你好","text/html;charset=UTF-8");
/*==================图片和附件的邮件=========================*/
//=================================准备图片数据
MimeBodyPart image = new MimeBodyPart();
//图片需要经过数据化的处理 DataHandler:数据处理 FileDataSource:加载文件的资源
DataHandler dh = new DataHandler(new FileDataSource("D:\\旧桌面\\常用\\最爱的图片\\电脑壁纸.png"));
//在part中放入这个处理的图片数据
image.setDataHandler(dh);
//给这个part设置一个ID名字,我们可以在后面使用
image.setContentID("bz.jpg");
//=================================准备正文数据
MimeBodyPart text = new MimeBodyPart();
text.setContent("这是一张正文<img src='cid:bz.jpg'>","text/html;charset=UTF-8");
//=================================准备附件数据
MimeBodyPart body1= new MimeBodyPart();
body1.setDataHandler(new DataHandler(new FileDataSource("D:\\旧桌面\\常用\\最爱的图片\\text.txt")));
body1.setFileName("1.txt");
//描述数据关系
MimeMultipart mm = new MimeMultipart();
mm.addBodyPart(body1);
mm.addBodyPart(text);
mm.addBodyPart(image);
mm.setSubType("mixed");//就是那三个范围属性
//设置到消息中,保存修改
message.setContent(mm);
message.saveChanges();
/*===========================================*/
//5.发送邮件
ts.sendMessage(message,message.getAllRecipients());
//6.关闭连接,一切网络都需要关闭
ts.close();
}
}
运行结果:

补充:cid

5. JavaWeb发送邮件
1. 项目结构

2. 代码实现
-
pom.xml,注意:需要将两个关于邮件发送的包导入到tomcat中,不然有可能报500
<?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>com.laxsilence</groupId> <artifactId>WebMail</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.2</version> </dependency> <dependency> <groupId>javax.servlet.jsp.jstl</groupId> <artifactId>jstl-api</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>taglibs</groupId> <artifactId>standard</artifactId> <version>1.1.2</version> </dependency> <dependency> <groupId>javax.mail</groupId> <artifactId>mail</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>javax.activation</groupId> <artifactId>activation</artifactId> <version>1.1.1</version> </dependency> </dependencies> </project> -
实体类:
package com.laxsilence.pojo; import java.io.Serializable; /** * @ClassName User * @Description TODO * @Author 刘英俊 * @Date 2022/4/28 14:34 * @Version 1.0 **/ //可以导入lombok,但是最好不要使用,有缺陷 //@Data 生成get set //@NoArgsConstructor 生成 无参构造 //@AllArgsConstructor 生成 有参构造 public class User implements Serializable { private String username; private String password; private String email; public User() { } public User(String username, String password, String email) { this.username = username; this.password = password; this.email = email; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } } -
工具类,注意:使用多线程防止用户等待,不使用多线程的话,网页需要一直加载到邮件发送成功才会显示到下一个界面,使用多线程之后,邮件发送的同时,网页就会加载到下一页了。
package com.laxsilence.util; import com.laxsilence.pojo.User; import com.sun.mail.util.MailSSLSocketFactory; import javax.mail.*; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import java.util.Properties; /** * @ClassName SendMail * @Description TODO * @Author 刘英俊 * @Date 2022/4/28 14:40 * @Version 1.0 **/ // 网站三秒原则:用户在一个界面加载不出来超过三秒,就可能关掉这个网页 // 为了能够留下用户,选择使用多线程, (异步处理) public class SendMail extends Thread{ //用于给用户发送邮件的邮箱 private String from = "Laxsilence@vip.qq.com" ; //邮箱的用户名 private String username = "Laxsilence@vip.qq.com" ; //邮箱的密码 private String password = "授权码"; //发送邮件的服务器地址 private String host ="smtp.qq.com" ; //导入一个类 private User user; public SendMail(User user){ this.user = user; } @Override public void run() { try { Properties prop = new Properties(); prop.setProperty("mail.host", host);///设置QQ邮件服务器 prop.setProperty("mail.transport.protocol", "smtp");///邮件发送协议 prop.setProperty("mail.smtp.auth", "true");//需要验证用户密码 //QQ邮箱需要设置SSL加密 MailSSLSocketFactory sf = new MailSSLSocketFactory(); sf.setTrustAllHosts(true); prop.put("mail.smtp.ssl.enable", "true"); prop.put("mail.smtp.ssl.socketFactory", sf); //使用javaMail发送邮件的5个步骤 //1.创建定义整个应用程序所需要的环境信息的session对象 Session session = Session.getDefaultInstance(prop, new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(from, password); } }); //开启session的debug模式,这样可以查看到程序发送Email的运行状态 session.setDebug(true); //2.通过session得到transport对象 Transport ts = session.getTransport(); //3.使用邮箱的用户名和授权码连上邮件服务器 ts.connect(host, username, password); //4.创建邮件:写文件 //注意需要传递session MimeMessage message = new MimeMessage(session); //指明邮件的发件人 message.setFrom(new InternetAddress(from)); //指明邮件的收件人 message.setRecipient(Message.RecipientType.TO, new InternetAddress(user.getEmail()));//从前端接收的 //邮件标题 message.setSubject("注册通知"); //邮件的文本内容 String info = "恭喜你(" + user.getUsername() + ")成功注册!" + "密码:" + user.getPassword(); message.setContent(info, "text/html;charset=UTF-8"); message.saveChanges();//保存更改 //5.发送邮件 ts.sendMessage(message, message.getAllRecipients()); //6.关闭连接 ts.close(); } catch (Exception e) { System.out.println(e); } } } -
Servlet类:
package com.laxsilence.servlet; import com.laxsilence.pojo.User; import com.laxsilence.util.SendMail; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @ClassName RegisterServlet * @Description TODO * @Author 刘英俊 * @Date 2022/4/28 14:29 * @Version 1.0 **/ public class RegisterServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //接收用户请求,封装成对象 String username = req.getParameter("username"); String password = req.getParameter("password"); String email = req.getParameter("email"); System.out.println(username); System.out.println(password); System.out.println(email); User user = new User(username, password, email); //启动线程,重定向页面 /* 用户注册成功后,给用户发送一封邮件 我们使用线程来专门发送邮件,防止出现耗时、白屏和网站注册人数过多的情况 */ SendMail send = new SendMail(user); send.start(); //注册用户 req.setAttribute("message","注册成功,我们已经发了一封要了注册信息的电子邮件,请查收!如网络不稳定,请稍等"); req.getRequestDispatcher("info.jsp").forward(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } } -
index.jsp(网页注册页面)
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>注册</title> </head> <body> <form action="${pageContext.request.contextPath}/RegisterServlet" method="post"> 用户名:<input type="text" name="username"><br/> 密码:<input type="password" name="password"><br/> 邮箱:<input type="text" name="email"><br/> <input type="submit" value="注册"> </form> </body> </html> -
info.jsp(注册完成之后的提醒)
<%-- Created by IntelliJ IDEA. User: 刘英俊 Date: 2022/4/28 Time: 14:49 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> ${message} </body> </html> -
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <servlet> <servlet-name>RegisterServlet</servlet-name> <servlet-class>com.laxsilence.servlet.RegisterServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>RegisterServlet</servlet-name> <url-pattern>/RegisterServlet</url-pattern> </servlet-mapping> </web-app>
3. 运行结果



后续,我们将使用SpringBoot实现邮件的发送,非常简单!
十八、补充:后续学习
SSM框架
Mybatis:持久化,会补充学习,Mybatis-Plus、log4j、通用Mapper

Spring:容器思想,补充讲解设计模式思想

SpringMVC:


浙公网安备 33010602011771号