【JavaWeb】JavaWeb 基础

JavaWeb

JavaWeb是指所有通过Java语言编写的,可以通过浏览器访问的程序的总称。JavaWeb是基于请求和响应开发的。

  • 请求:客户端给服务器发送数据,Request
  • 响应:服务器给客户端传回数据,Response

JavaWeb三大组件:Servlet程序、Filter过滤器、Listener监听器

HTTP

HTTP:超文本传输协议。是一个简单的请求-响应协议,通常运行在TCP之上,端口号:80。HTTPS相比HTTP更为安全。HTTP两个时代:

  • HTTP1.0:客户端与web服务器连接后,只能获得一个web资源。
  • HTTP2.0:客户端与web服务器连接后,可以获得多个web资源。

HTTP 请求

请求行

  • 请求行中的请求方式:GETPOST
  • 请求方式:GETPOSTHEADDELETEPUT
    • GET检索 && 获取。会在 URL 中显示数据内容(URL 长度有限制,视浏览器而定,例如Chrome的URL长度限制为2Mb,2048个字符),一般没有请求体。不安全,但高效。是幂等性的Content-Type 通常为 application/x-www-form-urlencoded
    • POST创建 || 更新。不会在URL中显示数据内容,而是显示在请求体中。相对安全,但不高效。请求对资源有副作用,不是幂等性的

HTTP POST 请求的内容是在请求体内的,但也不是绝对安全的。他人截获该请求后仍可以得到请求体内容。若想保证安全性,还需要使用 HTTPS 的加密方法对请求体内容进行加密。

GET请求

image-20210501214204632

中文在URL中使用百分号编码(Percent-Encoding,只是一种机制),字符的编码方式通常是UTF-8,而不是使用ASCII编码。

POST请求

image-20210501214745892

浏览器的收藏和前进跳转都是 GET 请求,因为 GET 请求会在 URL 中保存具体参数,而 POST 一般不会保存。GET 请求常用来做缓存,POST 请求一般不做缓存。

GET 请求用于缓存:

image-20220129123937818

HTTP 响应

响应状态码:

  • 101:转换协议(例如HTTP协议转换成WebSocket协议)
  • 200:请求响应成功
  • 3xx:请求与重定向(304可以使用缓存的内容)
  • 4xx:找不到资源(404 资源不存在),客户端的错误(比如输入错误的地址),访问被拒绝(403 Forbidden 权限不够)
  • 5xx:服务器内部错误(500),网关错误(502)

image-20210501215225830

URL 和 URI

URI:资源识别符;URL:比 URI 多了一个定位功能,就是用定位的方式实现的 URI。URL 是 URI 的子集

  • 在 Web 领域上,假设所有的 Html 文档都有唯一的编号,记作 html:xxxxx,xxxxx 是一串数字,即Html 文档的身份证号码,这个能唯一标识一个 Html 文档,那么这个号码就是一个 URI。
  • 而 URL 则通过描述是哪个主机上哪个路径上的文件来唯一确定一个资源,也就是定位的方式来实现的 URI。

示例:

Tomcat

Tomcat是由Apache软件基金会属下Jakarta项目开发的Servlet容器,按照Sun Microsystems提供的技术规范,实现了对ServletJavaServer PageJSP)的支持,并提供了作为Web服务器的一些特有功能,如Tomcat管理和控制平台、安全局管理和Tomcat阀等。由于Tomcat本身也内含了HTTP服务器,因此也可以视作单独的Web服务器。Tomcat提供了一个Jasper编译器用以将JSP编译成对应的Servlet。

在官网安装Tomcat 9.0.45版本压缩包,解压到本地即可,无需配置环境变量。在构建Servlet项目时,在IDEA中指定Tomcat根目录即可使用Tomcat。

Maven

Maven 项目架构管理工具

用于方便导入jar包。核心思想:约定大于配置。有约束,不要去违反。Maven会规定好如何去编写Java代码,必须按照这个规范组织结构。

修改为Maven阿里云镜像:

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

修改本地仓库的位置:localRepository

1
<localRepository>D:\Environment\apache-maven-3.8.1\maven-repo</localRepository>

Maven 仓库

Maven repository:https://mvnrepository.com/

Maven 约定

