JavaWeb

JavaWeb

基本概念

前言

  • 静态web
  • 动态web
    • 技术栈:Servlet/ISP,ASP,PHP

几乎所有网站都是动态Web,即不同的人会接收到不同的信息。

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

web应用程序

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

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

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

静态web

  • *htm, *.html, 常见网页后缀,如果服务器上一直存在这些东西,就可以直接进行读取。

page1

  • 静态web存在缺点
    • Web页面无法动态更新
      • 轮播图,点击特效:伪动态
      • JavaScript
      • VBScript
    • 无法和数据库交互(数据无法持久化,用户无法交互)

动态web

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

activeWeb

缺点:

  • 假如服务器的动态Web资源出现了错误,我们需要重新编写后台程序, 重新发布;
    • 停机维护

优点:

  • Web页面可以动态更新
  • 可以与数据库交互(数据持久化:注册,商品信息… …)

Web服务器

技术讲解

ASP:

  • 微软:国内最早流行;
  • 在HTML中嵌入了VB的脚本,ASP + COM;
  • 在ASP开发中,基本一个页面都有几千行业务代码,页面极其换乱
  • 维护成本高
  • C#
  • IIS

PHP:

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

JSP/Servlet:

B/S:浏览器和服务器

C/S:客户端和服务器

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

web服务器

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

IIS:

  • 微软开发;ASP…, Windows中自带

Tomcat:

  • 免费
  • Web应用服务器,轻量级
  • 适合初学

下载Tomcat:

  1. 安装or解压
  2. 了解配置文件及目录结构
  3. 作用

Tomcat

安装Tomcat

到官网安装

Tomcat启动和配置

启动:startup.bat 关闭:shutdown.bat

访问测试:http://localhost:8080/

可能遇到问题:

  1. java环境变量没有配置
  2. 闪退问题:需要配置兼容性
  3. 乱码问题:配置文件设置

配置

文件:conf/server.xml

配置启动的端口号

1
2
3
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
  • Tomcat的默认端口号为:8080
  • mysql: 3306
  • https: 443
  • http: 80

配置主机名称:

1
2
<Host name="localhost"  appBase="webapps"
unpackWARs="true" autoDeploy="true">
  • 默认主机名为:localhost-> 127.0.0.1
  • 默认网站应用存放的位置:webapps

网站如何进行访问的?

  1. 输入域名;

  2. 检查本机的C:/Windows/System32/drivers/etc/hosts配置文件下有没有这个域名映射;

    1. 有:直接返回对应的ip地址, 在这个地址中,有我们需要访问的web程序,可以直接访问

      1
      127.0.0.1 www.baidu.com
    2. 没有:去DNS服务器找,如果没有,返回404

    find_web

    发布Web网站

    • 将自己写的网站放到服务器(tomcat)中指定的web应用的文件夹(webapps)下,就可以访问了

    • 网站应有的结构

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      --webapps: Tomcat服务器的目录
      -ROOT
      -study:网站的目录名
      - WEB-INF
      -classes:java程序
      -web.xml
      -lib:web应用所依赖的jar包
      - index.html:默认的首页
      - static
      -css
      -style.css
      -js
      -img
      ...

HTTP

什么是HTTP?

HTTP(超文本传输协议)请求-响应协议,通常运行在TCP之上。

  • 文本:html, 字符串 …
  • 超文本:图片,音乐, 视频, 定位, 地图… …
  • 默认端口:80

HTTPS:

  • 默认端口:443
  • 安全的

两个时代

  • http1.0
    • HTTP/1.0:客户端可以与web服务器连接后,只能获得一个web资源,断开连接,重复连接很消耗流量
  • http2.0
    • HTTP/1.1:连接后可以获得多个web资源

HTTP请求

  • 客户端-> 发请求-> 服务器

百度:

1
2
3
4
5
6
General:
Request URL: https://www.baidu.com/ 请求地址
Request Method: GET //get方法/post方法
Status Code: 200 OK //状态码:200
Remote Address: 14.215.177.39:443 //远程地址
Referrer Policy: no-referrer-when-downgrade
1
2
3
4
5
6
Request Headers:
Accept: text/html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9 //语言
Cache-Control: max-age=0
Connection: keep-alive

请求行

  • 请求行中的请求方式:GET
  • 请求方式:Get/Post,HEAD,DELETE,PUT,TRACT…
    • get:请求能够携带的参数比较少,大小有限制,会在浏览器的URL地址栏显示数据内容,不安全,但高效
    • post:请求携带参数没有限制,大小没有限制,不会在浏览器显示数据内容,安全,但效率较低

