36讲——Java Web 入门到实战
Java Web 入门到实战
- Java Web 入门到实战
- (一)Tomcat
- (二)Http
- (三)Maven
- (四)Servlet 控制器
- (五) Cookie Session
- (六) JSP
- (七) MVC 三层架构
- (八) Filter
- (九) Listener
- (十一) 文件上传
- 配置
- JSP路径
- junit 测试单元
- 问题
(一)Tomcat
Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。
下载Tomcat

解压后就可以直接使用
下载Tomcat 历史版本
第一步 打开Tomcat 官网点击 archives (档案)
第二步 选择我们需要的版本号
第三步 选择版本
第四步 选择 Bin 目录打开
第五步 根据电脑的系统和位数 选择对应压缩包
Tomcat 启动和关闭
Tomcat 的启动和关闭 ——也可以通过关闭命令符关闭端口

文件夹的作用

-
Tomcat — 测试是否正常使用
-
正常启动页面
![]()
可能遇到的问题:
- java环境变量没有配置 _Tomcat 是基于java开发的依赖于java环境
- 闪退问题,需要配置兼容性
- 乱码问题:配置文件中设置(一般不影响正常使用不做设置,设置会影响使用)
配置

可以修改默认配置启动端口号
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
可以配置默认的主机名称
-
默认主机名为: localhost->127.0..0.1
-
默认的网站应用存放的位置为:webapps (可以改最好别改)
这里修改主机名后,需要修改本地配置 C:\Windows\System32\drivers\etc\hosts (重新加载)
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
面试题:
网站是如何进行访问的?
-
输入一个域名: enter
-
检查本机C:\Windows\System32\drivers\etc\hosts 配置文件下有没有这个域名映射;
# localhost name resolution is handled within DNS itself. # 127.0.0.1 localhost # ::1 localhost # 127.0.0.1 www.xiangxiaozhong.com -- # 号好像要去除-
有: 直接返回对应的 ip 地址,这个地址中,有我们需要访问的 web 程序,可以直接访问
127.0.0.1 www.xiangxiaozhong.com -
没有:去 DNS 服务器找,找到的话就返回,找不到就没有;
![]()
-
发布一个 Web
-
将自己写的网站,放到 服务器 (Tomcat)中指定的 web 应用的文件(webapps)下,就可以访问了 (名字规范为index)

-
网站应该有的结构
-- webapps: Tomcat 服务器的 web 目录 - Root - xxzStudy - WEB-INF - classes : java程序 - libweb应用所依赖的jar 包 - web.xml : 网站配置文件 - index.html 默认的首页(自己写的网页) - static - css - style.css - js - img - ...
注意事项
-
如果想同时开两个 Tomcat 一个直接用 Tomcat 一个 用 Idea 的话 要先开 Idea 的服务器 在启动Tomcat 才行。当然是两个不同端口的Tomcat
-
如果用 IDea 生成过的话 在项目路径 Target 下应该有War 包
注意War中 已编译为 class 文件把他直接放入 Tomacat --->webapps 下直接自动会解压不需要重启 Tomcat 就能访问
(二)Http
什么是http
HTTP HyperText Transfer Protocol(超文本传输协议),是一个简单的请求-响应协议,他通常运行在 TCP 之上。
- 文本: html ,字符串....
- 超文本:图片,音乐,多媒体,定位,地图...
- 默认端口 : 80
Https: 安全的
- 默认端口:443
两个时代
- http1.0 :客户端与 web 服务器连接后,只能获得一个web 资源,断开连接
- http2.0:客户端与 web 服务器连接后,可以获得多个web 资源
http 请求
- 客户端--发请求--服务器
百度:
Request URL: https://www.baidu.com/ 请求地址
Request Method: GET //请求方法/get/post
Status Code: 200 OK // 状态码: 200
Remote Address: 180.97.34.94:443 // 远程地址
Accept:text/html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9 // 语言
Connection: keep-alive
请求行
- 请求方式: GET
- 请求方式:Get,post,
- GRT: 请求能够携带的参数比较少,大小有限制,会在浏览器的URL地址栏显示数据内容,不安全,高效
- post:请求能够携带参数没有限制,大小没有限制,不会在浏览器的 URL 地址栏上显示数据内容,安全,但不高效
消息头
Accept:告诉浏览器,它所支持的数据类型 Accept-Encoding: 支持那些编码格式 GBK UTF-8 GB2312 ISO8859-1 Accept-Language:告诉浏览器,它的语言环境 Connection: 告诉浏览器,请求是否断开还是保持连接
http响应
- 服务器--响应--客户端
百度:
Cache-Control: private 缓存控制
Connection: keep-alive 连接
Content-Encoding: gzip 编码
Content-Type: text/html; 类型
响应体:
Accept:告诉浏览器,它所支持的数据类型
Accept-Encoding: 支持那些编码格式 GBK UTF-8 GB2312 ISO8859-1
Accept-Language:告诉浏览器,它的语言环境
Connection: 告诉浏览器,请求是否断开还是保持连接
refresh: 告诉客户端,多久刷新一次
Location:让网页重新定位
响应状态码
200: 请求响应成功 200
3xx: 请求重定向
- 重定向:你重新到我给你的新位置去
4xx: 找不到资源 404
- 资源不存在
5xx:服务器代码错误 500 502 :网关错误
常见面试题:当你的浏览器中地址栏输入地址并回车的一瞬间到页面能够展示回来,经历了什么?
(三)Maven
Maven项目对象模型(POM),可以通过一小段描述信息来管理项目的构建,报告和文档的项目管理工具软件。
Maven 除了以程序构建能力为特色之外,还提供高级项目管理工具。由于 Maven 的缺省构建规则有较高的可重用性,所以常常用两三行 Maven 构建脚本就可以构建简单的项目.
Maven 存在的意义
- 在javaWeb开发中,需要使用大量的 jar 包,我们手动去导入;
- 如何能够让一个东西自动帮我导入和配置这个jar包——Maven的作用
(3.0)Maven 历史版本下载
第一步 打开 Maven 官网 点击 DownLoad
第二步 点击 Archives (档案)
第三步 选择版本号
第四步 选择bin 目录
第五步 选择bin.zip 下载
(3.1)Maven 项目架构管理工具
我们目前用来就是方便导入 jar 包的
核心思想: 约定大约配置 有约束不要去违反
Maven 会规定如何去规范编写代码,必须

下载后解压到与Tomcat 同个文件夹下 方便管理
(3.2)配置Maven环境
在我们系统环境变量中配置
两个变量;

在path 中添加路径;

最后在 CMD 中输入
mvn -version判断是否配置好环境
success 的style

(3.3)配置Maven使用环境
apache-maven-3.8.5\conf\settings.xml
配置阿里云的镜像加速下载;
<mirrors>
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>central</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
</mirrors>
本地仓库 localRepository
<repositories>
<localRepository>D:\java\apache-maven-3.6.2\maven_repo</localRepository>
</repositories>
(3.4)在 IDEA 中创建模板 Maven 项目
- 创建一个 Maven 项目


注意本地仓库地址 是我们自己新建的 maven_repo

- 自动导入包


果然在我们新建的 maven_repo 本地仓库下多了下载文件

配置完成
注意事项;
留一个心眼,创建好一个 Maven后先看一眼,Maven 的配置


(3.4)在 IDEA 中创建普通 Maven 项目

其他步骤同上
在创建好的 Maven 项目中 确实配置文件 MavenHome 变成 IDEA 本地的了
结果;

只有 Web 应用下才有的

在 模板 Maven main 目录下创建 java 和resources 两个目录 并设置目录标记;

第二种 标记文件夹的方法

我这个配置 虽然能跑 但是不大对头,先这样吧


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 组id 项目id 版本-->
<groupId>com.xxz</groupId>
<artifactId>javaWeb_01_Maven</artifactId>
<version>1.0-SNAPSHOT</version>
<!--package 项目的打包方式
jar:java应用
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> 报错好像注释掉 就会自动 load–>-->
<!-- </dependency>-->
</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 的版本降一下
-
Idea 每次需要重新配置 Maven,只要在创建项目页面中设置默认设置中的配置就可以了
-
pom.xml 配置版本 爆红 删除缓存 并重启就行

(四)Servlet 控制器
- Servlet 是Sun 公司开发动态web 的一门技术
- Sun 在这些 Api 中提供了一个接口: Servlet
- 开发一个Servlet 程序,只需要完成两个小步骤;
- 编写一个类,实现 Servlet 接口
- 把开发好的java类部署到 web 服务器中
把实现了 Servlet 接口的java程序,叫做 Servlet
4.1 Hello Servlet
Servlet 接口 Sun 公司有两个默认的实现类: HttpServlet,
第一步
构建一个普通 Maven 项目(详细见上文),删除里面的src 目录,以后的学习就在这个项目建立 Module ;这个空工程就是 Maven 的主工程
第二步
在pom.xml 中添加依赖 Maven 仓库
自己需要添加 这里的话 直接复制添加进去就可以
<dependencies>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
第三步 创建子项目 Webapp maven_repo (就会下载资源)
创建 Module 子项目 webapp 项目 上面是普通项目
第一步 创建 module

第二步 选择 webapp md 千万别选错 是 org 开头的 webApp

第三步 取名

第四步 配置参数
注意版本文用的是3.6.2,版本不符合 会运行失败(查看参数是不是自己的参数,如果是自带的会有影响)

找不到的话 在 Settings 里面也是一样配置即可

建立项目后 修改 web. xml 为最新版本 如下;
此代码在D:\java\apache-tomcat-9.0.24\webapps\ROOT\WEB-INF\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="true"> <display-name>Welcome to Tomcat</display-name> <description> Welcome to Tomcat </description> </web-app>搭建Maven 完整建构即 在 Main下新建 java 和 resources 包 并标识正确的目录
第五步 编写一个 Servlet 程序
关于 Maven 父子工程的理解:(理论)
父项目中会有
<modules>
<module>Servlet_01</module>
</modules>
子项目中会有
<groupId>com.xxz</groupId>
<artifactId>Servlet_01</artifactId>
<version>1.0-SNAPSHOT</version>
// 可能是版本问题 没有 parent 标签 但内容一样
副项目中的 java 子项目可以直接使用
son extends father
- 先建立java 文件夹 在该文件夹下 编写一个普通类 HelloServlet

建立普通class

因为httpServerlet 继承并实现了GenericServerlet 所以这里我们直接导入 HttpServerlet 并重写 doGet 和doPost 方法
package com.xxz.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 HelloServlet extends HttpServlet { // 这里继承后爆红 因为子类并没有该依赖,要导入依赖 alt+enter
// 由于 get 和past 只是请求相应的不同方式,可以互相调用,业务逻辑都一样
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter writer = resp.getWriter(); // 响应流 getWrite() 返回一个响应实体 body
writer.println("Hello,World!!!");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 互相调用 别忘记
doGet(req, resp);
}
}

第六步 编写 Servlet 的映射
为什么要编写映射:我们写的是Java 程序,但是要通过浏览器访问,而浏览器需要连接Web服务器,所以我们需要在 Web 服务中注册我们写的 Servlet ,还需给他一个浏览器能够访问的路径;

配置 web.XML 不要配置错了

<!--添加配置内容-->
<!--配置内容-->
<!-- 注册 Servlet-->
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.xxz.Servlet.HelloServlet</servlet-class> <!--这里指向我们的类 即java 包下的class 文件-->
</servlet>
<!-- Servlet 的请求路径-->
<servlet-mapping>
<servlet-name>hello</servlet-name> <!--这个要和前面的 servlet name 相同-->
<url-pattern>/hello</url-pattern> <!-- 根据原理这就是访问的名称可以更改 必须要 要不然会报错 有斜杆 有斜杆 有斜杆 !!!-->
</servlet-mapping>
<!--没有斜杆会报错 Artifact WebMail:war exploded: Error during artifact deployment. See server log for details-->
<!--配置内容-->
第四步 配置 Tomcat
设置项目发布的 Artifact 和 编写 Application Content 就可以了

接上步

接上步

接上步

接上步

第六步 启动测试
如果有问题可以删除 target 重新跑


原理

发现问题
问题一 在 HelloServelet 配置映射时,配置映射这里生成的 子web项目 中Maven 的 plugins 有重复爆红的现象
解决方案:
降低 版本试试
Maven 3.6.2,Tomcat 9.0.24jdk9.0.4
问题二 在启动Tomcat 中报错 配置问题

-
问题一 idea 的tomcat 配置错误问题 在 Settings 中配置好 tomcat

-
问题二 没有找到HTTP连接器节点:在server.xml中设置一个
D:\java\apache-tomcat-9.0.24\conf\server.xml 把如下这段本来是注释的,去除注释
<Connector port="8443" protocol="org.apache.coyote.http11.Http11AprProtocol"
maxThreads="150" SSLEnabled="true" >
<UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
<SSLHostConfig>
<Certificate certificateKeyFile="conf/localhost-rsa-key.pem"
certificateFile="conf/localhost-rsa-cert.pem"
certificateChainFile="conf/localhost-rsa-chain.pem"
type="RSA" />
</SSLHostConfig>
</Connector>
问题三 Error 不支持发行版本5 爆红

解决方法 按住 Ctrl+shit+alt+s 出现以下面版
查看配置 是否是自己装的版本


还是不行就在 pom.xml 下添加配置
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>9</maven.compiler.source>
<maven.compiler.target>9</maven.compiler.target>
</properties>
问题四 启动Tomcat 但是 报404 未找到

但是 hello Word 可以正常运行 先不管了

结果是因为 index.jsp 没有了,所以报404 只要创建出 index.jsp 就可以了

问题五 启动 Tomcat 浏览器不自动弹起问题
要写到 可执行应用上 .exe

Maven 问题
IDEAMaven设置里面的Repositories这个设置问题 如果是仓库问题 可以清空仓库 点击 idea 的清空缓存并重启 试试

好像对操作不影响,就算了
C:\Users\夏天的风.IntelliJIdea2019.3\config\options project.default.xml
<component name="MavenImportPreferences">
<option name="generalSettings">
<MavenGeneralSettings>
<option name="localRepository" value="这里是你自己的local repository路径,例如(C:\ideaMaven\MavenRepository)" />
<option name="mavenHome" value="这里是你自己的maven Home路径,例如(C:\ideaMaven\apache-maven-3.6.1)" />
<option name="userSettingsFile" value="这里是你自己的user Settings File路径,例如(C:\ideaMaven\apache-maven-3.6.1\conf\settings.xml)" />
</MavenGeneralSettings>
</option>
</component>
4.2 Serverlet 原理
Serverlet 是由Web 服务器调用,web 服务器在收到浏览器请求之后,会...

4.3 Mapping问题 (映射问题)
四种虚拟路径配置

