JavaWeb基础入门到上手项目
前言
- Java Web 其实就是一个技术的总和,把Web看成一个容器而已主要使用JavaEE技术来实现.在加上各种中间件
- 整个javaWeb阶段的内容通过实际的案例贯穿学习, 所涉及到的技术知识点会在案例中根据不同的需求引入
- 我们首先了解javaWeb的整个技术体系,掌握常用的技术知识点
注:本篇博客是基于B站遇见狂神说的JavaWeb讲解视频进行编写是前面JavaWeb所有博客的总结,如有遗漏和错误欢迎指出,原视频地址为【狂神说Java】JavaWeb入门到实战,大家记得三连啊✧(≖ ◡ ≖✿)
下面我们进入正题!
1、基本概念
1.1、前言
web开发:
- web,网页的意思
- 静态web
- html,css
- 提供给所有人看的数据始终不会变化
- 动态web
- 淘宝,几乎所有的网站
- 提供给所有人看的数据始终会变化,每个人在不同时间不同地点看到的信息各不相同
- 技术栈:Servlet/JSP,ASP,PHP
在Java中,动态web资源开发的技术统称为JavaWeb
1.2、web应用程序
web应用程序:可以提供浏览器访问的程序
-
a.html b.html......多个web资源,这些web资源可以被外界访问,对外界提供服务
-
我们能访问到的任何一个页面或者资源,都存在于这个世界上的某一个角落的计算机上
-
URL
-
这些统一的web资源会被放在同一个文件夹下,web应用程序---->Tomcat:服务器
-
一个web应用由多部分组成(静态web 动态web)
- html,css,js
- jsp,servlet
- java程序
- jar包
- 配置文件
web应用程序编写完毕后,若想提供给外界访问,需要一个服务器来统一管理
1.3、静态web
- *.html, *.html,这些都是网页的后缀,如果服务器上一直存在这些东西,我们就可以直接进行读取
- 静态web存在的缺点
- web页面无法动态更新,所有用户看到的都是同一个页面
- 轮播图,点击特效:伪动态
- JavaScript(实际开发中使用最多)
- VBScript
- 它无法和数据库交互(数据无法持久化,用户无法交互)
- web页面无法动态更新,所有用户看到的都是同一个页面
1.4、动态web
页面会动态展示:web的页面展示的效果因人而异(不同的人打开的页面是不一样的)
缺点
- 加入服务器的动态web资源出现了错误,我们需要重新编写我们的后台程序,重新发布(停机维护)
优点
- web页面可以动态更新,所有用户看到的都不是同一个页面
- 它可以和数据库交互(数据持久化:注册,用户信息)
2、web服务器
2.1、技术讲解
ASP:
- 微软:国内最早流行的就是ASP
- 在html中嵌入了VB的脚本,ASP+DOM
- 在ASP开发中,基本一个页面都有几千行的业务代码,页面极其混乱
- 维护成本高
- C#
PHP:
- 开发速度很快,功能很强大,跨平台,代码简单
- 无法承载大访问量的情况(局限性)
JSP/Servlet:
- sun公司主推的B/S架构
- 基于Java语言
- 可以承载高并发,高可用,高性能带来的影响
- 语法像ASP
2.2、web服务器
服务器是一种被动的操作,用来处理用户的一些请求和给用户一些响应信息
IIS:
- 微软
- ASP
- Windows中自带的
Tomcat
Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,最新的Servlet 和JSP 规范总是能在Tomcat 中得到体现,Tomcat 5支持最新的Servlet 2.4 和JSP 2.0 规范。因为Tomcat 技术先进、性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为比较流行的Web 应用服务器。
Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。对于一个初学者来说,他是最佳选择
Tomcat 实际上运行JSP 页面和Servlet。另外,Tomcat和IIS等Web服务器一样,具有处理HTML页面的功能。
下载Tomcat:
- 安装/解压
- 了解配置文件及目录结构
- 作用
3、Tomcat
3.1、安装Tomcat
在安装之前给大家提一个建议,我在学习新知识的过程中经常会到软件版本不匹配报错的问题,而且经常卸载安装好几次也不成功,
所以建议大家在安装之前先去查看以下版本匹配的问题(不是最新的版本就是最好的),我在这里使用是Tomcat9
具体的下载流程大家可以参考以下博客,tomcat安装及配置教程(保姆级)_爱你的阿白~的博客-CSDN博客_tomcat安装及配置教程
3.2、Tomcat启动和配置
文件夹作用:
启动,关闭Tomcat:
可能遇到的问题:
- Java环境变量没有配置
- 闪退问题:需要配置兼容性
- 乱码问题:配置文件中设置
3.3、配置
可以配置启动的端口号
- Tomcat默认端口号为8080
- Mysql:3306
- http:80
- https:443
可以配置主机的名称
- 默认的主机名为:localhost->127.0.0.1
- 默认网站应用存放的位置为:webapps
思考问题
网站是如何进行访问的:
- 输入一个域名:回车
- 检查本机的hosts配置文件下有没有这个域名映射
- 有--->直接返对应的IP地址,这个地址中有我们需要访问的web程序,可以直接访问
- 没有--->去DNS服务器寻找(找到就返回,找不到就返回找不到)
3.4、发布一个web网站
- 将自己写的网站放到服务器(Tomcat)指定的webapp的文件夹下,就可以访问了
网站应该有的结构:
--webapps : Tomcat服务器的web目录
--ROOT
--Qstudy : 网站的目录名
--index.html : 默认的首页
--static
--css
--js
--img
--...
--WEB-INF
--classes : java程序
--lib : web应用所依赖的jar包
--web.xml : 网站的配置文件
4、HTTP
4.1、什么是HTTP
超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在TCP之上。
http端口:80 https端口:443 (s代表安全)
- 文本:html,字符串,...
- 超文本:图片,音乐,视频,定位,地图....
4.2、两个时代
- http1.0
- HTTP/1.0:客户端可以与Web服务器连街沟,只获得一个web资源,断开连接
- http2.0
- HTTP/1.1:客户端可以与Web服务器连街沟,可以获得多个web资源
4.3、HTTP请求
-
客户端------发请求(request)-----服务器
Request URL: https://www.baidu.com/ //请求地址(以百度为例) Request Method: GET //GET/POST方法 Status Code: 200 OK //状态码:200代表OK Remote Address: 110.242.68.3:443 //地址 Referrer Policy: strict-origin-when-cross-origin //协议
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9//语言 Cache-Control: max-age=0 Connection: keep-alive
-
请求行
-
消息头
Accept: //告诉浏览器它所支持的数据类型 Accept-Encoding: //支持哪种编码格式 GBK UTF-8 GB2312 ISO8859-1 Accept-Language: //告诉浏览器他的语言环境 Cache-Control: //缓存控制 Connection: //告诉浏览器,请求完是断开还是保持连接 HOET: //主机
4.4、HTTP响应
- 服务器-----响应(response)-----客户端
Cache-Control: private // 缓存控制
Connection: keep-alive // 连接
Content-Encoding: gzip // 编码
Content-Type: text/html;charset=utf-8 // 类型
- 响应体
Accept: //告诉浏览器它所支持的数据类型
Accept-Encoding: //支持哪种编码格式 GBK UTF-8 GB2312 ISO8859-1
Accept-Language: //告诉浏览器他的语言环境
Cache-Control: //缓存控制
Connection: //告诉浏览器,请求完是断开还是保持连接
HOET: //主机
REFRESH: //告诉客户端多久刷新一次
Location: //让网页重新定位
-
响应头
200:请求响应成功
202:接受
3**:
- 重定向:你重新到我给你的新位置去
400:错误的请求
404:找不到资源
5**:服务器代码错误(502网关错误)
5、Maven
- 在JAvaweb开发中,需要使用大量的jar包,我们需要手动导入
- 如何能够让一个东西自动帮我们导入和配置这个jar包
5.1、Maven项目架构管理工具
我们目前用来就是方便导入jar包的
Maven的核心思想:约定大于配置
- 有约束不要去违反
Maven会规定好你该如何去编写我们的Java代码,必须要按照规范
5.2、下载安装Maven
具体下载步骤大家可以参考这篇博客maven安装及配置(详细版)
这里尤其要注意Maven版本与IDEA版本对应的问题
官网;https://maven.apache.org/
这里我保存的位置是:E:\JavaBackend\Maven\apache-maven-3.5.2
5.3、配置环境变量
在我们的系统环境变量中
配置如下配置:
- M2_HOME maven目录下的bin目录
- MAVEN_HOME maven的目录
- 在系统的path中配置%MAVEN_HOME%\bin
测试是否安装成功
5.4、阿里云镜像
- 镜像:mirrors,可以加速我们的下载
setting.xml配置文件位置
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>central</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
5.5、本地仓库
本地仓库 localRepository,远程仓库
建立一个仓库:
- 原来的地址:
C:\Users\86134\.m2\repository
- 修改后的地址:
<localRepository>E:/JavaBackend/Maven/apache-maven-3.5.2/maven_repository</localRepository>
5.6、在IDEA中使用Maven
-
启动IDEA
-
创建一个Mavenweb项目
3.等待项目初始化完毕
4.观察maven仓库中多了哪些东西
5.idea中的maven设置(idea中经常使用自带的maven,需要手动修改,退出项目在最开始的页面可以修改默认设置)
5.7、创建一个普通maven项目
5.8、标记文件夹功能
(1)
(2)
5.9、在idea中配置Tomcat
(1)
(2)
(3)警告问题:我们访问一个网站的时候,需要指定一个文件夹名字
(4)启动测试
5.10、pom文件
pom.xml是maven的核心配置文件
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--maven的版本和头文件-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--这里就是我们刚才配置的GAV-->
<groupId>org.example</groupId>
<artifactId>Javaweb-01-maven</artifactId>
<version>1.0-SNAPSHOT</version>
<!--packaging打包方式
java:jar
Javaweb:war
-->
<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>
<!--maven会帮你导入这个jar包所依赖的其他jar包 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</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>
在build中配置resources,来防止我们资源导出失败的问题
<!-- 在build中配置resources , 来防止我们资源导出失败的问题-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
5.11、Maven仓库的使用
常见的问题
-
maven版本与jdk版本不相容(jdk1.8 maven3.5)
-
Tomcat闪退
-
IDEA中每次都要重复配置Maven(修改全局)
-
Maven项目中Tomcat无法配置
-
Maven默认web项目中的web.xml版本问题
-
- 替换为webapp4.0版本和Tomcat一致
<?xml version="1.0" encoding="UTF-8"?> <web-app metadata-complete="true" version="4.0" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee"> </web-app>
-
6、Servlet
6.1、Servlet简介
- Servlet就是sun公司开发动态web的一门技术
- sun公司在这些api中提供了一个接口叫Servlet,如果想发开一个Servlet程序,则需要两步
- 编写一个类实现Servlet接口
- 把开发号的java类部署到web服务器中
通常我们把实现了Servlet接口的java程序叫做Servlet
6.2、HelloServlet
Servlet接口Sun公司有两个默认的实现类:HttpServlet,GenericServlet
1.构建一个普通的Maven项目,删掉里面的src目录,以后我们的学习就在这个项目里面建立Moudel;这个空的工程就题Maven主工程;
关于Maven父子工程的理解
父项目中会有
<modules>
<module>servlet-01</module>
</modules>
子项目通常会有(这里如果没有可以手动添加)
<parent>
<artifactId>javaweb-02-servlet</artifactId>
<groupId>com.qjd</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
注:父项目中的java子项目可以直接使用
3.Maven环境优化
3.1.修改web.xml为最新的
在这里可以把最新的web.xml保存下来,下次直接拿来稍微修改就可以使用
web.xml
<web-app metadata-complete="true" version="4.0"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee">
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class></servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
3.2.将Maven的结构搭建(java,resources)
4.编写一个Servlet程序
-
编写一个普通类
- 实现Servlet接口,这里我们直接继承HttpServlet
public class HelloServlet extends HttpServlet {
//由于get或post只是请求实现的方式不同,可以相互调用,业务逻辑都一样
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("进入了doGet方法");
//ServletOutputStream outputStream = resp.getOutputStream();
PrintWriter writer = resp.getWriter();//响应流
writer.println("Hello,Servlet");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
5.编写Servlet的映射
为什么需要映射?
我们写的是Java程序,但是要通过浏览器访问,而浏览器需要连接web服务器,所以我们需要在web服务中注册我们写的Servlet,还需要给它一个浏览器能够访问的路径
<?xml version="1.0" encoding="UTF-8"?>
<web-app metadata-complete="true" version="4.0"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee">
<!--注册Servlet-->
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.qjd.servlet.HelloServlet</servlet-class>
</servlet>
<!--Servlet的请求路径-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
6.配置Tomcat
注意:配置项目发布的路径就可以了
7.启动测试
(1)为部署标记工件
(2)启动Tomcat发现在服务这个模块中显示了我们在HelloServlet中编写的“进入了doGet方法“”
(3)测试结果(注意这里的访问路径不要写错)
6.3、Servlet原理
Servlet是由web服务器调用,web服务器在收到浏览器的请求之后,会执行以下流程:
6.4、Mapping
1.一个Servlet可以指定一个映射路径
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
2.一个Servlet可以指定多个映射路径
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello2</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello3</url-pattern>
</servlet-mapping>
3.一个Servlet可以指定通用映射路径
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello/*</url-pattern>
</servlet-mapping>
<!--默认请求路径(不会进入index.jsp)-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
4.指定一些后缀或者前缀等
<!--可以自定义后缀实现请求映射(*前面不能加项目映射的路径)-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>*.qjd</url-pattern>
</servlet-mapping>
5.优先级问题
指定了固有的映射路径优先级最高,如果找不到就会走默认的处理请求
如下启动Tomcat默认进入ErrorServlet的404,但输入http://localhost:8080/s001/hello可以进入Hello.Servlet而不是404
public class ErrorServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html");
resp.setCharacterEncoding("utf-8");
PrintWriter writer = resp.getWriter();
writer.println("<h1>404</h1>");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
<!--注册Servlet-->
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.qjd.servlet.HelloServlet</servlet-class>
</servlet>
<!--Servlet的请求路径-->
<!--localhost:8080/s001/hello -->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<!-- 404-->
<servlet>
<servlet-name>error</servlet-name>
<servlet-class>com.qjd.servlet.ErrorServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>error</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
6.5、ServletContext
web容器在启动的时候,它会为每个web程序都创建一个对应的ServletContext对象,它代表了当前的web应用
1、共享数据
我们在这个Servlet中保存的数据可以在另一个Servlet中拿到
例子:
-
放置username的类
public class HelloServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // this.getInitParameter() 初始化参数 // this.getServletConfig() Servlet配置 // this.getServletContext() Servlet上下文 ServletContext context = this.getServletContext(); String username = "ikun";//数据 context.setAttribute("username",username);//将一个数据保存在了ServletContext中,名字为:username,值为username (= "ikun";) } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
-
读取username 的类
public class GetServlet extends HelloServlet{ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext context = this.getServletContext(); String username =(String) context.getAttribute("username"); resp.setContentType("text/html"); resp.setCharacterEncoding("utf-8"); resp.getWriter().println("名字是"+username); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doGet(req, resp); } }
-
配置web.xml
<servlet> <servlet-name>hello</servlet-name> <servlet-class>com.qjd.servlet.HelloServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> <servlet> <servlet-name>getc</servlet-name> <servlet-class>com.qjd.servlet.GetServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>getc</servlet-name> <url-pattern>/getc</url-pattern> </servlet-mapping>
-
测试访问(理解6.5开始的图):
-
直接访问http://localhost:8080/s002/getc不能显示出名字(名字存在http://localhost:8080/s002/hello中)
-
先访问http://localhost:8080/s002/hello再访问http://localhost:8080/s002/getc可以得到名字
-
2、获取初始化参数
-
在web.xml中配置参数
<!-- 配置一些web应用的初始化信息--> <context-param> <param-name>url</param-name> <param-value>jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8</param-value> </context-param>
-
得到参数
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext context = this.getServletContext(); String url = context.getInitParameter("url"); resp.getWriter().println(url); }
-
在web.xml中注册
<servlet> <servlet-name>gp</servlet-name> <servlet-class>com.qjd.servlet.ServletDemo03</servlet-class> </servlet> <servlet-mapping> <servlet-name>gp</servlet-name> <url-pattern>/gp</url-pattern> </servlet-mapping>
3、请求转发
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("进入了ServletDemo04");
ServletContext context = this.getServletContext();
context.getRequestDispatcher("/gp").forward(req,resp);//转发的请求路径并调用forward实现请求转发
}
4、读取资源文件
Properties
- 在java目录下新建properties
- 在resources目录下新建properties
发现都被打包到同一个路径下:classes,我们俗称这个路径为classpath(类路径)
注意:如果这里没有将properties放在resources目录下,可能会导致读取不成功,我们需要在build中配置resources , 来防止我们资源导出失败的问题
<!-- 在build中配置resources , 来防止我们资源导出失败的问题-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
思路:需要一个文件流
-
db.properties
username=root password=123456
-
ServletDemo05
public class ServletDemo05 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("进入了ServletDemo05"); InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties"); Properties prop = new Properties(); prop.load(is); String username = prop.getProperty("username"); String password = prop.getProperty("password"); resp.getWriter().println(username+":"+password); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doGet(req, resp); } }
-
web.xml
<servlet> <servlet-name>sd5</servlet-name> <servlet-class>com.qjd.servlet.ServletDemo05</servlet-class> </servlet> <servlet-mapping> <servlet-name>sd5</servlet-name> <url-pattern>/sd5</url-pattern> </servlet-mapping>
-
访问测试
6.6、HttpServletResponse
web服务器接收到客户端的http请求,针对这个请求,分别创建一个代表请求的HttpServletRequest对象和一个代表响应的HttpServletResponse对象
- 如果要获取客户端请求过来的参数:找HttpServletRequest
- 如果要给客户端响应一些信息:找HttpServletResponse
1、简单分类
负责向浏览器发送数据的方法
ServletOutputStream getOutputStream() throws IOException;
PrintWriter getWriter() throws IOException;
负责向浏览器发送响应头的方法
void setCharacterEncoding(String var1);
void setContentLength(int var1);
void setContentLengthLong(long var1);
void setContentType(String var1);
响应的状态码常量
200:请求响应成功
202:接受
3**:
重定向:重新到我给你的新位置去
400:错误的请求
404:找不到资源
5**:服务器代码错误(502网关错误)
以下具体的状态码了解即可,重点记住以上关于Http重点的状态码
int SC_CONTINUE = 100;
int SC_SWITCHING_PROTOCOLS = 101;
int SC_OK = 200;
int SC_CREATED = 201;
int SC_ACCEPTED = 202;
int SC_NON_AUTHORITATIVE_INFORMATION = 203;
int SC_NO_CONTENT = 204;
int SC_RESET_CONTENT = 205;
int SC_PARTIAL_CONTENT = 206;
int SC_MULTIPLE_CHOICES = 300;
int SC_MOVED_PERMANENTLY = 301;
int SC_MOVED_TEMPORARILY = 302;
int SC_FOUND = 302;
int SC_SEE_OTHER = 303;
int SC_NOT_MODIFIED = 304;
int SC_USE_PROXY = 305;
int SC_TEMPORARY_REDIRECT = 307;
int SC_BAD_REQUEST = 400;
int SC_UNAUTHORIZED = 401;
int SC_PAYMENT_REQUIRED = 402;
int SC_FORBIDDEN = 403;
int SC_NOT_FOUND = 404;
int SC_METHOD_NOT_ALLOWED = 405;
int SC_NOT_ACCEPTABLE = 406;
int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
int SC_REQUEST_TIMEOUT = 408;
int SC_CONFLICT = 409;
int SC_GONE = 410;
int SC_LENGTH_REQUIRED = 411;
int SC_PRECONDITION_FAILED = 412;
int SC_REQUEST_ENTITY_TOO_LARGE = 413;
int SC_REQUEST_URI_TOO_LONG = 414;
int SC_UNSUPPORTED_MEDIA_TYPE = 415;
int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
int SC_EXPECTATION_FAILED = 417;
int SC_INTERNAL_SERVER_ERROR = 500;
int SC_NOT_IMPLEMENTED = 501;
int SC_BAD_GATEWAY = 502;
int SC_SERVICE_UNAVAILABLE = 503;
int SC_GATEWAY_TIMEOUT = 504;
int SC_HTTP_VERSION_NOT_SUPPORTED = 505;
2、常见应用
1.向浏览器输出消息
(前面的练习就是)
2.下载文件
-
要获取下载文件的路径
-
下载的文件名是什么
-
设置想办法让浏览器能够支持下载我们需要的东西
-
获取下载文件的输入流
-
创建缓冲区
-
获取OutputStream对象
-
将FileOutputStream写入到buffer缓冲区
-
使用OutputStream将缓冲区中的数据输出到客户端
代码实现
public class FileServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//要获取下载文件的路径
String realPath = "E:\\JavaWeb\\Javaweb\\javaweb-02-servlet\\response\\src\\main\\resources\\3.jpg";
System.out.println("下载文件的路径"+realPath);
//下载的文件名是什么
String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1);
//设置想办法让浏览器能够支持Content-Disposition下载我们需要的东西,中文命名的文件用
//URLEncoder.encode(fileName,"utf-8")解决乱码问题
resp.setHeader("Content-Disposition","attachment; filename="+ URLEncoder.encode(fileName,"utf-8"));
//获取下载文件的输入流
FileInputStream in = new FileInputStream(realPath);
//创建缓冲区
int len = 0;
byte[] buffer = new byte[1024];
//获取OutputStream对象
ServletOutputStream out = resp.getOutputStream();
//将FileOutputStream写入到buffer缓冲区
//使用OutputStream将缓冲区中的数据输出到客户端
while ((len = in.read(buffer))>0){
out.write(buffer,0,len);
}
in.close();
out.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
测试访问
3.验证码功能
验证怎么来的?
-
前端实现
-
后端实现,需要用到Java的图片类,生成一个图片
public class ImageServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //如何让浏览器3秒自动刷新一次 resp.setHeader("refresh","3"); //在内存中创建一个图片 BufferedImage bufferedImage = new BufferedImage(80,60,BufferedImage.TYPE_INT_RGB); //得到图片 Graphics2D graphics =(Graphics2D) bufferedImage.getGraphics();//画笔 //设置图片的背景颜色 graphics.setColor(Color.BLUE); graphics.fillRect(0,0,80,60); //给图片写数据 graphics.setColor(Color.CYAN); graphics.setFont(new Font(null,Font.BOLD,20)); graphics.drawString(makeNum(),0,20); //告诉浏览器这个请求用图片的方式打开 resp.setContentType("image/jpg"); //网站存在缓存 resp.setDateHeader("expires",-1); resp.setHeader("cache-control","no-cache"); //把图片写给浏览器 ImageIO.write(bufferedImage,"jpg", resp.getOutputStream()); } //生成随机数 private String makeNum(){ Random random = new Random(); String s = random.nextInt(9999999) + ""; StringBuffer sb = new StringBuffer(); for (int i = 0; i < 7-s.length() ; i++) { sb.append("0"); } String s1 = sb.toString() + s; return s; } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
测试访问:每5秒钟刷新生成随机数(这个页面有点丑,大家请见谅)
4.实现重定向
一个web资源收到客户端请求后,它会通知客户端去访问另一个web资源,这个过程叫做重定向
常见场景:
用户登录
void sendRedirect(String var1) throws IOException;
测试:
public class RedirectServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//重定向
resp.sendRedirect("/r/img");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
测试访问
注:网页404时代表代码没有问题,只是路径出错了
jsp测试
-
index.jsp
<html> <body> <h2>Hello World!</h2> <%--这里提交的路径需要寻找到项目的路径--%> <%--pageContext.request.contextPath代表当前的项目--%> <form action="${pageContext.request.contextPath}/login" method="get"> 用户名: <input type="text" name="username"><br> 密码: <input type="password" name="password"><br> <input type="submit"> </form> <%@page contentType="text/html; ISO-8859-1" pageEncoding="UTF-8" %> </body> </html>
-
success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h1>Success</h1> </body> </html>
-
RequestTest
public class RequestTest extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("进入这个请求了"); //处理请求 String username = req.getParameter("username"); String password = req.getParameter("password"); System.out.println(username+":"+password); //重定向时候一定要注意,路径问题,否则404 resp.sendRedirect("/r/success.jsp"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
测试访问:
6.7、HttpServletRequest
HttpServletRequest代表客户端的请求,用户通过Http协议访问服务器,Http请求中的所有信息会被封装到HttpServletRequest,
通过这个HttpServletRequest的方法,获得客户端的所有信息
获取参数,请求转发
代码实现
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
String[] hobbies = req.getParameterValues("hobby");
System.out.println("=====================================");
System.out.println(username);
System.out.println(password);
System.out.println(Arrays.toString(hobbies));
System.out.println("=====================================");
//通过请求转发
//注意:这里的 / 代表当前的web应用(转发不用写/ 重定向写/)
req.getRequestDispatcher("/success.jsp").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
测试访问
思考题:请你聊聊重定向和转发的区别
相同点:页面都会实现跳转
不同点:
- 请求转发的时候,URL不会发生变化 307
- 重定向的时候,URL地址栏会发生变化 302
7、Cookie、Session
7.1、会话
会话:用户打开了一个浏览器,点击了很多超链接,访问多个web资源,关闭浏览器,这个过程可以称之为会话
有状态会话:记录了一些信息
大家在浏览网站的过程中有没有思考过这样一个问题,一个网站怎么证明客户访问过呢?
客户端 服务端
1.服务端给客户端一个信件,客户端下次访问服务端带上信件就可以了 : cookie
2.服务器登记客户已经来过了,下次访问的时候直接匹配登记过的客户 ; session
3.大家可以先看一下这张图,下面对Cookie与Session的讲解就是围绕这张图展开的,最后会对这张图进行详细的讲解
7.2、保存会话的两种技术
7.2.1、cookie
- 客户端技术(响应,请求)
- cookie一般是以键值对的形式进行表示的(key-value)
- 我们登录过一个网站,当我们再次打开就不需要再次登录了
7.2.2、session
- 服务器技术(保存用户的会话信息,可以把信息或者数据放在session中)
- 每个客户端都有自己的一个Session会话
- Session会话中,我们经常用来保存用户登录之后的信息
大部分的网站:网站登录之后,下次就不用再登录了,第二次访问直接就可以登录进去
7.3、Cookie
1.cookie中常用属性的解释
- Name:这个是cookie的名字
- Value:这个是cooke的值
- path:这个定义了Web站点上可以访问该Cookie的目录
- Expircs:这个值表示cookie的过期时间,也就是有效值,cookic在这个值之前都有效。
- size:这个表示cookic的大小
2.Cookie的Http传输
Cookie的Http传输 我们会在接下来的例子中进行抓包展示
3.Cookie的生命周期
cookie有2种存储方式,一种是会话性,一种是持久性
- 会话性:如果cookie为会话性,那么cookie仅会保存在客户端的内存中,当我们关闭客服端时cookie也就失效了
- 持久性:如果cookie为持久性,那么cookie会保存在用户的硬盘中,直至生存期结束或者用户主动将其销毁。
cookie我们是可以进行设置的,我们可以人为设置cookie的有效时间,什么时候创建,什么时候销毀。
4.cookie 使用的常见方法
java中Cookie对象的方法进行讲解
- new Cookie(String name, String value):创建一个Cookie对象,必须传入cookie的名字和cookic的值
- setValue():得到cookie保存的值
- getName():获取cookie的名字
- setMaxAge(int expiry):设置cookie的有效期,默认为-1。这个如果设置负数,表示客服端关闭,cookie就会删除。0表示马上删除。正数表示有效时间,单位是秒
- setPath(String uri):设置cookie的作用域
HttpServletRequest和Http ServletResponse对Cookie进行操作的常见方法
- response.addCookie(Cookie cookie):将cookie给客户端进行保存
- resquest.getCookies0:得到客服端传过来的所有cookie对象
这张图是客户端携带Cookie访问服务器的流程图
7.3.1、用代码实现保存用户上一次访问的时间
-
从请求中拿到cookie信息
-
服务端响应给客户端cookie
//保存用户上一次访问的时间
public class CookieDemon01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//服务器告诉你你来的时间,把这个时间封装为一个信件,你下次来,我就知道你来了
//解决中文乱码,这里解决乱码需要根据实际情况自己进行调试
req.setCharacterEncoding("gbk");
resp.setCharacterEncoding("gbk");
PrintWriter out = resp.getWriter();
//cookie,服务器端从客户端获取
Cookie[] cookies = req.getCookies();//返回数组说明cookie可能存在多个
//判断cookie是否存在
if (cookies != null) {
//如果存在怎么办
out.write("您上一次访问的时间是:");
for (int i = 0; i < cookies.length; i++) {
Cookie cookie = cookies[i];
//获取cookie的名字
if( cookie.getName().equals("LastLogTime")){
//获取cookie的值
long lastLogTime = Long.parseLong(cookie.getValue());
Date date = new Date(lastLogTime);
out.write(date.toLocaleString());
}
}
}else {
out.write("这是您第一次访问本站");
}
//服务器给客户端响应一个cookie
Cookie cookie = new Cookie("LastLogTime", System.currentTimeMillis()+"");
//设置cookie有效期为一天(浏览器关闭cookie还存在一天)
cookie.setMaxAge(24*60*60);
resp.addCookie(cookie);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
3.注册Servlet
<servlet>
<servlet-name>CookieDemo01</servlet-name>
<servlet-class>com.qid.servlet.CookieDemon01</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CookieDemo01</servlet-name>
<url-pattern>/c1</url-pattern>
4.测试访问
第一次访问
第二次访问
接下来我们抓包查看Cookie的Http传输
注:cookie一般会保存在本地的用户目录下AppData ,浏览器只会携带在当前请求的URL中包含了改cookie中path值的cookie
了解一下:
一个网站cookie是否存在上限?
- 一个cookie只能保存一个信息
- 一个web站点可以给浏览器发送多个cookie,最多存放20个cookie
- cookie大小上限为4kb
- 300个cookie大约为浏览器上限
7.3.2、删除cookie:
-
不设置有效期,关闭浏览器,自动失效
-
设置有效期为0
public class CookieDemon02 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //创建一个cookie.名字必须和要删除的名字一致 Cookie cookie = new Cookie("LastLogTime", System.currentTimeMillis()+""); //有限期为0 cookie.setMaxAge(0); resp.addCookie(cookie); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
7.3.3、中文传输数据乱码问题
这里再用上面记录访问时间的例子说一下传输中文数据的问题:
如果一直中文传输数据乱码,可以尝试以下代码:
编码解码
//编码
Cookie cookie = new Cookie("name", URLEncoder.encode("张三","utf-8"));
//解码
out.write( URLDecoder.decode(cookie.getValue(),"utf-8"));
整体代码:
//中文数据传递
public class CookieDemon03 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//解决中文乱码
req.setCharacterEncoding("utf-16");
resp.setCharacterEncoding("utf-16");
//cookie,服务器端从客户端获取
Cookie[] cookies = req.getCookies();//返回数组说明cookie可能存在多个
PrintWriter out = resp.getWriter();
//判断cookie是否存在
if (cookies != null) {
//如果存在怎么办
out.write("您的名字是:");
for (int i = 0; i < cookies.length; i++) {
Cookie cookie = cookies[i];
//获取cookie的名字
if( cookie.getName().equals("name")){
//获取cookie的值
System.out.println(cookie.getValue());
//解码
out.write( URLDecoder.decode(cookie.getValue(),"utf-8"));
}
}
}else {
out.write("这是您第一次访问本站");
}
//编码
Cookie cookie = new Cookie("name", URLEncoder.encode("张三","utf-8"));
resp.addCookie(cookie);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
注册servlet后测试结果
7.4、Session(重点)
7.4.1、Session的概念
1.什么是session
- 服务器会给每一个用户(浏览器)创建一个Session
- 一个Session独占一个浏览器,只要浏览器没有关闭,这个Session就存在
- 用户登录之后,整个网站它都可以访问--->保存用户的信息
2.Session的常用方法
- request.getSession():创建(第一次调用)或获取Session会话对象
- isNew():判断当前Session会话是否是新创建出来的
- getId():得到Session的会话ID
- setAttribute():往Session中保存数据
- getAttribute():获取Session域中的数据
3.Session的生命周期
-
手动注销:session.invalidate();
-
自动注销:在web.xml 中配置
<!--设置session默认失效时间-->
<session-config>
<!--15分钟后session自动失效,以分钟为单位-->
<session-timeout>15</session-timeout>
</session-config>
4.session应用:
- 保存一个登录用户的信息
- 购物车信息
- 在整个网站中经常会使用的数据,我们将它保存在Session中
下面这张图是服务器登记session,客户端用sessionID访问的流程图
7.4.2、具体使用session
1.保存数据:
- Person.java
- SessionDemon01(设置信息)
- SessionDemon02(得到信息)
public class Person {
private String name;
private int age;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.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;
}
}
public class SessionDemon01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//解决乱码问题
req.setCharacterEncoding("gbk");
resp.setCharacterEncoding("gbk");
resp.setContentType("text/html;charset = gbk");
//得到Session
HttpSession session = req.getSession();
//给Session中存东西
session.setAttribute("name",new Person("李四",18));
//获取session的id
String sessionId = session.getId();
//判断session是不是新创建的
if(session.isNew()){
resp.getWriter().write("session创建成功,id:"+sessionId);
}else {
resp.getWriter().write("session已经在服务器中存在了,id:"+sessionId);
}
//Session创建的时候做了什么事情
// Cookie cookie = new Cookie("JSESSIONID",sessionId);
// resp.addCookie(cookie);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
public class SessionDemon02 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//解决乱码问题
req.setCharacterEncoding("gbk");
resp.setCharacterEncoding("gbk");
resp.setContentType("text/html;charset = gbk");
//得到Session
HttpSession session = req.getSession();
Person person = (Person) session.getAttribute("name");
resp.getWriter().write(person.toString());
System.out.println(person.toString());
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
注册Servlet测试访问
(1)访问设置信息s1
(2)访问得到信息s2
2.会话过期
-
手动
public class SessionDemon03 extends HttpServlet { //注销,移除 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session = req.getSession(); session.removeAttribute("name"); //手动注销session session.invalidate();//一旦注销会重新产生一个sessionId } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
-
自动
<!--设置session默认失效时间--> <session-config> <!--15分钟后session自动失效,以分钟为单位--> <session-timeout>15</session-timeout> </session-config>
最后我们再回到刚开始的问题,一个网站怎么证明你来过呢?
思考 : Session和Cookie的区别
①cookie可以存储在浏览器或者本地,Session只能存在服务器
②session能够存储任意的java对象,cookie 只能存储String 类型的对象
③Session比tCookie更具有安全性(Cookie有安全隐患,通过拦截或本地文件找得到你的cookie后可以进行攻击)
④Session占用服务器性能,Session过多,增加服务器压力
⑤单个Cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个Cookie,Session是没有大小限制和服务器的
8、JSP
8.1、什么是JSP
JavaServletPages: Java服务器端页面,也和Serlvet一样,用于动态web技术
最大的特点:
- 写JSP就像写HTML
- 区别:
- HTML只给用户提供静态的数据
- JSP页面中可以嵌入Java代码,为用户提供动态数据
8.2、JSP原理
思路:JSP到底是怎么执行的
-
代码层面没有任何问题
-
服务器内部工作
Tomcat中有一个work目录:
idea中使用Tomcat会在idea的Tomcat中产生一个work目录
-
发现页面变成了Java程序
JSP最终也会被转成一个java类
JSP本质上就是一个Servlet
浏览器向服务器发送请求,不管访问什么资源,其实都是在访问servlet
1.判断请求
2.内置一些对象
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 request
//请求
HttpservletResponse response
//响应
3.输出页面前增加的代码
response.setcontentType("text/htm1");
//设望响应的页面类型
pagecontext = _jspxFactory.getPagecontext(this,request,response,
null,true,8192,true);
_jspx_page_context = pagecontext;
application = pagecontext.getservletcontext();
config = pagecontext.getservletconfig();
session = pagecontext,getsession();
out = pagecontext.getout();
_jspx_out=out;
以上的这些对象我们可以在JSP页面上直接使用
在JSP页面中,只要是Java代码就会原封不动的输出
如果是HTML代码就会被转换为:
out.write("<html>\n");
这样的格式输出到前端
8.3、JSP基础语法
任何语言都有自己的语法,Java中有;JSP作为Java技术的一种应用,他拥有一些自己的扩充语法,Java所有的语法都支持
JSP表达式
<%--JSP表达式
作用:用来将程序的输出,输出到客户端
<%= new 变量或者表达式%>
--%>
<%= new java.util.Date()%>
JSP脚本片段
<%--jsp 脚本片段 --%>
<%
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum+=i;
}
out.println("<h1>Sum="+sum+"</h1>");
%>
脚本片段的再实现
<%
int x = 10;
out.println(x);
%>
<p>这是一个jsp文档</p>
<%
int y = 20;
out.println(2);
%>
<hr>
<%--在代码中嵌入HTML元素 --%>
<%
for (int i = 0; i < 5; i++) {
%>
<h1>hello world <%=i%> </h1>
<%
}
%>
JSP声明
<%!
static {
System.out.println("Loading Servlet!");
}
private int global = 0;
public void qjd(){
System.out.println("进入了方法qjd");
}
%>
JSP声明:(<%!%>)会被编译到JSP生成的Java类中!其他的(<%%>)就会被生成到jspService 方法中
在JSP中嵌入Java代码
<%----%> 注释
<%%> 片段
<%=%> 表达式输出一个值
<%!%> 全局
${} el表达式
JSP的注释(<%----%>)不会在客户端显示,HTML()就会
8.4、JSP指令
<%@page args...%>
<%@ include file="" %>
1.定制错误页面<%@page args...%>
jsp2.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--定制错误页面(可以在这个jsp中引入也可以在web.xml 中配置)--%>
<%@ page errorPage="error/500.jsp" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
int x = 1/0;
%>
</body>
</html>
如果在web.xml中配置
<error-page>
<error-code>500</error-code>
<location>/error/500.jsp</location>
</error-page>
500.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<img src="../img/500.png" alt="500">
</body>
</html>
测试
500错误:
404错误:
2.共用部分<%@ include file="" %>
<head>
<title>Title</title>
</head>
<body>
<%--方法一@ include会将三个页面合三为一--%>
<%@ include file="common/header.jsp" %>
<h1>网页主体</h1>
<%@ include file="common/footer.jsp" %>
<hr>
<%--方法二jsp标签
jsp:include:拼接页面,本质还是3个
--%>
<jsp:include page="/common/header.jsp"></jsp:include>
<h1>网页主体</h1>
<jsp:include page="/common/footer.jsp"></jsp:include>
</body>
</html>
测试
8.5、九大内置对象
- PageContext (存东西)
- Request (存东西)
- Response
- Session (存东西)
- Application 【ServletContext】(存东西)
- config 【ServletConfig】
- out
- page
- exception
重点对象理解:
request:客户端向服务器发送请求,产生的数据,用户看完就没用了,比如:新闻,用户看完没用的!
session:客户端向服务器发送请求,产生的数据,用户用完一会还有用,比如:购物车;
application:客户端向服务器发送请求,产生的数据,一个用户用完了,其他用户还可能使用,比如:聊天数
据;
具体实现:
- 通过pageContext寻找的方式取出我们保存的值
pageContextDemon01.jsp(在一个页面取值)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--内置对象--%>
<%
pageContext.setAttribute("name1","张三1");//保存的数据只在一个页面中有效
request.setAttribute("name2","张三2");//保存的数据只在一次请求中有效,请求转发会携带这个数据
session.setAttribute("name3","张三3");//保存的数据只在一次会话中有效,打开到关闭浏览器
application.setAttribute("name4","张三4");//保存的数据在服务器中有效,打开到关闭服务器
%>
<%
//通过pageContext寻找的方式取出我们保存的值
//从底层到高层(作用域): page->request->session->application->找不到
//JVM:双亲委派机制 应用->扩展类->rt.jar
String name1 =(String) pageContext.findAttribute("name1");
String name2 =(String) pageContext.findAttribute("name2");
String name3 =(String) pageContext.findAttribute("name3");
String name4 =(String) pageContext.findAttribute("name4");
String name5 =(String) pageContext.findAttribute("name5");//不存在
%>
<%--使用el表达式输出 ${}--%>
<h1>取出的值为:</h1>
<h2>${name1}</h2>
<h2>${name2}</h2>
<h2>${name3}</h2>
<h2>${name4}</h2>
<h2>${name5}</h2>
</body>
</html>
pageDemon02.jsp(在另一个页面取值)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--在另一个页面取pageContextDemon01.jsp中的值只能取到3和4
如果 request请求转发就可以取出来
--%>
<%
//通过pageContext寻找的方式取出我们保存的值
//从底层到高层(作用域)
String name1 =(String) pageContext.findAttribute("name1");
String name2 =(String) pageContext.findAttribute("name2");
String name3 =(String) pageContext.findAttribute("name3");
String name4 =(String) pageContext.findAttribute("name4");
String name5 =(String) pageContext.findAttribute("name5");//不存在
%>
<%--使用el表达式输出 ${}--%>
<h1>取出的值为:</h1>
<h2>${name1}</h2>
<h2>${name2}</h2>
<h2>${name3}</h2>
<h2>${name4}</h2>
<h2>${name5}</h2>
</body>
</html>
手动修改作用域与请求转发
//手动修改作用域,第三个参数为作用域
pageContext.setAttribute("hello","hello1",PageContext.SESSION_SCOPE);//等价于 session.setAttribute("hello","hello1");
<%
//请求转发
pageContext.forward("/index.jsp");
//等价于 request.getRequestDispatcher("/index.jsp").forward(request,response);
%>
8.6、JSP标签、JSTL标签、EL表达式
<!-- jstl表达式依赖 -->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>
<!-- standard标签 -->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
EL表达式:${}
- 获取数据
- 执行运算
- 获取web开发的常用对象
JSP标签:
<%--jsp:include --%>
<h1>1</h1>
<%--转发时添加数据--%>
<jsp:forward page="/jsptag2.jsp">
<jsp:param name="name" value="ikun"/>
<jsp:param name="age" value="23"/>
</jsp:forward>
<%--取出参数--%>
名字:<%=request.getParameter("name")%>
年龄:<%=request.getParameter("age")%>
JSTL表达式:
JSTL标签库的使用就是为了弥补html标签的不足;它自定义许多标签,可以供我们使用,标签的功能和Java代码一样
- 核心标签(掌握部分)
-
格式化标签
-
SQL标签
-
XML标签
JSTL标签库使用步骤:
- 引入对应的taglib
- 使用其中的方法
- 在Tomcat也需要导入jstl的包。否则会报错:jstl解析错误
c:if:
<%@ taglib prefix="x" uri="http://java.sun.com/jsp/jstl/xml" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>if测试</h3>
<hr>
<form action="coreif.jsp" method="get">
<%--el表达式获取表单中的数据
${param.参数名}
--%>
<input type="text" name="username" value="${param.username}">
<input type="submit" value="登录">
</form>
<%--判断如果提交的用户名是管理员则登录成功--%>
<c:if test="${param.username == 'admin'}" var="isAdmin">
<c:out value="管理员进入"></c:out>
</c:if>
<c:out value="${isAdmin}"></c:out>
</body>
</html>
c:choose:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--引入jstl核心标签库--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="x" uri="http://java.sun.com/jsp/jstl/xml" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--定义一个标量scroe,值为99--%>
<c:set var="score" value="99"/>
<c:choose>
<c:when test="${score>=95}">
你的成绩为优秀
</c:when>
<c:when test="${score>=80}">
你的成绩为良好
</c:when>
<c:when test="${score>=70}">
你的成绩为一般
</c:when>
<c:when test="${score<60}">
你的成绩为不及格
</c:when>
</c:choose>
</body>
</html>
c:forEach:
<%@ page import="java.util.ArrayList" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--引入jstl核心标签库--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="x" uri="http://java.sun.com/jsp/jstl/xml" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
ArrayList<String> people = new ArrayList<>();
people.add(0,"张三");
people.add(1,"李四");
people.add(2,"王五");
people.add(3,"赵六");
people.add(4,"田七");
request.setAttribute("list",people);
%>
<%--var:每一次遍历出来的变量
items:要遍历的对象
begin:开始
end:结束
step:步长
--%>
<c:forEach var="people" items="${list}">
<c:out value="${people}"></c:out>
<br>
</c:forEach>
</body>
</html>
9、JavaBean
实体类
JavaBean有特定的写法:
- 必须要有一个无参构造
- 属性必须私有化
- 必须有对应的set/get方法
一般用来和数据库字段做映射,ORM;
ORM:对象关系映射
- 表--->类
- 字段--->属性
- 行记录--->对象
<%@ page import="com.qjd.pojo.People" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
// People people = new People();
// people.setId(001);
// people.setAddress("");
%>
<jsp:useBean id="people" class="com.qjd.pojo.People" scope="page"/>
<jsp:setProperty name="people" property="address" value="大连"/>
<jsp:setProperty name="people" property="id" value="01"/>
<jsp:setProperty name="people" property="age" value="18"/>
<jsp:setProperty name="people" property="name" value="张三"/>
姓名:<jsp:getProperty name="people" property="name"/>
</body>
</html>
10、三层架构
什么是MVC:Model View Controller (模型,视图,控制器)
10.1、早期开发
用户直接访问控制层,控制层就可以直接操作数据库
servlet--CRUD--数据库
弊端:程序十分臃肿,不利于维护
servlet的代码中!处理请求、响应、视图跳转、处理JDBC、处理业务代码、处理逻辑代码
架构:设有什么是加一层解决不了的!
程序调用
JDBC
Mysql oracle sqlserver ...
10.2、MVC三层架构
Model:
- 业务处理:业务逻辑(Service)
- 数据持久层:CRUD(Dao)
View:
- 展示数据
- 提供连接发起Servlet请求(a,form,img...)
Controller:
- 接受用户的请求:(req:请求参数、session信息...)
- 交给业务层处理对应的代码
- 控制视图的跳转
登录--->接收用户的登录请求--->处理用户的请求(获取用户登录的参数,username,password)--->交给业务层处理登录业务(判断用户名密码是否正确)--->Dao层查询用户名和密码是否正确--->数据库
11、Filter(重点)
Filter:过滤器,用来过滤网站的数据;
- 处理中文乱码
- 登录验证...
Filter开发步骤:
- 导包
- 编写过滤器
- 导包不要错 import javax.servlet.*;
- 实现Filter接口,重写对应的方法
- 在web.xml中配置Filter
CharacterEncodingFilter:
public class CharacterEncodingFilter implements Filter {
//初始化 web服务器启动就初始化了
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("CharacterEncodingFilter初始化");
}
//Chain:链
/*
* 1.过滤中的所有代码,在过滤特定请求的时候都会执行
* 2.必须要让过滤器继续通行filterChain.doFilter(servletRequest,servletResponse);
* */
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
servletRequest.setCharacterEncoding("utf-8");
servletResponse.setCharacterEncoding("utf-8");
servletResponse.setContentType("text/html;charset=utf-8");
System.out.println("CharacterEncodingFilter执行前...");
filterChain.doFilter(servletRequest,servletResponse);//让我们的请求继续走,如果不添加,程序到这里就被拦截停止
System.out.println("CharacterEncodingFilter执行后...");
}
//销毁 web服务器关闭的时候销毁
@Override
public void destroy() {
System.out.println("CharacterEncodingFilter销毁");
}
}
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.qjd.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-name>CharacterEncodingFilter</filter-name>
<filter-class>com.qjd.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<!--只要是/servlet的任何请求,都会经过这个过滤器 -->
<url-pattern>/servlet/*</url-pattern>
<!--所有请求都过滤 -->
<!--<url-pattern>/*</url-pattern>-->
</filter-mapping>
</web-app>
ShowServlet:
public class ShowServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// resp.setCharacterEncoding("utf-8");
// req.setCharacterEncoding("utf-8");
resp.getWriter().write("你好啊");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
12、监听器
实现一个监听器的接口:(有N种)
-
编写一个监听器(实现监听器的接口)
//统计网站在线人数:统计session public class OnlineCountListener implements HttpSessionListener{ // 创建session监听 // 一旦创建Session就会触发一次这个事件 @Override public void sessionCreated(HttpSessionEvent se) { System.out.println(se.getSession().getId()); ServletContext ctx = se.getSession().getServletContext(); Integer onlineCount =(Integer) ctx.getAttribute("OnlineCount"); if(onlineCount == null){ onlineCount = new Integer(1); }else { int count = onlineCount.intValue(); onlineCount = new Integer(count+1); } ctx.setAttribute("OnlineCount",onlineCount); } // 销毁session监听 // 一旦销毁Session就会触发一次这个事件 @Override public void sessionDestroyed(HttpSessionEvent se) { ServletContext ctx = se.getSession().getServletContext(); Integer onlineCount =(Integer) ctx.getAttribute("OnlineCount"); if(onlineCount == null){ onlineCount = new Integer(0); }else { int count = onlineCount.intValue(); onlineCount = new Integer(count-1); } ctx.setAttribute("OnlineCount",onlineCount); } /* * session 销毁 * 1.手动销毁 se.getSession().invalidate(); * 2.自动销毁 (在web.xml中配置) * <session-config> <session-timeout>1</session-timeout> </session-config> * */ }
-
配置监听器(web.xml中注册)
<!-- 注册监听器 --> <listener> <listener-class>com.qjd.listener.OnlineCountListener</listener-class> </listener>
-
根据情况是否使用
-
测试结果
13、过滤器、监听器常见应用
-
监听关闭事件(GUI)
public class TestPanel { public static void main(String[] args) { Frame frame = new Frame("好好学习");//新建一个窗口 Panel panel = new Panel(null);//面板 frame.setLayout(null);//设置窗体的布局 frame.setBounds(300,300,500,500); frame.setBackground(new Color(0, 255, 34)); panel.setBounds(50,50,300,300); panel.setBackground(new Color(128, 0, 255)); frame.add(panel); frame.setVisible(true); //监听关闭事件 frame.addWindowListener(new WindowListener() { @Override public void windowOpened(WindowEvent e) { System.out.println("打开"); } @Override public void windowClosing(WindowEvent e) { System.out.println("正在关闭"); System.exit(0);//0是正常终止。1是非正常终止 } @Override public void windowClosed(WindowEvent e) { System.out.println("已关闭"); } @Override public void windowIconified(WindowEvent e) { } @Override public void windowDeiconified(WindowEvent e) { } @Override public void windowActivated(WindowEvent e) { System.out.println("激活"); } @Override public void windowDeactivated(WindowEvent e) { System.out.println("未激活"); } }); } }
-
用户登录才能进入主页,用户注销后就不能进入主页(必须通过登录才能访问登录成功主页,通过直接访问登录成功主页不能进入)
-
用户登录之后,向Session中放入用户的数据
-
进入主页的时候要判断用户是否已经登录(在过滤器中实现)
public class SysFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { //ServletRequest HttpServlet HttpServletRequest request1 = (HttpServletRequest) servletRequest; HttpServletResponse response1 = (HttpServletResponse) servletResponse; String user_session = (String) request1.getSession().getAttribute(Constant.USER_SESSION); if(user_session == null){ response1.sendRedirect("/error.jsp"); } filterChain.doFilter(servletRequest,servletResponse); } @Override public void destroy() { } }
整体步骤:
- 制定登录页面Login.jsp
- LoginServlet
- 登录成功进入success.jsp
- 登录失败进入error.jsp
- 权限验证SysFilter
- 解决session名字重用问题(定义常量Constant)
- 配置web.xml
14、JDBC
什么是JDBC:Java DataBase Connection (Java连接数据库)
连接数据库需要jar包的支持:
- java.sql
- javax.sql
- mysql-connector-java(连接驱动)
JDBC 固定步骤:
1.加载驱动
2.连接数据库,代表数据库
3.向数据库发送SQL的对象Statement : CRUD
4.编写SQL (根据业务,不同的SQL)
5.执行SQL
6.关闭连接(先开的后关)
实验环境搭建:
-
数据库信息
-
导入数据库依赖
<!--mysql驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.29</version> </dependency>
-
IDEA中连接数据库
-
JDBC六步
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=true"; String username = "root"; String password = "123456789"; //1.加载驱动 Class.forName("com.mysql.jdbc.Driver"); //2.连接数据库,代表数据库 Connection connection = DriverManager.getConnection(url, username, password); //3.向数据库发送SQL的对象 Statement:CRUD Statement statement = connection.createStatement(); // PreparedStatement安全,可以防止sql注入(预编译) //4.编写sql String sql = "select *from jdbc.users"; //5.执行查询sql,返回一个ResultSet结果集 ResultSet rs = statement.executeQuery(sql); while (rs.next()){ System.out.println("id="+rs.getObject("id")); System.out.println("name="+rs.getObject("name")); System.out.println("password="+rs.getObject("password")); System.out.println("email="+rs.getObject("email")); System.out.println("birthday="+rs.getObject("birthday")); } //6.关闭连接,释放资源(先创建的后关闭) rs.close(); statement.close(); connection.close(); } }
- 结果:
-
使用预编译防止sql注入
public class TestJDBC2 { 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=true"; String username = "root"; String password = "123456789"; //1.加载驱动 Class.forName("com.mysql.cj.jdbc.Driver"); //2.连接数据库,代表数据库 Connection connection = DriverManager.getConnection(url, username, password); //3.编写sql String sql = "insert into jdbc.users(id, name, password, email, birthday) VALUES (?,?,?,?,?);"; //4.预编译 PreparedStatement安全,可以防止sql注入(预编译) PreparedStatement preparedStatement = connection.prepareStatement(sql); preparedStatement.setInt(1,4);//第一个占位符?的值为1 preparedStatement.setString(2,"赵六");//第二个占位符?的值为赵六 preparedStatement.setString(3,"523456");//第三个占位符?的值为123456 preparedStatement.setString(4,"zl@qq.com");//第四个占位符?的值为1503349647@qq.com preparedStatement.setDate(5,new Date(System.currentTimeMillis()));//第五个占位符?的值为当前年月日 //5.执行查询sql int i = preparedStatement.executeUpdate(); System.out.println(i); //6.关闭连接,释放资源(先创建的后关闭) preparedStatement.close(); connection.close(); } }
- 结果:
15、事务
要么都成功,要么都失败!
ACID原则:保证数据的安全
- 原子性
- 隔离性
- 持久性
- 一致性
开启事务
事务提交commit
事务回滚rollback
关闭事务
junit单元测试
-
依赖
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency>
-
简单使用:
@Test注解只有在方法上有效,只要加了这个注解的方法就可以直接运行
搭建测试事务环境:
count表:
public class TestJDBC3 {
@Test
public void test() {
//配置信息
//解决中文乱码问题useUnicode=true&characterEncoding=utf-8
String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8&useSSL=true";
String username = "root";
String password = "123456789";
Connection connection = null;
//1.加载驱动
try {
Class.forName("com.mysql.cj.jdbc.Driver");
//2.连接数据库,代表数据库
connection = DriverManager.getConnection(url, username, password);
//3.通知数据库开启事务(false是开启)
connection.setAutoCommit(false);
String sql = "update jdbc.account set money = money-100 where name = 'a';";
connection.prepareStatement(sql).executeUpdate();
//制造错误
int i=1/0;
String sql2 = "update jdbc.account set money = money+100 where name = 'b';";
connection.prepareStatement(sql2).executeUpdate();
connection.commit();//以上两条sql都提交成功了就提交事务
System.out.println("提交成功!");
} catch (Exception e) {
try {
//如果出现异常,就通知数据库回滚事务
connection.rollback();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
e.printStackTrace();
}finally {
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
-
出现错误就会回滚账户余额不变
-
没有错误就会正常提交事务
16、SMBMS项目
SMBMS项目是一个用JavaWeb编写的超市管理项目,大家可以在下面这个网站下载到原始项目
对于这个超市管理项目,大家可以结合前面学习的知识分析代码,遇到不会的可以标记,最后再看狂神的视频讲解SMBMS项目
我相信大家经过这个项目大家会对JavaWeb有更深的理解
17、文件上传
使用类介绍
【文件上传的注意事项】
1.为保证服务器安全,上传文件应该放在深无法直接访问的目录下,比如放于WEB-NF目录下。
2.为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名
3.要限制上传文件的最大值。
4.可以限制上传文件的类型,在收到上传文件名时,判断后缀名是否合法。
【需要用到的类详解】
ServletFileUpload负责处理上传的文件数据,并将表单中每个输入项封装成一个Fileltem对象,在使用ServletFileUpload对象解析请求时需要DiskFileltemFactory对象。所以,我们需要在进行解析工作前构造好DiskFileltemFactory>对象,通过ServletFileUpload对象的构造方法或setFileltemFactory()方法设置ServletFileUpload?对象的fileltemFactory属性。
文件上传源码
- FileSerlvet类
/**
* Servlet implementation class FileSerlvet
*/
public class FileSerlvet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
// response.getWriter().append("Served at: ").append(request.getContextPath());
// 判断上传的文件普通表单还是带文件的表单
if (!ServletFileUpload.isMultipartContent(request)) {
return;//终止方法运行,说明这是一个普通的表单,直接返回
}
//创建上传文件的保存路径,建议在WEB-INF路径下,安全,用户无法直接访间上传的文件;
String uploadPath =this.getServletContext().getRealPath("/WEB-INF/upload");
File uploadFile = new File(uploadPath);
if (!uploadFile.exists()){
uploadFile.mkdir(); //创建这个月录
}
// 创建上传文件的保存路径,建议在WEB-INF路径下,安全,用户无法直接访问上传的文件
String tmpPath = this.getServletContext().getRealPath("/WEB-INF/tmp");
File file = new File(tmpPath);
if (!file.exists()) {
file.mkdir();//创建临时目录
}
// 处理上传的文件,一般都需要通过流来获取,我们可以使用 request, getInputstream(),原生态的文件上传流获取,十分麻烦
// 但是我们都建议使用 Apache的文件上传组件来实现, common-fileupload,它需要旅 commons-io组件;
try {
// 创建DiskFileItemFactory对象,处理文件路径或者大小限制
DiskFileItemFactory factory = getDiskFileItemFactory(file);
/*
* //通过这个工厂设置一个缓冲区,当上传的文件大于这个缓冲区的时候,将他放到临时文件 factory.setSizeThreshold(1024 *
* 1024); //缓存区大小为1M factory.setRepository (file);//临时目录的保存目录,需要一个File
*/
// 2、获取ServletFileUpload
ServletFileUpload upload = getServletFileUpload(factory);
// 3、处理上传文件
// 把前端请求解析,封装成FileItem对象,需要从ServletFileUpload对象中获取
String msg = uploadParseRequest(upload, request, uploadPath);
// Servlet请求转发消息
System.out.println(msg);
if(msg == "文件上传成功!") {
// Servlet请求转发消息
request.setAttribute("msg",msg);
request.getRequestDispatcher("info.jsp").forward(request, response);
}else {
msg ="请上传文件";
request.setAttribute("msg",msg);
request.getRequestDispatcher("info.jsp").forward(request, response);
}
} catch (FileUploadException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
public static DiskFileItemFactory getDiskFileItemFactory(File file) {
DiskFileItemFactory factory = new DiskFileItemFactory();
// 通过这个工厂设置一个缓冲区,当上传的文件大于这个缓冲区的时候,将他放到临时文件中;
factory.setSizeThreshold(1024 * 1024);// 缓冲区大小为1M
factory.setRepository(file);// 临时目录的保存目录,需要一个file
return factory;
}
public static ServletFileUpload getServletFileUpload(DiskFileItemFactory factory) {
ServletFileUpload upload = new ServletFileUpload(factory);
// 监听长传进度
upload.setProgressListener(new ProgressListener() {
// pBYtesRead:已读取到的文件大小
// pContextLength:文件大小
public void update(long pBytesRead, long pContentLength, int pItems) {
System.out.println("总大小:" + pContentLength + "已上传:" + pBytesRead);
}
});
// 处理乱码问题
upload.setHeaderEncoding("UTF-8");
// 设置单个文件的最大值
upload.setFileSizeMax(1024 * 1024 * 10);
// 设置总共能够上传文件的大小
// 1024 = 1kb * 1024 = 1M * 10 = 10м
return upload;
}
public static String uploadParseRequest(ServletFileUpload upload, HttpServletRequest request, String uploadPath)
throws FileUploadException, IOException {
String msg = "";
// 把前端请求解析,封装成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 + "]");
// 可以使用UID(唯一识别的通用码),保证文件名唯
// 0UID. randomUUID(),随机生一个唯一识别的通用码;
String uuidPath = UUID.randomUUID().toString();
// ================处理文件完毕==============
// 存到哪? uploadPath
// 文件真实存在的路径realPath
String realPath = uploadPath + "/" + uuidPath;
// 给每个文件创建一个对应的文件夹
File realPathFile = new File(realPath);
if (!realPathFile.exists()) {
realPathFile.mkdir();
}
// ==============存放地址完毕==============
// 获得文件上传的流
InputStream inputStream = fileItem.getInputStream();
// 创建一个文件输出流
// realPath =真实的文件夹;
// 差了一个文件;加上翰出文件的名产"/"+uuidFileName
FileOutputStream fos = new FileOutputStream(realPath + "/" + fileName);
// 创建一个缓冲区
byte[] buffer = new byte[1024 * 1024];
// 判断是否读取完毕
int len = 0;
// 如果大于0说明还存在数据;
while ((len = inputStream.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
// 关闭流
fos.close();
inputStream.close();
msg = "文件上传成功!";
fileItem.delete(); // 上传成功,清除临时文件
//=============文件传输完成=============
}
}
return msg;
}
}
- 注册xml
<servlet>
<servlet-name>upload</servlet-name>
<servlet-class>com.qjd.FileSerlvet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>upload</servlet-name>
<url-pattern>/upload.do</url-pattern>
- 导入依赖的jar包
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
- index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<body>
<%--
GET:上传文件大小有限制
POST:上传文件大小没有限制
${pageContext.request.contextPath}
--%>
<form action="upload.do" enctype="multipart/form-data" method="post">
上传用户:<input type="text" name="username"><br/>
<P><input type="file" name="file1"></P>
<P><input type="file" name="file1"></P>
<P><input type="submit" value="提交"> | <input type="reset"></P>
</form>
</body>
</html>
- info.jsp
<%@ page language="java" contentType="text/html;charset=UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>Insert title here</title>
</head>
<body>
<%=request.getAttribute("msg")%>
</body>
</html>
到这里JavaWeb绝大部分的知识就结束了,但是我们的学习还没有结束哦,大家要掌握JavaWeb就要多练习几个JavaWeb的实战项目,在项目中成长并深入理解JavaWeb,最后希望大家都得到自己满意的结果(◍•ᴗ•◍)
大家觉得JavaWeb这篇文章还可以的话可以点个赞啊๐•ᴗ•๐