消息头

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

HTTP响应

  • 服务器 -> 响应 -> 客户端

百度:

1
2
3
4
Cache-Control: private                 //缓存控制
Connection: keep-alive //连接:保持
Content-Encoding: gzip
Content-Type: text/html;charset=utf-8 //类型

响应体

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

响应状态码

200:请求响应成功 200

3xx: 请求重定向

  • 重定向:重新到给定的新位置去;

4xx:找不到资源 404

  • 资源不存在;

5xx:服务器代码错误 500 502: 网关错误


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

Maven

简介

  1. 在java web开发中,需要使用大量的jar包,需要手动去导入;
  2. 自动帮助导入和配置这些jar包;

Maven项目架构管理工具

用来方便导入jar包

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

  • 有约束,不要去违反

Maven会规定好你该如何去编写好java代码,必须按照这个规范来;

下载安装Maven

官网下载

配置环境变量

在系统环境变量中:

  • M2_HOME Maven目录下的bin目录
  • MAVEN_HOME Maven的目录
  • 在系统的path中配置%MAVEN_HOME%\bin

阿里云镜像

mirrors

  • 作用:加速下载

国内建议使用阿里云镜像

1
2
3
4
5
6
7
8
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>*,!jeecg,!jeecg-snapshots</mirrorOf>
<name>Nexus aliyun</name>
<url>
http://maven.aliyun.com/nexus/content/groups/public/
</url>
</mirror>

本地仓库

建立一个本地仓库:localRepository

1
<localRepository>D:\Program Files (x86)\apache-maven-3.6.3\maven-repo</localRepository>

在IDEA中使用Maven

  1. 启动IDEA

  2. 创建一个Maven项目

    选择Maven创建项目,然后选择maven-archetype-webapp

    选择之前配置好的Maven路径

  3. 观察maven-repo

  4. IDEA中的Maven设置

    IDEA项目创建成功后,看一眼Maven的配置

  5. Maven在IDEA中的配置和使用完成

创建一个普通的Maven项目

不勾选模板选项

main: 放置java源代码

resources:放置配置文件

test:测试使用

在IDEA中标记文件夹功能

右键,Mark Directory as

在IDEA中配置Tomcat

在Add configuration中添加Tomcat Server

解决警告问题:

  • 原因:访问网站需要一个指定的文件夹名称
  • 解决办法:点击fix,选择… war

pom文件

pom.xml是maven的核心配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
<?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>org.example</groupId>
<artifactId>JavaWeb-maven-01</artifactId>
<version>1.0-SNAPSHOT</version>
<!--项目的打包方式
jar:java应用
war:JavaWeb应用
-->
<packaging>war</packaging>

<name>JavaWeb-maven-01 Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>

<!--配置-->
<properties>
<!--项目的默认构建编码-->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>

<!--项目依赖-->
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>

<!--项目构建用的东西-->
<build>
<finalName>JavaWeb-maven-01</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由于约定大于配置,之后可能会遇到配置文件无法被导出或者生效的问题,解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!--在build中配置resources,来放置资源导出失败的问题-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>**/*.properties</exclude>
<exclude>**/*.xml</exclude>
</excludes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>

Dependencies的整体架构:

image-20200911093822077

解决问题

  1. Maven默认web项目的web.xml版本问题,如果不兼容,则

    image-20200911095414224

    替换为Tomcat\webapps\ROOT下的web.xml

  2. Maven仓库的使用

    访问

    https://mvnrepository.com/

Servlet

简介

  • Servlet是sun公司开发动态web的一门技术
  • Sun公司在API中提供接口:Servlet,如果需要开发Servlet程序,只需要完成两个小步骤:
    • 编写类,实现Servlet接口
    • 把开发好的Java类部署到web服务器中

把实现了Servlet接口的Java程序叫做Servlet

HelloServlet