-
一个Serverlet 可以指定一个映射路径
<!-- http://localhost:8080/xxz01/hello--> <servlet-mapping> <servlet-name>hello</servlet-name> <!--这个要和前面的 servlet name 相同--> <url-pattern>/hello</url-pattern> <!-- 根据原理这就是访问的名称可以更改 有斜杆 !!!--> </servlet-mapping> -
一个Serverlet 可以指定多个映射路径(虚拟路径)
<servlet-mapping> <servlet-name>hello</servlet-name> <!--这个要和前面的 servlet name 相同--> <url-pattern>/hello01</url-pattern> <!-- 根据原理这就是访问的名称可以更改 有斜杆 !!!--> </servlet-mapping> <servlet-mapping> <servlet-name>hello</servlet-name> <!--这个要和前面的 servlet name 相同--> <url-pattern>/hello02</url-pattern> <!-- 根据原理这就是访问的名称可以更改 有斜杆 !!!--> -
一个Serverlet 可以指定通用映射路径
<servlet-mapping> <servlet-name>hello</servlet-name> <!--通配符 表示 可以表达 任意数 包括 无--> <!--这里就不会跳过 index.jsp 因为通配符 前有目录--> <url-pattern>/hello/*</url-pattern> </servlet-mapping>![image-20220905231059877]()
-
默认的求情路径
<servlet-mapping>
<servlet-name>hello</servlet-name>
<!-- 如果直接写统配符 会直接进入 hello Serverlet,跳过index.jsp (因为通配也能表示无)-->
<url-pattern>/*</url-pattern>
</servlet-mapping>

-
指定一些后缀或者前缀等等.....
<servlet-mapping> <servlet-name>hello</servlet-name> <!--可以自定义后缀,实现请求映射 注意点,*前面不能加项目映射的路径,也就是说有后缀的的情况,前面不能有路径 / 。例如; /hello/*.xiaozhong 就不行 这里不行网页上可以,因为通配表示 任意参数,包括路径名字,只要结尾符合要求就行--> <url-pattern>*.xiaozhong</url-pattern> </servlet-mapping>

可以使用 通配 写一个404 的窗口
java 文件夹下 和helloServerlet 同级目录

Application
package com.xxz.Servlet01;
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 ErrorServerlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 这里相当于,设置 HTML 的头格式
resp.setContentType("text/html");
resp.setCharacterEncoding("utf-8");
PrintWriter writer = resp.getWriter();
writer.print("<h1>404<h1>");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
web.xml ERROR 配置映射
<?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="true">
<display-name>Welcome to Tomcat</display-name>
<description>
Welcome to Tomcat
</description>
<!--添加配置内容-->
<!--配置内容-->
<!-- 注册 Servlet-->
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.xxz.Servlet01.HelloServlet</servlet-class> <!--这里指向我们的类 即java 包下的class 文件-->
</servlet>
<!-- Servlet 的请求路径-->
<!-- http://localhost:8080/xxz01/hello-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<!--可以自定义后缀,实现请求映射
注意点,*前面不能加项目映射的路径,也就是说有后缀的的情况,前面不能有路径 / 。
这里不行网页上可以,因为通配表示 任意参数,包括路径名字,只要结尾符合要求就行-->
<url-pattern>/xiaozhong</url-pattern>
</servlet-mapping>
<!--配置内容-->
<!-- 配置 映射 映射 ERRORServerlet-->
<servlet>
<servlet-name>ERROR</servlet-name>
<servlet-class>com.xxz.Servlet01.ErrorServerlet</servlet-class>
</servlet>
<!-- 配置 映射 映射 ERRORServerlet-->
<servlet-mapping>
<servlet-name>ERROR</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
Result

由此说明 有固定映射名的,优先级是最高的
4.4 ServerletContext对象
对于web容器来说,ServletContext接口定义了一个servlet环境对象,这个对象定义了一个在servlet引擎上的servlet的视图。通过使用这个对象,servlet可以记录事件,得到资源并得到来自servlet的引擎类。
servlet 容器在启动时会加载web应用,并为每个web应用创建唯一的servlet context对象,可以把ServletContext看成是一个Web 应用的服务器端组件的共享内存,在ServletContext中可以存放共享数据。
注意 : 在不同的浏览器一样生效,因为他是凌驾于web应用的。并不在浏览器中,他是最高级的存在
他提供了4个读取和设置共享数据的方法。具体见api帮助文档。
另外,ServletContext对象只在web应用被关闭的时候才被销毁,不同的web应用,ServletContext各自独立存在。
一个web应用由jsp,servlet,javabean等web组件的集合构成,每一个web应用,容器都会有一个背景对象,而 javax.servlet.ServletContext接口就提供了访问这个背景对象的途径。你可以通过一个servlet实例的 getServletContext()方法得到该servlet运行其中的这个背景对象,从这个背景对象中你可以访问如下信息资源:
1.初始化参数
2.存储在背景中的对象
3.与背景关联的资源
4.日志
最后针对ServletContext我自己的总结是:
ServletContext即servlet容器,其内提供的方法可以在同一web应用下的所有servlet中被使用
————————————————
1. 共享应用
简单的说就是在一个 web 中传递参数 给 Serverletcontext 然后通过 servletContext 让其他web 拿到数据 达到共享参数的目的
第一步 新建 Module

在新建的子工程中 pom.xml 配置中
<packaging>war</packaging>以下的数据 全都可以删除

因为平时可能要粘贴一些固定的数据 ,所以建立一个剪切板 ,先存着用

可以把平时要用的数据 什么的 放这里 和Typora 差不多的 可以当做记事本

第二步 建立一个 HelloServerlet 项目
先补全目录并标识

写好路径和 java 文件

Serverlet_Application
package com.xiaozhong.serverlet;
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 HelloServerlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 这里就就是在控制台打印一行字,访问一下,打印一次
System.out.println("Hello,Serverlet_xxz~");
System.out.println("你好~");
// this 表示当前对象的意思, 要在 doGet 用
// this.getInitParameter() 获取初始化参数 就是在在 初始化的时候我们可以配置参数 但是几乎不用 略
//this.getServletConfig() 获取 Serverlet配置 然而我们在 web.xml 中是能看到 配置的 因此不常用 略
ServletContext context = this.getServletContext();// 这个context 可以是对象 也可以是字符
String userName = "晓忠";
// 将一个数据保存在了 ServletContext 中,名字为: username 值: userName
// 看做一个键值对,值可以是任意对象
context.setAttribute("userName",userName);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
Serverlet_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="true">
<servlet>
<!-- 服务端 名字-->
<servlet-name>hello</servlet-name>
<!-- 服务端 路径 真实路径-->
<servlet-class>com.xiaozhong.serverlet.HelloServerlet</servlet-class>
</servlet>
<servlet-mapping>
<!-- 映射名字-->
<servlet-name>hello</servlet-name>
<!-- 映射路径(虚拟)-->
<url-pattern>/xxz</url-pattern>
</servlet-mapping>
</web-app>
Serverlet_Result tomcat 打包小心出错 详见下下文


同时打包两个项目,小心出错。比如网页的优先级。 一般 用什么项目就打包什么项目

在 index.JSP 中HTML 上配置才能显示中文
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
第三步 再建立一个 getServerlet
Serverlet_Application
package com.xiaozhong.serverlet;
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;
public class GetServerlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
// 我们知道传送来的是 String 但是系统不知道 所以要强转
String context = (String) servletContext.getAttribute("userName");
// 响应写下面
// 因为我们的页面里有中文 所以要设置返回格式 注意 text 在前 位置调换打印会变成下载
resp.setContentType("text/html");
resp.setCharacterEncoding("UTF-8");
// 本来设置一个 header 就可以包含上面俩项
//resp.setHeader();
resp.getWriter().print("名字; "+context);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
Serserlet_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="true">
<servlet>
<!-- 服务端 名字-->
<servlet-name>hello</servlet-name>
<!-- 服务端 路径 真实路径-->
<servlet-class>com.xiaozhong.serverlet.HelloServerlet</servlet-class>
</servlet>
<servlet-mapping>
<!-- 映射名字-->
<servlet-name>hello</servlet-name>
<!-- 映射路径(虚拟)-->
<url-pattern>/xxz</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>getC</servlet-name>
<servlet-class>com.xiaozhong.serverlet.GetServerlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>getC</servlet-name>
<url-pattern>/getC</url-pattern>
</servlet-mapping>
</web-app>
第四步 启动测试 启动 Tocat 发现,
-
页面要打印中文必须,添加UTF-8编码 -
先运行getC ,是拿不到hello 赋给 ServerletContext 的值的,这里是有先后顺序的,要先赋值,再取值


2. 获取配置参数
配置参数 web.xml
<!-- 配置web 应用的初始化参数-->
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
</context-param>
获取参数
ServletContext context = this.getServletContext();
// 我们可以获取一些参数配置——这个是 ServletContext 的一个方法
String url = context.getInitParameter("url"); // "url"配置参数的名字
// 打印出我们的 url 配置的内容在页面上
resp.getWriter().print(url);
System.out.println(url);
// 最后在 web.xml 配置路径
Result

3. 请求调度
顾名思义 就是请求调度的字面意思,就是把本来的页面调度到其他页面。
这里演示 从demo04 调度到demo03 即返回的 url 的配置参数
承接上文
Servlet_Application
// 第一步 创建 ServletContext
ServletContext context = this.getServletContext();
// 第二步 创建 请求调度
// 传入一个映射的名字 即你要请求调度的 路径名 虚拟路径名 /demo03
// 返回的是一个请求调度 类型为 RequestDispatcher 类型
RequestDispatcher requestDispatcher = context.getRequestDispatcher("/demo03");
// 第三步 启动 forward 进行调度
requestDispatcher.forward(req,resp);// 调用forword 实现请求调度
// 第二步 第三步 写成一步走
//context.getRequestDispatcher("/demo03").forward(req,resp);
System.out.println("进入了 demo04"); //这里测试有没有进入 demo04
/* 很好理解,在ServletContext 中调用 getRequestDispatcher
方法返回的是 RequestDispatcher, 在调用 forward 直接实现请求调度*/
Result

原理

4.读取资源文件
解决一; 在java 文件夹下的配置文件(即db.properties) 不被读取的问题。(即在 java 文件夹下配置文件 ,生成target 后不存在的问题)
- 先在资源目录下 创建 db.properties 并添加两个键值对。

清理目标文件 (target)


这里有个问题就是写在 java 路径下的配置文件(即 db.properties 文件) 是加载不出来的 解决方法如下;
Maven 由于他的约定大约配置,我们写的配置文件,无法被导出或者生效的问题,解决方案;
在 pom.xml
中 做如下配置 注意规范 一个xml 中只能有一个 build 一个build 中只能有一个 resources 以此类推
<resources>
<resource>
<!-- resource 应该有默认配置 不需要写 但是java 文件下要写 才能生效-->
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
选择你要配置的地方 做好配置即可

最后可以试试写在 java 下的配置(pp.properties) 是否可以生效 。运行tomcat 前 clear 一下maven。

然后发现是能够生效的,然后目录在平时的 resources 目录中。猜想应该idea 解析出它不是java文件 所以把它丢在外面。写路径的时候可以注意一下这个细节。 他们的父目录,classes 俗称 classpath
以上解决,资源读取加载不出的问题,以下是 如何读取资源的教程
值得注意的是,资源文件本身提供了,Properties 类提供使用,打印资源文件时调用即可

Properties
user=root
password=OOoo5555
#calsspath
Servlet _Application
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 这里相当于,设置 HTML 的头格式 必须放在最前 才会生效
resp.setContentType("text/html");
resp.setCharacterEncoding("utf-8");
// 第一步 避免乱码
resp.setContentType("text/html");
resp.setCharacterEncoding("utf-8");
// 第二步 获取Context
ServletContext context = this.getServletContext();
// 前面表示当前目录,即当前项目target 下 (Serverlet_02)目录下。
// 因为java 目录下的配置文件也会踢出到 classes 目录下因此 写在java 目录下的文件也可以写这个路径
// 第三步 获取 Resource / 是指当前项目目录 资源目录 是看加载成 out 目录后的目录
// 注意看这个是 资源流
InputStream is = context.getResourceAsStream("/WEB-INF/classes/db.properties");
// 第四步 创建属性(所有物)
Properties prop = new Properties();
// 第五步 从输入流中载入属性列表(键和元素对)。
prop.load(is);
// 第六步 获取 属性(所有物)
String user = prop.getProperty("user");
String password = prop.getProperty("password");
// 第七步 打印
PrintWriter writer = resp.getWriter();
// 这里设置了字体格式 我没设置所以是正文格式。一旦设置猜想全篇都是h1,以此类推用最近的格式
writer.print("<h1>404<h1>");
writer.print("user: "+user);
writer.println("<br/>");
writer.println("password: "+password);
}
Result


5. HttpServletResponse
Web 服务器接受到客户端的http请求,针对这个请求,分别创建一个代表请求的 HttpServletRequest 对象,和代表响应的一个 HttpServletResponse;
- 如果要获取请求过来的参数; 找 HttpServletRequest
- 如果要给客户端响应一些信息: 找HttpServletRequest
简单分类
HttpServletResponse ServletResponse 两个接口下的方法
负责向浏览器发送数据的方法
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 setDateHeader(String var1, long var2);
void addDateHeader(String var1, long var2);
void setHeader(String var1, String var2);
void addHeader(String var1, String var2);
void setIntHeader(String var1, int var2);
void addIntHeader(String var1, int var2);
响应的状态码
记住常见的状态码就行; 200 响应正常。3XX、请求重定向。4XX 找不到资源。5xx 服务器代码错误 502 :网关错误
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;
常用应用
向浏览器输出消息,(getWriter(),``)
下载文件(获取资源)
- 获取下载文件的路径
- 下载的文件名是啥?
- 设置想办法让浏览器能够支持下载我们需要的东西
- 获取下载文件的输入流
- 创建缓冲区
- 获取OutputStream对象
- 将FileOutStream流写入到buffer缓冲区
- 使用OutStream将缓冲区中的数据输出到客户端
Context 的几个路径
| Method | Parameter | Result |
|---|---|---|
| context.getContextPath() | 无 | /s1 |
| context.getRealPath(); | "/WEB-INF/classes/image/xion.jpg" | D:\java\apache-tomcat-9.0.24\webapps\s1\WEB-INF\classes\image\xion.jpg |
Application
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 第一步 获取下载文件路径 及其文件名
ServletContext context = this.getServletContext();
String filePath = context.getRealPath("/WEB-INF/classes/image/金.jpg");
// \\ 转义 \
String fileName = filePath.substring(filePath.lastIndexOf("\\") + 1);
System.out.println(filePath+"---"+fileName);
// 第二步 配置浏览器响应头头 让浏览器能够支持下载我们需要的东西 (配置信息头)
//setHeader 格式(名称,值)
resp.setHeader("Content-Disposition","attachment;filename="+URLEncoder.encode(fileName,"UTF-8"));
// attachment 附件下载 inline 在线观看
// Content-Disposition Content !!! 不是 context 这里是分号 ; 别写错
//public static String encode(String s,String enc)使用指定的编码机制将字符串转换为 application/x-www-form-urlencoded 格式。该声明应使用 UTF-8。如果不使用该编码,可能造成不兼容性。
// Content-Disposition: attachment; filename="filename.jpg"
// 第三步 读取文件
FileInputStream is = new FileInputStream(filePath);
// 第四步 调出 响应输出流
ServletOutputStream out = resp.getOutputStream();
// 第五步 输出并关闭资源
int len = 0;
byte[] buffer= new byte[1024];
while((len=is.read(buffer))>0){
out.write(buffer,0,len);
}
out.close();
is.close();
}
Result


验证码
制作一个,打开网页验证码,每3秒,刷新一次; 如下

设计思路
-
设置响应头
-
三秒刷新
-
告诉浏览器这是图片格式
-
禁止缓存
- 禁止缓存
- 缓存控制 禁止缓存
- 编译器 禁止缓存
-
-
制作图片
- 图片背景
- 制作随机数
- 结合
-
发送图片到浏览器
Application
package com.xiaozhong.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;
public class ImageServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 第一步 配置浏览器参数
// 让浏览器 3秒自动刷新一次; 响应可以写前端的任何东西
resp.setHeader("refresh","3");
// 清除缓存 设置网站存在缓存,不然浏览器缓存,我们通过设置响应头,来减轻浏览器缓存负担
resp.setDateHeader("expires",-1); //-1 禁止缓存
resp.setHeader("Cache-Control","no-Control"); // 控制缓存
resp.setHeader("pragma","no-Cache");
// 编写响应头出错 prama 发布时好像也不会报错
// 设置内容类型 告诉浏览器 这个请求用图片方式打开
resp.setContentType("image/jpeg");
// 后面填写什么没关系 例如:image/png 也行 我们要告诉浏览器这是什么文件具体格式不用管
// 第三步 创建缓存图片
// 在内存中创建一个图片 图片( width,height,色彩模式 )
BufferedImage image = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB);
// 得到图片 返回一只 2D 的笔 Java 是可以写大型游戏的
Graphics2D g = (Graphics2D)image.getGraphics();
// 设置图片的背景颜色 并填充成长方形
g.setColor(Color.red);
g.fillRect(0,0,80,20);
// 给图片写数据
g.setColor(Color.cyan);
// 这里xy 位置 设置的是写字时 左下角的位置
g.setFont(new Font(null,Font.BOLD,20));
g.drawString(makeNum(),0,20);
// 第四步 把图片写给浏览器
boolean jpg = ImageIO.write(image, "jpg", resp.getOutputStream());
resp.getWriter().println("传输结果; "+jpg);
}
// 第二步 生成 验证码的七位随机数
private String makeNum(){
Random random = new Random();
// 这里是 填写随机数的为数 然后将int 转化为 String 也可以添加空字符转化
// 这里也不知道 为什么一定要转化成字符串 难道不能直接数字么 待看
String num = String.valueOf(random.nextInt(9999999));
// 添加一个字符串的缓存 不知道为什么一定要用缓存
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 7-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);
}
}
Result


Mapping-serverlet 出错导致发布失败
[2022-09-25 12:42:08,032] Artifact Response:war: Error during artifact deployment. See server log for details.

重定向

一个web资源收到客户端请求后,他会通知客户端去访问另外一个web资源,这个过程叫做重定向。
常见场景;
-
用户登入
先访问登入页面,如果成功跳转页面
void sendRedirect(String var1) throws IOException;
Application
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 如果不 加 r 会跳转404报错 http://localhost:8080/redirect 没有了虚拟路径(项目路径) /r
resp.getWriter().println("进入了 Redirect");
//resp.sendRedirect("/r/image"); ------重定向
// 拆开写
resp.setHeader("location","/r/image");
resp.setStatus(SC_MOVED_TEMPORARILY);// 302 也可以
System.out.println("进入了 Redirect");
}
Result

面试题: 请你聊聊重定向和转发的区别
相同点;
- 都会实现页面跳转
不同点;
- 重定向在跳转页面的时候会地址栏发生变化
小练习
需要先导入 Maven 包 如 下面

index.jsp
<%@page contentType="text/html; charset=utf-8" pageEncoding="UTF-8" %>
<%--这个头不写 会乱码--%>
<html >
<head>
<meta charset="utf-8" content="text/html">
</head>
<body>
<h2>欢迎_login</h2>
<%-- 要导入maven包 才能实现 这个虚拟路径--%>
<form action="${pageContext.request.contextPath}/requestTest01" method="get" >
<%-- 返回当前项目虚拟路径 等同于/r ${pageContext.request.contextPath}--%>
<%-- action="/r/image" 试过 也可以的 不过他说最好导入 包写 --%>
用户名:<input type="text" name="userName"><br>
密码: <input type="password" name="pwd"><br>
<input type="submit" value="登入" >
<hr>
</form>
</body>
</html>
RequestTest01
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html");
resp.setCharacterEncoding("utf-8");
resp.getWriter().println("你好~");
System.out.println("进入了这个请求");
String userName = req.getParameter("userName");// 这里是获取请求的参数
String pwd = req.getParameter("pwd");
System.out.println("账号: "+userName+"\n"+"密码: "+pwd);
// 写了 success.jsp 和index.jsp 一个路径index.jsp
// 我第一次的时候 报错 404 因为放错了路径 注意 要和index.jsp 一个路径才正确 要写后缀
resp.sendRedirect("/r/success.jsp");
}
Success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--这里会默认跳出 不知道 为什么 index.jsp 是默认没有的--%>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>Success</h1>
<%--测试跳转--%>
</body>
</html>
Result
qq截屏 并不是mp4 别被马化腾骗了!!!
支持 Maven 虚拟路径 Start
Pom.xml
Maven_包路径 ——作用于支持指向虚拟项目路径 (/r)
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>