创建JavaWeb项目时,webapp目录创建在src/main目录下才能够自动导入maven配置文件中添加的包,否则需要在项目结构中手动导入包。

Servlet

Servlet是Sun公司开发动态web的一门技术。Sun在这些API中提供了一个接口:Servlet。开发Servlet程序需要完成两个步骤:

  • 编写一个Java类,实现Servlet接口
  • 把开发好的Java类部署到web服务器中

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

HelloServlet

Sevlet接口Sun公司提供有两个默认的实现类:HttpServlt,GenericServlet

  1. 构建一个普通的Maven项目(不带模板),删掉里面的src目录,这个空的工程就是Maven的主工程。之后在这个项目里建立Module,新建的Module均为Maven父项目的子项目。
  2. 关于Maven父子工程的理解:父项目中的Maven依赖环境Jar包子项目可以直接使用
1
2
3
<modules>
<module>servlet-01</module>
</modules>
  1. Maven环境优化:修改web.xml(与本地Tomcat中的内容一致)
1
2
3
4
5
6
7
8
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0"
metadata-complete="true">

</web-app>
  1. 编写一个Servlet程序
    • 编写一个普通类
    • 实现Servlet接口,这里继承HttpServlet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.zhao.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class HelloServlet extends HttpServlet {
// 由于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);
}
}
  1. 编写Servlet映射:写的Java程序要通过浏览器访问,浏览器需要连接web服务器,所以需要在web服务中注册我们写的Servlet,还需要给他一个浏览器能访问到的路径。
1
2
3
4
5
6
7
8
9
10
<!-- 注册Servlet -->
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.zhao.servlet.HelloServlet</servlet-class>
</servlet>
<!-- Servlet的请求路径 -->
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
  1. 配置Tomcat,注意配置项目发布的路径
  2. 启动测试

Servlet 原理

Servlet是由web服务器调用,web服务器在收到浏览器请求后会调用service()方法,该方法会根据请求的类型GETPOST分发处理,执行相应的doGet()doPost()方法。

image-20210501200459226

Mapping 问题

  1. 一个Servlet可以指定一个映射路径
1
2
3
4
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
  1. 一个Servlet可以指定多个映射路径
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello1</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello2</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello3</url-pattern>
</servlet-mapping>
  1. 一个Servlet可以指定通用映射路径