Servlet接口在Sun公司有两个默认的实现类:HTTPServlet,

  1. 构建一个普通的Maven项目,删掉里面的src,以后在这里建立Module.这个空的工程 就是Maven主工程。

  2. 关于maven父子工程的理解:

    在父项目会有

    1
    2
    3
    <modules>
    <module>servlet-01</module>
    </modules>

    父项目中的java,子项目可以直接使用

    1
    son extends father
  3. Maven 环境优化

    1. 修改web.xml为最新
    2. 将maven结构搭建完成
  4. 编写Servlet程序

    1. 编写一个普通类

    2. 实现Servlet接口,这里直接继承HTTPServlet

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      public class HelloServlet extends HttpServlet {
      //由于get或者POST只是请求实现的不同方式,可以相互调用,因为业务逻辑都一样

      @Override
      protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      PrintWriter writer = resp.getWriter(); //响应流
      writer.print("Hello Servlet");

      }

      @Override
      protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      doGet(req, resp);
      }
      }
  5. 编写Servlet的映射

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

  6. 配置Tomcat

    注意:配置项目发布的路径

  7. 启动测试

Servlet原理

Servlet是由Web服务器调用,Web服务器在收到浏览器请求之后,会:

image-20200911194338468

Mapping问题

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

    1
    2
    3
    4
    <servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
    </servlet-mapping>
  2. 一个Servlet能指定多个映射路径

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello1</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello2</url-pattern>
    </servlet-mapping>
  3. 一个Servlet能指定通用映射路径

    1
    2
    3
    4
    5
    6
    7
    8
    9
      <servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello/*</url-pattern>
    </servlet-mapping>
    <!--默认请求路径,优先级高-->
    <servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/*</url-pattern>
    </servlet-mapping>
  4. 一个Servlet能自定义后缀实现请求映射

    1
    2
    3
    4
    5
    <!--可以自定义后缀实现请求映射,*前面不能加任何映射的路径-->
    <servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>*.do</url-pattern>
    </servlet-mapping>
  5. 优先级问题

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

ServletContext

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

image-20200911204228611

共享数据

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

1
2
3
4
5
6
7
8
9
10
11
12
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Hello");
//this.getInitParameter(); //得到初始化参数
//this.getServletConfig(); //Servlet配置
//this.getServletContext(); //Servlet上下文
ServletContext servletContext = this.getServletContext();

String username = "德丽莎"; //data
servletContext.setAttribute("username", username); //将一个数据保存在servletContext中,名字为username,值username

}
1
2
3
4
5
6
7
8
9
10
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html");
resp.setCharacterEncoding("utf-8");

ServletContext servletContext = this.getServletContext();
String username = (String) servletContext.getAttribute("username");
resp.getWriter().print(username);

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.kuang.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.kuang.servlet.GetServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>getc</servlet-name>
<url-pattern>/getc</url-pattern>
</servlet-mapping>

测试访问结果;

获取初始化参数

1
2
3
4
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
</context-param>
1
2
3
4
5
6
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
String url = servletContext.getInitParameter("url");
resp.getWriter().print(url);
}

请求转发

image-20200911213004349

1
2
3
4
5
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
servletContext.getRequestDispatcher("/gp").forward(req, resp);
}

读取资源文件

Properties

  • 在java目录下新建properties

  • 在resource目录下新建properties

发现:都被打包到了同一路径下:class, 称这个路径为classpath;

思路:需要一个文件流作为桥梁;

1
2
3
4
5
6
7
8
9
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");
Properties properties = new Properties();
properties.load(is);
String username = properties.getProperty("username");
String password = properties.getProperty("password");
resp.getWriter().print(username + ":" + password);
}

访问测试接口

HttpServletRequest

HttpServletRequest代表客户端的请求,用户通过Http访问服务器,Http请求中的所有信息会被封装到HTTPServletRequest。通过这个HttpServletRequest的方法,获取客户端的所有信息。

获取前端传递的参数,请求转发

前端通过form传递相应的键值对,通过提交方式被后端的request对象接收,后端可以通过获得参数(键)来获取相应的值,一般使用post请求,这样不会在URL中直接显示信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//初始化编码
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");

String username = req.getParameter("username");
String password = req.getParameter("password");
String[] hobbies = req.getParameterValues("hobbies");

System.out.println(username + ":" + password);
System.out.println(Arrays.toString(hobbies)); //Arrays处理数组的类,这里返回数组的字符串形式

//通过请求转发
//这里的/代表当前的web应用
req.getRequestDispatcher("/success.jsp").forward(req, resp);
//resp.sendRedirect(req.getContextPath() + "/success.jsp");

HttpServletResponse

web服务器接收到客户端的http请求,会针对这个请求,分别创建一个代表请求的HTTPServletRequest对象,代表响应的一个HttpServletResponse对象;

  • 如果要获取客户端请求过来的参数:找HTTPServletRequest
  • 如果要给客户响应一些信息:找HttpServletResponse

简单分类

负责向浏览器发送数据的方法

1
2
ServletOutputStream getOutputStream() throws IOException;
PrintWriter getWriter() throws IOException; //可能造成字符串损坏或者丢失

负责向浏览器发送响应头的方法

1
2
3
4
5
void setCharacterEncoding(String var1);

void setContentLength(int var1);

...

响应状态码

1
2
3
int SC_OK = 200;
int SC_NOT_FOUND = 404;
int SC_INTERNAL_SERVER_ERROR = 500;

下载文件

  1. 向浏览器输出消息(getWriter,getOutputStream)

  2. 下载文件

    1. 要获取下载文件的路径
    2. 下载的文件名
    3. 设置,让浏览器能够支持下载我们需要的东西
    4. 获取下载文件的输入流
    5. 创建缓冲区
    6. 获取OutputStream对象
    7. 将FileOutputStream写入到buffer缓冲区
    8. 使用OutputStream将缓冲区中的数据输出到客户端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
String realPath = this.
getServletContext().
getRealPath("/WEB-INF/classes/sakura.jpg");
System.out.println("download file's path:" + realPath);

String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1);
System.out.println("download file's name:" + fileName);

//set file header, use URLEncoder to change encode
resp.setHeader("Content-Disposition", "attachment; filename="+ URLEncoder.encode(fileName,"UTF-8"));

//input file to buffer and to outputStream
FileInputStream in = new FileInputStream(realPath);
int len = 0;
byte[] buffer = new byte[1024];
ServletOutputStream out = resp.getOutputStream();
while((len = in.read(buffer)) > 0){
out.write(buffer, 0, len);
}

in.close();
out.close();

验证码功能

验证码来源

  • 前端实现
  • 后端实现,需要用到Java图片类,产生一个图片
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//如何让浏览器5s自动刷新一次
resp.setHeader("refresh", "5");
//在内存中生成图片
BufferedImage bufferedImage = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB);
//得到图片
Graphics2D graphics = (Graphics2D) bufferedImage.getGraphics();
//设置图片的背景颜色
graphics.setColor(Color.white);
graphics.fillRect(0,0,80,20);
//给图片写数据
graphics.setColor(Color.blue);
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");
resp.setHeader("Pragma", "no-cache");
//把图片写给浏览器
ImageIO.write(bufferedImage, "jpg", resp.getOutputStream());
}

//生成随机数
private String makeNum(){
Random random = new Random();
String randomString = random.nextInt(99999999) + "";
StringBuffer buffer = new StringBuffer();
//用0填充不够的位数
for (int i = 0; i < 7 - randomString.length(); i++){
buffer.append("0");
}
String num = buffer.toString() + randomString;
return num;
}

实现重定向

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

常见场景:

  • 用户登录
1
void sendRedirect(String var1) throws IOException;

实现:

1
2
3
4
//原理
//resp.setHeader("Location", "/rsp/image");
//resp.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
resp.sendRedirect("/rsp/image"); //重定向

Q: 重定向和转发的区别?

相同点:

  • 页面都会实现跳转

不同点:

  • 请求转发的时候URL不会产生变化,一般是从转发地那东西到自己这里来 状态码:307
  • 重定向的时候,URL会定向到新的位置 状态码:302

Cookie、Session

会话

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

有状态会话:服务端记录了访问过的客户端的信息。

一个网站,怎么证明你来过?

客户端 服务端

  1. 服务端给客户端一个信件,客户端下次访问服务端,带上信件就行;cookie
  2. 服务器登记来过,下次来的时候我来匹配你; session

保存会话的两种技术

cookie

  • 客户端技术(响应,请求)

session

  • 服务器技术(行为),利用这个技术,可以保存用户的会话信息。我们可以把信息或者数据放在session中。

常见场景:网站登录之后,下次不用再登录,第二次访问直接上去。

  1. 从请求中拿到cookie信息
  2. 服务器响应给客户端cookie
  3. Cookie是具有Domain(域)的

image-20200913160139940

1
2
3
4
5
6
Cookie[] cookies = req.getCookies();  //获得cookie
cookie.getName(); //获得cookie中的key
cookie.getValue(); //获得cookie中的value
new Cookie(); //新建一个cookie
cookie.setMaxAge(); //设置cookie的有效期
resp.addCookie(); //响应给客户端一个cookie

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


一个网站cookie是否存在上限?

  • 一个cookie只能保存一个信息;
  • 一个web站点可以给浏览器发送多个cookie,上限大概为300个,每个站点最多存放20个
  • Cookie大小有限制,最多为4kb

删除Cookie:

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

在传输中文字符时,转码: URLEncoder.encode(“中文”, “编码类型”),解码:URLEncoder.decode(“ “,” “)

Session

什么是session?

  • 服务器会给每一个用户(浏览器)创建一个session对象;
  • 一个session独占一个浏览器,只要浏览器没有关闭,这个session就存在;
  • 用户登录之后,整个网站它都可以访问->保存用户的信息,保存购物车的信息… …;

Session和Cookie的区别:

  • Cookie是把用户的数据写给用户的浏览器,是浏览器保存(可以保存多个),且直接保存内容
  • Session把用户的数据写到用户独占的session中,是服务器端保存(保存重要信息,减少服务资源的浪费),客户端只保存ID
  • Session对象由服务器创建

使用场景:

  • 保存一个登录用户的信息
  • 保存购物车信息
  • 在整个网站中,经常会使用的数据,我们将它保存在session中

使用Session:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("GBK");
resp.setContentType("text/html");

HttpSession session = req.getSession();

session.setAttribute("name", new Person("德丽莎", 500));

//获取sessionID
String id = session.getId();

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

//Session创建的时候,会把sessionID放到cookie中
//Cookie cookie = new Cookie("JSESSIONID",id);
//resp.addCookie(cookie);

HttpSession reqSession = req.getSession();
Person person = (Person) reqSession.getAttribute("name");

System.out.println(person);

//reqSession.removeAttribute("name");
//reqSession.invalidate(); //手动注销session

会话自动过期:web.xml

1
2
3
4
5
<!--    设置session默认的失效时间-->
<session-config>
<!-- n分钟后session自动失效-->
<session-timeout>1</session-timeout>
</session-config>

image-20200913101905117

JSP

什么是JSP?

Java Server Pages:Java服务器端页面,也和Servlet一样用于开发动态web技术

最大的特点:

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

JSP原理

思路:JSP到底怎么执行

  • 代码层面没有变化

  • 服务器内部工作

    Tomcat中有一个work目录,

    在IDEA中使用Tomcat,会在IDEA的Tomcat中产生一个work目录

    1
    C:\Users\树文\AppData\Local\JetBrains\IntelliJIdea2020.2\tomcat\Unnamed_javaweb-session-cookie\work\Catalina\localhost\ROOT\org\apache\jsp\index_jsp.java

    发现页面转变成Java程序!

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

JSP最终也会被转换成Java类,并且是继承自Servlet的

image-20200913104152022

JSP本质上就是Servlet!

1
2
3
4
5
6
7
8
9
//初始化  
public void _jspInit() {
}
//销毁
public void _jspDestroy() {
}
//JSP Service
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException {
  1. 判断请求

  2. 内置了一些对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
       final javax.servlet.jsp.PageContext pageContext;   //页面上下文
    javax.servlet.http.HttpSession session = null; //session
    final javax.servlet.ServletContext application; //applicationContext
    final javax.servlet.ServletConfig config; //配置
    javax.servlet.jsp.JspWriter out = null; //输出流
    final java.lang.Object page = this; //当且页面
    javax.servlet.jsp.JspWriter _jspx_out = null;
    javax.servlet.jsp.PageContext _jspx_page_context = null;

    HttpServletRequest request //请求
    HttpServletResponse response //响应
  3. 输出页面前增加的代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    response.setContentType("text/html");                               //设置响应类型(转换成HTML)
    pageContext = _jspxFactory.getPageContext(this, request, response,
    null, true, 8192, true);
    _jspx_page_context = pageContext;
    application = pageContext.getServletContext();
    config = pageContext.getServletConfig();
    session = pageContext.getSession();
    out = pageContext.getOut();
    _jspx_out = out;
  4. 以上这些对象可以在JSP页面中直接使用!

    image-20200913105855211

在JSP页面中,只要是java代码,就会原封不动地输出;

如果是HTML代码,就会被转换为out.write(“”) 输出到前端;

JSP基础语法

JSP有一些自己扩充的语法,Java所有语法都支持!

JSP表达式:

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

jsp脚本片段:

1
2
3
4
5
6
7
8
<%--JSP脚本片段--%>
<%
int sum = 0;
for(int i = 0; i <= 100; i++){
sum += i;
}
out.println("<h1>Sum=" + sum + "</h1>");
%>

脚本片段的再实现:

1
2
3
4
5
6
7
8
<%--在代码中嵌入HTML元素--%>
<%
for(int i = 0; i < 5;i++){
%>
<h1>Hello World <%=i%> </h1>
<%
}
%>

JSP声明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<%!
//static静态块
//(1) static关键字还有一个比较关键的作用,用来形成静态代码块(static{}(即static块))以优化程序性能。
//(2) static块可以置于类中的任何地方,类中可以有多个static块。
//(3) 在类初次被加载的时候执行且仅会被执行一次(这是优化性能的原因!!!),会按照static块的顺序来执行每个static块,一般用来初始化静态变量和调用静态方法。
static{
System.out.println("Loading Servlet!");
}
private int globalVar = 0;

public void function(){
System.out.println("enter function!");
}
%>

区别:

jsp声明会被编译到JSP生成的java_jsp的类中,其他的就会被生成到_Service方法中。

在JSP中,嵌入java代码即可!

1
2
3
4
<%%>   //片段
<%=%> //表达式输出
<%!%> //全局元素
<%--注释--%>

JSP的注释不会被渲染到客户端,但HTML会

JSP指令

1
2
3
4
5
6
7
8
9
10
11
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<%--定制错误页面--%>
<%@ page errorPage="error/500.jsp" %>
<%--显式声明这是一个错误页面--%>
<%@ page isErrorPage="true" %>

<%--导入jar包--%>
<%@ page import="java.util.*" %>


错误页面的两种方式:

  1. 直接在jsp中定制错误页面

    1
    2
    <%--定制错误页面--%>
    <%--<%@ page errorPage="error/500.jsp" %>--%>
  2. 在web.xml中配置

    1
    2
    3
    4
    5
    6
    7
    <error-page>
    <error-code>404</error-code>
    <location>/error/404.jsp</location>
    </error-page>
    <error-page>
    <error-code>500</error-code>
    <location>/error/500.jsp</location>

页面内嵌的方式(比如设置网页有相同的头部,尾部):

1
2
3
4
5
6
7
8
9
10
11
<%--会将两个页面合二为一,有点像宏扩展--%>
<%@include file="common/header.jsp"%>
<h1>网页主体</h1>
<%@include file="common/footer.jsp"%>

<hr>
<%--JSP标签(更常用)--%>
<%--拼接页面,本质还是3个--%>
<jsp:include page="/common/header.jsp"></jsp:include>
<h1>网页主体</h1>
<jsp:include page="/common/footer.jsp"></jsp:include>

9大内置对象

  • PageContext 保存
  • Request 保存
  • Response
  • Session 用来保存
  • Application(ServletContext) 用来保存
  • config(ServletConfig)
  • out
  • page 几乎不用
  • exception

image-20200913154655427

1
2
3
4
5
6
//从底层到高层(作用域): page->request->session->application
//类似JVM:双亲委派机制:类加载器会优先加载上层的包,如果能够找到就直接使用,这样可以防止一些危险重名代码被加载
pageContext.setAttribute("name1","Alice1"); //保存的数据只在一个页面中有效
request.setAttribute("name2","Alice2"); //只在一次请求中有效,请求转发会携带这个数据
session.setAttribute("name3","Alice3"); //在一次会话中有效,从打开到关闭浏览器
application.setAttribute("name4","Alice4"); //在服务器中有效,从打开到关闭服务器

在获取各值时,根据存储方式和生命周期的不同,会产生不同的结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<%--脚本片段中的代码会被原封不动地生成到*_jsp.java中--%>
<%--要求:这里面的代码必须保证Java语法的正确性--%>
<%
//从pageContext取出, 我们通过寻找的方式来
String name1 = (String)pageContext.findAttribute("name1"); //pageContext
String name2 = (String)pageContext.findAttribute("name2"); //request
String name3 = (String)pageContext.findAttribute("name3"); //session
String name4 = (String)pageContext.findAttribute("name4"); //application
String name5 = (String)pageContext.findAttribute("name5");
%>
<%--使用EL表达式输出 ${}--%>
<h1>取出的值为:</h1>
<h3>${name1} 取不出</h3>
<h3>${name2} 取不出</h3>
<h3>${name3} 可以取出</h3>
<h3>${name4} 可以取出</h3>
<%--EL表达式好处:即使值为null,也能自动识别而不会输出--%>
<h3>${name5} 空</h3>

应用场景:

  • request:客户端向服务器发送请求产生的数据用户看完就没用了,比如:新闻(不用存起来);
  • session:产生数据用户用完一会儿还有用,比如:购物车;
  • application:产生的数据一个用户用完了,其他用户还可能使用,比如:聊天数据,统计访问人数;

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

导入到web.xml中的包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!--    JSP依赖-->
<!-- 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>

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

<!-- standard标签库-->
<!-- https://mvnrepository.com/artifact/taglibs/standard -->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>

EL表达式:

  • 获取数据
  • 执行运算
  • 获取web开发的常用对象
  • 调用java方法(不常用)

JSP标签

1
2
3
4
5
6
<jsp:include page="jsptag2.jsp"></jsp:include>

<jsp:forward page="/jsptag2.jsp">
<jsp:param name="value1" value="value1"/>
<jsp:param name="value2" value="value2"/>
</jsp:forward>

JSTL表达式

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

格式化标签

SQL标签

XML标签

核心标签(掌握部分)

image-20200913170908703

JSTL标签使用:

  • 引入对应的taglib

    1
    2
    <%--引入JSTL核心标签库,才能使用JSTL标签 core--%>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
  • 使用其中的方法

  • 在Tomcat也需要引入jstl包,否则会报错:JSTL解析错误

c:if 标签:

1
2
3
4
5
6
7
8
9
10
11
<h4>if测试</h4>
<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}"/>

其他标签见 https://www.runoob.com/

总结:还是写java更舒服

JavaBean

实体类

JavaBean有特定的写法:

  • 必须要有一个无参构造
  • 属性必须私有化
  • 必须有对应的get/set方法

一般用于和数据库的字段做映射 ORM;

ORM:对象关系映射

  • 表->类
  • 字段-> 类的属性
  • 行记录-> 对象

people表

id name age address
1 张三 10 北京
2 李四 12 湖北
3 王五 14 山东
1
2
3
4
5
6
7
8
9
10
11
12
class People{
private int id;
private String name;
private int age;
private String address;
}

class A{
new People(1,"张三",10,"北京");
new People(1,"李四",12,"湖北");
new People(1,"王五",13,"山东");
}

使用方法:

1
2
3
4
5
6
7
8
9
10
11
<jsp:useBean id="people" class="com.shuwen.entity.People" scope="page"/>

<jsp:setProperty name="people" property="address" value="西安"/>
<jsp:setProperty name="people" property="id" value="1"/>
<jsp:setProperty name="people" property="age" value="3"/>
<jsp:setProperty name="people" property="name" value="张三"/>

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

MVC三层架构

概念:

Model view Controller 模型 视图 控制器

早期架构

image-20201004100236448

用户直接访问控制层,控制层就可以直接操作数据库

1
2
3
4
5
6
7
8
9
10
servlet--CRUD-->数据库
弊端:
程序十分臃肿,不利于维护
servlet的代码中:处理请求,响应,视图跳转,处理JDBC,处理业务代码,处理逻辑代码
架构:没有什么是加一层解决不了的;
程序员调用
|
JDBC;
|
Mysql Oracle sqlServer... ...

MVC三层架构

image-20201004101527916

Model

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

View

  • 展示数据
  • 提供链接发起Servlet请求(a, form, img…)

Controller

  • 接收用户的请求:(request:请求参数,Session信息… …)

  • 交给业务层处理对应的代码

  • 控制视图跳转

    1
    2
    登录--> 接收用户的登录请求--> 处理用户的请求(获取用户登录的参数) --> 交给业务层处理登录业务(判断用户名密码是否正确:事务)
    --> Dao层查询用户名和密码是否正确--> 数据库

Filter

Filter:过滤器,用来过滤网站的数据;

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

image-20201004102615729

Filter开发步骤:

  1. 导包

  2. 编写过滤器

    1. 导包不要错,要导入java.servlet下面的Filter

    2. 实现filter接口,重写对应的方法

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      public class CharacterEncodingFilter implements Filter {

      //web 服务器启动时就已经初始化了,随时等待过滤对象出现
      public void init(FilterConfig filterConfig) throws ServletException {
      System.out.println("CharacterEncodingFilter 初始化");
      }

      //1. 过滤器中的所有代码在过滤特定请求的时候都会执行
      //2. 必须要让过滤器继续通行 chain.doFilter
      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 服务器关闭的时候,过滤器会销毁
      public void destroy() {
      System.out.println("CharacterEncodingFilter 销毁");
      }
      }
    3. 在Web.xml中配置Filter过滤器

      1
      2
      3
      4
      5
      6
      7
      8
      9
          <filter>
      <filter-name>CharacterEncodingFilter</filter-name>
      <filter-class>com.shuwen.filter.CharacterEncodingFilter</filter-class>
      </filter>
      <filter-mapping>
      <filter-name>CharacterEncodingFilter</filter-name>
      <!-- 只要是/servlet下的任何请求,都会经过这个过滤器-->
      <url-pattern>/servlet/*</url-pattern>
      </filter-mapping>

监听器

实现一个监听器的接口;

  1. 编写一个监听器

    实现监听器的接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    //统计网站在线人数: 统计session
    public class OnlineCountListener implements HttpSessionListener {
    //创建session监听:看你的一举一动
    //一旦创建一个session,就会触发一次这个事件
    @Override
    public void sessionCreated(HttpSessionEvent httpSessionEvent) {
    ServletContext servletContext = httpSessionEvent.getSession().getServletContext();

    System.out.println(httpSessionEvent.getSession().getId());

    Integer onlineCount = (Integer) servletContext.getAttribute("OnlineCount");
    if(onlineCount == null){
    onlineCount = 1;
    } else {
    onlineCount += 1;
    }

    servletContext.setAttribute("OnlineCount", onlineCount);
    }

    //销毁session监听
    //一旦销毁Session,就会触发一次这个事件
    @Override
    public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
    System.out.println("会话销毁后...");
    ServletContext servletContext = httpSessionEvent.getSession().getServletContext();
    Integer onlineCount = (Integer) servletContext.getAttribute("OnlineCount");
    if(onlineCount == null){
    onlineCount = 0;
    } else {
    onlineCount -= 1;
    }

    servletContext.setAttribute("OnlineCount", onlineCount);
    }

    /*
    * Session销毁
    * 1. 手动销毁 httpSessionEvent.getSession().invalidate();
    * 2. 自动销毁 <session-config>
    <session-timeout>1</session-timeout> -- 一分钟后自动销毁
    </session-config>
    * */
    }
  2. 配置监听器

    1
    2
    3
    <listener>
    <listener-class>com.shuwen.listener.OnlineCountListener</listener-class>
    </listener>
  3. 看情况是否使用