<%@page contentType="text/html; charset=utf-8" pageEncoding="UTF-8" %>
<%--这个头不写 会乱码--%>
<html >
<head>
<meta charset="utf-8" content="text/html">
</head>
<body>
<h2>欢迎_login</h2>
<%-- 要导入maven包 才能实现 这个虚拟路径--%>
<form action="${pageContext.request.contextPath}/requestTest01" method="get" >
<%-- 返回当前项目虚拟路径 等同于/r ${pageContext.request.contextPath}--%>
<%-- action="/r/image" 试过 也可以的 不过他说最好导入 包写 --%>
用户名:<input type="text" name="userName"><br>
密码: <input type="password" name="pwd"><br>
<input type="submit" value="登入" >
<hr>
</form>
</body>
</html>
支持 Maven 虚拟路径 end
jsp 中文乱码问题Start
jsp 中文乱码问题

在最顶部加这条
<%@page contentType="text/html; charset=utf-8" pageEncoding="UTF-8" %>
jsp 中文乱码问题 end
6. HttpServletRequest

两个主要功能
-
获取前端传递的参数,String passWord = req.getParameter("密码: "+"passWord"); // 获取单个参数 String[] hobbys = req.getParameterValues("hobbys");//获取多个参数 字符串组

一个返回字符串,一个返回字符串数组
-
请求转发req.getRequestDispatcher("/success.jsp").forward(req,resp);
原理

从index.jsp 提交登入后,到LoginServlet 中 然后后端打印请求参数,再转发到success.jsp
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登入</title>
</head>
<body>
<h1 style="text-align: center"> 登入</h1>
<div style="text-align: center">
<%-- 获取项目虚拟路径 /r 但是前端 index 中路径一定要加 后端的 转发不用加--%>
<%-- 这种路径还是要记得导入maven包 到 pom.xml --%>
<form action="${pageContext.request.contextPath}/login" method="post">
用户名:<input type="text" name="userName" value="xxz"><br>
密码: <input type="password" name="passWord" value="pwd"><br>
<input type="checkbox" name="hobbys" value="女孩"> 女孩
<input type="checkbox" name="hobbys" value="java"> java
<input type="checkbox" name="hobbys" value="电影"> 电影
<input type="checkbox" name="hobbys" value="唱歌"> 唱歌
<br>
<input type="submit">
</form>
</div>
</body>
</html>
LoginServlet
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 解决后台请求回来的 中文乱码问题 哪里来的数据 设置谁的字符编码 为 utf-8
// 所以提前把这两个写好 就不会有乱码了
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
// 获取字符串的请求参数
String userName = req.getParameter("账号: "+"userName");
String passWord = req.getParameter("密码: "+"passWord");
// 获取字符串数组的请求参数
String[] hobbys = req.getParameterValues("hobbys");
System.out.println("=========================");
System.out.println(userName);
System.out.println(passWord);
System.out.println(Arrays.toString(hobbys));
System.out.println("=========================");
//通过请求转发 实现跳转页面 forward 前进
// 我们从 404 提示中看见 /r/r/success.jsp 说明是路径问题
// index.jsp 请求路径和 我们请求的路径 req.getContextPath() 是一样的 我们打印出来看看 问题在哪里
req.getRequestDispatcher("/success.jsp").forward(req,resp);
// 打印结果符合预期 /r
System.out.println(req.getContextPath());
//结论 重定向 需要项目路径 /r 转发 中的 / 代表当前项目所有不用 /r
//例如 /success.jsp = /r/success.jsp ; //success.jsp = /r/r/success.jsp
// 但是前端 index 中路径一定要加
}
success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登入成功</title>
</head>
<body>
<h3>登入成功</h3>
</body>
</html>
Result

(五) Cookie Session
5.1 回顾 创建 Maven webapp项目的过程
首先 创建 webapp 项目
首先 创建 webapp 项目
第一步 (要等加载完全后操作)
第二步
第一步
>
>第二步
>
>第三步
>
>第三步
![]()
<!-- 项目依赖--> <dependencies> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.3</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.0</version> <scope>provided</scope> </dependency> </dependencies>确认导入成功
![]()
第四步 配置和启动 Tomcat
Maven webapp项目 创建结束 End
5.2 Session(会话)
会话; 用户打开一个浏览器,点击了很多超链接,访问多个web资源,关闭浏览器,这个过程可以称之为会话。
有状态会话; 一个同学来过教室,下次再来教室,我们会知道这个同学,曾经来过。称之为有效对话。
你怎么能证明你是公司的员工?
你 公司
- 劳动合同 公司给你的劳动合同
- 公司花名册登记 公司给你登记了名字
一个网站怎么证明你来过?
客户端 服务端
- 服务端给客户端一个 信件 ,客户端下次访问带上信件就可以了;
cookie - 服务器登记你来过来,下次你来的时候 服务器 会匹配你的信息 ;
Session
5.3 保存会话的两种技术
Cookie
- 客户端技术 (请求,响应)
Session
- 服务器技术,利用这个技术,可以保存用户的会话信息?我们可以把信息或者数据放在 Session中!
常见场景; 网站登入之后,下次就不需要输入账号密码了,就可以直接登入,相对于记住密码,但是不止这个功能。
(一)Cookie
操作步骤
-
从请求中 拿到 Cookie 信息
-
服务器响应给客户端 Cookie
Cookie[] cookies = req.getCookies();// 获取 Cookie 服务器从客户端获取
Cookie cookie = new Cookie("LastLoginTime",String.valueOf( .currentTimeMillis()));// 给客户端响应一个 cookie
String name = cookie.getName(); // 获取 cookie 的名字 Key
String value = cookie.getValue(); // 获取 cookie 中的值这是一个时间戳 是一个数字
cookie.setMaxAge(24*60*60); // 设置 Cookie 的有效期 设置为0 就会立马过期
resp.addCookie(cookie);// 响应客户端一个 Cookie 添加到 cookie
这里涉及到字符编码的
编码和解码来解决字符串乱码 (虽然 后面没用到 这里还是带一笔)Cookie cookie = new Cookie("LastLoginTime", URLEncoder.encode("项晓忠","utf-8"));//编码 resp.getWriter().println(URLDecoder.decode(cookie.getValue(),"utf-8");//解码
CookieDemo01
package com.xiaozhong.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;
// 导入 Maven 包之后才会有 HttpServlet
public class CookieDemo01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 服务器,告诉你 你来的时间,把这个时间封装称为一个 信件 ,你下次带来,我就知道你来了。
// 解决中文乱码的问题
resp.setCharacterEncoding("utf-8");
req.setCharacterEncoding("utf-8");
// resp 设置 utf-8 后繁体问题 我们要设置 内容类型为 "text/html" 类型
resp.setContentType("text/html");
// Cookie 服务器从请求获取
Cookie[] cookies = req.getCookies();
PrintWriter writer = resp.getWriter();
// 判断 Cookie 是否存在 其实 Cookie 我们不追加时 默认也存在 Cookie 所以要判断有没有我们要的 Cookie
if (cookies!=null){
Boolean flag = true;
for (int i = 0; i < cookies.length; i++) {
// 整个 Cookie[] 数组是Cookie[]数组 但是取其中一个就是 Cookie 类型
Cookie cookie = cookies[i];
// 获取 cookie 的名字 并判断 是不是我们要的Cookie
if (cookie.getName().equals("loginTime")){
// 获取 cookie 中的值
String value = cookie.getValue();
// 转化格式
long loginTime = Long.parseLong(value);
Date date = new Date(loginTime);
String sFormat = "yyyy-MM-dd HH:mm:ss";
SimpleDateFormat format = new SimpleDateFormat(sFormat, Locale.CHINA);
writer.println("上次登入时间为: "+format.format(date));
flag =!flag;
}
System.out.println(cookie.getName());
}
if (flag){
writer.println("welcome! First Login");
}
}
long millis = System.currentTimeMillis();
// 1. 创建 cookie ;我们查看Structure 发现只能放入字符串 name 和 value
Cookie cookie = new Cookie("loginTime",String.valueOf(millis));
// 2. 添加到 cookie
resp.addCookie(cookie);
// cookie.setMaxAge(24*60*60); // 设置 Cookie 的有效期
}
}
CookieDemo02
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 创建一个 Cookie 名字必须要和要删除的名字一样 相对于 替换我们要删除的Cookie
Cookie cookie = new Cookie("LastLoginTime",String.valueOf(System.currentTimeMillis()));// 转化成字符串
cookie.setMaxAge(0); // 设置 Cookie 有效期
resp.addCookie(cookie);
}
Result
第一次 访问浏览器是空的 因为我们没有传递 cookie 内容 但是从第二次 开始我们就会传递 cookie但是我们的 cookie 没有保存下来 如果不关闭 tomcat 重启浏览器 访问时会重新开始
所以我们要 设置 cookie 的到期时间 SetMaxAge() 单位 秒
查看 Cookie
添加到期时间后 查看 Cookie
Cookie 一般会保存在本地的 用户目录下 appdata
C:\Users\夏天的风\AppData
一个网站是否存在 Cookie 上限
- 一个 Cookie 只能保存一个信息
- 一个 web 网点可以给浏览器发送多个Cookie 最多存放 20 个 Cookie
- Cookie 大小有限制 4kb
- 300个Cookie 浏览器上
删除Cookie;
- 不设置有效期,关闭浏览器自动失效
- 设置有效期时间为
Test 添加手动删除 Cookie
(二)Session(重点)
2.1——Session 详解
比Cookie 用的多 因为他是
服务器行为
什么是Seession
- 服务器会给每一个用户(浏览器) 创建一个 Session 对象
- 一个Session 独占一个浏览器,只要浏览器没有关闭,这个Session 就存在
- 用户登入以后,整个网站他都可以访问,-->保存用户的信息,保存购物车的信息;
Seesion 常用方法

Session 和 cookie 的区别:
- Cookie 是吧用户的数据写给用户的浏览器,浏览器保存(可以保存多个)
- Session 把用户的数据写到用户独占 Session 中,服务器保存(重要的信息,减少服务器的资源的浪费)
- Session对象由服务创建
使用场景:
- 保存一个登入用户的信息
- 购物车的信息
- 整个网站中经常使用的数据,我们将它保存在 Session 中
2.2—— Session 存入和输出字符串
SessionDemo01
存入Session
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 解决乱码问题
resp.setCharacterEncoding("utf-8");
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");// 响应回去的页面内容
// 得到 Session 和Cookie 不同的是 Session 可以存对象 Cookie 只能放字符串
// 注意 Session 的特点是 全浏览器 唯一 所以存取 都用Session
HttpSession session = req.getSession();
//给 Session 中存东西
session.setAttribute("name","晓忠");
// 获取 Session 的id id 唯一
String sessionId = session.getId();
// 最大的激活有效期间隔
// 举例:session.setMaxInactiveInterval(30 * 60); 单位是秒(s),此设置的有效期是30min
// 注意:如果设置的值为零或负数,则表示会话将永不超时!
session.setMaxInactiveInterval(3);
//Session 是浏览器一打开就存在的
//判断 Session 是不是新创建的 从这步中我们可以推断 Session 是从网页打开后一开始就存在的
if (session.isNew()){
resp.getWriter().write("Session创建成功: id :"+sessionId);
} else {
resp.getWriter().write("session 以及在服务器中存在了,ID: "+sessionId);
}
// 因为 Cookie 中有JSESSIONID 我们猜想 Session 创建的时候做了什么事情:
// Cookie cookie = new Cookie("JSESSIONID",sessionId);
// resp.addCookie(cookie);
}
Result

