1、初识JavaWeb
1.1、什么是JavaWeb?
JavaWeb是指用Java技术来解决Web互联网领域的技术栈。Web包括:Web客户端和Web服务端两部分
Web开发:
-
Web(World Wide Web),即全球广域网,也称为万维网,它是一种基于超文本和HTTP的、全球性的、动态交互的、跨平台的分布式图形信息系统。是建立在Internet上的一种网络服务,为浏览者在Internet上查找和浏览信息提供了图形化的、易于访问的直观界面
-
静态Web
- HTML、CSS
- 早些年,提供给所有人观看的数据始终不会发生变化!
-
动态web
- 现在,几乎所有的网站都是动态Web的
- 提供给所有人观看的数据会发生变化,每个人在不同的时间、地点看到的信息各不相同
- 技术栈:Servlet/JSP、ASP、PHP
2、Web服务器
2.1、技术栈
ASP:
- 微软:国内最早流行的就是ASP
- 在HTML嵌入了VB的脚本,ASP+COM
- 在ASP开发中,基本一个页面都有几千行的业务代码,页面及其紊乱(维护成本高)
- C#
PHP:
- PHP开发速度快,功能强大,跨平台,代码简单(70%,WP)
- 无法承载大访问量的情况(局限性)
JSP/Servlet:
BS架构:Browser/Server,浏览器/服务器架构模式。客户端只需要浏览器,应用程序的逻辑和数据都存储在服务端(维护方便、体验一般)
CS架构:Client/Server,客户端/服务器架构模式(开发、维护麻烦,体验较好)
- SUN公司主推的B/S架构
- 基于Java语言的(所有的大公司,或者一些开源的组件,都是用Java写的)
- 可以承载三高(高性能、高可用、高并发)问题带来的影响;
2.2、WEB服务器-Tomcat
什么是服务器?
服务器硬件:也是计算机,只不过服务器要比我们平常使用的计算机在各方面性能好很多!
服务器只是一台设备,必须安装服务器软件才能提供相应的服务
Tomcat
Tomcat服务器软件是一个免费的开源的Web应用服务器。是Apache软件基金会的一个核心项目。由 Apache,Sun和其他一些公司及个人共同开发而成
由于Tomcat只支持Servlet/JSP少量JavaEE规范,所以是一个开源免费的轻量级Web服务器
JavaEE规范: Java Enterprise Edition(Java企业版) JavaEE规范就是指Java企业级开发的技术规范总和。包含13项技术规范:JDBC、JNDI、EJB、 RMI、JSP、Servlet、XML、JMS、Java IDL、JTS、JTA、JavaMail、JAF
因为Tomcat支持Servlet/JSP规范,所以Tomcat也被称为Web容器、Servlet容器。JavaWeb程序需要依赖Tomcat才能运行
下载与安装
解压缩安装到指定目录即可!
2.3、tomcat启动和配置
目录结构
启动和关闭Tomcat
Tomcat的默认端口为8080,所以在浏览器的地址栏输入: http://127.0.0.1:8080,即可访问 tomcat服务器!
关闭:
- ctrl+c:正常关闭
- 运行命令:
bin/shutdown.bat
:正常关闭 - 直接关闭运行窗口,强制关闭
可能遇到的问题
- Java环境变量没有配置。因为Tomcat是用Java写的,所以运行需要JRE,JDK包含JRE(配置环境变量即可)
- 控制台乱码问题
乱码问题解决方案:修改conf/logging.prooperties
文件解决,修改为GB
可以配置启动的端口号
- tomcat的端口号为:8080
- mysql:3306
- http:80
- https:443
如果端口被占用,可在conf/server.xml
配置端口号
<Connector port="80" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
Tomcat部署项目:将编写好的项目放到 webapps
目录下,即可部署完成!
3、HTTP协议
3.1、什么是HTTP协议?
HTTP:Hyper Text Transfer Protocol(超文本传输协议),规定了浏览器与服务器之间数据传输的规则
- http是互联网上应用最为广泛的一种网络协议
- http协议要求:浏览器在向服务器发送请求数据时,或是服务器在向浏览器发送响应数据时,都必须按照固定的格式进行数据传输
3.2、HTTP协议特点
- 基于TCP协议:面向连接,安全
- TCP是一种面向连接的(建立连接之前是需要经过三次握手)、可靠的、基于字节流的传输层 通信协议,在数据传输方面更安全
- 基于请求-响应模型:一次请求对应一次响应(先请求后响应)
- 请求和响应是一 一对应关系,没有请求,就没有响应
- HTTP协议是无状态协议:对于数据没有记忆能力。每次请求和响应都是独立的
- 无状态指的是客户端发送HTTP请求给服务端之后,服务端根据请求响应数据,响应完后,不会记录任何信息
- 缺点:多次请求间不能共享数据
- 优点:速度快
3.3、HTTP-请求协议
浏览器和服务器是按照HTTP协议进行数据通信的
HTTP协议又分为:请求协议和响应协议
请求协议:浏览器将数据以请求格式发送到服务器
- 包括:请求行、请求头 、请求体
响应协议:服务器将数据以响应格式返回给浏览器
- 包括:响应行 、响应头 、响应体
GET和POST请求的区别
区别方式 | GET请求 | POST请求 |
---|---|---|
请求参数 | 请求参数在请求行中 | 请求参数在请求体中 |
请求参数长度 | 请求参数长度有限制(浏览器的不同限制也不同) | 请求参数长度无限制 |
安全性 | 安全性低。原因:请求参数暴露在浏览器地址栏中 | 安全性相对高 |
3.4、HTTP-响应协议
与HTTP的请求一样,HTTP响应的数据也分为3部分:响应行、响应头 、响应体
响应状态码
状态码 | 说明 |
---|---|
1xx | 响应中,临时状态码。表示请求已经接受,告诉客户端应该继续请求或者如果已经完成则忽略 |
2xx | 成功,表示请求已经被成功接收,处理已完成 |
3xx | 重定向,重定向到其它地方,让客户端再发起一个请求以完成整个处理 |
4xx | 客户端错误,处理发生错误,责任在客户端,如:客户端的请求一个不存在的资源,客户端未被授权,禁止访问等 |
5xx | 服务器端错误,处理发生错误,责任在服务端,如:服务端抛出异常,路由出错, HTTP版本不支持等 |
4、Maven
4.1、什么是Maven?
为什么要学习Maven
- 在JavaWeb开发中,需要使用大量的jar包,需要手动去导入
- 如何能够让一个工具自动帮助我导入和配置这个jar包;由此,Maven就诞生了!
Maven是Apache旗下的一个开源项目,是一款用于管理和构建java项目的工具
Maven的核心思想:约定大于配置
Apache 软件基金会,成立于1999年7月,是目前世界上最大的最受欢迎的开源软件基金会,也是一个专门为支持开源项目而生的非盈利性组织
Maven的作用
- 依赖管理:方便快捷的管理项目依赖的资源(jar包),避免版本冲突问题
- 统一项目结构:提供标准、统一的项目结构
- 项目构建:maven提供了标准的、跨平台(Linux、Windows、MacOS) 的自动化项目构建方式
- 清理、编译、打包、测试、发布
Maven模型
- 项目对象模型 (Project Object Model)
- 依赖管理模型(Dependency)
- 坐标,就是资源(jar包)的唯一标识,通过坐标可以定位到所需资源(jar包)位置(gav)
- 构建生命周期/阶段(Build lifecycle & phases)
之前在项目中需要jar包时,直接就把jar包复制到项目下的lib目录,而现在书写在pom.xml文件中的坐标又是怎么能找到所要的jar包文件的呢?Maven仓库
Maven仓库
仓库:用于存储资源,管理各种jar包
仓库的本质就是一个目录(文件夹),这个目录被用来存储开发中所有依赖(就是jar包)和插件
Maven仓库分为:
- 本地仓库:自己计算机上的一个目录(用来存储jar包)
- 中央仓库:由Maven团队维护的全球唯一的。仓库地址
- 远程仓库(私服):一般由公司团队搭建的私有仓库
当项目中使用坐标引入对应依赖jar包后,首先会查找本地仓库中是否有对应的jar包
- 如果有,则在项目直接引用
- 如果没有,则去中央仓库中下载对应的jar包到本地仓库
还可以搭建远程仓库(私服),将来jar包的查找顺序则变为: 本地仓库 —> 远程仓库—> 中央仓库
4.2、下载安装并配置Maven
下载之后解压缩到指定位置即可;
目录结构如下:
- bin目录 : 存放的是可执行命令
- conf目录 :存放Maven的配置文件
- lib目录 :存放Maven依赖的jar包。(Maven也是基于java开发的,所以它也依赖其他的jar包)
配置环境变量
-
在系统变量处新建一个变量MAVEN_HOME
- MAVEN_HOME环境变量的值,设置为maven的解压安装目录
-
在Path中进行配置
- PATH环境变量的值,设置为:
%MAVEN_HOME%\bin
- PATH环境变量的值,设置为:
-
使用
mvn -version
测试是否配置成功(在cmd窗口中)
配置本地仓库
在maven安装目录中新建一个目录:MavenRepository
(本地仓库,用来存储jar包)
进入到conf目录下修改settings.xml
配置文件,大概50行左右
<!-- maven本地仓库地址 -->
<localRepository>E:\Environment\apache-maven-3.8.1\MavenRepository</localRepository>
配置阿里云私服
由于中央仓库在国外,所以下载jar包速度可能比较慢,而阿里公司提供了一个远程仓库,里面基本也都有开源项目的jar包
进入到conf目录下修改settings.xml
配置文件,大概160行左右,在<mirrors></mirrors>标签中添加 mirror 子节点:
<!-- 阿里云(私服)远程仓库 -->
<mirror>
<id>aliyunmaven</id>
<mirrorOf>*</mirrorOf>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
4.3、IDEA中集成Maven
- 启动IDEA
- 创建一个mavenweb项目
- 等待项目初始化完毕
注:项目创建成功后,看下Maven的配置!
4.4、创建一个普通的Maven项目
Maven项目的目录结构
first-maven-project
— src (源代码目录和测试代码目录)
--- main (源代码目录) --- java (源代码java文件目录) --- resources (源代码配置文件目录) --- test (测试代码目录) --- java (测试代码java目录) --- resources (测试代码配置文件目录)
— target (编译、打包生成文件存放目录)
注:在web应用下才会出现
4.5、标记文件夹功能
4.6、在IDEA中配置tomcat
解决警告问题
必要的配置:为什么会有这个问题?访问一个网站,需要指定文件夹名字!
启动
4.7、pom文件
POM (Project Object Model) :指的是项目对象模型,用来描述当前的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">
<!-- POM模型版本 -->
<modelVersion>4.0.0</modelVersion>
<!-- 当前项目坐标 GAV-->
<groupId>com.baidu</groupId>
<artifactId>javaweb-01-maven</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 项目的打包方式
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>
</dependency>
</dependencies>
</project>
maven由于它的约定大于配置,之后可以会遇到我们写的配置文件,无法被导出或者生效的问题,解决方案:
<!--在build中配置resources,来防止我们资源导出失败的问题-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
4.8、依赖管理
依赖传递和依赖排除
直接依赖:在当前项目中通过依赖配置建立的依赖关系
间接依赖:被依赖的资源如果依赖其他资源,当前项目间接依赖其他资源
排除依赖:指主动断开依赖的资源。(被排除的资源无需指定版本)
<!-- 排除依赖, 主动断开依赖的资源 -->
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
依赖范围
在项目中导入依赖的jar包后,默认情况下,可以在任何地方使用
作用范围:
- 主程序范围有效(main文件夹范围内)
- 测试程序范围有效(test文件夹范围内)
- 是否参与打包运行(package指令范围内)
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
给junit依赖通过scope标签指定依赖的作用范围。 那么这个依赖就只能作用在测试环境,其他环境下不能使用
scope标签的取值范围:
scope值 | 主程序 | 测试程序 | 打包(运行) | 范例 |
---|---|---|---|---|
compile(默认) | Y | Y | Y | log4j |
test | - | Y | - | junit |
provided | Y | Y | - | servlet-api |
runtime | - | Y | Y | jdbc驱动 |
生命周期
Maven的生命周期就是为了对所有的构建过程进行抽象和统一。 描述了一次项目构建,经历哪些阶段
Maven对项目构建的生命周期划分为3套(相互独立):
- clean:清理工作
- default:核心工作。如:编译、测试、打包、安装、部署等
- site:生成报告、发布站点等
常用生命周期命令
- clean:移除上一次构建生成的文件
- compile:编译项目源代码
- test:使用合适的单元测试框架运行测试(junit)
- package:将编译后的文件打包,如:jar、war等
- install:安装项目到本地仓库
生命周期的顺序是:clean —> validate —> compile —> test —> package —> verify —> install —> site —> deploy
注:在同一套生命周期中,在执行后面的生命周期时,前面的生命周期都会执行
执行可通过idea可视化界面操作或者打开cmd窗口输入:mvn clean/test/package...
5、Servlet
5.1、什么是Servlet?
-
Servlet是Sun公司开发动态web的一门技术
-
Servlet是JavaEE规范之一,规范就是接口
-
Servlet是JavaWeb三大组件之一,三大组件分别是:Servlet程序、Filter过滤器、Listener监听器
-
Servlet是运行在服务器上的一个java小型程序,它可以接收客户端发送过来的请求,并响应数据给客户端
-
sun在这些API中提供一个接口:Servlet,如果想开发一个Servlet程序,只需要完成两个小步骤
- 编写一个类,实现Servlet接口
- 把开发好的Java类部署到web服务器中
把实现了Servlet接口的Java程序叫做:Servlet
Servlet可以动态生成HTML内容从而对客户端进行响应
5.2、Servlet生命周期
1.执行Servlet构造器方法
2.执行init初始化方法
第一、二步,是在第一次访问的时候创建Servlet程序会调用
3.执行service方法
第三步,每次访问都会调用
4.执行destroy销毁方法
第四步,在web工程停止的时候调用
5.3、Servlet类的继承体系
Servlet接口有两个默认的实现类:HttpServlet、GenericServlet
- Servlet接口,只负责定义Servlet程序的访问规范
- GenericServlet类实现了Servlet接口,做了很多实现,并持有一个ServletConfig类的引用,并对ServletConfig的使用做了一些方法
- 该类抽取类实现了service()方法,并实现了请求的分发处理
- 我们只需要根据自己的业务需要去重写doGet或doPost方法即可
-
构建一个普通Maven项目,删除里面的src目录,在这个项目里面建立Module;这个空的工程就是Maven主工程
-
环境优化
- 修改web.xml为最新的
- 将maven的结构搭建完整
-
编写一个servlet程序
- 编写一个普通类
- 实现Servlet接口,直接继承HttpServlet,根据自己的业务需要去重写doGet或doPost方法即可!
public class HelloServlet extends HttpServlet { //由于get或者post只是请求实现的不同的方式,可以相互调用,因为业务逻辑都一样 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //ServletOutputStream outputStream = resp.getOutputStream(); PrintWriter writer = resp.getWriter();//响应流 writer.println("hello,Servlet!"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doPost(req, resp); } }
-
编写servlet的映射
为什么需要映射:写的是java程序,但要通过浏览器访问,而浏览器需要连接web服务器,所以要在web服务注册我们写的servlet。还需要给他能够访问的路径
<!--servlet标签给Tomcat配置Servlet程序-->
<servlet>
<!--servlet-name标签 给Servlet程序起一个别名(一般是类名)-->
<servlet-name>hello</servlet-name>
<!--servlet-class是Servlet程序的全类名-->
<servlet-class>com.zhang.servlet.HelloServlet</servlet-class>
<!--init-param是初始化参数!-->
<init-param>
<!--参数名-->
<param-name>username</param-name>
<!--参数值-->
<param-value>root</param-value>
</init-param>
</servlet>
<!--servlet-mapping标签给Servlet程序配置访问地址-->
<servlet-mapping>
<!--servlet-name标签的作用是告诉服务器,我当前配置的地址给那个Servlet程序使用-->
<servlet-name>hello</servlet-name>
<!--url-pattern标签配置访问地址-->
<!--
/ 斜杆在服务器解析的时候,表示地址为:http://ip地址:端口号/工程路径
/hello 表示地址为:http://ip地址:端口号/工程路径/hello
配置的url-pattern(资源路径)就是你要访问的地址!
-->
<url-pattern>/hello</url-pattern>
</servlet-mapping>
-
配置tomcat
注:配置项目发布的路径就OK了
-
启动测试
5.4、Servlet原理
Servlet是由Web服务器调用,web服务器是在收到浏览器请求之后会:
5.5、ServletContext
- ServletContext类是一个接口,它表示Servlet上下文对
- 一个web工程,只有一个ServletContext对象实例
- Servlet对象是一个域对象
- ServletContext是在web工程部署启动的时候创建,在web工程停止的时候销毁!
1、共享数据
- 我在这个Servlet保存的数据,可以在另外一个servlet拿到
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();
context.setAttribute("username","张三"); // 将一个数据保存着在servletContext 键=>值对
}
}
public class GetServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
String username = (String) context.getAttribute("username");
resp.setContentType("text/html;charset=UTF-8");
resp.getWriter().println("名字是:" + username);
}
}
<servlet>
<servlet-name>getServlet</servlet-name>
<servlet-class>com.zhang.servlet.GetServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>getServlet</servlet-name>
<url-pattern>/getServlet</url-pattern>
</servlet-mapping>
测试访问结果
2、获取初始化参数
<!--配置一些web应用初始化参数-->
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://127.0.0.1:3306//mybatis</param-value>
</context-param>
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
String url = servletContext.getInitParameter("url");
resp.getWriter().println(url);
}
3、请求转发
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
System.out.println("进入了s3");
// RequestDispatcher dispatcher = servletContext.getRequestDispatcher("/gp"); //转发的请求路径
// dispatcher.forward(req,resp); //调用forward实现请求转发
servletContext.getRequestDispatcher("/s2").forward(req,resp);
}
4、读取资源文件
Properties
- 在Java目录下新建properties
- 在resources目录下新建properties
发现:都被打包了同一个路径下,classes,俗称这个路径为classpath
需要一个文件流:
username = root
password = 123456
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");
Properties prop = new Properties();
prop.load(is);
String user = prop.getProperty("username");
String pwd = prop.getProperty("password");
resp.getWriter().println(user + ":" + pwd);
}
访问测试即可ok;
5.6、HttpServletRequest
- 获取请求数据:HttpServletRequest
- 设置响应数据:HttpServletResponse
每次只要有请求进入Tomcat服务器,Tomcat服务器就会把请求过来的HTTP协议信息解析好封装到Request对象中然后传递到service 方法( doGet和doPost)中给我们使用。可以通过HttpServletRequest对象,从而获取到所有请求的信息
1、获取前端传递的参数
2、请求转发
方法名称 | 说明 |
---|---|
String getParameter(String name) | 根据表单组件名称获取提交数据 |
String[ ] getParameterValues(String name) | 获取表单组件对应多个值时的请求数据 |
// 后台接收中文乱码问题
req.setCharacterEncoding("utf-8");
String username = req.getParameter("username");
String pwd = req.getParameter("pwd");
String[] hobbys = req.getParameterValues("hobbys");
System.out.println("====================");
System.out.println(username);
System.out.println(pwd);
System.out.println(Arrays.toString(hobbys));
System.out.println("====================");
// 通过请求转发
req.getRequestDispatcher("/success.jsp").forward(req, resp);
5.7、HttpServletResponse
HttpSerletRespise类和HttpServletRequest类一样。每次请求进来Tomcat 服务器都会创建一个Response对象传递给 Servlet 程序去使用。HttpServletRequest 表示请求过来的信息,HttpServletResponse 表示所有响应的信息,如果需要设置返回给客户端的信息,可以通过HttpServletResponse对象来进行设置
1、简单分类
负责向浏览器发送数据的方法
// 字节流 常用于下载(传递二进制数据)
ServletOutputStream getOutputStream() throws IOException;
// 字符流 常用于回传字符串(常用)
PrintWriter getWriter() throws IOException;
注:两个流同时只能使用一个,如果使用了字节流,就不能再使用字符流,反之亦然
2、常见应用
- 向浏览器输出消息
- 下载文件
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1. 获取下载文件的路径
String realPath = "E:\\Program Files (x86)\\IntelliJ IDEA Project\\Jsp\\javaweb-01-servlet\\target\\classes\\6.jpeg";
System.out.println("下载的文件的路径:" + realPath);
//2. 下载的文件名是什么?
String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1);
//3. 设置想办法让浏览器支持(Content-disposition)下载我们需要的东西 中文文件名URLEncoder.encode编码,否则可能乱码
resp.setHeader("Content-Disposition", "attachment;filename" + URLEncoder.encode(fileName,"utf-8"));
//4. 获取下载文件的输入流
FileInputStream in = new FileInputStream(realPath);
//5. 创建缓冲区
int len = 0;
byte[] buffer = new byte[1024];
//6. 获取OutputStream对象
ServletOutputStream out = resp.getOutputStream();
//7. 将FileOutStream流写入到buffer缓冲区,使用OutputStream将缓冲区中的数据输出到客户端
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
//8. 关闭流
in.close();
out.close();
}
3、实现重定向
B一个web资源收到客户端A请求后,B它会通知A客户端区访问另外一个web资源C,这个过程叫重定向
常见场景:用户登录
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.sendRedirect("/login.jsp"); // 重定向
}
面试题:请你聊聊重定向和转发的区别?
请求转发是指:服务器收到请求后,从一次资源跳转到另一个资源的操作叫请求转发
请求重定向是指:客户端给服务器发请求,然后服务器告诉客户端说。我给你一个地址。你去新地址访问。叫请求重定向
请求转发的特点:
- 浏览器地址栏无变化
- 它们是一次请求
- 所以他们共享request域中的数据
- 可以转发到WEB-INF目录下
- 不可以访问工程外的资源
请求重定向的特点:
- 浏览器地址栏会发生变化
- 两次请求
- 不共享Request域中数据
- 不能访问WEB-INF目录下的资源
- 可以访问工程外的资源
6、Cookie、Session
6.1、什么是会话?
- 在日常生活当中,会话指的就是谈话、交谈
- 在web开发当中,会话指的就是浏览器与服务器之间的一次连接,我们就称为一次会话,在一次会话当中,是可以包含多次请求和响应的
- 浏览器不能关闭、服务器不能断开,否则该次会话结束!
7.2、保存会话的两种技术
- Cookie(客户端会话跟踪技术)
- 数据存储在客户端浏览器当中
- Session(服务端会话跟踪技术)
- 数据存储在储在服务端
常见场景:网站登录之后,你下次不用登录了,第二次访问直接就登录上去了
- 从请求中拿到Cookie信息
- 服务器响应给客户端
Cookie[] cookies = req.getCookies(); // 获取Cookie
cookie.getName(); // 获取Cookie的key
cookie.getValue(); // 获得Cookie的值
new Cookie("lastLoginTime", System.currentTimeMillis() + ""); // 新建一个Cookie
cookie.setMaxAge(24 * 60 * 60); // 设置Cookie的有效期
resp.addCookie(cookie); // 响应给客户端一个Cookie
- 响应头 Set-Cookie :设置Cookie数据的
- 请求头 Cookie:携带Cookie数据的
删除Cookie:
- 不设置有效期,关闭浏览器,自动失效;
- 设置有效期时间为0;
优缺点
- 优点:HTTP协议中支持的技术(像Set-Cookie 响应头的解析以及 Cookie 请求头数据的携带,都是浏览器自动进行的,是无需我们手动操作的)
- 缺点:
- 移动端APP(Android、IOS)中无法使用Cookie
- 不安全,用户可以自己禁用Cookie
- Cookie不能跨域
7.4、Session
优缺点
- 优点:Session是存储在服务端的,安全
- 缺点: 服务器集群环境下无法直接使用Session
- 移动端APP(Android、IOS)中无法使用Cookie
- 用户可以自己禁用Cookie
- Cookie不能跨域
Session和Cookie的区别:
会话自动过期:web.xml配置
<!--设置session默认注销的时间-->
<session-config>
<!-- 15分钟后session自动失效,以分钟为单位-->
<session-timeout>15</session-timeout>
</session-config>
7、JSP (Java Server Pages)
7.1、什么是Jsp?
JSP (Java Server Pages):Java服务器端页面,也和Servlet一样,用于动态web技术!
jsp的主要作用是代替Servlet程序回传html页面的数据
因为Servlet程序回传html页面数据是一件非常繁锁的事情。开发成本和维护成本都极高
最大的特点:
- 写Jsp就像在写HTML,较为方便
- 区别:
- HTML只给用户提供静态的数据
- Jsp页面中可以嵌入Java代码,为用户提供动态数据
7.2、Jsp原理
Jsp到底是如何执行的?
- 在代码层面和上看不出任何问题!
- 服务器内部工作
- tomcat中有一个work目录;
- IDEA中使用tomcat的会在IDEA的tomcat中产生一个work目录
发现页面转变成了Java程序!
浏览器想服务器发送请求,不管访问什么资源,实际上都是在访问Servlet!
Jsp最终也会被转换成为一个Java类,Jsp本质上就是一个Servlet程序
//初始化
public void _jspInit() {
}
//销毁
public void _jspDestroy() {
}
//JspService
public void _jspService(HttpServletRequest request, HttpServletResponse response)
7.3、JSP基础语法
任何语言都有自己的语法,JSP 作为Java技术的一种应用, 它拥有一些自己扩充的语法,Java中的所有语法都支持!
jsp表达式
<%--jsp表达式 作用:用来将程序的输出,输出到客户端
<%=变量或者表达式%>
--%>
<%=new java.util.Date()%>
jsp脚本片段
<%--jsp脚本片段--%>
<%
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
}
out.print("<h1>Sum=" + sum + "</h1>");
%>
<!--打印5次hello,world!-->
<%
for (int i = 1; i <= 5; i++) {
%>
<h2>hello,world!<%=i%></h2>
<%
}
%>
jsp声明
<%--jsp声明--%>
<%!
static {
System.out.println("Loading Servlet!");
}
private int globalVar = 0;
public void sayHello(){
System.out.println("hello,jsp!");
}
%>
jsp声明:会被编译到Jsp生成Java的类中!其他的,就会生成到_jspSServlet方法中!
在Jsp中,嵌入Java代码即可!
<%%> <!--脚本片段 -->
<%=%> <!--表达式 -->
<%!%> <!--声明 -->
<!--我是html的注释--> <!--注释 -->
<%--我是Jsp的注释--%>
Jsp的注释,不会再客户端显示,html注释会在客户端显示!
小结:
7.4、JSP指令
<%@ page language="java" import="java.util.*,java.text.*" contentType="text/html; charset=utf-8" %>
- language:指定JSP页面使用的脚本语言
- import:通过该属性来引用脚本语言中使用到的类文件
- contentType:用来指定JSP页面所采用的编码方式
<%--设置页面编码--%>
<%@ page pageEncoding="utf-8" %>
<%--定制错误页面--%>
<%@ page errorPage="error/500.jsp"%>
<%--显示的声明这是一个错误页面--%>
<%@ page isErrorPage="true"%>
<%--设置公共部分--%>
<%@ include file="common/header.jsp"%>
<%--设置公共部分--%>
<%@ include file="common/header.jsp"%>
<h2 class="content">页面主体</h2>
<%--设置公共部分--%>
<%@ include file="common/footer.jsp"%>
<%--jsp标签--%>
<jsp:include page="common/header.jsp"/>
<h2 class="content">页面主体</h2>
<jsp:include page="common/footer.jsp"/>
两者区别:
- @include会将两个页面合二为一
- jsp标签:拼接页面,本质上还是三个
7.5、9大内置对象
- pageContext
- request 请求对象
- Response 响应对象
- Session 会话对象
- Application ServletContext对象
- config ServletConfig对象
- out jsp输出流对象
- page 指向当前jsp的对象
- exception 异常对象
<%
//存放数据
//(PageContextImpl类) 保存的数据值能在一个页面中有效
pageContext.setAttribute("name1", "小张");
// (HttpServletRequest类) 一次请求内有效
request.setAttribute("name2", "小赵");
//(HttpSession类) 一个会话范围内有效(打开浏览器访问服务器,直到关闭浏览器) 默认30分钟
session.setAttribute("name3", "小林");
// (ServletContext类) 整个web工程范围内都有效(只要web工程不停止,所有数据都在)
application.setAttribute("name4", "小李");
%>
<%
//通过pageContext取出保存的值,通过寻找的方式来找
//从底层到高层(作用域): page-->request-->session-->application
//JVM:双亲委派机制
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>
request:客户端向服务器发送请求,产生的数据,用户看完就没用了,比如:新闻,用户看完没用的!
session:客户端向服务器发送请求,产生的数据,用户用完一会还有用,比如:购物车
application:客户端向服务器发送请求,产生的数据,一个用户用完了,其他用户还可能使用,比如:聊天数据
7.6、Jsp标签、JSTL标签库、EL表达式
<!-- JSTL表达式依赖 -->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>
<!-- stardust标签库 -->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
EL表达式:${ }
- 获取数据
- 执行运算
- 获取web开发的常用对象
Jsp标签
<jsp:include page="header.jsp"></jsp:include>
<%--http:localhost:8082/jsp04.jsp?name=zhangtao&age=19--%>
<jsp:forward page="jsp04.jsp">
<%-- 携带参数--%>
<jsp:param name="name" value="zhang"/>
<jsp:param name="age" value="19"/>
</jsp:forward>
JSTL表达式
JSTL标签库的使用就是为了弥补HTML标签的不足;它自定义许多标签,可以供我们使用,标签的功能和java代码一样!
核心标签
JSTL标签库使用步骤
- 引入对应的taglib
- 使用其中的方法
- 在tomcat也需要引入jstl的包,否则会报错;jstl解析错误
c:if
<form action="coreif.jsp" method="get">
<%--
EL表达式获取表单的数据
${param.参数名}
--%>
<input type="text" name="username" value="${param.username}">
<input type="submit" value="登录"/>
</form>
<%--判断,如果提交的用户名是管理员,则登录成功--%>
<c:if test="${param.username=='admin'}" var="isAdmin">
<c:out value="管理员,欢迎你!"/>
</c:if>
<%--自闭和标签--%>
<c:out value="${isAdmin}"/>
c:choose c:when
<%--定义一个变量score,值为85--%>
<c:set var="score" value="98"/>
<c:choose>
<c:when test="${score>=90}">你的成绩为优秀!</c:when>
<c:when test="${score>=80}">你的成绩为一般!</c:when>
<c:when test="${score>=70}">你的成绩为良好!</c:when>
<c:when test="${score<=60}">你的成绩为一般般,继续努力!</c:when>
</c:choose>
c:forEach
<%
ArrayList<String> person = new ArrayList<>();
person.add("张三");
person.add("李四");
person.add("王五");
person.add("赵六");
request.setAttribute("list", person);
%>
<%--
var,每一次遍历处理的变量
items,要遍历的对象
begin, 哪里开始
end, 到哪里
step, 步长
--%>
<c:forEach var="person" items="${list}">
<c:out value="${person}"/><br/>
</c:forEach>
<hr/>
<c:forEach var="person" items="${list}" begin="1" end="3" step="1">
<c:out value="${person}"/><br/>
</c:forEach>
8、Filter过滤器
8.1、什么是过滤器?
- Filter过滤器它是JavaWeb的三大组件之一。三大组件分别是:Servlet程序、Listener 监听器、Filter 过滤器
- Filter过滤器它是JavaEE的规范。也就是接口
- Filter 过滤器它的作用是:拦截请求,过滤响应
拦截请求常见的应用场景有
权限检查
日记操作
事务管理
8.2、Filter过滤器的使用步骤
- 编写一个类实现Filter接口
- 实现过滤方法 doFilter()
- 到web.xml去配置Filter的拦截路径
代码示例:
public class AdminFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
HttpSession session = req.getSession();
Object user = session.getAttribute("user");
// 如果等于空,说明用户还没有登录!
if (user == null) {
HttpServletResponse resp = (HttpServletResponse) servletResponse;
resp.sendRedirect(req.getContextPath() + "/login.jsp");
} else {
// 让程序继续往下访问用户的目标资源
filterChain.doFilter(servletRequest, servletResponse);
}
}
}
<!--filter标签用于配置一个Filter过滤器-->
<filter>
<!--给Filter起一个别名-->
<filter-name>AdminFilter</filter-name>
<!--配置Filter的全类名-->
<filter-class>com.zhang.filter.AdminFilter</filter-class>
<init-param>
<param-name>username</param-name>
<param-value>root</param-value>
</init-param>
<init-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3303/flower</param-value>
</init-param>
</filter>
<!--filter-mapping 配置Filter过滤器的拦截路径-->
<filter-mapping>
<!--表示当前的拦截路径给那个filter使用-->
<filter-name>AdminFilter</filter-name>
<!--
url-pattern 表示:http://ip:port/工程路径/ 映射到IDEA的web目录
/admin/* 表示请求地址到 http://ip:port/工程路径/admin/*
-->
<url-pattern>/admin/*</url-pattern>
</filter-mapping>
8.3、Filter的生命周期
1.构造器方法
2.init初始化方法
第1、2步,在web工程启动的时候执行(Filter已经创建)
3.doFilter过滤方法
第3步,每次拦截请求,就会执行
4.destroy销毁
第4步,停止web工程的时候,就会执行(停止web工程的时候,也会销毁过滤器
8.4、FilterChain 过滤器链
FilterChain:多个过滤器如何一起工作
FilterChain()方法的作用:
执行下一个Filter过滤器(如果有)
执行目标资源(如果没有)
在多个Filter过滤器执行的时候,他们执行的优先顺序是由它们在web.xml中从上到下配置的顺序决定!
多个Filter过滤器执行的特点:
1.所有Filter的目标资源默认都执行在同一个线程中
2.多个Filter共同执行的时候,它们都使用同一个Request对象
9、Ajax
9.1、JSON
- JSON(JavaScript Object Notation)是一种轻量级的数据交换格式
- 采用完全独立于编程语言的文本格式来存储和表示数据
- 简洁和清晰的层次结构使得 JSON 成为理想的数据交换格式。
- 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率
注:轻量级指的是跟xml做比较!
数据交换指的是客户端和服务器之间业务数据的传递格式
9.2、JSON的定义
json是由键值对组成,并且由花括号(大括号) 包裹。每个键由引号引起来,键和值之间使用冒号进行分隔,多组键值对之间进行逗号进行分隔
let jsonStr = {
"id": 1001,
"name": '晓琳',
"age": 19,
"hobby": ['girl', 'code', 'swim', 'game'],
}
json的两个常用方法
JSON.stringify() // 把Js对象转换成为json字符串
JSON.parse() // 把json字符串转换成为Js对象
let jsonStr = JSON.stringify(jsonObj); // 把js对象转换成为json字符串
console.log(jsonStr);
let parseJsonObj = JSON.parse(jsonStr); // 把json字符串解析成为json对象
console.log(parseJsonObj);
9.3、JSON在Java中的使用
需导入fastjson1.2.78.jar包
JavaBean和Json的互转
public void test1() {
// JavaBean和Json的互转
Person p1 = new Person(1001, "admin");
// toJSONString() 将Java对象转换成为json字符串
String personJsonStr = (String) JSON.toJSONString(p1);
System.out.println(personJsonStr);
// parseObject() 将json字符串转换成为Java对象
Person person = JSON.parseObject(personJsonStr, Person.class);
System.out.println(person);
}
List和Json的互转
@Test
public void test2() {
// List和Json的互转
List<Person> list = new ArrayList<>();
Person p1 = new Person(1001, "admin");
Person p2 = new Person(1002, "小张");
Person p3 = new Person(1003, "晓琳");
list.add(p1);
list.add(p2);
list.add(p3);
// 将list集合转换成为Json字符串
String listStr = JSON.toJSONString(list);
System.out.println(listStr);
// 将Json字符串转换为list集合 匿名内部类
ArrayList<Person> personList = JSON.parseObject(listStr, new TypeReference<ArrayList<Person>>() {});
System.out.println(personList);
}
Map和Json的互转
@Test
public void test3() {
// Map和Json的互转
Map<Integer, Person> map = new HashMap<>();
map.put(1, new Person(100, "晓琳"));
map.put(2, new Person(102, "小张"));
map.put(3, new Person(103, "小赵"));
// 将map转换成为json字符串
String mapStr = JSON.toJSONString(map);
System.out.println(mapStr);
// 将json字符串转换成为map
HashMap<Integer, Person> personHashMap = JSON.parseObject(mapStr, new TypeReference<HashMap<Integer, Person>>() {
});
System.out.println(personHashMap);
}
9.4、原生Ajax(XMLHttpRequest)
什么是Ajax请求?
- Ajax即"Asynchronous Javascript And XML"(异步JavaScript和XML),是指一种创建交互式网页应用的网页开发技术
- ajax是一种浏览器通过js异步发起请求。局部更新页面的技术
- Ajax请求的局部更新,浏览器地址栏不会发送变化
- 局部更新不会舍弃原来页面的内容
使用原生Ajax技术实现异步交互
五步使用法:
1.创建XMLHTTPRequest对象
2.使用open方法设置和服务器的交互信息
3.设置发送的数据,开始和服务器端交互
4.注册事件
5.更新界面
get请求:
<button>点击发送请求</button>
<div class="result">
</div>
<script>
document.querySelector("button").addEventListener("click", () => {
let uname = "小昭"
// 1.创建对象
const xhr = new XMLHttpRequest()
// 2.初始化 设置请求方法 和 url
xhr.open("POST", `http://localhost:8000/server?name=${uname}`)
// 设置请求头
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
// 设置响应体数据的类型
xhr.responseType = "json"
// 超时设置 超过2s取消发送请求
xhr.timeout = 2000
// 超时回调
xhr.ontimeout = () => alert("请求超时 请稍后重试")
// 网络异常回调
xhr.onerror = () => alert("你的网络似乎出现了一些问题 请稍后重试")
xhr.onreadystatechange = () => {
// readyState = 4 请求已完成并且响应已经准备好
// status = 200 说明可以接收到响应数据,表示服务端已经返回所有的结果
if (xhr.readyState === 4 && xhr.status === 200) {
// 处理结果
console.log(xhr.status); // 状态码
console.log(xhr.statusText); // 状态字符串
console.log(xhr.getAllResponseHeaders()); // 所有响应头
console.log(xhr.response); // 响应体
document.querySelector(".result").innerHTML = xhr.response
}
}
// 3.发送请求
xhr.send(`name:${uname}&pwd:${upwd}`)
})
</script>
9.5、使用jQuery实现Ajax
ajax格式:
$.ajax方法
url 表示请求的地址
type 表示请求的方式GET/POST请求
data 表示发送给服务器的数据
两种格式:
1.name=value&name=value
2.{key:value}
dataType 预期服务器端返回的数据类型
常用的数据类型有:text(纯文本)、xml(xml数据)、json(json对象)
contentType 设置请求头
success 请求成功时调用次函数
error 请求失败时调用次函数
示例:
<div class="container">
<button class="btn btn-danger">POST</button>
<button class="btn btn-info">通用型(ALL)</button>
</div>
<script>
$("button").eq(0).click(() => {
$.post("http://localhost:8000/user", { uname: "小昭", age: 18 }, function (data) {
console.log(data)
})
})
$("button").eq(1).click(() => {
$.ajax({
// 请求地址
url: "http://localhost:8000/user",
// 请求类型
type: "POST",
// 请求参数
data: { uname: "小昭", age: 18 },
// 响应体结果
dataType: "json",
// 成功回调
success: function (data) {
console.log(data)
},
// 超时时间
timeout: 2000,
// 失败回调
error: function () {
console.log("出错了");
},
// 头信息
headers: {
uname: "Mr zhang"
}
})
// 表单序列化 serialize(),serialize()可以把表单中所有表单项的内容都获取到
// 并以name=value&name=value的形式进行拼接
$("#submit").click(function () {
// 序列化表单内容为字符串,用于 Ajax 请求!
// alert($("#form1").serialize());
$.getJSON("http://localhost:8080/07_json_ajax/ajax", "action=jquerySerialize&" + $("#form1").serialize(), function (data) {
$("#msg").html(" serialize方法 编号:" + data.id + ",姓名:" + data.name);
});
})
})
</script>
同源策略
同源策略(Same-Origin Policy)最早由Netscape 公司提出,是浏览器的一种安全策略
- 同源:协议、域名、端口号必须完全相同
- 违背同源策略就是跨域!
如何解决跨域?
CORS
CORS是什么?
CORS (Cross-Origin Resource Sharing),跨域资源共享。CORS 是官方的跨域解决方案,它的特点是不需要在客户端做任何特殊的操作,完全在服务器中进行处理,支持get和post请求。跨域资源共享标准新增了一组HTTP首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源
CORS 是如何工作的?
CORS是通过设置一个响应头来告诉浏览器,该请求允许跨域,浏览器收到该响应以后就会对响应放行
CORS的使用
主要是服务器端的设置:
<script>
document.querySelector("button").addEventListener("click", () => {
$.get("http://localhost:8000/cors-server", function (resp) {
console.log(resp)
})
})
// 服务器端设置
app.get("/cors-server", (req, resp) => {
// 设置响应头 允许跨域
resp.setHeader("Access-Control-Allow-Origin", "*") // 所有地址地址都可跨域请求
// resp.setHeader("Access-Control-Allow-Origin", "http://localhost:8000") // 只有该地址才可跨域请求
resp.setHeader("Access-Control-Allow-Headers", "*") // 所有请求头都可跨域请求
resp.setHeader("Access-Control-Allow-Method", "*") // 所有请求方式都可跨域请求
</script>