1
2
3
4
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello/*</url-pattern>
</servlet-mapping>
  1. 默认请求路径
1
2
3
4
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>

优先级问题:

指定了固有的映射路径优先级最高,如果找不到匹配的固有映射路径,则就会走默认路径(*)

1
2
3
4
5
6
7
8
9
10
<!-- 注册Error Servlet -->
<servlet>
<servlet-name>ErrorServlet</servlet-name>
<servlet-class>com.zhao.servlet.ErrorServlet</servlet-class>
</servlet>
<!-- ErrorServlet的请求路径 -->
<servlet-mapping>
<servlet-name>ErrorServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>

ServletContext

是一个接口,代表Servlet上下文对象,一个工程只有一个ServletContext对象,是一个域对象(这里的域指的是整个web工程)。

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

作用1:共享数据,即在某个Servlet中保存的数据可以在另一个Servlet中获得。

存入数据的Servlet类,用于保存数据到ServletContext对象中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("hello");
// this.getInitParameter(); 初始化参数
// this.getServletConfig(); Servlet配置
// this.getServletContext(); Servlet上下文

ServletContext context = this.getServletContext();
String username = "zhangsan"; // 数据

// 将一个数据以键值对形式保存在了ServletContext中。
context.setAttribute("username", username);
}
}

读入数据的Servlet类,用于从ServletContext对象中读取数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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.getWriter().print(username);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}

作用2:获取初始化参数。

1
2
3
4
5
<!-- 是上下文参数(属于整个web工程) -->
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
</context-param>
1
2
3
4
ServletContext context = this.getServletContext();

String url = context.getInitParameter("url");
resp.getWriter().print(url);

作用3:请求转发。

访问当前url时,将消息转发给指定的其他url(当前url不会发生变化,重定向会变化)。当前url只充当转发功能。请求转发不需要添加项目名,只需要/+映射路径

1
2
3
// 转发的请求参数
RequestDispatcher requestDispatcher = context.getRequestDispatcher("/servlet01");
requestDispatcher.forward(req, resp); // 调用forward实现请求转发

请求转发的特点:

  • 浏览器地址栏没有变化
  • 是一次请求
  • 共享Request域中的数据
  • 可以转发到WEB-INF目录下
  • 无法访问项目以外的其他资源(如百度)

作用4:读取资源文件

  • 在java目录下新建properties
  • 在resources目录下新建properties

发现都被打包到了同一个路径下:/WEB-INF/classes,将这个路径称为classpath。

classpathsrc/main/下的java目录和resources目录下的文件会被合并到/WEB-INF/classes下,将该路径称为classpath

1
2
username=root
password=123456
1
2
3
4
5
6
7
8
InputStream resourceAsStream = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");

Properties prop = new Properties();
prop.load(resourceAsStream);
String user = prop.getProperty("username");
String password = prop.getProperty("password");

resp.getWriter().print(url + ':' + password);

HttpServletResponse

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

  • 如果要获取客户端请求过来的参数,使用HttpServletRequest
  • 如果要给客户端响应一些信息,使用HttpServletResponse

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

  • getOutputStream()
  • getWriter()

常见应用:

  1. 向浏览器输出消息
  2. 下载文件
1
2
// 设置让浏览器能够支持附件下载
respones.setHeader("Content-Disposition", "attachment;filename="+fileName);
  1. 实现重定向
1
response.sendRedirect("/projectName/url"); // 重定向到其他url

重定向和转发的区别

  • 请求转发时,url不会发生变化。(转发在服务器内部完成,不需要加项目名路径,如"/工程名")
  • 重定向时,url会发生变化。(需要加项目名路径,如"/projectName/url")
  • 请求转发是服务器级别的,可以访问/WEB-INF/下的资源
  • 重定向是浏览器级别的,无法访问/WEB-INF/下的资源
  • 请求转发会将斜杠"/"发送给服务器解析,得到http://ip:port/工程名
  • 重定向会将斜杠"/"发送给浏览器解析,得到http://ip:port/

在前端文件中写跳转链接时,因其不能得知服务器内部的项目结构,因此需要人为指定contextPath(在Servlet程序中不需要再指定当前项目在服务器内的路径)

细节:当用户提交完请求,浏览器会记录下最后一次请求的全部信息。当用户按下功能键F5,就会发起浏览器记录的最后一次请求。在此情况下如果使用请求转发的方式跳转页面,用户按下F5后会再次发起请求,因此这种情况应该使用重定向。

HttpServletRequest

HttpServletRequest代表客户端的请求,用户通过HTTP协议访问服务器,HTTP协议中的所有消息信息会被封装到HttpServletRequest,通过该类的方法可以获得客户端传来的请求信息。

  1. 获取传递的参数
1
2
String username = request.getParameter("username");
String[] hobbies = request.getParameterValues("hobbies");
  1. 请求转发
1
request.getRequestDispatcher("/success.jsp").forward(requset, response);

image-20210501221519475

  1. 获取请求头中Referer信息(浏览器发起请求时的url),可用于重定向回原地址
1
String url = req.getHeader("Referer");

Web中 / 斜杠的不同意义

  • 浏览器内代表所有资源:/**
  • 服务器内代表所有资源:/

在Web中,/ 是一种绝对路径

  • / 如果被浏览器解析,得到的地址是:http://ip:port/ (指写在静态html代码中,无法被服务器解析,只能被浏览器解析)
1
<a href="/">斜杠</a>
  • / 如果被服务器解析,得到的地址是:http://ip:port/工程路径/
1
2
3
4
5
6
7
8
// 映射
<url-pattern>/servlet1<url-pattern>

// 获取绝对路径
servletContext.getRealPath("/");

// 请求转发
request.getRequestDispacther("/");
  • 特殊情况:response.sendRedirect("/");会将斜杠发送给浏览器解析,得到http://ip:port/ ,因此需要再加上工程名response.sendRedirect("/projectName/xxx");

/WEB-INF/目录下的资源文件,客户端无法直接访问(即不能在浏览器中输入url直接跳转),而只能在servlet程序中跳转

在IDEA中,"/“代表的项目文件路径为”target/项目名-1.0-SNAPSHOT/"

在Web应用的前端程序(.jsp)中:

  • 不以 / 开始的相对路径找资源时以当前资源的路径为基准,容易出现问题(不推荐使用)
  • 以 / 开始的相对路径找资源时以http://ip:port/为基准,不包含当前项目名称路径,因此需要在资源前加上${pageContext.request.contextPath}/以使程序能找到"target/项目名-1.0-SNAPSHOT/"下的资源文件。例如若想在.jsp文件中引入css文件的路径,需要写 href="${pageContext.request.contextPath}/css/style.css"

image-20210604105603326

关于 Session 和 Cookie 的完整介绍见文章【JavaWeb】Session 和 Cookie

  • 会话(Session):用户打开一个浏览器,访问了一些web资源,关闭浏览器,这个过程可以称为一个会话。
  • 有状态会话:浏览器能够保存客户端信息的会话。

保存会话

  • Cookie:客户端技术(请求,响应),服务器给客户端创建Cookie。是服务器通知客户端保存键值对的一种技术,客户端有了Cookie后,每次请求都会发送给服务器,每个Cookie的大小不能超过4kb
  • Session:服务器技术,可以保存用户的会话信息。可以把信息和数据放在session中

服务器创建Cookie

image-20210505095556020

服务器获取客户端的Cookie

image-20210505100507884

  1. 从请求中拿到Cookie信息
  2. 服务器响应给客户端Cookie
1
2
3
4
5
6
7
8
9
Cookie[] cookies = req.getCookies(); // 获得cookies

cookie.getName(); // 获得cookie中的Key
cookie.getValue(); // 获得cookie中的Value

new Cookie("keyName", value); // 新建一个Cookie

cookie.setMaxAge(24*60*60); // 设置cookie的有效期,不设置默认关闭浏览器时删除
resp.addCookie(cookie); // 响应给客户端一个cookie

删除Cookie的方法:

  • 不设置有效期,关闭浏览器(关闭Session),自动失效;
  • 设置有效期,时间为0。

Cookie有效路径Path的设置:通过设置path路径可以过滤掉不符合路径的Cookie。

Session

服务器会给每一个用户(浏览器)创建一个Session对象。一个Session独占一个浏览器,只要浏览器没关,这个Session就一直存在。用户登录之后,整个网站都可以访问到用户信息。

Session技术,底层是基于Cookie技术实现的:每次创建出的Session对象都会保存成一个Cookie对象传给浏览器保存(该Cookie对象的生命周期为Session级别,即关闭浏览器后销毁)。

image-20210506103813937

Session和Cookie的区别:

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

使用场景:

  • 保存一个登录用户的信息
  • 购物车信息
  • 在整个网站中经常用到的数据
1
2
3
4
5
6
7
8
9
10
11
12
// 在session中存入信息
HttpSession session = req.getSession();
session.setAttribute("name", "zhangsan");
String sessionId = session.getId();

// 从session中获取信息
HttpSession session = req.getSession();
String name = session.getAttribute("name");

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

Session销毁方法:

  1. 手动销毁:
1
getSession().invalidate();
  1. 自动销毁:设置会话自动过期:web.xml配置
1
2
3
4
<session-config>
<!-- 15分钟后过期 -->
<session-timeout>15</session-timeout>
</session-config>

JSP

Java Server Pages:Java 服务器端页面,也和Servlet一样,用于动态web技术。JSP页面中可以嵌入Java代码,为用户提供动态数据。其主要用于代替Servlet程序回传html页面的数据。

JSP 原理

浏览器向服务器发送请求,不管访问什么资源,其实都是在访问Servlet。在Tomcat部署网页后,会为每个JSP文件生成对应的Java代码(放在work目录下),即JSP最终会被转换成Java源码JSP本质上就是一个Servlet

只有在访问JSP页面时,才会编译出对应的.class文件

在JSP页面中,Java代码会原封还不动的输出,html代码会进行转换:

1
out.write("<html>\r\n");

image-20210423133239836

JSP 基础语法

JSP表达式:将结果输出到客户端

1
2
<%-- JSP表达式
<%= new java.util.Date()%>

JSP脚本片段:代码片段会被生成到_jspService方法中作为Java代码的一部分。

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

JSP声明:会被编译到JSP生成的Java的类中,作为属性或方法声明。

1
2
3
4
5
6
7
8
9
10
11
<%!
static {
System.out.println("Loading Servlet!");
}

private int globalVar = 0;

public void testFunction(){
System.out.println("进入了test方法");
}
%>

JSP 指令

静态包含:<%@ include file="common/header.jsp" %>

特点:静态包含不会翻译被包含的jsp页面,其实是把被包含的jsp页面的代码拷贝到包含的位置执行输出,两个页面的作用域会冲突。

动态包含:< jsp:include page=" " ></jsp:include>

特点:动态包含会被包含的jsp页面翻译成java代码,两个页面的作用域不会冲突。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<%@ page args... %>

<%--@ include :静态包含,会将两个页面合而为一,在这里定义的变量不能重复,作用域冲突--%>
<%@ include file="common/header.jsp" %>
<h1>
网页主体
</h1>
<%@ include file="common/footer.jsp" %>

<%-- JSP标签
jsp:include 拼接两个页面,本质还是三个网页,在这里定义的变量可以重复,作用域不冲突
< jsp:include page="common/header.jsp" />
<h1>
网页主体
</h1>
< jsp:include page="common/footer.jsp" />

九大内置对象

  • PageContext
  • Request
  • Response
  • Session
  • Application【ServletContext】
  • Config【ServletConfig】
  • out
  • page
  • exception

四个能够保存数据信息的对象:

  • PageContext:保存的数据只在一个页面中有效
  • Request:保存的数据只在一次请求中有效,请求转发会携带这个数据
  • Session:保存的数据只在一次会话中有效,从打开服务器到关闭服务器
  • Application【ServletContext】:保存的数据只在服务器中有效,从打开服务器到关闭服务器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<%
pageContext.setAttribute("name1", "zhangsan1"); // 保存的数据只在一个页面中有效
request.setAttribute("name2", "zhangsan2"); // 保存的数据只在一次请求中有效,请求转发会携带这个数据
session.setAttribute("name3", "zhangsan3"); // 保存的数据只在一次会话中有效,从打开服务器到关闭服务器
application.setAttribute("name4", "zhangsan4"); // 保存的数据只在服务器中有效,从打开服务器到关闭服务器
%>

<%
String name1 = (String) pageContext.getAttribute("name1");
String name2 = (String) pageContext.findAttribute("name2");
String name3 = (String) pageContext.findAttribute("name3");
String name4 = (String) pageContext.findAttribute("name4");

request.setAttribute("name2", "zhangsan2");
session.setAttribute("name3", "zhangsan3");
application.setAttribute("name4", "zhangsan4");
%>

out 和 response.getWriter() 区别

image-20210502203944192

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

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>

EL(Expression Language)表达式${}

  • 获取数据:${username}
  • 执行运算:${param.username=="zhangsan"}
  • 获取web开发的常用对象:${param.username}

JSP标签

1
2
3
4
<jsp:forward page="/jsptag2.jsp">
<jsp:param name="name", value="zhangsan"></jsp:param>
<jsp:param name="age", value="3"></jsp:param>
</jsp:forward>

JSTL表达式

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

  • 核心标签
  • 格式化标签
  • SQL标签
  • XML标签

JSTL标签库使用步骤:

  1. 引入对应的taglib才能使用JSTL标签
1
<%@ taglib prefix="c" uri=:http://java.sun.com/jsp/jstl/core" %>
  1. 在Tomcat也需要引入JSTL的包,否则无法解析JSTL库
  2. 使用其中的方法
1
2
3
<c:if test="${param.username == 'admin'}" var="isAdmin" >
<c:out value="管理员欢迎您"></c:out>
</c:if>
1
2
3
<c:forEach var="people" items="${list}" begin="1" end="3" step="1">
<c:out value="&{people}"/> <br>
</c:forEach>

JavaBean

JavaBean有特定的写法:

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

一般用来和数据库的字段做映射。

ORM:对象关系映射:

  • 数据库中的表——>Java中的类
  • 数据库中的字段——>Java中的类的属性
  • 数据库中的行数据——>Java中的类的对象

MVC 三层架构

MVC:Model View Controller

Controller(Servlet):专注于处理请求以及控制视图跳转

  • 接受用户的请求
  • 响应给客户端内容(交给业务层处理,接受业务层返回的数据)
  • 重定向或转发(视图跳转)

View(JSP):专注于显示网页与数据

  • 展示网页界面
  • 提供用户操作,为Controller提供请求

Model(数据库)

  • 存储数据

MVC (1)

Model

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

View

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

Controller

  • 接受用户的请求(req:请求参数,Session信息)
  • 交给业务层处理对应的代码,接受业务层返回的结果
  • 控制视图的跳转

Filter


面试题:Filter 和 Interceptor 几乎拥有相同的功能,二者有何区别?

  • Filter 是 Servlet 定义的原生组件,好处是可以脱离 Spring 应用也能使用;其工作时机早于 Interceptor,在请求映射前执行
  • Interceptor 是 Spring 定义的接口,被 Spring 容器管理,可以使用 Spring 的自动装配等功能

Filter 在请求到达 Spring MVC 的 DispatcherServlet 前就会进行拦截过滤,只有通过后才会进入到 Spring MVC 里的请求转发,Interceptor 则是在进入到 DispatcherServlet 后才拦截的。


Filter:过滤器,用来对每个请求进行过滤。责任链模式。可用于:

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

image-20210507213832192

Filter的先后执行顺序由Filter类在web.xml中定义的顺序有关

匹配方式:

  • 精确匹配:精确到具体的文件名
  • 目录匹配:匹配到指定目录下的所有文件:/admin/*
  • 后缀名匹配:匹配到指定后缀名的文件:*.html(不能以/开头)
  1. 实现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
25
26
27
28
29
30
31
package com.zhao.filter;

import javax.servlet.*;
import java.io.IOException;

public class CharacterEncodingFilter implements Filter {

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

// Chain:链
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=UTF-8");

System.out.println("CharacterEncodingFilter初始化执行前");
chain.doFilter(request, response); // 让请求继续写,如果不写,程序到这里就被拦截停止
System.out.println("CharacterEncodingFilter初始化执行后");
}

// 销毁:web服务器关闭的时候,过滤会销毁
@Override
public void destroy() {
System.out.println("CharacterEncodingFilter销毁");
}
}
  1. 在web.xml中配置Filter
1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- 注册Filter -->
<filter>
<filter-name>Filter</filter-name>
<filter-class>com.zhao.filter.CharacterEncodingFilter</filter-class>
</filter>
<!-- Filter的请求路径 -->
<filter-mapping>
<filter-name>Filter</filter-name>
<!-- 指定url下的任何请求都会经过这个过滤器 -->
<url-pattern>/hello</url-pattern>
<!-- 若想任意url下的请求都可以经过这个过滤器,则写/* 其会拦截jsp文件,否则无法过滤jsp文件里= -->
<url-pattern>/*</url-pattern>
</filter-mapping>

Filter的使用场景之一(事务):在Filter中捕获Service层的异常

image-20210509102119700

Listener

Listener:监听器

  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
package com.zhao.listener;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class OnlineCountListener implements HttpSessionListener {

// 一旦创建Session就会触发一次这个方法
@Override
public void sessionCreated(HttpSessionEvent se) {
ServletContext servletContext = se.getSession().getServletContext();
Integer onlineCount = (Integer) servletContext.getAttribute("OnlineCount");

if (onlineCount == null) {
onlineCount = new Integer(1);
} else {
int count = onlineCount.intValue();
onlineCount = new Integer(count+1);
}

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

// 销毁Session监听
// 一旦销毁Session就会触发一次这个方法
@Override
public void sessionDestroyed(HttpSessionEvent se) {
ServletContext servletContext = se.getSession().getServletContext();
Integer onlineCount = (Integer) servletContext.getAttribute("OnlineCount");

if (onlineCount == null) {
onlineCount = new Integer(0);
} else {
int count = onlineCount.intValue();
onlineCount = new Integer(count-1);
}

servletContext.setAttribute("OnlineCount", onlineCount);
}
}
  1. 在web.xml中配置Listener
1
2
3
<listener>
<listener-class>com.zhao.listener.OnlineCountListener</listener-class>
</listener>