SessionDemo02
获取Session
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 解决乱码问题
resp.setCharacterEncoding("utf-8");
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
// 得到 Session
HttpSession session = req.getSession();
String name = (String)session.getAttribute("name"); // 我们知道是String 类型所以强转为 String
resp.getWriter().write("Name: "+name);
System.out.println(name);
}
Result `先访问demo02 再访问demo01 再回来 demo02
展开 .com 文件夹 取消隐藏空文件夹
2.3——Session 存入和输出对象
SessionDemo03
在Session中存入和读出对象
第一步——建立对象
package com.xiaozhong.pojo;
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
第二步 —— 改SessionDemo01
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 解决乱码问题
resp.setCharacterEncoding("utf-8");
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");// 响应回去的页面内容
// 得到 Session 和Cookie 不同的是 Session 可以存对象 Cookie 只能放字符串
HttpSession session = req.getSession();
//给 Session 中存东西
session.setAttribute("name",new Person("晓忠",22)); // 这里是因为 import 导入了这个类所以能调用这个类
// 获取 Session 的id
String sessionId = session.getId();
//Session 是浏览器一打开就存在的
//判断 Session 是不是新创建的 从这步中我们可以推断 Session 是从网页打开后一开始就存在的
if (session.isNew()){
resp.getWriter().write("Session创建成功: id :"+sessionId);
} else {
resp.getWriter().write("session 以及在服务器中存在了,ID: "+sessionId);
}
}
第三步——改SessionDemo02
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 解决乱码问题
resp.setCharacterEncoding("utf-8");
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
// 得到 Session
HttpSession session = req.getSession();
Person person = (Person)session.getAttribute("name"); // 我们知道是String 类型所以强转为 String
resp.getWriter().write("Name: "+person.toString());
System.out.println(person);
}
Result ——
第一次 空指针异常 是因为我们没有走 s01 所以拿不到对象的值
Result
2.4——Session 移除和使失效
3种方法可以设置session有效期
一、据我所知,目前有3中方法可以设置session有效期:
A、使用java函数:session.setMaxInactiveInterval()
- 举例:session.setMaxInactiveInterval(30 * 60); 单位是秒(s),此设置的有效期是30min
- 注意:如果设置的值为零或负数,则表示会话将永不超时!
B、在工程web.xml中的session-config中配置
-
举例:设置为30分钟有效期
1. <session-config> 2. <session-timeout>30</session-timeout> 3. </session-config>
注意:以分钟为单位,必须为整数。如果 session-timeout元素的值为零或负数,则表示会话将永不超时!
C、直接在应用服务器中设置(举例tomcat)
- 可以在tomcat目录下conf/web.xml中找到
<session-config>元素,tomcat默认设置是30分钟, - 注意:以分钟为单位,必须为整数。如果 session-timeout元素的值为零或负数,则表示会话将永不超时!
二、这三种方法设置session有效期的优先级(此处优先级指的是如果A、B、C三种方法同时使用时的优先级)
A>B>C
SessionDemo03
注意不同的浏览器 Session 的ID不同
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 我们要移除 和更新 Session
HttpSession session = req.getSession();
// 移除一个属性
session.removeAttribute("name");
// 使失效 使 Session 失效 所谓失效就是从新生成一个 SessionId
session.invalidate();
}
Result
Result
也可以在 Web.xml 中配置 让他 自动失效
<!-- 设置Session 默认的失效时间 -->
<session-config>
<!-- 1分钟后自动失效 单位 分钟-->
<session-timeout>1</session-timeout>
</session-config>
(六) JSP
WEB-INF 目录下的 jsp 不能直接访问 但是有方法可以访问
只有没有更新 proFiles 和 java代码 不用 redeploy(重新发布)
6.1、 什么是 JSP
Java Server Pages : Java 服务器端页面,也和 Servlet 一样,用于动态 Web 技术!
最大的特点;
- 写JSP 就像写 HTML
- 区别:
- HTML 只给用户提供静态的数据
- JSP 页面中可以嵌入java 代码,为用户提供动态数据
6.2、 JSP 原理
思路: JSP到底是怎么执行的!
-
代码层面没有任何问题
-
服务器内部工作
Tomcat 中有一个work 目录
IDEA 中使用 Tomcat 就会在IDEA中重新产生一个 work 目录
如果编译失败 就不存在 文件了
我的电脑的地址:
C:\Users\夏天的风\.IntelliJIdea2019.3\system\tomcat\Unnamed_javaWeb-session-cookie_2\work\Catalina\localhost\r\org\apache\jsp
发现页面转化成了 Java 程序

浏览器向服务器发送请求,不管访问什么资源,其实都是在访问 Servlet
JSP 最终也会被转化成为一个 Java 类!
JSP 本质上就是一个Servlet
index_jsp.java 继承了 HttpJspBase
public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
HttpBase继承了HttpServlet
public abstract class HttpJspBase extends HttpServlet implements HttpJspPage
一开始 找不到 HttpBase 类的解决方法
// 初始化
public void _jspInit() {
}
//销毁
public void _jspDestroy() {
}
// JSP Serverice
public abstract void _jspService(HttpServletRequest var1, HttpServletResponse var2)
- 判断请求
- 内置一些对象
final javax.servlet.jsp.PageContext pageContext; //页面上下文
javax.servlet.http.HttpSession session = null; //session
final javax.servlet.ServletContext application; //applicationcontext
final javax.servlet.ServletConfig config; //config
javax.servlet.jsp.JspWriter out = null; //out
final java.lang.Object page = this; //page:当前
HttpServletRequest var1 //请求
HttpServletResponse var2 //响应
- 输出页面前增加的代码
response.setContentType("text/html;charset=UTF-8");
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;
out.write("\r\n");
out.write("\r\n");
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write("<title>你好 晓忠</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");
out.write("<h1>Hello Tomcat</h1>\r\n");
out.write("</body>\r\n");
out.write("</html>\r\n");
以上的这些对象我们可以在JSP页面中直接使用
流程分析
小透露
在JSP 页面中:
只要是JAVA 代码就会原封不动的输出;
如果是HTML 代码,就会被转化为
out.write("<h1>Hello Tomcat</h1>\r\n");
这样的格式,输出到前端!
6.3、 JSP 基础语法
创建的 Maven 项目,普通的项目没有 web功能,web项目没有普通的功能,所以要结合使用。
在普通项目中添加支持 web 应用
pom.xml 添加四大依赖 缺一不可
<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>
<!-- standard JSTL 需要这个 标签库 -->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
</dependencies>
添加完毕后 先启动 Tomacat 看看能不能正常启动 以后写项目前都要先跑一遍 然后再开始学习 JSP
启动 Tomcat 小细节(速度提升)
Result
1 JSP 语法
<%--
1. jsp 表达式
作用: 用来将程序的输出,打印到前端
<%= 变量或者 表达式%>
--%>
<%= new java.util.Date()%> <br>
<% String name = new String("xxz");%>
name: <%= name %><br>
<%--在 HTML 中本身可以存在一个空格 但是只能一个 再多也只显示一个 所以要转义--%>
<hr>
<%--
2. JSP 脚本片段
<% 这里写 java 代码%>
作用; 比表达式要广,可以写java 的任何代码--%>
<%
int sum =0;
for (int i = 0; i <= 100; i++) {
sum+=i;
}
// out 能用是因为 前面提到的 已经内置这些对象 虽然不理解 反正就是特殊处理过的 out 不一般
out.println("这框里可以写 普通的字符 也能写 html 代码"+"\n"+"<h1>Sum= "+sum+"</h1>");
%>
<%= // 说明在 一整个jsp 中 java 代码的作用域是在一起的 前端会打印出1314
sum =1314
%>
<hr>
<%--
3. 可以在代码中嵌入 HTML 元素 同样 在 HTML 中也能嵌入 java--%>
<%
for (int i = 0; i < 3; i++) {
out.print(" "+"<h1>"+i +" ");
%>
hello xxz <%=i+3%> </h1>
<%
}
%>
<hr>
Result
2 JSP 声明
我们通过 tomcat 中的work 文件夹下查看源码 index_jsp.java 发现以下情况
public final class index_jsp{
// 中间还有很多的方法
public void _jspService(){
// 我们写的代码 都是在这个方法内 发送给前端
}
}
所以 推出一种方法 写在方法的外面 类的里面——JSP 声明
声明的作用:
- 可以声明 变量(
作用域于类) - 可以声明 类方法(
作用域于类)
JSP 声明
<%! %>
<%!
static {
System.out.println("Loading Servlet");
}
private int global = 10;
public void xxz(){
System.out.println(" 进入了,方法 xxz");
}
%>
<%
xxz();
%>
Result
JSP声明: 会被编译到 JSP 生成Java 的类中! 其他的, 就会被生成到 _jspService 方法中!
JSP ,嵌入代码的几种方法;
<% %> 普通java 代码
<%= %> jsp 表达式 用于向前端输出信息
<%! %> jsp 声明 写到类中 作用域更大
<!--HTML 注释-->
<%--JSP 注释--%>
JSP 的注释,不会在客户端显示,HTML 就会!
3 JSP 指令
关于 JSP 配置
<%--配置 页面--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--配置 导入 在page 设置导入 就一次到位 不需要重复写--%>
<%@ page import="java.util.Date"%>
<%--配置 报错--%>
<%--如果当前页面报错就会 跳转到 500.jsp--%>
<%@ page errorPage="error/500.jsp" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--如果你要用类 就要导入包 --%>
<%--1. 写成这种格式 每次都要写 及其麻烦--%>
<%
new java.util.Date();
%>
<% new Date();%>
<%--导入第三方标签库--%>
<%-- 引入 JSTL 核心标签库 , 我们才能使用 JSTL 标签 core--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
</body>
</html>
1——自定义错误页面 page errorPage

Application 500.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%-- 最好把 资源所在目录标记(mark)为 资源目录 --%>
<img src="./img/1.png" alt="500错误" >
<%
for (int i = 0; i < 200; i++) {
%>
<h3>ie 会判断你的错误页面, 是否大于 512 字节,否就跳转自带页面,是则进入自定义页面</h3>
<%
}
%>
</body>
</html>
在web.xml 中 添加配置
<error-page>
<error-code>404</error-code>
<location>/error/404.jsp</location>
</error-page>
<!-- 修改了 web.xml 就必须重启Tomcat-->
<error-page>
<error-code>500</error-code>
<location>/error/500.jsp</location>
</error-page>
ie 自定义错误页面不加载问题
ie 会判断你 自定义的 错误 页面是否超过512字节内容,如果否,就返回自带页面
如果浏览器没预期加载 那么可以测试一下其他浏览器运行效果

2——固定页面头部 和 脚部 page errorPage

Application Header
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<h1>我是 Footer</h1>
Application Footer
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%@include file="common/Header.jsp"%>
<h2>网页主体</h2>
<%@include file="common/Footer.jsp"%>
</body>
</html>
Result
拓展
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%-- 查看源码 发现 是把页面 合二为一 所以 在声明参数的时候 不能重名 这就要注意 : page contentType charset language 只要声明一次即可--%>
<%@include file="common/Header.jsp"%>
<h2>网页主体</h2>
<%@include file="common/Footer.jsp"%>
<hr>
<%-- 查看源码 发现 是把页面 拼接 互不干扰 所以 可以声明 重复参数--%>
<jsp:include page="common/Header.jsp"/>
<h2>网页主体</h2>
<jsp:include page="common/Footer.jsp"/>
</body>
</html>

index3_jsp.java work 目录下源码
out.write("\r\n");
out.write("\r\n");
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write(" <title>Title</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");
out.write("\r\n");
out.write("\r\n");
out.write("\r\n");
out.write("<h1>我是 Header</h1>");
out.write("\r\n");
out.write("<h2>网页主体</h2>\r\n");
out.write("\r\n");
out.write("\r\n");
out.write("<h1>我是 Footer</h1>");
out.write("\r\n");
out.write("\r\n");
out.write("<hr>\r\n");
out.write("\r\n");
org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "common/Header.jsp", out, false);
out.write("\r\n");
out.write("<h2>网页主体</h2>\r\n");
org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "common/Footer.jsp", out, false);
out.write("\r\n");
out.write("</body>\r\n");
out.write("</html>\r\n");
4 JSP 内置对象及其作用域
- PageContext
存东西 - Request
存东西 - Response
- Session
存东西 - Application 【ServletContext】
存东西- Config 【ServletConfig】
- out
打印到浏览器 - page
- exception
Application
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--内置对象--%>
<%
pageContext.setAttribute("name_01","xxz_01");// 保存的数据只在一个页面中有效
request.setAttribute("name_02","xxz_02");// 保存的数据只在一次请求中有效,请求转发会携带这个数据
session.setAttribute("name_03","xxz_03");// 保存的数据只在一次会话中有效,从打开浏览器到关闭浏览器
application.setAttribute("name_04","xxz_04");// 保存的数据只在服务器中有效,从打开服务器到关闭服务器
%>
<%--
<%%> 中的片段代码,会原封不动的生成到 JSP.java
要求: 这里的代码:必须保证 Java 语法的正确性
--%>
<%
// c从poageContext 取出,我们通过寻找的方式来
// 从底层到高层 (作用域): page-->request-->session-->application
String name_01 = (String) pageContext.findAttribute("name_01");
String name_02 = (String) pageContext.findAttribute("name_02");
String name_03 = (String) pageContext.findAttribute("name_03");
String name_04 = (String) pageContext.findAttribute("name_04");
String name_05 = (String) pageContext.findAttribute("name_05");// 不存在
%>
<%--使用 EL表达式输出 ${} 和<%=%> 一样
优点: 输出参数 如果不存在 不会报null--%>
<h1>取出的值为</h1>
<h3>name_01 = ${name_01} </h3>
<h3>name_02 = ${name_02} </h3>
<h3>name_03 = ${name_03} </h3>
<h3>name_04 = ${name_04} </h3>
<h3>name_05 = ${name_05} </h3>
<hr>
<h3>name_05 = <%= name_05 %></h3> <!--null-->
</body>
</html>
Result
测试 在demo01页面 加载后 在其他页面 能获取的值
Application
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
// c从poageContext 取出,我们通过寻找的方式来
// 从底层到高层 (作用域):
String name_01 = (String) pageContext.findAttribute("name_01");
String name_02 = (String) pageContext.findAttribute("name_02");
String name_03 = (String) pageContext.findAttribute("name_03");
String name_04 = (String) pageContext.findAttribute("name_04");
String name_05 = (String) pageContext.findAttribute("name_05");// 不存在
%>
<%--使用 EL表达式输出 ${} 和<%=%> 一样
优点: 输出参数 如果不存在 不会报null--%>
<h1>取出的值为</h1>
<h3>name_01 = ${name_01} </h3> // 保存的数据只在一个页面中有效
<h3>name_02 = ${name_02} </h3> // 保存的数据只在一次请求中有效,请求转发会携带这个数据
<h3>name_03 = ${name_03} </h3>// 保存的数据只在一次会话中有效,从打开浏览器到关闭浏览器
<h3>name_04 = ${name_04} </h3>// 保存的数据只在服务器中有效,从打开服务器到关闭服务器
<h3>name_05 = ${name_05} </h3>
<hr>
<h3>name_05 = <%= name_05 %></h3> <!--null-->
</body>
</html>
Result

设置 Attribute 的作用域

Application
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
//设置 属性的作用域 因为 不同的页面用的是一个浏览器 数据是"共享"的
pageContext.setAttribute("name_01","xxz_01",pageContext.SESSION_SCOPE);
// 等同于 session.setAttribute("name_03","xxz_03");
/*
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;
*/
%>
<%-- 以下仅供 理解使用 如果加上以下代码 会报错因为已经有过定义 键值对--%>
<%
pageContext.setAttribute("name_01","xxz_01");// 保存的数据只在一个页面中有效
request.setAttribute("name_02","xxz_02");// 保存的数据只在一次请求中有效,请求转发会携带这个数据
session.setAttribute("name_03","xxz_03");// 保存的数据只在一次会话中有效,从打开浏览器到关闭浏览器
application.setAttribute("name_04","xxz_04");// 保存的数据只在服务器中有效,从打开服务器到关闭服务器
%>
</body>
</html>
java里面的getAttribute和findAttribute的区别
findAttribute:
abstract Object findAttribute(String name) Searches for the named attribute in page, request, session (if valid), and application scope(s) in order and returns the value associated or null.
依次在page,request,session(如果有效的话)和application Scope(范围)查找以name为名的Attribute,找到就返回对象,都找不到返回null。
getAttribute:
abstract Object getAttribute(String name) Returns the object associated with the name in the page scope or null if not found.
在page scope内查找与name相关的属性,找到返回就返回对象,找不到就返回null。
两种的区别是,查找范围不同
pageContex 页面转发
Application
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
pageContext.forward("./index.jsp");
// ./index.jsp http://localhost:8080/jsp/index.jsp
// /index.jsp http://localhost:8080/jsp/index.jsp
// index.jsp http://localhost:8080/jsp/index.jsp
// 但是 访问 http://localhost:8080/jsp/ 也是一样的
// 等同于 request.getRequestDispatcher("./index.jsp").forward(request,response);
%>
</body>
</html>
Result

使用场景
request: 客户端向服务器发送求情,产生的数据,用户看完就没用了,比如: 新闻,用户看完就没用的
session: 客户端向服务器发送求情,产生的数据,用户看完一会还有用; 购物车
application: 客户端向服务器发送求情,产生的数据,一个用户看完了,其他用户可能还要用,比如聊天数据;
6.4、 JSP标签 、JSTL标签、EL 表达式
使用前要导入依赖才能使用
<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>
<!-- standard 标签库_1 -->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<!-- standard 标签库_2 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
EL表达式: ${}
-
EL表达式全称:Expression Language,即表达式语言
-
EL表达式作用:代替JSP页面中表达式脚本进行数据的输出(只能获取数据,不能设置数据)
-
EL表达式的格式是:${表达式} ,注:EL表达式写在jsp页面中,表达式一般是域对象的key
- 获取数据 : 使用 EL表达式输出 ${} 和<%=%> 一样
优点: 输出参数 如果不存在 不会报null - 执行运算 : 加减乘除 取模 取反 和java差不多
- 获取web开发的常用对象
调用Java方法
6.4.1、 JSP标签
页面拼接${
<%-- 查看源码 发现 是把页面 合二为一 所以 在声明参数的时候 不能重名--%>
<%@include file="common/Header.jsp"%>
<h2>网页主体</h2>
<%@include file="common/Footer.jsp"%>
<hr>
<%-- 查看源码 发现 是把页面 拼接 互不干扰 所以 可以声明 重复参数--%>
<jsp:include page="common/Header.jsp"/>
<h2>网页主体</h2>
<jsp:include page="common/Footer.jsp"/>
Application -Dmo01
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>1</h1>
<%--1. 拼接页面 <jsp : include>--%>
<%--
2. 实现页面转发
3. 携带参数
http://localhost:8080/jsp/jspPage02.jsp?name=xxz&password=xxz123456
--%>
<jsp:forward page="/jspPage02.jsp">
<jsp:param name="account" value="xxz"/>
<jsp:param name="password" value="xxz123456"/>
</jsp:forward>
</body>
</html>
Application -Dmo02
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>2</h1>
account = <%=request.getParameter("account")%>
<br>
password = <%=request.getParameter("password")%>
<hr>
</body>
</html>
Demo01 转发到 Demo02

6.4.2、JSTL标签
JSTL 标签库的使用就是为了类弥补 HTML 标签的不足, 他自定义了许多标签可以供我们使用标签的功能和java代码一样!
必须引入taglib,才能使用
<%--JSP 自动生成的 头标签--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%-- 引入 JSTL 核心标签库 , 我们才能使用 JSTL 标签 core--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
格式化标签
SQL 标签
XML 标签
核心标签 (掌握部分 其他可以放放)

JSTL 标签库的使用步骤
-
引入对应的 taglib
<%-- 引入 JSTL 核心标签库 , 我们才能使用 JSTL 标签 core--%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> -
使用其中的方法
-
在 Tmocat 中也需要 引入jstl 和 standard 的包 不然会报500
jstl 标签 if 用法
Application jstl 标签 if 用法
<%--JSP 自动生成的 头标签--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%-- 引入 JSTL 核心标签库 , 我们才能使用 JSTL 标签 core--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h2>if 测试</h2>
<hr>
<%--表单 结果提交给自己--%>
<form action="coreIf.jsp" method="get">
<%--
EL 表达式获取表单中的数据
${pram.参数名}
--%>
<input type="text" name="userName" value="${para.userName}">
<input type="submit" value="登入">
</form>
<h3>如果表单 名字 是 admin 则成功</h3>
<hr>
<h4>使用 java </h4>
<%
// 这里会抛出 nullPointerException 因为前参数为空 所以可以用 Objects.equals 或位置互换一下
if (request.getParameter("userName").equals("admin")){
out.println("Success");
}else{
out.println("加载失败");
System.out.println("加载失败");
}
%>
<hr>
<h4>使用jstl 标签</h4>
<%-- 把两个 尖括号敲齐<> 按住tab就可以生成闭合标签--%>
<c:if test="${param.userName=='admin'}" var="isAdmin" scope="request" > <%-- 'admin' 外面用了 双引号 里面可以单引号 scope 作用域--%>
<c:out value="jstl_Success"></c:out>
</c:if>
<%--要注意标签是否闭合--%>
<c:out value="${isAdmin}"></c:out>
</body>
</html>
Request

jstl 标签 choose (switch) 用法
Application
<%@ 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>
<form action="corewhen.jsp" method="get">
请输入您的成绩: <input type="text" name="score" >
<input type="submit" value="提交">
</form>
<%--定义一个变量 score 值为85--%>
<c:set var="score" value="${param.score}" scope="session"></c:set>
<%--就是 java 中的 Switch--%>
<c:choose>
// 比较大小一定要在花括号里面比较
<c:when test="${score>=90}">
你的成绩为—————优秀
</c:when>
<c:when test="${score>=80}">
你的成绩为—————一般
</c:when>
<c:when test="${score>=60}">
你的成绩为—————合格
</c:when>
<c:when test="${score>=40}">
你的成绩为—————不及格
</c:when>
<c:otherwise>
<%-- 默认值 也是 如果全都不符合的情况下的值 注意:不要有多余的空格 test="${score>=40} " 会影响计算并且不报错--%>
您没有输入成绩, 如果您已经提交成绩, 说明成绩太低不与展示
</c:otherwise>
</c:choose>
</body>
</html>
Result
jstl 标签 forEach 用法
Application
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page import="java.util.ArrayList" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
ArrayList<String> people = new ArrayList<>();
// 下标 必须从 0 开始
people.add(0,"张三");
people.add(1,"李四");
people.add(2,"王五");
people.add(3,"赵六");
people.add(4,"田七");
request.setAttribute("List",people);
%>
<%--默认值--%>
<c:forEach var="people" items="${List}" >
<h3><c:out value="${people}"></c:out></h3>
</c:forEach>
<hr>
<%--
var: 每次遍历出来的变量
item: 遍历对象
begin: 哪里开始 默认 0
end: 哪里结束 默认length
step: 步长 默认1
--%>
<c:forEach var="people" items="${List}" begin="0" end="4" step="2" >
<%-- 注意 <c:out 中间是没有空格的--%>
<h3><c:out value="${people}"></c:out></h3>
</c:forEach>
</body>
</html>
Result

问题 使用jstl 标签后报 500 错误

最终解决方法:
jstljar包在ide项目中有,但在tomcat发布的应用WEB-INF/lib下没有, 这是I具发布项目的问题,复制一个jar包(只要复制jar包)过去问题就解决了。

6.5、 JavaBean (实体类)
实体类
javaBean 有特定的写法
- 必须有一个无参构造
- 属性必须私有化
- 必须有对应的 get/set 方法
一般用来和数据库的字段做映射 ORM;
ORM : 对象关系映射
- 表--->类
- 字段--->属性
- 行记录--->对象
| id | name | age | address |
|---|---|---|---|
| 1 | xxz_1 | 8 | 北京 |
| 2 | xxz_2 | 12 | 上海 |
| 3 | xxz_3 | 18 | 杭州 |
class people{
private int id;
private String name;
private int age;
private String address;
}
第一步 在Mysql 中新建库 jdbc 并新建表 people
第二步 idea 链接数据库
第三步 创建 people对象 与数据库 字段对应
Application
package com.xiao.pojo;
// 实体类我们一般和数据库中的表结构一一对应
public class People {
private int id ;
private String name;
private int age;
private String address;
public People() {
}
public People(int id, String name, int age, String address) {
this.id = id;
this.name = name;
this.age = age;
this.address = address;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "People{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
补充 前面 jsp 标签功能 创建 java 对象 并打印到前端
Application
<%@ page import="com.xiao.pojo.People" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--
<%
People people = new People();
people.setId(12);
people.setName("xxz");
people.setAge(18);
people.setAddress("浙江丽水");
%>
--%>
<%--jsp:useBean 用来生成对象 --%>
<jsp:useBean id="people" class="com.xiao.pojo.People" scope="page">
<jsp:setProperty name="people" property="id" value="1"></jsp:setProperty>
<jsp:setProperty name="people" property="name" value="xxz"></jsp:setProperty>
<jsp:setProperty name="people" property="age" value="3"></jsp:setProperty>
<jsp:setProperty name="people" property="address" value="浙江丽水"></jsp:setProperty>
<%-- <%= people.getAddress() ...%> --%>
学号 : <jsp:getProperty name="people" property="id"/>
姓名 : <jsp:getProperty name="people" property="name"/>
年龄 : <jsp:getProperty name="people" property="age"/>
地址 : <jsp:getProperty name="people" property="address"/>
</jsp:useBean>
</body>
</html>
用 jsp标签 创建 java 对象并打印到前端
${param.score} <!--通过 param.表单属性名 得到参数-->
<!-- servlet 在属性中存储 对象和值 jsp 脚本片段也行-->
req.getSession().setAttribute("userSession",user);
req.getSession().setAttribute("test","测试");
<!-- 拿到 servlet 中存储 对象和值 的几种方法 -->
${pageContext.session.getAttribute("test")}<!-- 1.通过 相对应的 存储范围 可以使用 find get 方法 需注意的是 find 向下兼容 大范围找小范围-->
${userSession.userName} ${test}<!-- 2. 对象和值都可以用键名称被直接找到(应该是从大范围向下找) 对象可以通过键. .出属性值-->
${(pageContext.session.getAttribute("userSession")).userName} <!-- 了解即可 先找到对应范围内的对象 再.出属性值-->
6.7 遇到的一些问题
- 资源不能直接引用本地路径 会404
- a标签相对路径和绝对路径
(七) MVC 三层架构
MVC 三层架构 : model(模式) 、view(视图)、controller(控制器)
所谓MVC三层架构就是: 一种设计模式,MVC 是指 model(模式) View(视图) Controller(控制器) 。
将几种技术操作,分开进行。每种模块只负责自己那块的实现。比如:jSP 只负责显示数据,Servlet 只负责处理请求和视图跳转

Model 因为你会发现 不管是 Dao 还是service 他们其实都是一个模子 所以称为模型
- 业务处理 : 业务逻辑(Service)
- 数据持久层: CURD (Dao)
View
- 展示数据
- 提供链接发起的Servlet 请求(a、form、img...)
Controller (servlet)
- 接受用户的请求: (req :请求参数、Session 信息)
- 交给业务层处理对应的代码
- 控制视图的跳转
登入--> 接受用户的登录请求-->处理用户的请求(获取用户登入的参数,username,password)-->交给业务层处理登入业务(判断用户密码是否正确:事务)--> Dao 层查询用户名和密码是否正确
包目录结构
com.company:基本包(公司域名要倒写);
com.company.dao:持久层;
com.company.service:业务层;
com.company.pojo:实体类(JavaBean);
com.company.servlet:servlet类。
com.company.util:工具类。
(八) Filter
Filter: 过滤器,用来过滤一些乱码,使其能够正常输出

Filter 开发步骤
Layout

创建普通 Maven 项目 然后 添加 webApp 功能

第一步 导入依赖
<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>
<!-- standard JSTL 需要这个 标签库 -->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<!-- 连接MySQL数据库 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
</dependencies>
第二步 编写过滤器 和测试对象
Filter
package com.xiao.filter;
// 一定要导入这个包 不能导入错
import javax.servlet.*;
import java.io.IOException;
// 要实现 Filter Override method third 重写三个方法
public class CharacterEncodingFilter implements Filter{
// 初始化 : web服务器启动,就一起初始化,随时等待过滤器对象的出现
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("字符编码过滤器初始化_CharacterEncodingFilter_Init");
}
// chain : 链
/*
1. 过滤中的所有代码,在过滤特定的请求的时候都会执行。(特定请求: server/)
2. 必须要让过滤器继续同行
chain.filter(request,response); 固定写法 初步理解把过滤后的东西 再丢出去
*/
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// ReNameTo servletRequest request
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
System.out.println("字符编码过滤器执行前_CharacterEncodingFilter_performed_front");
chain.doFilter(request,response); // 让我们的请求继续走,如果不写,程序到这里就会被拦截停止!!!
System.out.println("字符编码过滤器执行后_CharacterEncodingFilter_performed_behind");
}
// 销毁 : web服务器关闭的时候,过滤才会销毁
public void destroy() {
System.out.println("字符编码过滤器销毁_CharacterEncodingFilter_destroy");
}
}
TestServlet
package com.xiao.servlet;
import javax.servlet.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 {
// 正常来说 我们需要添加 设置字符编码为 utf-8 设置页面内容为 text/html 如果设置多个 Servlet 就设置不过来了
// resp.setCharacterEncoding("utf-8");
// resp.setContentType("text/html");
resp.getWriter().write("如果没走过滤器这会是乱码,你就看不见我啦!!!");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
}
第三步 pom.xml中配置 Filter
Filter细节过滤器拦截路径配置
1.具体资源路径:/sys/index.jsp 只有访问index.jsp资源时,过滤器才会被执行
2.拦截目录:/user/* 访问/user下的所有资源时,过滤器都会被执行
3.后缀名拦截:*.jsp 访问所有后缀名为jsp资源时,过滤器都会被执行
4.拦截所有资源:/* 访问所有资源时,过滤器都会被执行
Pom.xml
<servlet>
<servlet-name>ShowServlet</servlet-name>
<servlet-class>com.xiao.servlet.ShowServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ShowServlet</servlet-name>
<url-pattern>/servlet/show</url-pattern>
</servlet-mapping>
<!--我们同一个 servlet 添加了两个虚拟路径 为了 对比学习 我们如果走 servlet 下 就经过过滤器 如果不走就不会被过滤-->
<servlet-mapping>
<servlet-name>ShowServlet</servlet-name>
<url-pattern>/show</url-pattern>
</servlet-mapping>
<!-- Filter 也要添加路径 -->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>com.xiao.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<!-- 只要是 servlet 下的任何请求,都会经过这个过滤器 -->
<url-pattern>/servlet/*</url-pattern>
<!-- <url-pattern>/*</url-pattern>-->
</filter-mapping>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<!-- 任何请求,都会经过这个过滤器 -->
<url-pattern>/*</url-pattern>
<!-- <url-pattern>/*</url-pattern>-->
</filter-mapping>
Result
Filter
console 会显示 字符编码过滤器执行前.. 字符编码过滤器执行后.. 因为我们在编写过滤器的时候 , 写进去了(九) Listener
监听器 有很多的作用多见于,图形(awt)监听,键盘事件,鼠标事件等等...
在网页中用的比较多的是 session 监听.
本章节 介绍 统计累计在线人数
DeployCase
9.1 Listener 对于Session的应用
第一步 编写 index.JSP
index.JSP
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<%--统计网站在线人数 : 统计 Session--%>
<%-- 拿到 Context 方法很多--%>
<h1>当前在线人数 <span style="color: cyan" ><%=this.getServletConfig().getServletContext().getAttribute("OnlineCount") %></span> 人</h1>
</body>
</html>
第二步 编写 监听器 Listener
Listener
package com.xiao.listener;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
// 学习方法 学监听器 Listener 就直接 implements List 就会有很多的监听器可以选择 学什么选什么
// 实现类 必须 重写方法
// 统计网站在线人数 : 统计 Session
// 问题: 关闭浏览器并不会销毁session 我猜测因为用的是最高级
// serverContent 所以存在服务器中所以是不会随浏览器关闭销毁的
// 要么 设置手动销毁 或定时销毁 手动销毁其实是更新 session 其实不起作用
// 那么最好降低 上下文的作用域来 使关闭浏览器销毁session
public class OnlineCountListener implements HttpSessionListener {
// 创建 Session 监听
// 一旦创建 Session 就会触发这个时间 event
@Override
public void sessionCreated(HttpSessionEvent se) {
// 获取该网页的 Session 到上下文 中 意思在提高作用域
ServletContext ctx = se.getSession().getServletContext();
// 包装类 估计不用包装类 也可以 下面拆箱 装箱 要看懂
// 获取特征 attribute 现在还没有创建 所以是 null
Integer onlineCount = (Integer) ctx.getAttribute("OnlineCount");
if (onlineCount ==null){
onlineCount =new Integer(1);
} else{
int count = onlineCount.intValue();
onlineCount = new Integer(count+1);
}
// 一开始忘记 创建了 所以 一直为null
ctx.setAttribute("OnlineCount",onlineCount);
}
// 销毁 Session 监听
@Override
public void sessionDestroyed(HttpSessionEvent se) {
// 因为销毁 session 要么 web.xml 设置有效时间 要么点击手动销毁
// 但是点击后还在运行本服务器的网站
// 所以还是会生成新的session 所以是一个 -1 +1 的过程目前没攻克
// 获取该网页的 Session 到上下文 中 意思在提高作用域
ServletContext ctx = se.getSession().getServletContext();
// 包装类 估计不用包装类 也可以 下面拆箱 装箱 要看懂
Integer onlineCount = (Integer) ctx.getAttribute("OnlineCount");
if (onlineCount ==null){
onlineCount =new Integer(0);
} else{
int count = onlineCount.intValue();
onlineCount = new Integer(count-1);
}
// 一开始忘记 创建了 所以 一直为null
ctx.setAttribute("OnlineCount",onlineCount);
}
}
第三步 配置 Web.xml
配置 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>ShowServlet</servlet-name>
<servlet-class>com.xiao.servlet.ShowServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ShowServlet</servlet-name>
<url-pattern>/servlet/show</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ShowServlet</servlet-name>
<url-pattern>/show</url-pattern>
</servlet-mapping>
<!-- Filter 也要添加路径 -->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>com.xiao.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<!-- 只要是 servlet 下的任何请求,都会经过这个过滤器 -->
<url-pattern>/servlet/*</url-pattern>
<!-- <url-pattern>/*</url-pattern>-->
</filter-mapping>
<!-- 注册监听器 Listener 没有其他配置 把我们写的监听器 配进去 就生效了-->
<listener>
<listener-class>com.xiao.listener.OnlineCountListener</listener-class>
</listener>
</web-app>
同一个浏览器的session 是唯一的,要打开不同的浏览器才能统计有效
Result
注意 因为 Tamcat 的原因 可能初始会是 3 人 .原因不知道 他们说可能是三次握手确认链接。 待考证
重新部署就会变 1 人
Result
9.2 监听器 在 GUI 中的理解
Application
package com.xiao.listener;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
public class TestPanel {
public static void main(String[] args){
// 监听器 在 GUI 中的理解
Frame frame = new Frame("Hello Frame"); // 新建一个窗体
frame.setLayout(null); // 设置窗体的布局
Panel panel = new Panel( new FlowLayout(FlowLayout.LEFT)); // 新建一个面板
frame.setBounds(300,300,500,500);// 设置窗体初始位置和大小
frame.setBackground( new Color(255, 0, 0)); // 窗体设置背景颜色
panel.setBounds(30,60,300,300);// 设置面板初始位置和大小
panel.setBackground(new Color(0,255,0));// 面板设置背景颜色
frame.add(panel);
frame.setVisible(true);
// 如果要只要其中的一个功能却要重写所有的方法 太麻烦 所以有了 适配器 Adapter
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.out.println("关闭中");
System.exit(0);
}
});
/* 监听事件 监听关闭事件
frame.addWindowListener(new WindowListener() {
@Override
public void windowOpened(WindowEvent e) {
System.out.println("打开窗口");
}
@Override
public void windowClosing(WindowEvent e) {
System.out.println("关闭中");
System.exit(0);
}
@Override
public void windowClosed(WindowEvent e) {
System.out.println("已关闭");
}
@Override
public void windowIconified(WindowEvent e) {
System.out.println("最小化");
//窗口从正常状态变为最小化状态时调用。
}
@Override
public void windowDeiconified(WindowEvent e) {
System.out.println("正常化(最小化)");
//窗口从最小化状态变为正常状态时调用。
}
@Override
public void windowActivated(WindowEvent e) {
System.out.println("已激活");
}
@Override
public void windowDeactivated(WindowEvent e) {
System.out.println("未激活");
}
});
*/
}
}
Result
用户登入之后才能进入主页! 用户注销后就不能进入主页、(正常来说,直接访问主页,也能访问主页,我们要用过滤器来防止这个 bug)
测试用的是 chrome 浏览器
总览
Layout
Login.jsp 登入页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/Servlet/Login" method="post"> <%--这个是虚拟地址 要去 web.xml 中去配置虚拟路径--%>
<p>
账号:<input type="text" placeholder="请输入账号" name="userName" required>
</p>
<p>
密码:<input type="password" name="pwd" >
</p>
<input type="submit" value="登入" >
<hr>
</form>
</body>
</html>
ServletLogin 登入面点击提交后 的servlet
package com.xiao.ServletLogin;
import com.xiao.Util.Constant;
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;
public class ServletLogin extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取前端请求的参数
String userName = req.getParameter("userName");
HttpSession session = req.getSession();// 获取 Session
System.out.println("Account: "+userName);
if (userName.equals("admin")){ // 如果是 设置 session 并转入成功主页
// 初步理解 用 session 就是要一个网页上的一个全局变量 来实现判断是否登入成功的 和本身没关系
session.setAttribute(Constant.USER_NAME, session.getId());
// resp.sendRedirect("sys/success.jsp"); // 错误网址 http://localhost:8080/Servlet/sys/LoginFailure.jsp
resp.sendRedirect("/sys/Login/success.jsp");// 网址要加 / 相对于http://localhost:8080/ 默认路径 因为Tomcat 设置默认路径 为 /。 默认路径 + /sys/success.jsp 即为正确路径
System.out.println("账号正确");
}else{ // 如果不是 进入错误
resp.sendRedirect("/sys/LoginFailure.jsp");
System.out.println("账号错误");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
}
LoginFailure.jsp 导入失败页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登入失败</title>
</head>
<body>
<h1>登入失败</h1>
<a href="/sys/Login.jsp" target="_self"> 点击返回登入 </a>
</body>
</html>
success.jsp 登入成功页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>主页</title>
</head>
<body>
<h1>主页</h1>
<a href="/Servlet/Logout" target="_self" get>Logout</a>
</body>
</html>
ServletLogout 登入主页 注销后的 servlet
package com.xiao.ServletLogin;
import com.xiao.Util.Constant;
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;
public class ServletLogout extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 因为我们把信息存在 session 只要移除session 就相当于 注销
// 第一步 拿到 session
HttpSession session = req.getSession();
// 第二步 拿到 session 获取 特征 我们要判断 是不是空
// 这里有个问题 当你走过注销后 再退回页面走注销就会 报500 所以我这里用抛出异常来解决
Object user_session = session.getAttribute(Constant.USER_NAME);
//直接 访问然后点注销 java.lang.NullPointerException 结论; null 不能toString() 会报错
//System.out.println("注销:"+user_session.toString());
if (user_session !=null){ // 如果不为空 我们就移除 特征 并回到登入面
System.out.println("user_session: is not null");
session.removeAttribute(Constant.USER_NAME);
resp.sendRedirect("/sys/Login.jsp");
} else {
resp.sendRedirect("/sys/Login.jsp");
System.out.println("user_session : is null" );
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
ServletFilter 直接访问登入面的过滤器
注意转化 和链子
package com.xiao.ServletLogin;
import com.xiao.Util.Constant;
import javax.servlet.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ServletFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {// 初始化
}
@Override// 过滤
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// ServletRequest HttpServletRequest 父与子的关系 要强转 本身get不到 session
HttpServletRequest req = (HttpServletRequest)request;
HttpServletResponse resp= (HttpServletResponse)response;
if (req.getSession().getAttribute(Constant.USER_NAME)==null){
resp.sendRedirect("/sys/LoginFailure.jsp");
System.out.println("登入失败");
} else {
System.out.println("登入成功");
}
chain.doFilter(req,resp); //别忘记! 传出去 的是我们的 HttpServletRequest 和response 内容
}
@Override
public void destroy() {// 销毁
}
}
Constant
package com.xiao.Util;
public class Constant {
// 把要操作的 常量 都放在这里 好处是 后面修改时只要修改这里 即可
// 注意 常量的命名规则 全部大写
public static final String USER_NAME = "USER_SESSION";
}
web.xml
<!-- 登入成功 失败 测试 Start-->
<!-- 登入的时候 去这里做判断 是否 符合登入条件 -->
<servlet>
<servlet-name>servletLogin</servlet-name>
<servlet-class>com.xiao.ServletLogin.ServletLogin</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servletLogin</servlet-name>
<url-pattern>/Servlet/Login</url-pattern>
</servlet-mapping>
<!--注销的时候 跑这个-->
<servlet>
<servlet-name>ServletLogout</servlet-name>
<servlet-class>com.xiao.ServletLogin.ServletLogout</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletLogout</servlet-name>
<url-pattern>/Servlet/Logout</url-pattern>
</servlet-mapping>
<!--登入走过滤器 跑这个-->
<filter>
<filter-name>LoginFilter</filter-name>
<filter-class>com.xiao.ServletLogin.ServletFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LoginFilter</filter-name>
<url-pattern>/sys/Login/*</url-pattern><!-- 过滤sys/Login 下的所有网页 选错 会一直报警-->
</filter-mapping>
<!-- 登入成功 失败 测试 End-->
Result
什么是JDBC; Java Database Connectivity ——java 数据库联结
在 各种数据库的 driver 上加了一层 通用 driver 就是 JDBC;
需要 Jar包的支持;
- java.sql
- javax.java
- mysql.connect-java 连接驱动(必须导入)
JDBC idea连接mysql数据库复习 E:\study\javawb-jdbc
1. 环境搭建
1.1新建普通 Maven 项目
新建普通 Maven 项目
Pom.xml
<dependencies>
<!-- Mysql 的驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
</dependencies>
2. Mysql处理
2.1 Mysql 数据生成
DROP TABLE users;
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT NOT NULL,
`name` VARCHAR(40),
`password` VARCHAR(40),
email VARCHAR(40),
birthday DATE
);
INSERT INTO users(`name`,`password`,email,birthday ) VALUES ('项晓忠','123456','299175482@qq.com','2021-07-28');
INSERT INTO users(`name`,`password`,email,birthday ) VALUES ('周伦','123456','299175482@qq.com','2019-05-08');
INSERT INTO users(`name`,`password`,email,birthday ) VALUES ('刘必武','123456','299175482@qq.com','2018-03-25');
INSERT INTO users(`name`,`password`,email,birthday ) VALUES ('杨国忠','123456','299175482@qq.com','2028-08-18');
SELECT *FROM users FOR UPDATE ;
Result
Result
3. 连接 Mysql
连接 Mysql
连接成功的样子
Success
jdbc:mysql://localhost:3306?serverTimezone=GMT // 要设置时区
连接 Mysql 错误
Error Server returns invalid timezone
- 步骤
- 配置 信息
- 连接数据库 这个代表数据库
- 加载驱动
- 创建向数据库发送Sql 的对象 Statement : CRUD 增删改查
- 编写 SQL
- 执行 SQL 返回一个 ResultSet : 结果集(如果是增删改的话 只要打印受影响数就可以)
- 关闭连接 关闭资源 先开后关原则
jdbc_Application
package com.xiao.test;
import java.sql.*;
import java.util.Scanner;
import java.util.function.DoubleToIntFunction;
public class TestJdbc {
public static void main(String[]args) throws ClassNotFoundException, SQLException {
// 配置 信息
// useUnicode=true&characterEncoding=utf-8 解决中文乱码
String url ="jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8&useSSL=false";
String userName = "root";
String passWord = "xxz123456";
// 1. 加载驱动
Class.forName("com.mysql.jdbc.Driver");
// 2. 连接数据库 这个代表数据库
Connection connection = DriverManager.getConnection(url, userName, passWord);
// 3. 创建向数据库发送Sql 的对象 Statement : CRUD 增删改查(read)
Statement statement = connection.createStatement();
// 4. 编写 SQL
String sql_select ="select * from users;";
String sql_update ="delete from users where id=4;";
// 受影响的行数 增删改 都是用executeUpdate
int i = statement.executeUpdate(sql_update);
System.out.println("受影响的行数:"+i);
// 到这里就结束了
System.out.println("--------------------------");
// 5. 执行 SQL 返回一个 ResultSet : 结果集 (如果是增删改的话 只要打印受影响数就可以)
ResultSet rs = statement.executeQuery(sql_select);
// boolean next() throws SQLException
// 将光标从当前位置向前移动一行。 ResultSet光标最初位于第一行之前;
// 第一次调用方法next使第一行成为当前行; 第二个调用使第二行成为当前行,依此类推。
//Object getObject(String columnLabel) throws SQLException
// 获取此的当前行中指定列的值ResultSet作为对象Object在Java编程语言。
// 可以 把 next 看做指针 指在第一行 就打印第一行 指在第二行就打印第二行 没有指针 就报错 不知道打印什么数据了
while (rs.next()){
System.out.print("id="+ rs.getObject("id")+"\t");
System.out.print("name="+ rs.getObject("name")+"\t");
System.out.print("password="+ rs.getObject("password")+"\t");
System.out.print("email="+ rs.getObject("email")+"\t");
System.out.print("birthday="+ rs.getObject("birthday")+"\n");
}
// 6. 关闭连接 关闭资源 先开后关原则
rs.close();
statement.close();
connection.close();
/* 执行结果有一串红色 警告 在mysql连接字符串的url中添加配置 &useSSL=false即可:
useSSL=false 禁用SSL
useServerPrepStmts=true 开启预编译功能*/
}
}
Result
ResultSet set = statement.executeQuery(query);
int i = statement.executeUpdate(update);
-- 这两个语句上下调换会报错 ResultSet关闭后不允许操作
-- Exception in thread "main" java.sql.SQLException: Operation not allowed after ResultSet closed
Result
执行jdbc 一串红色报警 解决方法
执行jdbc 一串红色报警 解决方法
当进行需要连接数据库的操作时,控制台会报下面这种红色警报:
Sat Jul 09 14:57:03 CST 2022 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
报这个警告的原因主要是JDBC的版本与MySQL的版本不兼容,而MySQL在高版本需要指明是否进行SSL连接。
解决方法:
在mysql连接字符串的url中添加配置 &useSSL=false即可:
String url ="jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8&useSSL=false";
useSSL=false 禁用SSL
useServerPrepStmts=true 开启预编译功能
预编译编写jdbc 代码
预编译_jdbc_Application
package com.xiao.test;
import java.sql.*;
public class TestJdbc_1 {
public static void main(String[] args) throws Exception {
// 预编译 SQL
String url ="jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8&useSSL=false";
String userName = "root";
String passWord = "xxz123456";
// 1. 加载驱动
Class.forName("com.mysql.jdbc.Driver");
// 2. 连接数据库 这个代表数据库
Connection connection = DriverManager.getConnection(url, userName, passWord);
// 3. 预编译编写 SQL
String sql_select ="select * from users where id =100;";
String sql_update ="insert into users(id, name, password, email, birthday) VALUES (?,?,?,?,?);";
// 4. 预编译
PreparedStatement ppst_select = connection.prepareStatement(sql_select);
PreparedStatement ppst_update = connection.prepareStatement(sql_update);
ppst_update.setInt(1,100);// 给第一个?占位符,传输值: 1
ppst_update.setString(2,"项宇");// 给第二?个占位符,传输值: 项宇
ppst_update.setString(3,"xyu123");// 给第三?个占位符,传输值: xyu123
ppst_update.setString(4,"1602450@qq.com");// 给第四个?占位符,传输值: 1602450@qq.com
ppst_update.setDate(5,new java.sql.Date(new java.util.Date().getTime()));// 给第五个?占位符,传输值: 当前日期
//new java.sql.Date(new java.util.Date().getTime()) 包 重名了 需要加上包名
// 5. 执行SQL 增删改最好都用executeUpdate
// 执行修改
int i = ppst_update.executeUpdate();
System.out.println(i>0?"执行成功 受影响行数为: "+i:"Failure");
System.out.println("------------------");
// 执行查询 才有结果集
ResultSet rs = ppst_select.executeQuery();
while (rs.next()){
System.out.print("id="+ rs.getObject("id")+"\t");
System.out.print("name="+ rs.getObject("name")+"\t");
System.out.print("password="+ rs.getObject("password")+"\t");
System.out.print("email="+ rs.getObject("email")+"\t");
System.out.print("birthday="+ rs.getObject("birthday")+"\n");
}
// 6. 关闭连接 关闭资源 先开后关原则
rs.close();
ppst_select.close();
ppst_update.close();
connection.close();
}
}
Result
Result
jdbc事务复习
-
事务
要么都成功 ,要么都失败
acid原则; 保证数据的安全
Atomicity(原子性):一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被恢复(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。 Consistency(一致性):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。(就是两个账号A50 B50 一共100元就是完整值,A转给B 50,A-50 但是中间服务器崩了 B并没有收到 50 所以 此时A00 B50 ,破坏了完整性即一致性) Isolation(隔离性):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。 Durability(持久性):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
-- 开启事务 transaction (false) 相反的
-- 事务提交 commiit
-- 事务回滚 rollback
-- 关闭事务 transaction (true)
-- 转账 :
A: 1000
B: 1000
-- 正常的转账流程应该是这样的
A(900) --100--> B(1100)
-- 但是如果服务器突然断电(中间报错等等情况)一般情况,
-- 执行 A-100 后 B 服务器断电 不会执行 B+100 那么 100 就凭消失
-- 事务则不然 要么都成功 要么都失败
1. 预先准备一个 账户表
MYSQL
-- 账户表
DROP TABLE account;
CREATE TABLE account(
id INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(40),
money FLOAT
);
INSERT INTO account(`name`,money) VALUES ('项晓忠',1000);
INSERT INTO account(`name`,money) VALUES ('刘必武',1000);
INSERT INTO account(`name`,money) VALUES ('杨国忠',1000);
DELETE FROM account WHERE id >3;
SELECT * FROM account
Result
Result
MYSQL 的事务
MySql
tip: SQL 字符串用单引号
-- 开启事务
start transaction ;
-- 一个 减 一个加 模拟转账 这就是一个事务
update account set money = money-100 where name ='项晓忠';
update account set money = money+100 where name ='刘必武';
-- 提交 没有提交前的数据 都是暂时的数据
commit;
-- 回滚 让数据 回到事务前
rollback;
select * from account;
java jdbc 事务
package com.xiao.test;
import org.junit.Test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class test_transaction_01 {
public static void main(String[] args) throws Exception{
// 配置 信息
// useUnicode=true&characterEncoding=utf-8 解决中文乱码
String url ="jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8&useSSL=false";
String userName = "root";
String passWord = "xxz123456";
// 1. 加载驱动
Class.forName("com.mysql.jdbc.Driver");
// 2. 连接数据库 这个代表数据库
Connection connection = DriverManager.getConnection(url, userName, passWord);
// 3. 向数据库发送Sql 的对象 Statement : CRUD 增删改查
Statement statement = connection.createStatement();
// 4. 开启事务 (是否自动提交 : false) 默认自动提交 所以一定要设置为 false
connection.setAutoCommit(false);
try {
// 4. 编写 事务 SQL
String sql_subtract ="update account set money = money-100 where name ='项晓忠';";
String sql_add ="update account set money = money+100 where name ='刘必武';";
// 5. 执行事务
int i_subtract = statement.executeUpdate(sql_subtract);
// 制造错误 在两个事务之间
//int i = 1/0;
int i_add = statement.executeUpdate(sql_add);
// 5. 提交事务
connection.commit(); // 以上两条2SQL 都执行成功 才提交事务!
System.out.println("Success");
System.out.println("add effected:"+i_subtract);
System.out.println("sub effected:"+i_add);
} catch (SQLException e) {
// 如果中间有异常 就通知数据库 执行 rollback
connection.rollback();
System.out.println("failure");
} finally {
// 6. 关闭资源 不管失败 和成功都抛出异常
statement.close();
connection.close();
}
}
}
(十一) 文件上传
功能: 每次上传文件都会新增一个文件夹,这样就不会覆盖原有文件
这次我们用的是一个 Empty Project 空项目 这样子就可以在里面建立多种不同项目(比如 webApp android ...)。选中后要选择 jdk 版本,在里面新建一个
module(注意是 module ) ,即可。
Empty Project

需要手工设置 JDK 版本

11.1 准备工作
11.1.1 下载相关 jar 包 并添加到项目类库中
新建 Java webApp 组件

对于文件上传,浏览器在上传的过程中是将文件以流的形式提交到服务器端的。
一般选择采用 apache 的开源工具 common-fileupload 这个文件上传组件
common-fileupload 是依赖于 common-io 这个包,所以需要在Maven仓库下载以下两个包
https://mvnrepository.com/artifact/commons-io/commons-io/2.6
https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload/1.4
实际上 还导入了以下相关包
javax.servlet-api-4.0.1.jar
javax.servlet.jsp-api-2.3.3.jar
jstl-api-1.2.jar
junit-4.12.jar
standard-1.1.2.jar
新建lib目录,并添加到项目类库,注意如果新增 jar 包需要重新添加项目类库
第一种方法

第二种方法

需要注意的是默认没有打包到 产品 中需要我们手动修复

[2023-10-06 08:37:08,099] Artifact fileUpload:war exploded: Error during artifact deployment. See server log for details.
11.2 使用类介绍
【文件上传的注意事项】
- 为保证服务器安全,上传文件应该放在外界无法直接访问的目录下,比如放于 WEB-INF 目录下
- 为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名
- 要限制上传文件的最大值
- 可以限制上传文件的类型,在收到上传文件名时,判断后缀名是否合法
【需要用到的类详解】
ServletFileUpload 负责处理上传的文件数据,并将表单中每个输入项装成一个 FileItem 对象,在使用 ServletFileUpload 对象解析请求时需要DiskFileItemFactory 对象。所以我们需要在进行解析工作前构造好 diskFileItemFactory 对象,通过 ServletFileUpload 对象构造方法或 setFileItemFactory()方法设置 ServletFileUpload 对象的 FileItemFactory 属性。
11.2.1 FileItem类
在Html 页面 input 必须有 name <input type="file" name="fileName" >
表单如果包含一个文件上传输入项的话,这个表单的 enctype(encoding Type 编码类型) 属性就必须设置为 multipart/form-data
<%-- 通过表单上传文件
get: 上传文件大小有限制
post: 上传文件大小没有限制
enctype(编码类型)="multipart/form-data"
--%>
<form action="${pageContext.request.contextPath}/upload.do" enctype="multipart/form-data" method="post" target="_self">
<%-- p 段落标签用起来有间隔 会好看一点,而 br 换行标签 是没有段落间距的 --%>
<p>上传用户: <input type="text" name="username"> </p>
<p>上传文件1: <input type="file" name="file1" > </p>
<p>上传文件2: <input type="file" name="file2" > </p>
<p> <input type="submit" value="提交"> | <input type="reset" value="重置"> </p>
</form>
【常用方法介绍】
//isFormField 方法用于判断 FileItem 类对象封装的数据是一个普通文本表单还是一个文件表单,如果是普通表单则返回 true,否则返回 false
boolean isFormField();
// getFieldName 方法用于返回表单标签 name 属性的值
String getFieldName();
// getString 方法用于将 FieldItem 对象中保存的数据流内容以一个字符串返回。
String getString();
// getName 方法用于获取文件上传字段中的文件名
String getName();
// 以流的形式返回上传文件的数据内容
InputStream getInputStream();
// delete 方法用来清空 FiledItem 类对象中存放的主体内容
// 如果主体内容被保存在临时文件中,delete 方法将删除该临时文件
void delete();
11.2.2 ServletFileUpload 类
ServletFileUpload 负责处理上传的文件数据,并将表单中每个输入项,封装成一个FileItem 对象中,使用其 parseRequest(HttpServletRequest) 方法可以将通过表单中每一个 Html 标签提交的数据 封装成一个FileItem 对象,然后以List 列表的形式放回,使用该方法处理上传文件简单易用。
11.3 代码编写
创建目录并生成 Servlet (记得添加 mapping) `本来没有 create New Servlet 不知道怎么出来的`

index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>FileUpload</title>
</head>
<body>
<h1>文件上传</h1>
<%-- 缺点: 没有对超过文件大小限制的,进行捕捉. 待优化--%>
<%-- 通过表单上传文件
get: 上传文件大小有限制
post: 上传文件大小没有限制
真实开发要加: ${pageContext.request.contextPath}/ 获取服务器路径
--%>
<form action="${pageContext.request.contextPath}/upload.do" enctype="multipart/form-data" method="post" target="_self">
<%-- p 段落标签用起来有间隔 会好看一点,而 br 换行标签 是没有段落间距的 --%>
<p>上传用户: <input type="text" name="username"> </p>
<p>上传文件1: <input type="file" name="file1" > </p>
<p>上传文件2: <input type="file" name="file2" > </p>
<p> <input type="submit" value="提交"> | <input type="reset" value="重置"> </p>
</form>
<hr>
<h5>文件上传结果: <span style="color: red">${msg}</span></h5>
</body>
</html>
FileServlet
package com.xiao.servlet;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadBase;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.junit.Test;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
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.math.BigDecimal;
import java.net.URLEncoder;
import java.text.DecimalFormat;
import java.util.List;
import java.util.UUID;
import static java.math.BigDecimal.ROUND_DOWN;
@WebServlet(name = "FileServlet")
// 这里 不先导包 也能导入 用 alt+Enter 导入 要注意发布后 是不是打包 包了
public class FileServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("Start");
try {
// 判断上传的文件是普通表单还是带文件的表单
if(!ServletFileUpload.isMultipartContent(request)){// 是否带文件
System.out.println("是否带文件--->否");
return;// 终止方法运行,说明这是一个普通的表单,直接返回 ||如果不是带文件的 直接退出方法
}
//创建上传文件的保存路径,建议在WEB-INF路径下,安全,用户无法直接访问上传的文件;
String uploadPath = this.getServletContext().getRealPath("/WEB-INF/upload");
System.out.println("文件上传路径: "+uploadPath);
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,他需要依赖于 common-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("index.jsp").forward(request, response);
} catch (FileUploadException e) {
e.printStackTrace();
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
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) {
BigDecimal total = BigDecimal.valueOf(pContentLength);
BigDecimal fileSize = BigDecimal.valueOf(pBytesRead);
System.out.println("总大小:" + pContentLength + "已上传:" + pBytesRead +"--->"+fileSize.divide(total, 2, ROUND_DOWN).multiply(new BigDecimal("100"))+"%");
}
});
//处理乱码问题
upload.setHeaderEncoding("UTF-8");
//设置单个文件的最大值 单个的最大值就是最大值
upload.setFileSizeMax(1024 * 1024 * 1000);
//设置总共能够上传文件的大小
//1024 = 1kb * 1024 = 1M * 10 = 10M
upload.setSizeMax(1024 * 1024 * 1000);
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(),随机生一个唯一识别的通用码;
//网络传输中的东西,都需要序列化
//POJO,实体类,如果想要在多个电脑上运行,传输===>需要把对象都序列化了
//JNI = java native interface
//implements Serializable:标记接口,JVM--->Java栈 本地方法栈 native--->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();
msg = "文件上传成功!";
fileItem.delete(); //上传成功,清除临时文件
//=======================文件传输完毕===============================//
}
}
return msg;
}
}
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-mapping>
<servlet-name>FileServlet</servlet-name>
<url-pattern>/upload.do</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>FileServlet</servlet-name>
<servlet-class>com.xiao.servlet.FileServlet</servlet-class>
</servlet>
</web-app>
11.4 Result
Result
因为我们创建的 Empty Project 是空项目没有 out 目录 所以我们要找到项目发布目录
文件上传文件夹
【前言】
- 要在网络上实现邮件功能,必须要有专门的邮件服务器
- 这些邮件服务器类似于现实生活中的邮局,它主要负责接收用户投递过来的邮件,并把邮件投递到邮件接收者的电子邮箱中。
- SMTP服务器地址: 一般是 smtp.xxx.com ,比如163邮箱是smtp.163.com,qq邮箱是 smtp.qq.com
- 电子邮箱(E-Mail地址)的获得需要再邮件服务器上进行申请.比如我们要使用QQ邮箱,就需要开通邮箱功能
SMTP有关的详细内容可以看看 《TCP/IP详解 卷一》 第28章会比较清晰,这里有一个地址,可以看看 第28章 SMTP:简单邮件传送协议_《TCP/IP详解 卷1:协议》_即时通讯网(52im.net) _即时通讯开发者社区!
【传输协议】
1、邮件服务器
①SMTP邮件服务器:替用户发送邮件和接收外面发送给本地用户的邮件
②POP3/IMAP邮件服务器:帮助用户读取SMTP邮件服务器接收进来的邮件
2、邮件传输协议
①电子邮件需要在邮件客户端和邮件服务器之间,以及两个邮件服务器之间进行邮件传递,那就必须要遵守一定的规则,这个规则就是邮件传输协议
②SMTP协议(发送邮件):全称为 Simple Mail Transfer Protocol,简单邮件传输协议。它定义了邮件客户端软件和SMTP邮件服务器之间,以及两台SMTP邮件服务器之间的通信规则. 我们通常把处理用户 smtp 请求(邮件发送请求)的服务器称之为 SMTP 服务器(邮件发送服务器)
③POP3协议(接收邮件):全称为 Post Office Protocol,邮局协议。它定义了邮件客户端软件和POP3邮件服务器的通信规则 。我们通常把处理用户 POP3 请求(邮件接收请求)的服务器称之为POP3服务器(邮件接收服务器)
④IMAP协议:全称为 Internet Message Access Protocol,Internet消息访问协议,它是对POP3协议的一种扩展,也是定义了邮件客户端软件和IMAP邮件服务器的通信规则
所有的邮件服务器和邮件客户端软件程序都是基于上面的协议编写的
【传输流程】
传输流程
- A 用户通过SMTP协议连接到Smtp服务器,然后发送一封邮件给网易的邮件服务器
- 网易分析发现需要去QQ的邮件服务器,通过smtp协议将邮件转投给QQ的 Smtp 服务器
- QQ将接收到的邮件存储在 B用户的邮件账号的空间空间中
- 用户 B 通过Pop3协议连接到Pop3服务器收取邮件
- 从 B 邮件账号的空间中取出邮件
- Pop服务器将取出来的邮件送到 B 用户手中
【注意】有可能你收件人地址,发件人地址等等信息都正确的,控制台也打印了正确的信息,但是收件箱就是收不到信息。这是因为可能收件箱服务器拒收了你发的邮件(比如认为你的邮件是广告或者是其他友商的邮箱),那可能会保存在垃圾箱里。我看了一下,我们用java 发的邮件,在浏览器上是没有发送的记录的
12.1 概述
用代码完成邮件的发送,在实际项目中应用的非常广泛,比如注册需要发送邮件进行账号激活,再比如OA项目中利用邮件进行任务提醒等等.
使用 Java (注意是java不是webapp) 发送 E-mail 十分简单,但是首先你应该准备 JavaMail API 和 JavaActivation Framework。
得到两个 Jar包 千万别导错包 !!!
JavaMail是sun公司(被甲骨文收购) 为方便Java开发人员在应用程序中实现邮件发送和接收功能而提供的一套标准开发包,他支持一些常用的邮件协议,例如:smtp、pop3、imap,还有Mime等.我们在使用 JavaMail Api 编写邮件时,无须考虑邮件的底层实现细节,只要调用JavaMail开发包中相应的 API类就行了。
我们可以先尝试发送一封简单的邮件(纯文本邮件),前提确保电脑可以连接网络。
- 创建包含邮件服务器的网络连接信息的Session对象
- 创建代表邮件内容的Message对象
- 创建Transport对象,连接服务器,发送Message,关闭连接
主要有四个核心类,我们在编写程序时,记住这四个核心类,就很容易编写出 Java 邮件处理程序
四个核心类
12.2.1 准备工作
在空项目下构建新的项目组件结构如下
-
导入jar包并添加到项目类库
1. 导入jar包到lib目录
2. lib目录添加到项目类库
3. 选择项目类库作用域 (选择后点击 apply)
4. 注意 Problem 有没有提示 自动将项目类库打包到项目 如果有提示 要手动 添加一下
开启邮箱服务并获取当前授权码
授权码: hmjhuptwusqrdhef
package com.xiao;
import com.sun.mail.util.MailSSLSocketFactory;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.Properties;
public class mailStudy {
public static void main(String[] args) throws Exception {
/*
* 简单邮件: 没有附件和图片,纯文本邮件
* 复杂邮件: 有附件和图片
* 要发送邮件,需要获得协议和支持 ! 开启 Pop3/smtp 服务 例如: 我们需要开启QQ邮箱的 Pop3/smtp 服务 才能获得支持
* 授权码: hmjhuptwusqrdhef 重新获取后 该授权码失效
*/
Properties prop = new Properties(); // 可以自己设置值,也可以写成配置文件
prop.setProperty("mail.host", "smtp.qq.com"); //设置QQ邮件服务器
prop.setProperty("mail.transport.protocol", "smtp"); // 邮件发送协议
prop.setProperty("mail.smtp.auth", "true"); // 需要验证用户名密码
String privileges ="hmjhuptwusqrdhef"; // 授权码 qq 才有
// ======== QQ邮箱,设置SSL加密,其他邮箱不需要 Start ========
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
// prop.put("mail.smtp.ssl.protocols", "TLSv1.2");
prop.put("mail.smtp.ssl.enable", "true");
prop.put("mail.smtp.ssl.socketFactory", sf);
// ======== QQ邮箱,设置SSL加密,其他邮箱不需要 End ========
//使用JavaMail发送邮件的5个步骤
//1、创建定义整个应用程序所需的环境信息的 Session 对象
//使用QQ邮箱的时候才需要,其他邮箱不需要这一段代码
// ============= QQ邮箱 开启 Session 监听 Start ========
Session session = Session.getDefaultInstance(prop, new Authenticator() {//获取和SMTP服务器的连接对象
public PasswordAuthentication getPasswordAuthentication() {
//发件人邮件用户名、授权码
return new PasswordAuthentication("2991755428@qq.com", privileges);
}
});
// ============= QQ邮箱 开启 Session 监听 End ========
//开启Session的debug模式,这样就可以查看到程序发送Email的运行状态 可以不看
session.setDebug(true);
//2、通过Session 得到 transport 对象
Transport ts = session.getTransport();//通过这一次和SMTP服务器的连接对象获取发送邮件的传输对象
//3、使用邮箱的用户名和授权码连上邮件服务器
ts.connect("smtp.qq.com", "2991755428@qq.com", privileges);
//4、创建邮件 : 写邮件
//创建一个邮件对象
MimeMessage message = new MimeMessage(session);
//指明邮件的发件人——在网页上填写发件人
message.setFrom(new InternetAddress("2991755428@qq.com"));//设置发件人
//指明邮件的收件人,现在发件人和收件人是一样的,那就是自己给自己发——在网页上填写收件人
message.setRecipient(Message.RecipientType.TO, new InternetAddress("1608342875@qq.com"));//设置收件人
//邮件的标题——在网页上填写邮件标题
message.setSubject("简单纯文本邮件发送实现");//设置邮件主题
//邮件的文本内容——在网页上填写邮件内容
message.setContent("<h2 style='color:red'>Hello World!</h2>", "text/html;charset=UTF-8");//设置邮件的具体内容
//5、发送邮件
ts.sendMessage(message, message.getAllRecipients());
//6、关闭连接对象,即关闭服务器上的连接资源
ts.close();
}
}
Result
12.3 带附件的邮件发送
12.3.1 前言
先介绍一下一个名词和两个类
MIME (多用途互联网邮件拓展类型)
Multipurpose Internet Mail Extensions-->多用途互联网邮件扩展类型-->附件
MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型。是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开。多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。
它是一个互联网标准,扩展了电子邮件标准,使其能够支持(通俗的说使其能支持附件发送):
非ASCII字符文本;非文本格式附件(二进制、声音、图像等);由多部分(multiple parts)组成的消息体;包含非ASCII字符的头信息(Header information)。
MimeBodyPart类:
javax.mail.internet.MimeBodyPart 类,表示一个MIME消息,他和MimeMessage类一样都是从Part接口继承过来。
MimeMultipart类:
javax.mail.internet.MimeMultipart是抽象类,Multipart的实现子类,它用来组合多个MIME消息。一个MimeMultipart对象可以包含多个代表MIME消息的MimeBodyPart对象。
MimeMultipart 对象使用的时候需要设置setSubType()的属性值,一共就下面3种取值(如下图):

mixed: 混合的,最高级,可以放内嵌资源和附件
related: 内嵌资源 (因为邮件本身是 html 所以可以带图片、视频 、mp3 ....)
alternative: 纯文本/超文本(html)
比如说你是要发送 纯文本的 就设置setSubType()为 alternative 如果是要内嵌图片什么的 就用related 要添加附件就设置为 mixed
向下兼容 mixed 最大
我们在使用的时候如果不知道使用什么级别的信件,那么可以直接使用 mixed 即可,因为他是向下兼容的
在带附件邮件中,整封邮件是 MimeMultipart ,他是由多个 MimeBodyPart 组成的。
12.3.2 发送内嵌资源的邮件
mail 包 和activation 包 还是要的
package com.xiao;
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;
public class MyMail {
public static void main(String[] args) throws Exception {
/*
* 简单邮件: 没有附件和图片,纯文本邮件
* 复杂邮件: 有附件和图片
* 要发送邮件,需要获得协议和支持 ! 开启 Pop3/smtp 服务 例如: 我们需要开启QQ邮箱的 Pop3/smtp 服务 才能获得支持
* 授权码: hmjhuptwusqrdhef 重新获取后 该授权码失效
*/
Properties prop = new Properties(); // 可以自己设置值,也可以写成配置文件
prop.setProperty("mail.host", "smtp.qq.com"); //设置QQ邮件服务器
prop.setProperty("mail.transport.protocol", "smtp"); // 邮件发送协议
prop.setProperty("mail.smtp.auth", "true"); // 需要验证用户名密码
String privileges ="hmjhuptwusqrdhef"; // 授权码 qq 才有
// ======== QQ邮箱,设置SSL加密,其他邮箱不需要 Start ========
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
// prop.put("mail.smtp.ssl.protocols", "TLSv1.2");
prop.put("mail.smtp.ssl.enable", "true");
prop.put("mail.smtp.ssl.socketFactory", sf);
// ======== QQ邮箱,设置SSL加密,其他邮箱不需要 End ========
//使用JavaMail发送邮件的5个步骤
//1、创建定义整个应用程序所需的环境信息的 Session 对象
//使用QQ邮箱的时候才需要,其他邮箱不需要这一段代码
// ============= QQ邮箱 开启 Session 监听 Start ========
Session session = Session.getDefaultInstance(prop, new Authenticator() {//获取和SMTP服务器的连接对象
public PasswordAuthentication getPasswordAuthentication() {
//发件人邮件用户名、授权码
return new PasswordAuthentication("2991755428@qq.com", privileges);
}
});
// ============= QQ邮箱 开启 Session 监听 End ========
//开启Session的debug模式,这样就可以查看到程序发送Email的运行状态 可以不看
session.setDebug(true);
//2、通过Session 得到 transport 对象
Transport ts = session.getTransport();//通过这一次和SMTP服务器的连接对象获取发送邮件的传输对象
//3、使用邮箱的用户名和授权码连上邮件服务器
ts.connect("smtp.qq.com", "2991755428@qq.com", privileges);
//4、创建邮件 : 写邮件
//创建一个邮件对象
MimeMessage message = new MimeMessage(session);
//指明邮件的发件人——在网页上填写发件人
message.setFrom(new InternetAddress("2991755428@qq.com"));//设置发件人
//指明邮件的收件人,现在发件人和收件人是一样的,那就是自己给自己发——在网页上填写收件人
message.setRecipient(Message.RecipientType.TO, new InternetAddress("1608342875@qq.com"));//设置收件人
//邮件的标题——在网页上填写邮件标题
message.setSubject("内嵌资源的邮件发送实现");//设置邮件主题
System.out.println("当前项目目录:"+System.getProperty("user.dir"));
//================ 发送包含图片的复杂邮件 Start ================
/*邮件的文本内容——在网页上填写邮件内容---> 纯文本的 邮件内容 要发送内嵌资源的邮件 就是替换掉 这段代码
message.setContent("<h2 style='color:red'>Hello World!</h2>", "text/html;charset=UTF-8"); //设置邮件的具体内容*/
//准备图片数据。
MimeBodyPart image = new MimeBodyPart();
DataHandler dh = new DataHandler(new FileDataSource(System.getProperty("user.dir")+"/SendMail/src/resources/baidu.png"));
image.setDataHandler(dh);//在我们的Body中放入这个处理的图片数据。
image.setContentID("baidu.png");//给图片设置一个id,提供后续调用 --> cid
// 准备正文数据。
MimeBodyPart text = new MimeBodyPart();
//这里的cid就是上面的哪个setContentID。
text.setContent("这是一封邮件正文带图片<br><img src='cid:baidu.png'><br>的邮件", "text/html;charset=UTF-8"); // 两个细节: 1. 内嵌可能只能放图片 2. 双引号里面的双引号请用单引号
// 描述数据关系。 相当于 把资源都放在一起,拼接成一封完整的信
MimeMultipart mm = new MimeMultipart();
mm.addBodyPart(text);
mm.addBodyPart(image);
mm.setSubType("related");//设置MimeMultipart对象的相对熟悉为related,即发送的数据为文本+内嵌资源。
//设置到消息中,保存修改。
message.setContent(mm);//把最后编辑好的邮件放到消息当中。
message.saveChanges();//保存修改。
//================ 发送包含图片的复杂邮件 Start ================
//5、发送邮件
ts.sendMessage(message, message.getAllRecipients());
//6、关闭连接对象,即关闭服务器上的连接资源
ts.close();
}
}
内嵌资源Result
12.3.3 发送内嵌资源+附件的邮件
mail 包 和activation 包 还是要的
package com.xiao;
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;
public class MyMailMixed {
public static void main(String[] args) throws Exception {
/*
* 简单邮件: 没有附件和图片,纯文本邮件
* 复杂邮件: 有附件和图片
* 要发送邮件,需要获得协议和支持 ! 开启 Pop3/smtp 服务 例如: 我们需要开启QQ邮箱的 Pop3/smtp 服务 才能获得支持
* 授权码: hmjhuptwusqrdhef 重新获取后 该授权码失效
*/
Properties prop = new Properties(); // 可以自己设置值,也可以写成配置文件
prop.setProperty("mail.host", "smtp.qq.com"); //设置QQ邮件服务器
prop.setProperty("mail.transport.protocol", "smtp"); // 邮件发送协议
prop.setProperty("mail.smtp.auth", "true"); // 需要验证用户名密码
String privileges ="hmjhuptwusqrdhef"; // 授权码 qq 才有
// ======== QQ邮箱,设置SSL加密,其他邮箱不需要 Start ========
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
// prop.put("mail.smtp.ssl.protocols", "TLSv1.2");
prop.put("mail.smtp.ssl.enable", "true");
prop.put("mail.smtp.ssl.socketFactory", sf);
// ======== QQ邮箱,设置SSL加密,其他邮箱不需要 End ========
//使用JavaMail发送邮件的5个步骤
//1、创建定义整个应用程序所需的环境信息的 Session 对象
//使用QQ邮箱的时候才需要,其他邮箱不需要这一段代码
// ============= QQ邮箱 开启 Session 监听 Start ========
Session session = Session.getDefaultInstance(prop, new Authenticator() {//获取和SMTP服务器的连接对象
public PasswordAuthentication getPasswordAuthentication() {
//发件人邮件用户名、授权码
return new PasswordAuthentication("2991755428@qq.com", privileges);
}
});
// ============= QQ邮箱 开启 Session 监听 End ========
//开启Session的debug模式,这样就可以查看到程序发送Email的运行状态 可以不看
session.setDebug(true);
//2、通过Session 得到 transport 对象
Transport ts = session.getTransport();//通过这一次和SMTP服务器的连接对象获取发送邮件的传输对象
//3、使用邮箱的用户名和授权码连上邮件服务器
ts.connect("smtp.qq.com", "2991755428@qq.com", privileges);
//4、创建邮件 : 写邮件
MimeMessage message = mixedMail(session); //创建一个邮件对象 封装的方法
//5、发送邮件
ts.sendMessage(message, message.getAllRecipients());
//6、关闭连接对象,即关闭服务器上的连接资源
ts.close();
}
public static MimeMessage mixedMail(Session session) throws Exception {
//创建一个邮件对象
MimeMessage mimeMessage = new MimeMessage(session);
//指明邮件的发件人——在网页上填写发件人
mimeMessage.setFrom(new InternetAddress("2991755428@qq.com"));//设置发件人
//指明邮件的收件人,现在发件人和收件人是一样的,那就是自己给自己发——在网页上填写收件人 他说能同时发送多个人 我还不知道方法
mimeMessage.setRecipient(Message.RecipientType.TO, new InternetAddress("1608342875@qq.com"));//设置收件人
//邮件的标题——在网页上填写邮件标题
mimeMessage.setSubject("mixed 有附件的邮件发送实现");//设置邮件主题
//邮件的文本内容——在网页上填写邮件内容---> 纯文本的 邮件内容 要发送内嵌资源的邮件 就是替换掉 这段代码
mimeMessage.setContent("<h2 style='color:red'>Hello World!</h2>", "text/html;charset=UTF-8"); //设置邮件的具体内容
/*
编写邮件内容
1.图片
2.附件
3.文本
*/
String resourcesUrl = System.getProperty("user.dir")+"/SendMail/src/resources";
//图片。
MimeBodyPart body1 = new MimeBodyPart();
DataHandler dh = new DataHandler(new FileDataSource(resourcesUrl+"/baidu.png"));
body1 .setDataHandler(dh);//在我们的Body中放入这个处理的图片数据。
body1 .setContentID("baidu.png");//给图片设置一个id,提供后续调用 --> cid 不能用中文 注意 要不然文件会消失
// 准备正文数据。
MimeBodyPart body2 = new MimeBodyPart();
//这里的cid就是上面的哪个setContentID。
body2.setContent("这是一封带附件的邮件正文也带图片<br><img src='cid:baidu.png'><br>的邮件", "text/html;charset=UTF-8"); // 两个细节: 1. 内嵌可能只能放图片 2. 双引号里面的双引号请用单引号
//附件 qq 邮箱附件大小有限制 不能大于 50MB 会报错~
MimeBodyPart body3 = new MimeBodyPart();
body3.setDataHandler(new DataHandler(new FileDataSource(resourcesUrl+"/中药名.txt")));
body3.setFileName("zhongyaoming.txt"); //附件设置名字 附件和图片 不一样的是 setContentID 一个是 cid 一个是设置文件名称 不能用中文 注意 要不然文件会消失
MimeBodyPart body4 = new MimeBodyPart();
body4.setDataHandler(new DataHandler(new FileDataSource(resourcesUrl+"/form.html")));
body4.setFileName("123.html"); //附件设置名字 不能用中文 注意 要不然文件会消失
//拼装邮件正文内容
MimeMultipart multipart1 = new MimeMultipart();
multipart1.addBodyPart(body1);
multipart1.addBodyPart(body2);
multipart1.setSubType("related"); //1.文本和图片内嵌成功!
//new MimeBodyPart().setContent(multipart1); //将拼装好的正文内容 multipart 设置为 MimeBodyPart 这样就又可以拼接了 想象成 拼积木,先拼 文字和图片 拼起来后 看做整体 再拼接 附件
MimeBodyPart contentText = new MimeBodyPart();
contentText.setContent(multipart1);
//拼接附件
MimeMultipart allFile =new MimeMultipart();
allFile.addBodyPart(body3); //附件
allFile.addBodyPart(body4); //附件
allFile.addBodyPart(contentText);//正文
allFile.setSubType("mixed"); //正文和附件都存在邮件中,所以类型设置为mixed;
//设置到消息中,保存修改
mimeMessage.setContent(allFile);//将MimeMultipart放入消息对象中。
mimeMessage.saveChanges();//保存上面的修改。
return mimeMessage;
}
}
发送内嵌资源+附件的邮件 Result
12.4.1 前言
现在很多的网站都提供有用户注册功能,通常我们注册成功之后就会收到一封来自网站的邮件.邮件里面的内容可能包含了我们注册的用户名和密码以及一个激活账号的超链接信息,或验证码等等...。
我们写一个 网页实现这样的功能: 等用户注册成功之后,就将用户的注册信息以 Email 的形式发送到用户的注册邮箱当中,实现发送邮件功能就得借助于 JavaMail 了。
12.4.2 实现过程
-
新建 普通Maven项目 添加web项目支持
-
配置 Tomcat
启动测试(开发前一定要启动测试一下,养成好习惯)
-
建造项目结构
- 建立实体类
- 邮件发送封装成工具类
- 导入相关依赖
- 编写注册首页并添加到欢迎页
- 编写servlet并注册mapping
-
Result
package com.xiao.pojo;
import java.io.Serializable;
// 只要是 webapp 项目不管要不要使用 都实现一下 实体类
public class User implements Serializable {
String userName;
String passWord;
String email;
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;
}
@Override
public String toString() {
return "User{" +
"userName='" + userName + '\'' +
", passWord='" + passWord + '\'' +
", email='" + email + '\'' +
'}';
}
}
package com.xiao.utils;
import com.sun.mail.util.MailSSLSocketFactory;
import com.xiao.pojo.User;
import javax.mail.Authenticator;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.Properties;
// 网站3秒原则:3秒必须有动作--> 用户体验
// 多线程实现 (异步处理) 一边跳转网页,一边发送邮件
public class SendMail extends Thread {
// 用于给用户发送邮件的邮箱
private String from = "2991755428@qq.com";
// 邮件的用户名
private String username = "2991755428@qq.com";
// 邮箱密码/授权码
private String password = "hmjhuptwusqrdhef";
// 发送邮箱的服务器地址
private String host = "smtp.qq.com";
private User user;
public SendMail(User user) {
this.user=user;
}
// 重写 run 方法的实现,在run方法中发送邮件给指定的用户
@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");// 需要验证用户密码
//prop.put("mail.smtp.ssl.protocols", "TLSv1.2"); QQ 也许需要这个 实际没用到
// ======== QQ邮箱,设置SSL加密,其他邮箱不需要 Start ========
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
// prop.put("mail.smtp.ssl.protocols", "TLSv1.2");
prop.put("mail.smtp.ssl.enable", "true");
prop.put("mail.smtp.ssl.socketFactory", sf);
// ======== QQ邮箱,设置SSL加密,其他邮箱不需要 End ========
// 使用 JavaMail 发送邮件的5个步骤
// 1. 创建定义整个应用程序所需要的环境信息的 session 对象 此Session非彼Session 是mail包下的
// ============= QQ邮箱 开启 Session 监听 Start ========
Session session = Session.getDefaultInstance(prop, new Authenticator() {//获取和SMTP服务器的连接对象
public javax.mail.PasswordAuthentication getPasswordAuthentication() {
//发件人邮件用户名、授权码
return new PasswordAuthentication("2991755428@qq.com", password);
}
});
// ============= QQ邮箱 开启 Session 监听 End ========
//开启Session的debug模式,这样就可以查看到程序发送Email的运行状态 可以不看
session.setDebug(true);
// 开启session 的debug 模式,这样可以查看程序发送 EMail 的运行过程和状态
// 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(MimeMessage.RecipientType.TO,new InternetAddress(user.getEmail()));
// 邮件标题
message.setSubject("Welcome Register :)");
// 邮件文本内容
String info ="恭喜 :)<hr> 账号: "+user.getUserName()+"<br>密码:"+user.getPassWord()+"<br>注册 Success !";
message.setContent(info,"text/html;charset=utf-8");
message.saveChanges();// 保存更改
// 5. 发送邮件
ts.sendMessage(message, message.getAllRecipients());
// 6. 关闭连接
ts.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
<?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.xiao</groupId>
<artifactId>WebMail</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- servlet 依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.0</version>
<scope>provided</scope>
</dependency>
<!-- jsp 依赖 支持虚拟路径 ${pageContext.request.contextPath} 等价 项目路径/s -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
</dependency>
<!-- JavaWebEmail 依赖 Start -->
<!-- https://mvnrepository.com/artifact/javax.mail/mail -->
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.activation/activation -->
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
<!-- JavaWebEmail 依赖 End -->
</dependencies>
</project>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>Welcome Register</h1>
<hr>
<form action="${pageContext.request.contextPath}/RegisterServlet.do" method="post">
<table>
<tr>
<td>
用户名:
</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>
密码:
</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td>
邮箱:
</td>
<td><input type="text" name="email" placeholder="仅支持QQ邮箱"></td>
</tr>
<tr>
<td><hr></td>
</tr>
<tr>
<td><input type="submit" value="注册"></td>
</tr>
</table>
</form>
</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">
<!--===================设置欢迎页 Start ===================-->
<welcome-file-list>
<welcome-file>Register.jsp</welcome-file>
</welcome-file-list>
<!--===================设置欢迎页 End ===================-->
<!-- 写完Servlet 第一时间加到 web.xml 中-->
<servlet>
<servlet-name>RegisterServlet</servlet-name>
<servlet-class>com.xiao.servlet.RegisterServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>RegisterServlet</servlet-name>
<url-pattern>/RegisterServlet.do</url-pattern> <!--注意一定要有 斜杠 ! 斜杠 ! 斜杠 ! 要不然 Tomacat 会报错的!-->
</servlet-mapping>
</web-app>
package com.xiao.servlet;
import com.xiao.pojo.User;
import com.xiao.utils.SendMail;
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 RegisterServlet extends HttpServlet {
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
// 接受前端内容
String username = req.getParameter("username");
String password = req.getParameter("password");
String email = req.getParameter("email");
// 封装成对象
User user = new User(username, password, email);
System.out.println(user.toString());;
SendMail sendMail = new SendMail(user);
/*
用户注册成功后,给用户发送一份邮件
我们使用多线程来专门发送邮件,防止出现耗时,白屏和网站注册人数过多的情况
*/
sendMail.start();// 调用 start() 方法后,线程会在独立的执行路径上运行,与主线程并行执行(多线程异步处理)。 run() 是空方法
req.setAttribute("message", "Success,我们已经发了一封注册信息的电子邮件,请查收!如网络不稳定,等稍等");
req.getRequestDispatcher("info.jsp").forward(req, resp);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
Result
12.5 可能出现的问题
网友收集的一些问题解决方案
配置
JSP
<%--去除中文乱码--%>
<%@page contentType="text/html; charset=utf-8" pageEncoding="utf-8" language="java" %>
<%-- 引入 JSTL 核心标签库 , 我们才能使用 JSTL 标签 core--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<body>
<h2>Welcome Login</h2>
<form action="${pageContext.request.contextPath}/RedirectServlet" method="get">
用户名<input type="text" name="account" value="xxz"> <br>
密码 <input type="password" name="pwd" value="123456"><br>
<input type="text" value="Login">
</form>
<%--关于正则表达式--%>
<%-- 如果输入 非数字 会清空 内容 --%>
成绩: <input type="number"
onkeyup="this.value=this.value.replace(/\D/g,'')" >
<hr>
</body>
</html>
web.xml webApps
<?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="true">
<!-- 设置 欢迎页 Start -->
<welcome-file-list>
<welcome-file>Register.jsp</welcome-file>
</welcome-file-list>
<!-- 设置 欢迎页 End -->
<display-name>Welcome to Tomcat</display-name>
<description>
Welcome to Tomcat
</description>
<!-- 虚拟路径 写这里 -->
<!-- 错误页面 写这里 -->
<error-page>
<error-code>404</error-code>
<location>/error/404.jsp</location>
</error-page>
<!-- 修改了 web.xml 就必须重启Tomcat-->
<error-page>
<error-code>500</error-code>
<location>/error/500.jsp</location>
</error-page>
</web-app>
pom.xml Maven
<dependencies>
<!-- 注意 依赖 的版本也会导致程序出错 或者某些功能不能用 要及时更新版本 -->
<!-- servlet 依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.0</version>
<scope>provided</scope>
</dependency>
<!-- jsp 依赖 支持虚拟路径 ${pageContext.request.contextPath} 等价 项目路径/s -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
</dependency>
<!-- Mysql 的驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- JSTL 表达式的依赖 -->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>
<!-- standard JSTL 需要这个 标签库 -->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<!-- junit 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>compile</scope>
<!-- scope 作用域 不懂百度 -->
</dependency>
<!-- junit 单元测试 如果失效 加上以下试试-->
<!--<dependency>-->
<!-- <groupId>org.hamcrest</groupId>-->
<!-- <artifactId>hamcrest-core</artifactId>-->
<!-- <version>1.3</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.hamcrest</groupId>-->
<!-- <artifactId>hamcrest-library</artifactId>-->
<!-- <version>1.3</version>-->
<!-- </dependency>-->
<!-- 阿里巴巴的fastJson-->
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.23</version>
</dependency>
<!-- JavaWebEmail 依赖 Start -->
<!-- https://mvnrepository.com/artifact/javax.mail/mail -->
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.activation/activation -->
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
<!-- JavaWebEmail 依赖 End -->
<!-- 该注解实现 自动 get set 有参无参构造 Start -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
<!-- 注意 我还不会使用该注解 (主要功能是给类添加 get set方法 和 构造方法目前了解到需要用插件什么的 先放这里)
@Data get set 方法
@AllArgsConstructor 有参构造 (全部参数)
@NoArgsConstructor 无参构造-->
<!-- 该注解实现 自动 get set 有参无参构造 End -->
</dependencies>
JSP路径
// 读取 resources 下pp.properties 文件 用自带 资源输入流
InputStream is = servletContext.getResourceAsStream("/WEB-INF/classes/pp.properties");
// 读取 文件资源 用绝对路径
String Url = servletContext.getRealPath("/WEB-INF/classes/MyVideo/WeChat.mp4");
//E:\study\practice\out\artifacts\practice_war_exploded\WEB-INF\classes\MyUtil\WeChat.mp4
junit 测试单元
junit 依赖 pom.xml
<!-- junit 单元测试 注意版本 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
package com.xiao.test;
import org.junit.Test;
public class test_transaction_01 {
// 平时 如果我们要测试方法 只能通过 main 方法实现
public static void main(String[] args) {
new test_transaction_01().test();
}
// junit 单元测试
// 但是导入 junit 依赖 让我们不用 main 方法也能 运行
/* 使用方法
1. 导入 junit 依赖
2. 添加注解@Test (注意: 路径 org.junit.Test)
3. 注意 要声明为 public (亲测有效)
*/
@Test
public void test(){
System.out.println("hello,xxz");
}
}
Result
Result
问题
-
查看报警内容 什么内容查什么 如果部署问题 查配置 web.xml pom.xml ....
-
405 – Method Not Allowe
// 是因为我在重写 doPost 方法中引用了 super的doGet方法,super.doGet 改成 this.doGet 即可,如果你在子类中引用了父类的 doGet 方法,那么会进入父类的方法中最后判断为 405 或 400
// package javax.servlet.http;这是父类的方法
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_get_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(405, msg);
} else {
resp.sendError(400, msg);
}
}
// 我打印了一下 方法 和 协议 确实是1.1结束的 所以会返回 405
String method = req.getParameter("method");
String protocol = req.getProtocol();
System.out.println("method--->"+method);
System.out.println("protocol--->"+protocol);
method--->query
protocol--->HTTP/1.1
- js 乱码问题
用notepad++ 转换js文件 为成utf-8-bom编码 清除浏览器缓存 redeploy一下 不用重启服务器
-
要注意观察 Browser 的 console 提示
-
当你改变整个WebApp 文件夹名字时,需要Tomacat 中重新配置 Deployment Name










浙公网安备 33010602011771号