一、基本概念
1.1 概述
- Web 开发:
- web,网页的意思,如:
www.baidu.com
- 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 包;
- 配置文件 (Properties);
- Web 应用程序编写完毕后,若想提供给外界访问,需要一个服务器,来统一管理。
1.3 静态 Web
-
*.htm
,*.html
都是网页后缀,如果服务器上,一直存在这些资源,可以通过网络,直接进行读取; -
静态 Web 的缺点:
- Web 页面无法动态更新,所有用户看到都是同一个页面;
- 轮播图,点击特效:伪动态;
- JavaScript(实际开发中,用的最多);
- VBScript;
- 无法和数据库交互(数据无法持久化,用户无法交互)。
- Web 页面无法动态更新,所有用户看到都是同一个页面;
1.4 动态 Web
-
页面动态展示:Web 的页面展示,内容因人而异;
-
动态 Web 的缺点:
- 加入服务器的动态 Web 资源,出现了错误,需要重新编写后台程序,重新发布;
- 停机维护;
-
优点:
- 动态更新,所有用户看到都不是同一个页面;
- 可以与数据库交互(数据持久化:注册、商品信息、用户信息...);
二、Web 服务器
2.1 实现方式
- ASP
- 微软:国内最早流行的就是 ASP;
- 在 HTML 中嵌入了 VB 的脚本,
ASP + COM
; - 在 ASP 开发中,业务代码繁多,页面混乱;
- 维护成本高;
- IIS;
- php
- PHP 开发速度快,功能强大,跨平台,代码简单(70% , WP);
- 无法承载大的访问量(局限性);
- JSP/Servlet
- B/S:浏览器和服务器;
- C/S:客户端和服务器:
- Sun 公司主推的 B/S 架构;
- 基于 Java 语言;
- 可以承载 高并发、高可用、高性能 带来的影响;
- 语法类似 ASP;
2.2 Web 应用服务器
- 服务器是被动的操作,用来处理用户的请求,给用户响应信息;
- Web 应用服务器:
- IIS:ASP,Windows 自带;
- Tomcat:
- Apache 下 Jakarta 中的核心项目;
- 最新的 Servlet 和 JSP 规范,能在 Tomcat 中得到体现;
- 技术先进、性能稳定、免费,目前比较流行的 Web 应用服务器;
- 轻量级应用服务器,在中小型系统和并发访问,用户不是很多的场合下使用;
- 开发和调试 JSP 程序的首选;
三、Tomcat
四、HTTP
4.1 HTTP 概述
- Http(超文本传输协议):简单的
请求-响应
协议,运行在 TCP 之上;- 文本:html、字符串…. ;
- 超文本:图片、音乐、视频、定位、地图…;
- 端口:80;
- Https:安全的;
- 端口:443;
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
Remote Address:14.215.177.39:443 # 远程地址
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 请求:能够携带的参数没有限制,大小没有限制,不会在浏览器的 URL 地址栏显示数据内容,安全,但不高效;
- 消息头
Accept: # 告诉浏览器,所支持的数据类型
Accept-Encoding: # 支持编码格式 GBK UTF-8 GB2312 ISO8859-1
Accept-Language: # 告诉浏览器,语言环境
Cache-Control: # 缓存控制
Connection: # 告诉浏览器,请求完成是断开还是保持连接
HOST: # 主机..../.
4.4 Http 响应
- 服务器 --- > 响应 --- > 客户端
- 百度测试:
Cache-Control:private # 缓存控制
Connection:Keep-Alive # 保持连接
Content-Encoding:gzip # 编码
Content-Type:text/html # 文件类型
- 响应体
Accept: # 告诉浏览器,所支持的数据类型
Accept-Encoding: # 支持编码格式 GBK UTF-8 GB2312 ISO8859-1
Accept-Language: # 告诉浏览器,语言环境
Cache-Control: # 缓存控制
Connection:# 告诉浏览器,请求完成是断开还是保持连接
HOST: # 主机..../.
Refresh: # 告诉客户端,多久刷新一次
Location:# 网页重新定位
- 响应状态码
- 200:请求响应成功 200;
- 3xx:请求重定向:
- 重定向:重新到给指定新位置去;
- 4xx:找不到资源 404;
- 资源不存在;
- 5xx:服务器代码错误 500;
- 502:网关错误;
五、Maven
六、Servlet
6.1 Servlet 简介
- Servlet 是 sun 公司,开发动态 Web 的一门技术;
- Servlet 是一个 API 接口,开发 Servlet 程序,只需完成两个步骤:
- 编写类,实现 Servlet 接口;
- 把开发好的 Java 类,部署到 Web 服务器中;
- 实现了 Servlet 接口的 Java 程序叫做,Servlet;
- Serlvet 接口,有两个默认的实现类:
HttpServlet
、GenericServlet
;
6.2 构建 ServLet
- 构建 Maven 项目
-
构建一个普通的 Maven 项目,删掉里面的 src 目录,然后在这个项目里,建立 Moudel(模块),这个空的工程就是 Maven 主工程(父工程);
-
关于 Maven 父子工程的概念:
-
父项目
pom.xml
中,包含子项目(模块)的名称:<!--子项目名称--> <modules> <module>servlet-01</module> </modules>
-
子项目
pom.xml
中,包含父项目的信息:<!--父项目名称及信息 Maven3.84 创建项目后,内容被自动删除--> <parent> <artifactId>javaweb-02-servlet</artifactId> <groupId>org.example</groupId> <version>1.0-SNAPSHOT</version> </parent>
-
或在子项目依赖中,添加父项目的依赖:
- 但父项目依赖添加
dependencyManagement
标签,子项目在显示引用时,会有问题;
<dependency> <groupId>com.study</groupId> <artifactId>javaweb-02-servlet</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
- 但父项目依赖添加
-
子项目可以直接继承使用父项目内容(反之不行);
子项目 extends 父项目
-
- Maven 环境配置及优化:
-
父项目
pom.xml
中,添加 Servlet 依赖: -
Tomcat10 是
jakarta.servlet
包,而不是javax.servlet
;<!-- Tomcat10依赖:jakarta.servlet 5.0.0--> <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> <version>5.0.0</version> </dependency>
-
修改 Web 项目中的版本号 ;
-
web.xml
版本,改为与 Tomcat 一致的; - 修改 JDK 版本;
- 修改其它依赖的版本;
-
-
将 Maven 的结构搭建完整(补全相关目录);
- 编写 Servlet 程序
- 编写一个普通类:
- 实现 Servlet 接口,直接继承
HttpServlet
:
public class HelloServlet extends HttpServlet {
// doGet,doPost只是请求实现的不同方式,逻辑一样,可相互调用
@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 {
doGet(req, resp);
}
}
-
web.xml
中,编写 Servlet 映射:
<!--注册Servlet-->
<servlet>
<!--名字自定义,但要与下面映射名字保持一致-->
<servlet-name>hello</servlet-name>
<!--对应的Servlet程序-->
<servlet-class>com.study.servlet.HelloServlet</servlet-class>
</servlet>
<!--Servlet的请求路径-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<!--路径名自定义,访问:localhost:8080/Tomcat路径映射/路径名-->
<url-pattern>/hello</url-pattern>
</servlet-mapping>
-
注解方式,创建 Servlet 映射:建议使用注解的方式配置;
// 格式:@WebServlet(name = “userServlet”,urlPatterns ="/userServlet") // 注释方式1: @WebServlet("/test") // 注释方式2: // @WebServlet(name = "test",urlPatterns = {"/t1"}) public class HelloServlet extends HttpServlet { } // 设置多个路径 @WebServlet({"/test/edit.do", "/test/delete.do"})
为什么需要映射:
- Servlet 是 Java 程序,如通过浏览器访问,浏览器就需要连接 Web 服务器,所以需要在 Web 服务中,要注册 Servlet 程序,并给浏览器提供能够访问的路径;
- 配置 Tomcat 测试程序
-
在 IDEA 中配置 Tomcat 服务;
-
启动测试:
- 注:访问地址为
localhost:8080/Tomcat路径映射/路径名
;
- 注:访问地址为
6.3 Servlet 原理
-
Servlet 是由 Web 服务器调用,Web 服务器,收到浏览器请求的执行流程:
6.4 映射路径 Mapping
- 指定一个映射路径:
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
-
指定多个映射路径:多个路径都指向同一个 Servlet
<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> ...
-
指定通用映射路径:
*
匹配所有路径 ;<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>
-
指定后缀或者前缀:
<!-- 可以自定义后缀实现请求映射: 注意点:* 前面不能加项目映射的路径(如 /*.do 或 hello/*.do) --> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>
-
优先级问题:
- 指定了固有的映射路径,优先级最高,如果没有,就按默认的处理请求;
<!--404页面:访问未指定的路径,都会指向404页面--> <servlet> <servlet-name>error</servlet-name> <servlet-class>com.study.servlet.Error</servlet-class> </servlet> <servlet-mapping> <servlet-name>error</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
6.5 ServletContext 上下文
-
Web 容器在启动时,会为每个Web 程序,都创建一个对应的 ServletContext 对象,它代表了当前的 Web 应用:
- 一个 Web 应用,对应一个 ServletContext;
- 共享数据
- 在一个 Servlet 中保存的数据,可以在另外一个 Servlet 中拿到;
- 创建存储数据的 Servlet:
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// this.getInitParameter(); // 初始化参数
// this.getServletConfig(); // Servlet 配置
// ServletContext:Servlet 上下文
ServletContext context = this.getServletContext();
String username = "测试";
// 保存数据:格式 键,值
context.setAttribute("username", username);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
- 创建读取数据的 Servlet:
public class GetServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 创建 ServletContext 对象
ServletContext context = this.getServletContext();
// 获取数据,转换成String类型
String username = (String) context.getAttribute("username");
// 设置编码格式
resp.setContentType("text/html");
resp.getWriter().println("名字:" + username);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
- 在
web.xml
中注册 Servlet:
<servlet>
<servlet-name>set</servlet-name>
<servlet-class>com.study.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>set</servlet-name>
<url-pattern>/set</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>get</servlet-name>
<servlet-class>com.study.servlet.GetServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>get</servlet-name>
<url-pattern>/get</url-pattern>
</servlet-mapping>
- 获取初始化参数
-
web.xml
配置信息:
<!--配置Web应用初始化参数-->
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
</context-param>
- 获取信息:
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
// 获取参数信息 字符串对应配置文件中的参数名
String url = context.getInitParameter("url");
resp.getWriter().println(url);
}
- 请求转发
- 请求转发,URL 地址不会发生改变;
- 重定向,URL 地址会发生改变;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
// 转发的请求路径
// RequestDispatcher requestDispatcher = context.getRequestDispatcher("/gp");
// 调用forward实现请求转发;
// requestDispatcher.forward(req,resp);
// 字符串为需要转发的另一个Servlet程序路径
context.getRequestDispatcher("/url").forward(req,resp);
}
- 读取资源文件
- 读取 Properties 配置文件:
- 在
java
目录下新建.properties
文件; - 在
resources
目录下新建.properties
文件;
- 在
- 项目打包时,类和资源文件,都被打包到了
classes
同一个路径下,这个路径称为classpath
; - 用文件流,在指定的路径下读取即可;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 文件流读取资源文件,第一个路径前必须要有 /
// getResourceAsStream 将资源转换为流
InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/com/study/servlet/aa.properties");
// 创建属性集合对象
Properties prop = new Properties();
prop.load(is);
String user = prop.getProperty("username");
String pwd = prop.getProperty("password");
resp.setContentType("text/html");
resp.getWriter().println(user + ":" + pwd);
}
- 注:项目打包时,资源文件有时导出失败,需要在
pom.xml
中做相应配置:
<!--在build中配置resources,防止资源导出失败的问题-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
6.6 HttpServletResponse 响应
- Web 服务器接收到客户端的 http 请求,针对这个请求,分别创建:
- HttpServletRequest 对象:获取客户端 请求 过来的参数;
- HttpServletResponse 对象:给客户端 响应 信息;
- 简单分类
- 负责向浏览器发送数据的方法:
// 字节流
ServletOutputStream getOutputStream() throws IOException;
// 字符流 中文
PrintWriter getWriter() throws IOException;
- 负责向浏览器,发送响应头的方法:
void setCharacterEncoding(String var1); // 编码
void setContentLength(int var1); // 字符长度
void setContentLengthLong(long var1);
void setContentType(String var1); // 类型
void setDateHeader(String var1, long var2);
void addDateHeader(String var1, long var2);
void setHeader(String var1, String var2);
void addHeader(String var1, String var2);
void setIntHeader(String var1, int var2);
void addIntHeader(String var1, int var2);
- 响应的状态码:
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;
- 常用方法
方法名 | 说明 |
---|---|
response.sendRedirect(URL url) | 重定向 |
setContentType(String type) | 设置输出内容(MIME)类型(text/html) |
setContentLength(int length) | 设置响应报文的长度 |
getWriter( ) | 获取输出字符流 |
addHeader( String name, String value) | 添加指定的键值到响应头信息中 |
containsHeader(String name) | 判断响应的头部是否被设置 |
encodeURL(String url) | 编码指定的URL |
sendError(int sc) | 使用指定状态码发送一个错误到客户端 |
setHeader( String name, String value) | 设置指定响应头的值 |
setStatus(int sc) | 给当前响应设置状态 |
- 下载文件
- 向浏览器输出消息;
- 下载文件实现过程:
- 获取下载文件的路径;
- 下载的文件名;
- 设置浏览器的下载支持;
- 获取下载文件的输入流;
- 创建缓冲区;
- 获取 OutputStream 对象;
- 将 FileOutputStream 流写入到 buffer 缓冲区;
- 使用 OutputStream,将缓冲区中的数据,输出到客户端。
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1.获取下载文件的路径
String realPath = this.getServletContext().getRealPath("/WEB-INF/classes/images/01.png");
System.out.println("下载文件的路径:" + realPath);
// 2.下载的文件名
String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1);
// 3.设置浏览器的下载支持(Content-Disposition),并将文件名转码,避免乱码
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.将 FileOutputStream 流写入到 buffer 缓冲区,使用 OutputStream,将缓冲区中的数据,输出到客户端
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
// 8.关闭流
in.close();
out.close();
}
- 验证码功能
- 验证码实现:
- 前端实现;
- 后端实现:需要用到 Java 的图片类,生成一个图片;
public class ImageServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 让浏览器10秒自动刷新一次;
resp.setHeader("refresh", "10");
// 在内存中创建一个图片
BufferedImage image = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB);
// 得到图片 g 为笔
Graphics2D g = (Graphics2D) image.getGraphics();
// 设置图片的背景颜色
g.setColor(Color.white);
g.fillRect(0, 0, 80, 20);
// 给图片写数据
g.setColor(Color.BLUE);
g.setFont(new Font(null, Font.BOLD, 20));
g.drawString(makeNum(), 0, 20);
// 告诉浏览器,这个请求用图片的方式打开
resp.setContentType("image/png");
// 网站存在缓存,不让浏览器缓存
resp.setDateHeader("expires", -1);
resp.setHeader("Cache-Control", "no-cache");
resp.setHeader("Pragma", "no-cache");
// 把图片写给浏览器
ImageIO.write(image, "png", resp.getOutputStream());
}
// 生成随机数
private String makeNum() {
Random random = new Random();
String num = random.nextInt(9999999) + "";
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 7 - num.length(); i++) {
sb.append("0");
}
num = sb.toString() + num;
return num;
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
- 实现重定向
- 重定向,URL 地址会发生改变:
-
Web 资源
B
收到客户端A
请求后,B
通知A
去访问另一个 Web 资源C
,这个过程叫重定向; -
常见场景:用户登录
void sendRedirect(String var1) throws IOException;
-
重定向路径:
-
/
表示:http://localhost:8080,与转发的不同;
-
-
测试:
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/*
实现过程:
resp.setHeader("Location","/r/img");
resp.setStatus(302);
*/
// ‘/’ 表示:http://localhost:8080
resp.sendRedirect("/r/img");
}
- 重定向和转发
- 相同点:
- 都会实现页面跳转;
- 不同点:
- 请求转发,URL 地址不会发生改变;307
- 重定向,URL 地址会发生改变;302
- 相同点:
- 简单实现登录重定向
-
Tomcat 10 所需依赖:Tomcat 版本
10.0.16
,JDK 17
<!--Servlet 依赖--> <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> <version>5.0.0</version> </dependency> <!-- 备用Servlet 依赖 <dependency> <groupId>com.guicedee.services</groupId> <artifactId>jakarta.servlet-api</artifactId> <version>1.2.2.1-jre17</version> </dependency> --> <!--JSP 依赖--> <dependency> <groupId>com.guicedee.services</groupId> <artifactId>jakarta.servlet.jsp-api</artifactId> <version>62</version> </dependency> <!--jstl 表达式依赖--> <dependency> <groupId>org.glassfish.web</groupId> <artifactId>jakarta.servlet.jsp.jstl</artifactId> <version>2.0.0</version> </dependency>
可能遇到的问题:
-
JSP 中 out 内置对象红色,输入代码无提示:
- 缺少
jsp-api.jar
和servlet-api.jar
;
- 缺少
-
-
解决方法:
- 方法 1:更换依赖版本;
- 方法 2:从 Tomcat 安装目录 lib 目录下,复制到工程库里;
- 方法 3:在 IDEA 里直接添加;
-
添加对应版本:
-
添加成功:
- 登录页面:
<div>
<%--这里提交的路径,需要寻找到项目的路径--%>
<%--${pageContext.request.contextPath}代表当前的项目--%>
<form action="${pageContext.request.contextPath}/login" method="get">
<p>
<label for="username">用户名:</label>
<input type="text" id="username" name="username">
</p>
<p>
<label for="password">密码:</label>
<input type="password" id="password" name="password">
</p>
<input type="submit">
</form>
</div>
- servlet:
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 处理请求 getParameter:获取参数
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println(username + ":" + password);
// 注意,路径问题(/r1虚拟映射地址),否则404;
resp.sendRedirect("/r1/success.jsp");
}
- 注册 Servlet:
<servlet>
<servlet-name>request</servlet-name>
<servlet-class>com.study.servlet.RequestTest</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>request</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
- 跳转页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>success</title>
</head>
<body>
<h1>登录成功</h1>
</body>
</html>
6.7 HttpServletRequest 请求
- HttpServletRequest 代表客户端的请求,用户通过 HTTP 协议访问服务器,请求中的所有信息,都会被封装到 HttpServletRequest,通过这个 HttpServletRequest 的方法,获得客户端的所有信息;
- 常用方法:
方法名 | 含义 |
---|---|
String getHeader(String name) | 获取请求中的报头信息 |
Enumeration getHeaderNames() | 获取请求中所有报头名的集合 |
String getContextPath() | 获取请求上下文路径(webapp的) |
String getRequestURI() | 获取请求中的 URI |
String getMethod( ) | 获取 HTTP 请求方法,GET 还是 POST |
getRemoteAddr() | 远程 IP,即客户端 IP |
- 获取客户端的参数:
方法名 | 含义 |
---|---|
String getParameter(String name) | 获取请求(表单)中参数名为 name 的参数值 |
String[] getParameterValues(String name) | 获取请求中(表单)中所有参数名为 name 的参数值的集合(数组) |
Enumeration getParameterNames( ) | 获取请求中所有参数名的集合(枚举 不常用) |
- 其它方法:
方法名 | 说明 |
---|---|
setCharacterEncoding("utf-8") | 设置请求的编码方式 |
getLocalAddr() | 获取本地 IP,即服务器 IP |
getLocalName() | 获取本地名称,即服务器名称 |
getLocalPort() | 获取本地端口号,即 Tomcat 端口号 |
getLocale() | 用户的语言环境 |
getProtocol() | 协议,http协议 |
getQueryString() | 查询字符串 |
getRemotePort() | 远程端口,即客户端端口 |
getRemoteUser() | 远程用户 |
getRequestedSessionId() | 客户端的 Session 的 ID |
getRequestURI() | 用户请求的 URL |
getScheme() | 协议头,例如 http |
getServerName() | 服务器名称 |
getServerPort() | 服务器端口 |
getServletPath() | Servlet 路径 |
HttpServletRequest 测试:获取表单信息
- 登录页面
<form action="${pageContext.request.contextPath}/login" method="post">
<p>
<label for="username">用户名:</label>
<input type="text" id="username" name="username">
</p>
<p>
<label for="password">密码:</label>
<input type="password" id="password" name="password">
</p>
<p>
<input type="checkbox" name="hobbies" value="运动">运动
<input type="checkbox" name="hobbies" value="阅读">阅读
<input type="checkbox" name="hobbies" value="购物">购物
<input type="checkbox" name="hobbies" value="电影">电影
</p>
<input type="submit">
</form>
- servlet
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 设置请求的编码
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:" + username);
System.out.println("password:" + password);
System.out.println("========================");
System.out.println(Arrays.toString(hobbies));
System.out.println("========================");
System.out.println(req.getContextPath());
// 通过请求转发
// 这里的 / 代表当前的web应用
req.getRequestDispatcher("/success.jsp").forward(req, resp);
}
- 注册 servlet
<servlet>
<servlet-name>login</servlet-name>
<servlet-class>com.study.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>login</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
getRequestDispatcher 转发的两种方式:
-
request.getRequestDispatcher(“url”)
:- 可以是相对路径,或绝对路径;
-
this.getServletContext().getRequestDispatcher(“url”)
:- 只能是绝对路径;
-
获取前端传递的参数
-
getParameter()
:一个参数值; -
getParameterValues()
:多个参数值,数组;
-
七、Cookie、Session
7.1 会话
- 会话:用户打开浏览器,打开很多超链接,访问多个 Web 资源,关闭浏览器,这个过程可以称之为会话;
- 有状态会话:用户访问服务器,下次再访问时,服务器知道这个用户访问过,即有状态会话;
- 服务器如何知道客户端访问过:
- 服务端给客户端一个信件,客户端下次访问服务端,带上信件就可以; cookie
- 服务器登记客户端信息,下次客户端访问时,由服务器来匹配客户端; seesion
7.2 保存会话的两种技术
- cookie:客户端技术(响应,请求);
- session:服务器技术,把信息或者数据,放在 Session 中;
- 常见应用:网站登录后,下次不用再登录,可以直接访问;
7.3 Cookie
-
从请求中拿到 cookie 信息;
-
服务器响应给客户端 cookie;
-
测试:Cookie
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 解决乱码问题
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
resp.setContentType("text/html");
// 获取响应流
PrintWriter out = resp.getWriter();
// 服务器从客户端请求中获取Cookie,返回数组(Cookie存在多个)
Cookie[] cookies = req.getCookies();
// 判断Cookie是否存在
if (cookies != null) {
// 如果Cookie存在
out.write("上一次访问时间:");
// 遍历 cookies
for (int i = 0; i < cookies.length; i++) {
Cookie cookie = cookies[i];
// 获取Cookie的名字
if (cookie.getName().equals("lastLoginTime")) {
// 获取Cookie的值,并转化成长整型(时间戳)
Long lastLoginTime = Long.parseLong(cookie.getValue());
// 将时间戳转换为日期格式
Date date = new Date(lastLoginTime);
out.write(date.toLocaleString());
}
}
} else {
out.write("第一次访问!");
}
// 服务器给客户端响应(发送)一个Cookie
// System.currentTimeMillis()+"":获取时间,并转成字符串
Cookie cookie = new Cookie("lastLoginTime", System.currentTimeMillis() + "");
// 设置Cookie有效期,1天
cookie.setMaxAge(24 * 60 * 60);
resp.addCookie(cookie);
}
- Cookie 常用方法:
// 获得Cookie
Cookie[] cookies = req.getCookies();
// 获得cookie中的key
cookie.getName();
// 获得cookie中的vlaue
cookie.getValue();
// 新建一个cookie(设置新的cookie)
new Cookie("lastLoginTime", System.currentTimeMillis()+"");
// 设置cookie的有效期1天
cookie.setMaxAge(24*60*60);
// 响应(发送)给客户端一个cookie
resp.addCookie(cookie);
- cookie:一般会保存在本地的用户目录下 appdata;
- cookie 存储上限:
- 一个 Cookie 只能保存一个信息;
- 一个 Web 站点,可以给浏览器发送多个 cookie(最多20个);
- Cookie 大小有限制 4kb;
- 浏览器上限:300 个 Cookie;
- 删除 Cookie:
- 不设置有效期,关闭浏览器,自动失效;
- 设置有效期时间为 0 ;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 创建另一个Cookie,名字与之前的必须相同(键不能重复)
Cookie cookie = new Cookie("lastLoginTime", System.currentTimeMillis() + "");
// 设置Cookie有效期为0
cookie.setMaxAge(0);
resp.addCookie(cookie);
}
- Cookie 传递中文数据时乱码,解决方法:编码、解码
// 对中文Value值编码
URLEncoder.encode("中文的Cookie数据","utf-8")
// 解码
URLDecoder.decode(cookie.getValue(),"UTF-8")
7.4 Session(重点)
- 什么是 Session:
- 服务器会给每一个用户(浏览器),创建一个 Seesion 对象;
- 一个 Seesion 独占一个浏览器,只要浏览器没有关闭,这个 Session 就存在;
- 用户登录之后,整个网站都可以访问;
- Session 常用方法:
方法名 | 说明 |
---|---|
getCreationTime():long | 返回 Session 的创建时间 |
getId():String | 返回 Session 的唯一 ID |
getLastAccessedTime():long | 返回 Session 的最后活跃时间 |
getServletContext():ServletContext | 获取 Servlet 上下文(Web 服务) |
getMaxInactiveInterval():int | 返回 Session 的超时时间,秒 |
setMaxInactiveInterval(int interval) | 设置 Session 的超时时间,秒 |
getAttribute(String name):Object | 返回 Session 属性 |
setAttribute(String name, Object value) | 设置 Session 属性 |
getAttributeNames():Enumeration | 返回 Session 中存在的属性名 |
removeAttribute(String name) | 移除 Session 属性 |
invalidate() | 注销 |
isNew():boolean | 判断 Session 是否是新创建 |
- Session 和 Cookie 的区别:
- Cookie 是把用户的数据,写给浏览器,由 浏览器保存(可以多个);
- Session 把用户的数据,写到 用户独占 Session 中(每个浏览器一个Session),服务器端保存(保存重要的信息,减少服务器资源的浪费);
- Session 对象由 服务器创建;
- 使用场景:
- 保存一个登录用户的信息;
- 购物车信息;
- 在整个网站中,经常会使用的数据,将它保存在 Session 中;
Session 测试:
- Servlet 1:存储 Session 的数据;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 解决乱码
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
resp.setContentType("text/html");
// 获取Session
HttpSession session = req.getSession();
// Session中存值
session.setAttribute("name", "Session测试");
// Session 中存储对象
session.setAttribute("name1", new Person("学生1",20));
// 获取Session的ID
String id = session.getId();
//Session创建的时候,把Session ID响应(发送)给了Cookie
// Cookie cookie = new Cookie("JSESSIONID",sessionId);
// resp.addCookie(cookie);
// 判断Session是不是新创建
if (session.isNew()) {
resp.getWriter().write("Session 创建成功,ID:" + id);
} else {
resp.getWriter().write("Session ID:" + id);
}
}
- Servlet 2:获取 Session 中的数据,并响应(发送)给客户端
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 解决乱码
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
resp.setContentType("text/html");
// 获取Session
HttpSession session = req.getSession();
// 获取Session的值
String name = (String) session.getAttribute("name");
// 获取Session存储的对象
Person name1 = (Person) session.getAttribute("name1");
// 把Session的值响应(发送)给客户端
resp.getWriter().write("name:" + name);
resp.getWriter().write("<br/>");
resp.getWriter().write("name1:" + name1);
// 输出到控制台测试
System.out.println(name);
System.out.println("==========================");
System.out.println(name1);
}
- Servlet 3:移除 Session 属性、注销
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取Session
HttpSession session = req.getSession();
// 移除 Session 属性
session.removeAttribute("name");
// 手动注销Session:注销后,会重新生成Session ID
session.invalidate();
}
- 注册 Servlet:
web.xml
; - 会话自动过期:
web.xml
配置;
<!--设置Session默认的失效时间-->
<session-config>
<!--3分钟后Session自动失效,以分钟为单位-->
<session-timeout>3</session-timeout>
</session-config>
-
多用户访问:
八、JSP
8.1 什么是 JSP
- JSP:
Java Server Pages
,java 服务器端页面,和 Servlet 一样,用于动态Web。 - 特点:
- JSP 类似 HTML;
- 区别:
- HTML 只给用户提供 静态数据;
- JSP 页面中,可以嵌入 JAVA 代码,为用户提供 动态数据;
8.2 JSP原理
分析 JSP 执行的过程:
-
JSP 代码层面:查看打包后的文件,没有发生变化;
-
服务器内部分析:
-
Tomcat 服务器中有 work 目录;
-
IDEA 中使用 Tomcat,会在 IDEA 的 Tomcat 中生产 work 目录:
-
Catalina 目录下,生成 java 文件:
-
结论:
- 浏览器向服务器发送请求,不管访问什么资源,其实都是在访问 Servlet;
- JSP 最终也会被转换成为一个 Java 类;
- JSP 本质上就是 Servlet;
- JSP 的三个方法:
// 初始化
public void _jspInit() {
}
// 销毁
public void _jspDestroy() {
}
// JSPService
public void _jspService(HttpServletRequest request, HttpServletResponse response) {
}
JSP 的执行过程
-
判断请求:
- GET;
- POST;
-
内置一些对象:
// 页面上下文 final jakarta.servlet.jsp.PageContext pageContext; // session jakarta.servlet.http.HttpSession session = null; // applicationContext ServletContext改名 final jakarta.servlet.ServletContext application; // 配置 final jakarta.servlet.ServletConfig config; // out:输出 jakarta.servlet.jsp.JspWriter out = null; // page:当前 final java.lang.Object page = this; // 请求 HttpServletRequest request // 响应 HttpServletResponse response
-
输出页面前,增加的代码:
// 设置响应的页面类型 response.setContentType("text/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;
-
以上的对象,可以在 JSP 页面中直接使用;
-
流程图:
-
JSP 页面输出格式:
-
JAVA 代码就会原封不动的输出;
-
HTML代码,会被转换为:
out.write("<html>\r\n");
8.3 JSP 基础语法
-
JSP 在 Java 的基础上,增加了扩充的语法(了解);
-
JSP 表达式:
<%= %>
<%--表示注释--%> <%-- JSP 表达式: 作用:将程序的输出内容,输出到客户端 格式:<%= 变量或者表达式%> --%> <%--输出当前时间--%> <%= new java.util.Date()%>
-
JSP 脚本片段:
<% %>
<%--jsp脚本片段--%> <% int sum = 0; for (int i = 1; i <= 100; i++) { sum += i; } out.println("<h1>Sum=" + sum + "</h1>"); %>
-
JSP 脚本片段的再实现:
<%--JSP 脚本片段的再实现--%> <% int x = 10; out.println(x); %> <p>这是一个JSP文档</p> <% int y = 2; out.println(y); %> <hr> <%--在代码嵌入HTML元素--%> <% for (int i = 0; i < 5; i++) { %> <h1>Hello,World <%=i%></h1> <% } %>
-
JSP 声明:
<%! %>
- JSP 声明,被 编译到 JSP 生成的 Java 类中,其他代码,被生成到
_jspService
方法中,在 JSP,嵌入 Java 代码即可;
<%--JSP声明--%> <%! static { System.out.println("Loading Servlet!"); } private int globalVar = 0; public void test() { System.out.println("进入了方法test"); } %>
- JSP 声明,被 编译到 JSP 生成的 Java 类中,其他代码,被生成到
-
JSP 的注释:
<%-- --%>
不会在客户端显示,HTML 的注释,会显示到客户端;
8.4 JSP 指令
- 格式:
<%@ 指令名称 属性1="属性值1" 属性2="属性值2" ... 属性n="属性值n" %>
- page 指令:
- 定义网页依赖属性,比如脚本语言、error页面、缓存需求等;
- 格式:
<%@ page ... %>
;
<%--设置文件格式和编码格式--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--导入包--%>
<%@ page import="java.util.Date" %>
<%--自定义错误页面--%>
<%@ page errorPage="error/500.jsp" %>
- 通过
web.xml
配置跳转页面:
<error-page>
<!--错误代码-->
<error-code>404</error-code>
<!--设置对应跳转页面-->
<location>/error/404.jsp</location>
</error-page>
- 属性说明:
属性 | 描述 |
---|---|
buffer | 指定 out 对象使用缓冲区的大小 |
autoFlush | 控制 out 对象的 缓存区 |
contentType | 指定当前 JSP 页面的MIME类型和字符编码 |
errorPage | 指定当 JSP 页面发生异常时需要转向的错误处理页面 |
isErrorPage | 指定当前页面是否可以作为另一个 JSP 页面的错误处理页面 |
extends | 指定 servlet 从哪一个类继承 |
import | 导入要使用的 Java 类 |
info | 定义 JSP 页面的描述信息 |
isThreadSafe | 指定对 JSP 页面的访问是否为线程安全 |
language | 定义 JSP 页面所用的脚本语言,默认是 Java |
session | 指定 JSP 页面是否使用 session |
isELIgnored | 指定是否执行 EL 表达式 |
isScriptingEnabled | 确定脚本元素能否被使用 |
- Include 指令
- 静态包含(统一编译):会将两个页面以静态方式编译,合二为一;
- 格式:
<%@ include ... %>
;
<%@ include file="included.jsp"%>
<%--
JSP标签方式拼接
jsp:include:拼接页面,每个页面还是单独的整体
--%>
- Taglib 指令
- 用于在 JSP 页面中导入标签库(JSP 标准标签库、第三方、自定义) ;
- 格式:
<%@ taglib ... %>
;
<%--核心标签(c)是最常用的 JSTL标签 --%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
8.5 JSP 内置对象及作用域
-
9 大内置对象:
- PageContext:存东西;
- Request:存东西;
- Session 存东西;
- Application 【SerlvetContext】 存东西;
- Response;
- config 【SerlvetConfig】;
- out;
- page,不用了解 ;
- exception;
-
作用域图示:
-
测试:在对象中存值,并测试取值
<%--内置对象:存入属性值--%>
<%
// 保存的数据只在一个页面中有效
pageContext.setAttribute("name1", "pageContext");
// 保存的数据只在一次请求中有效,请求转发会携带这个数据
request.setAttribute("name2", "request");
// 保存的数据只在一次会话中有效,从打开浏览器到关闭浏览器
session.setAttribute("name3", "session");
// 保存的数据只在服务器中有效,从打开服务器到关闭服务器
application.setAttribute("name4", "application");
%>
<%--取出属性值--%>
<%
// JSP代码内的java代码,必须保证语法的正确
// 通过寻找方式取值,从底层到高层(作用域)
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");
%>
<%--测试转发携带数据--%>
<%
pageContext.forward("/jsp3.jsp");
// 作用等同
// request.getRequestDispatcher("/index.jsp").forward(request,response);
%>
<%--使用EL表达式输出 ${变量}--%>
<h1>取出的值为:</h1>
<h3>${name1}</h3>
<h3>${name2}</h3>
<h3>${name3}</h3>
<h3>${name4}</h3>
<h3>${name5}</h3> // 未创建变量,页面不显示
<%--EL表达式会过滤 null值,普通表达式不能--%>
<h3><%= name5 %></h3> // 未创建变量,页面显示 null
- 测试:在另一个页面中,取值上个页面的值
<h1>取出的值为:</h1>
<h3>${name1}</h3>
<h3>${name2}</h3>
<h3>${name3}</h3>
<h3>${name4}</h3>
<h3>${name5}</h3>
对象作用域及应用场景:
- request:客户端向服务器发送请求,产生的数据,用户看完就没用了;
- 比如:新闻;
- session:客户端向服务器发送请求,产生的数据,用户用完一会还有用;
- 比如:购物车;
- application:客户端向服务器发送请求,产生的数据,一个用户用完了,其他用户还可能使用;
- 比如:聊天数据;
8.6 JSP 标签、JSTL 标签、EL 表达式
- EL 表达式:
${ }
- 获取数据;
- 执行运算;
- 获取 web 开发的常用对象;
- JSP 标签:
<%--包含页面--%>
<jsp:include page="jsp3.jsp"></jsp:include>
<%--
http://localhost:8080/test.jsp?name=liu&age=20
--%>
<%--转发、携带参数转发--%>
<jsp:forward page="/test.jsp">
<jsp:param name="name" value="liu"></jsp:param>
<jsp:param name="age" value="20"></jsp:param>
</jsp:forward>
- JSTL 标签:
- JSTL 标签库的使用,是为了弥补 HTML 标签的不足;
- 自定义许多标签,标签的功能和 Java 代码一样
- 分类:核心标签、格式化标签、SQL标签、XML 标签;
核心标签(掌握):
- 引用核心标签库的语法:使用前,必须在页面头部引用
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
-
标签说明:
-
JSTL 标签库使用步骤:
- 引入对应的 taglib;
- 使用其中的方法;
- 复制依赖包到 Tomcat lib 目录下,否则会报错:JSTL 解析错误;
标签库添加依赖说明:Tomcat 版本
10.0.16
-
在
pom.xml
文件中,增加依赖,导入标签库依赖;<!--标签库依赖--> <dependency> <groupId>org.apache.taglibs</groupId> <artifactId>taglibs-standard-spec</artifactId> <version>1.2.5</version> </dependency> <!-- 如果不够,再补充下面这个依赖 <dependency> <groupId>org.apache.taglibs</groupId> <artifactId>taglibs-standard-impl</artifactId> <version>1.2.5</version> </dependency> -->
- 依赖关系图:
-
将导入的 jar 包,复制到 Tomcat 服务器的
lib
目录下:(避免 JSTL 解析错误)-
jakarta.servlet.jsp.jstl-api-2.0.0.jar
; -
jakarta.servlet.jsp.jstl-2.0.0.jar
; -
taglibs-standard-spec-1.2.5.jar
; -
Tomcat 的 lib 目录:
-
- 标签测试:
<c: if>
<%--表单测试:以get方式,提交给当前页面--%>
<form action="coreif.jsp" method="get">
<%--
EL表达式获取表单中的数据:${param.参数名}
--%>
<input type="text" name="username" value="${param.username}">
<input type="submit" value="登录">
<%--如果用户名=admin,登录成功 var 为布尔值--%>
<c:if test="${param.username=='admin'}" var="isAdmin">
<c:out value="管理员登录!"/>
</c:if>
<%--输出布尔值,注意自闭合标签--%>
<c:out value="${isAdmin}"/>
</form>
- 标签测试:
<c:choose>
、<c:when>
<%--定义一个变量score,值为85--%>
<c:set var="score" value="85"/>
<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>
<%
// 创建集合,并添加数据,注意:索引从0开始
ArrayList<String> people = new ArrayList<>();
people.add(0, "张三");
people.add(1, "李四");
people.add(2, "王五");
people.add(3, "赵六");
people.add(4, "田七");
// 把集合添加到request(请求)中
request.setAttribute("list", people);
%>
<%--
var:每一次遍历出来的变量
items:要遍历的对象
begin:哪里开始
end:到哪里
step:步长
--%>
<%--从请求中遍历数据,注意:被取值的变量要包含在 ${} 中}--%>
<c:forEach var="student" items="${list}">
<c:out value="${student}"/><br/>
</c:forEach>
<hr/>
<c:forEach var="student" items="${list}" begin="1" end="3" step="1">
<c:out value="${student}"/><br/>
</c:forEach>
可能遇到的问题
- JavaWeb 项目不生成 target 目录:
- 解决方法:查看链接
九、JavaBean
9.1 实体类
- 实例类:一般和数据中的 表结构一一对应;
- JavaBean 有特定的写法:
- 必须要有一个无参构造;
- 属性必须私有化;
- 必须有对应的 get/set 方法;
9.2 字段映射
-
JavaBean 一般用来和数据库的字段做映射(ORM);
-
ORM :对象关系映射:
- 表 ---> 类;
- 字段 ---> 属性;
- 行记录 ----> 对象;
-
数据表示例:people
id name age address 1 张三 20 北京 2 李四 18 上海 3 王五 22 深圳 // 类-->表 class People{ private int id; // 属性-->字段 private String name; private int age; private String address; } class A{ new People(1,"张三",20,"北京"); // 对象-->一条记录 new People(2,"李四",18,"上海"); new People(3,"王五",22,"深圳"); }
-
测试:
<%
// People people = new People();
// people.setId();
// people.setName();
// people.setAge();
// people.setAddress();
%>
<%--创建对象--%>
<jsp:useBean id="people" class="com.study.pojo.People" scope="page"/>
<%--给对象赋值:数据库一条记录--%>
<jsp:setProperty name="people" property="id" value="1"/>
<jsp:setProperty name="people" property="name" value="张三"/>
<jsp:setProperty name="people" property="age" value="20"/>
<jsp:setProperty name="people" property="address" value="北京"/>
<%--取值--%>
<%--表达式方式:<%=people.getAddress()%>--%>
ID:<jsp:getProperty name="people" property="id"/><br/>
姓名:<jsp:getProperty name="people" property="name"/><br/>
年龄:<jsp:getProperty name="people" property="age"/><br/>
地址:<jsp:getProperty name="people" property="address"/><br/>
十、MVC 三层架构
- MVC:
-
Model
:模型,指实体类对应数据库中的字段; -
view
:视图,指 JSP 页面; -
Controller
:控制器,指 Servlet,负责跳转页面;
10.1 以前的架构
-
Servlet 和 JSP 都可以写 Java 代码;
- Servlet 专注于处理请求,以及控制视图跳转;
- JSP 专注于显示数据;
-
用户直接访问控制层,控制层,可以直接操作数据库;
-
Servlet 的代码中:处理请求、响应、视图跳转、处理JDBC、处理业务代码、处理逻辑代码;
- Servlet --> CRUD --> 数据库;
- 弊端:程序臃肿,不利于维护;
-
架构:没有什么是加一层解决不了的,如果不行,再加一层;
/* 程序员调用 | JDBC | Mysql Oracle SqlServer... */
10.2 MVC 三层架构
-
架构图:
-
Model:模型
- 业务处理:业务逻辑(Service);
- 数据持久层:CRUD(Dao);
-
View:视图
- 展示数据;
- 提供链接发起 Servlet 请求 (a,form,img…)
-
Controller(Servlet):控制器
- 接收用户的请求:(req:请求参数、Session 信息….);
- 交给业务层处理对应的代码;
- 控制视图的跳转;
登录 --> 接收用户的登录请求 --> 处理用户的请求(获取用户登录的参数,username,password)--> 交给业务层处理登录业务(判断用户名密码是否正确:事务)--> Dao层(查询用户名和密码是否正确) --> 数据库
十一、Filter(重点):过滤器
11.1 Filter 概述
-
Filter:过滤器,过滤网站数据;
-
应用:
- 处理中文乱码;
- 登录验证...;
11.2 Filter 开发步骤
-
导包:
-
注意:不要导错包,选 Servlet 包;
-
-
编写过滤器:
- 实现 Filter 接口, 重写对应的方法;
// 通过注解,设置过滤路径:show下的所有请求都被过滤 // 也可以通过 web.xml 文件配置 @WebFilter("/show/*") public class CharacterEncodingFilter implements Filter { // 初始化:web服务器启动,过滤器就初始化了,随时等待过滤对象出现 @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("CharacterEncodingFilter 初始化"); } // FilterChain : 链 /* 1.过滤中的所有代码,在过滤特定请求的时候,都会执行 2.必须要让过滤器继续执行 filterChain.doFilter(request,response); */ @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("过滤器执行前"); // 设置过滤执行的内容 servletRequest.setCharacterEncoding("utf-8"); servletResponse.setCharacterEncoding("utf-8"); servletResponse.setContentType("text/html;charset=UTF-8"); // 让请求继续执行,如果不写,程序到这里就被拦截,停止! filterChain.doFilter(servletRequest, servletResponse); System.out.println("过滤器执行后"); } // 销毁:web服务器关闭的时候,过滤器才会销毁 @Override public void destroy() { System.out.println("CharacterEncodingFilter 销毁"); } }
-
在
web.xml
中配置 Filter:- 也可以通过注解方式:
@WebFilter("/show/*")
<filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>com.study.filter.CharacterEncodingFilter</filter�class> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <!--只要是 /show 下的任何请求,会经过这个过滤器--> <url-pattern>/show/*</url-pattern> <!--过滤所有内容,不建议使用--> <!--<url-pattern>/*</url-pattern>--> </filter-mapping>
- 也可以通过注解方式:
十二、监听器 Listener(了解)
- 实现监听器的接口(有 N 种);
- 编写一个监听器,实现监听器的接口:
// 统计网站在线人数:统计session
// 注解方式:注册监听,也可以通过web.xml设置
@WebListener
public class OnlineCountListener implements HttpSessionListener {
// 创建session监听:监测程序执行
// 一旦创建Session,就触发一次这个事件
@Override
public void sessionCreated(HttpSessionEvent se) {
// 获取上下文:作用域提升到最大
ServletContext context = se.getSession().getServletContext();
// 获取在线人数
Integer onlineCount = (Integer) context.getAttribute("onlineCount");
// 如果为空,赋值为1
if (onlineCount == null) {
onlineCount = 1;
} else {
// 不为空,值自增
onlineCount++;
}
// 将属性存储到上下文
context.setAttribute("onlineCount", onlineCount);
System.out.println("监听初始化");
}
// 销毁session监听
// 一旦销毁Session,就触发一次这个事件
@Override
public void sessionDestroyed(HttpSessionEvent se) {
// 过程与创建相反
ServletContext context = se.getSession().getServletContext();
Integer onlineCount = (Integer) context.getAttribute("onlineCount");
// 如果为空,赋值为0
if (onlineCount == null) {
onlineCount = 0;
} else {
onlineCount--;
}
// 将属性存储到上下文
context.setAttribute("onlineCount", onlineCount);
System.out.println("监听销毁");
}
/*
Session销毁:
1.手动销毁:getSession().invalidate();
2.自动销毁:设置web.xml
*/
}
- JSP 页面获上下文属性:
<%=this.getServletConfig().getServletContext().getAttribute("onlineCount")%>
-
web.xml
中注册监听器:或用注解方式:@WebListener
<listener>
<listener-class>com.study.listener.OnlineCountListener</listener-class>
</listener>
十三、过滤器、监听器常见应用
13.1 监听器在 GUI 中的应用
- GUI 基础
- 实例:监听器在 GUI 编程中使用;
public class TestPanel {
public static void main(String[] args) {
// 新建一个窗体
Frame frame = new Frame("GUI 监听测试");
// 面板
Panel panel = new Panel(null);
// 设置窗体的布局
frame.setLayout(null);
// 设置背景颜色
frame.setBounds(300, 300, 500, 500);
frame.setBackground(new Color(217, 217, 217));
panel.setBounds(50, 50, 300, 300);
// 设置背景颜色
panel.setBackground(new Color(70, 142, 192));
frame.add(panel);
frame.setVisible(true);
// 监听事件:监听关闭事件
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
}
13.2 Filter 实现权限拦截:
- 过滤器在用户登录中的使用;
- 用户登录后,向 Sesison 中放入用户的数据;
- 进入主页时,判断用户是否已经登录,未登录,进行页面跳转;
测试:
- JSP 页面:
<%--login.jsp--%>
<form action="<%= request.getContextPath()%>/login" method="post">
<input type="text" name="username">
<input type="submit">
</form>
<%--error.jsp--%>
<h3>没有权限,用户名错误</h3>
<p><a href="<%= request.getContextPath()%>/login.jsp">返回登录页面</a></p>
<%--/sys/success.jsp--%>
<h1>登录成功</h1>
<p><a href="<%= request.getContextPath()%>/logout">注销</a></p>
- 常量类:便于后期管理
public class Constant {
public static final String USER_SESSION = "USER_SESSION";
}
- Servlet:登录
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取前端的请求参数
String username = req.getParameter("username");
if (username.equals("admin")) {
// 登录成功:把属性值存入session
req.getSession().setAttribute(Constant.USER_SESSION, req.getSession().getId());
// 获取路径后,再拼接地址
// 注意:如果路径用相对路径表示,”/“表示:http://localhost:8080,与转发的不同
resp.sendRedirect(req.getContextPath()+"/sys/success.jsp");
} else {
// 登录失败
// 请求转发
// req.getRequestDispatcher("/error.jsp").forward(req,resp);
// 重定向
resp.sendRedirect(req.getContextPath()+"/error.jsp");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
- Servlet:注销
@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取session
Object user_session = req.getSession().getAttribute(Constant.USER_SESSION);
// 如果不为空,移除 Session 属性
if (user_session != null) {
req.getSession().removeAttribute(Constant.USER_SESSION);
}
// 重定向
resp.sendRedirect(req.getContextPath() + "/login.jsp");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
- 过滤器:
// 过滤所有sys下的请求
@WebFilter("/sys/*")
public class SysFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
// 类型转换:获取session,重定向
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
// 过滤:所有session为空,跳转页面
if (request.getSession().getAttribute(Constant.USER_SESSION) == null) {
response.sendRedirect(request.getContextPath() + "/error.jsp");
}
chain.doFilter(req, resp);
}
@Override
public void destroy() {
}
}
- 注册:
web.xml
配置,或通过注解;
过滤器实现,不同用户登录不同页面
- 思路:建立不同等级用户文件夹及页面,如:VIP1、VIP2、VIP3...;
- 过滤器里设置,不同等级对应的跳转页面;
// 通过对象属性判断
if (request.getSession().getAttribute(Constant.USER_SESSION).Level == VIP1) {
response.sendRedirect( "vip1/index.jsp");
}
if (request.getSession().getAttribute(Constant.USER_SESSION).Level == VIP2) {
response.sendRedirect( "vip2/index.jsp");
}
if (request.getSession().getAttribute(Constant.USER_SESSION).Level == VIP3) {
response.sendRedirect( "vip3/index.jsp");
}
十四、JDBC
14.1 实验环境搭建
-
创建 MySQL 数据库:相关链接
-
导入 JDBC 依赖:
<!--MySQL 依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<!--druid 依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
- IDEA 中连接数据库:相关链接
14.2 JDBC 固定步骤
- 加载驱动;
- 连接数据库,代表数据库;
- 向数据库发送 SQL 的对象 Statement : CRUD;
- 编写 SQL(根据业务,不同的 SQL);
- 执行 SQL;
- 关闭连接;
14.3 事务
14.4 Junit 单元测试
-
Junit 依赖:
<!--单元测试--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> </dependency>
-
使用方式:
- @Test 注解,只有在方法上有效;
- 只要加了这个注解的方法,不需要 main 方法,就可以直接运行;
@Test public void test(){ System.out.println("Hello World"); }
-
错误时,报红色信息;
网友评论