过滤器、监听器常见应用

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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,0, 255)); //设置背景颜色

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

frame.add(panel);

frame.setVisible(true);

//监听事件,监听关闭事件
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
}

用户登录之后才能进入首页,用户注销后就不能进入主页。

  1. 用户登录之后向Session中放入用户的数据

  2. 进入主页的时候,要判断用户是否已经登录

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    // ServletRequest
    HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
    HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
    Object userSession = httpServletRequest.getSession().getAttribute(Constant.USER_SESSION);
    if(userSession == null){
    httpServletResponse.sendRedirect("/error.jsp");
    }
    filterChain.doFilter(servletRequest, servletResponse);
    }

JDBC

详细内容见我的博客MySQL。

Junit单元测试

依赖:junit包

简单实用

@Test注解只有在方法上生效,只要加了这个方法,就可以直接运行。

1
2
3
4
@Test
public void test(){
System.out.println("Hello");
}

项目实例:SMBMS

基本架构

image-20201004224144180

项目搭建

  1. 搭建一个Maven Web项目

  2. 配置Tomcat

  3. 测试项目是否能够跑起来

  4. 导入项目中会遇到的jar包

    jsp,servlet, mysql驱动, jstl, standard…

  5. 创建项目包结构

    image-20201005223120901

  6. 编写实体类

    ORM映射:表-类映射

  7. 编写基础公共类

    1. 数据库配置文件

    2. 编写公共类

    3. 编写字符编码过滤器

  8. 导入静态资源(前端的内容)

登录功能

image-20201006163150542

  1. 编写前端
  2. 在web.xml中设置欢迎页
  3. 编写Dao层,得到用户登录的接口
  4. 编写Dao接口的实现类
  5. 业务层接口
  6. 业务层实现类
  7. 编写Servlet
  8. 注册Servlet
  9. 测试访问,确保以上功能成功

JavaWeb
http://example.com/2023/01/10/Java_Web/
作者
Chen Shuwen
发布于
2023年1月10日
许可协议