JavaWeb02
6. Servlet
6.1 Servlet简介
- Servlet是sun公司开发动态web的一门技术。
- Sun在这些API中提供了一个接口叫做:Servlet,如果你想开发一个Servlet程序,只需要完成两个小步骤:
- 编写一个类,实现Servlet接口。
- 把开发好的Java类部署到Web服务器中。
- Servlet项目由一个父项目和众多子项目构成,servlet接口在子项目中实现。
6.2 父项目与子项目
关于父子工程的理解:
- 父项目的配置文件中存在如下标签,记录了子项目的个数:
<modules>
<module>servlet-01</module>
</modules>
- 相对应的,子项目中存在如下标签,记录了父项目的情况:
<parent>
<artifactId>JavaWeb_Servlet</artifactId>
<groupId>com.zfy</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
- 父项目中的java,子项目可以直接使用(继承与多态)
son extends father
- Servlet接口于子项目中实现。
6.3 Hello Servlet
对于初学者而言,步骤较为复杂,总而言之分为如下几步:
- 创建父工程
- 创建子工程
- 编写一个servlet程序,即在子工程中实现servlet接口
- 配置服务器,启动servlet。
6.3.1 父工程的创建
- 构建一个普通Maven项目(即不启用Maven模板。详细参考如上节所示)
- 删除src文件夹,其余不可删。
- 所得到的空工程即为Maven父工程(也称作主工程),可以在此创建众多子工程(即Module)。
- 在父工程中导入需要父/子工程需要的依赖,根据需求查询有哪些依赖需要导入。
以JSP工程为例,在父工程的pom.xml文件中导入如下依赖:
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
</dependency>
- 至此,父工程创建完毕。
6.3.2 子工程的创建
- 在父工程文件夹处new一个module
- 子项目启用Maven模板创建。
- 如图,此处创建的servlet项目为子项目,父项目就是原项目。
- 重写子项目web.xml文件,将其改成最新的服务器配置:
该步骤内容查询自:Tomcat-conf-web.xml,复制粘贴即可。
- 在main文件夹下创建:java文件夹和resource文件夹,并标记。以此补子全项目结构(main下面有三个文件夹才算结构齐全)。
- 至此,子工程创建完毕。
6.3.3 在子工程中创建servlet程序
- servlet是一个接口程序,也就是一个java程序,其存放于main-java文件夹中。
- servlet接口在sun公司有两个默认的实现类:①HttpServlet,②GenericServlet
- 在(子项目)java文件中new一个package,并在该package下新建一个class。
- 继承HttpServlet类,基于此类实现servlet接口,编写servlet程序。这是通过方法重载实现的。
(servlet相关class关系说明图)
- ctrl+o呼出接口与方法菜单,写入需要重载的方法(继承自HttpServlet)。
- 重载的方法原始代码如下:
- 方法重载(以Get和Post为例):
// 由于get或者post只是请求实现的不同方式,因此可以相互调用。二者的业务逻辑是一致的。
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// ServletOutputStream outputStream = resp.getOutputStream();
PrintWriter writer = resp.getWriter(); // 响应流
writer.print("Hello,Servlet!");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
- 在子项目网页配置文件web.xml中编写Servlet映射
为什么需要映射?
我们写的是java程序,但是要通过浏览器访问,而浏览器需要连接web服务器。
所以我们需要:- 在web服务器中注册我们的Servlet程序(class类)。
- 提供一个浏览器可以访问的路径(mapping映射)
- 至此,子项目servlet程序创立完毕。
6.3.4 配置Tomcat服务
- Tomcat配置过程如3.4节所述
- 启动测试:右上角运行Tomcat9服务器。
- Tomcat编译之后产生target文件夹,内容如图所示:
(生成的target文件夹可以通过Maven-lifecycle-clean清除)
- 弹出Tomcat默认的js界面:Hello World!
- 输入我们设置的servlet映射地址,即可访问到我们编写的servlet class文件。
- 发布并访问一个简单的servlet程序到此结束
6.4 Servlet原理
(一)Servlet总体原理
- 典型的Servlet业务流程如下所示:
- 其中我们自己编写的实现类重构service方法。
(二)Mapping问题
Mapping即路径映射,是提供一个访问Servlet程序的路径,其规则如下:
- 一个Servlet程序可以指定任意个映射路径(一个或者多个)
- 一个Servlet可以指定通用的(通配符*)映射路径:
<servlet-mapping>
<!-- servlet的内部名称,与上面的保持一致-->
<servlet-name>hello</servlet-name>
<!-- servlet的访问路径.hello/* ,*为通配符,代表*号位置的任意输入都可以访问到该映射 -->
<url-pattern>/hello/*</url-pattern>
</servlet-mapping>
- 可以指定一些后缀或者前缀作为映射路径(正则式)
<servlet-mapping>
<!-- servlet的内部名称,与上面的保持一致-->
<servlet-name>hello</servlet-name>
<!-- servlet的访问路径,*为通配符,.zfy为后缀。代表*号位置的任意输入+.zfy后缀都可以访问到该映射 -->
<url-pattern>*.zfy</url-pattern>
</servlet-mapping>
6.5 ServletContext对象(类)
web容器在启动的时候,它会为每个web程序都创建一个对应的ServletContext对象,该对象代表了当前的web应用。
ServletContext对象是凌驾在所有Servlet程序之上的,同一个web应用的servlet程序共用同一个ServletContext对象。
6.5.1 Context对象实现数据共享
在任意Servlet中保存的数据,通过Context对象,可以在另一个Servlet中拿到。
- 在第一个Servlet文件中,通过在ServletContext对象中创建键值对保存信息:
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// this.getInitParameter(); 初始化参数
// this.getServletConfig(); 初始化配置
// this.getServletContext(); 获取Context对象,即上下文对象。
// ①获取ServlContex对象
ServletContext context = this.getServletContext();
// ②创建一个Servlet02中的对象(数据)
String username = "青梧成林";
// ③将该数据保存在了ServlContex对象中,以键值对的形式保存。
context.setAttribute("username", username);
System.out.println("Hello!");
}
}
- 在另一个Servlet文件中取出刚才保存的信息:
public class GetServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// ①获取Context对象
ServletContext context = this.getServletContext();
// ②将另一个servlet程序保存在Context对象中的键值对取出,这里输入键名即可。
// 键值对中保存的是object类型,将其强转为String对象。
String username = (String) context.getAttribute("username");
// ③将取到的值在网页上输出
resp.setContentType("text/html"); // 设置文本格式
resp.setCharacterEncoding("utf-8"); // 设置中文编码格式
resp.getWriter().print("username: " + username);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
-
给两个Servlet文件配置相关映射路径:
-
测试.先访问Servlet1文件,再访问Servlet2文件:
6.5.2 Context对象获取初始化参数
- web.xml中可能存在一些网页初始化所需要的参数:
<!-- 配置一些web应用初始化参数 -->
<!-- 数据库框架连接参数 -->
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
</context-param>
- 使用ServletContext对象获取初始化参数
public class ServletDemo03 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
String url = context.getInitParameter("url"); // 获得web初始化参数,这是配置在web.xml里面的
System.out.println("url: " + url); // Sout输出只能在idea后台给出,无法在网页中输出。
resp.getWriter().print(url); // resp对象的getWriter()方法获取网页输出对象,再用print()方法打出。
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
- 注册Servlet,并测试访问。
网页测试结果,顺利拿到了初始化参数url:
6.5.3 Context对象请求转发
(1)请求转发:
- 访问A网页,A网页将请求转发到C网页。结果:路径为A,实际访问页面为C(即A拿到了C)。
- 请求转发的状态码为:200
(2)重定向: - 访问A,A刷新路径为C,改为访问C。访问路径发生改变。
请求转发的实现:
- 编写Servlet程序,context.getRequestDispatcher 实现转发
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
// 请求转发分成两步来写
// RequestDispatcher requestDispatcher = context.getRequestDispatcher("/gp"); // 转发的请求路径
// requestDispatcher.forward(req, resp); // 调用forward实现请求转发
//合成一步来写:
context.getRequestDispatcher("/gp").forward(req,resp);
}
- 网页测试
6.5.4 Context对象读取资源文件
- main-resource文件夹下新建 **配置文件 **,命名为db.properties:
- 创建Servlet程序.通过编译后的资源地址获取Properties资源:
public class ServletDemo05 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// ①流通过资源地址获取properties对象
InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classer/db.properties");
// ②将资源读出,赋予对象
Properties prop = new Properties();
prop.load(is);
String user = prop.getProperty("username");
String pwd = prop.getProperty("password");
// ③在网页上写出
resp.getWriter().print("username: " + user +", " + "password: " + pwd);
}
- web注册并测试:
成功拿到资源:
6.6 HttpServletResponse对象(类)
web服务器接收到客户端的http请求,针对这个请求,分别创建一个代表请求的 HttpServletRequestd 对象 和
一个代表响应的 HTTPServletResponse 对象。
6.6.1 HttpServletResponse方法简单分类
响应的方法来自HttpServletResponse 和 ServletResponse两个类,简单分类如下:
- 负责向浏览器发送 数据 的方法:
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 setBufferSize(int var1);
void setHeader(String var1, String var2);
void addHeader(String var1, String var2);
void setIntHeader(String var1, int var2);
void addIntHeader(String var1, int var2);
void setStatus(int var1);
- 状态码(可以自己修改,但不建议)
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;
6.6.2 HttpServletResponse对象的常见应用
- 向浏览器输出信息(此处略)。
- 使用Response对象(方法)下载文件,步骤如下:
- 获取下载文件的路径。
- 获取下载文件的文件名。。
- 设置浏览器支持下载我们的所需文件。
- 获取下载文件的输入流。
- 创建缓冲区。
- 获取OUTputStream对象。
- 将FileOutPutStream流写入到buffer缓冲区。
- 使用OutputStream将缓冲区中的数据输出到客户端。
类文件代码实现如下:
public class FileServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 使用Response对象(方法)下载文件,步骤如下:
// 1. 获取下载文件的 路径(绝对路径) ,从taget-classes-com文件夹中复制路径。
String realPath = "D:\\B-Working\\A-Software-work\\Code-works\\" +
"Java_Works\\JavaWeb_Servlet\\response\\target\\classes\\棉被王.jpg";
System.out.println("下载的文件路径: " + realPath);
// 2. 获取下载文件的 文件名 (重点是怎么拿到这个文件名,这里使用"/"为定位符,拿它后面的一位肯定是文件名)。
String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1);
System.out.println("文件名: " + fileName);
// 3. 设置浏览器支持(Content-Dispisition)下载我们的所需文件,此步为关键步骤。
// 如果是中文名称文件,则需要额外的转码操作,否则文件名不显示,以"___"代替。
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);
}
in.close(); // IO流对象一定要关闭
out.close();
}
(target文件夹下获取文件的绝对路径)
(成功的弹出文件下载窗口,英文文件名。)
(成功弹出中文下载窗口,中文文件名。)
- 使用Response对象(方法)设置网页验证码图片,代码如下:
仅仅作为参考,不要求掌握的过气技术。
package com.zfy.servlet;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
public class imageServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 让浏览器3秒自动刷新一次
resp.setHeader("refresh", "3");
// 在内存中创建一个图片
BufferedImage image = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB);
// 得到图片
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/jpeg"); // jpeg 多的这个 e ,是浏览器的意思。
// 浏览器默认存在缓存,现在取消这一项。
resp.setDateHeader("expires", -1); // -1 为不缓存
resp.setHeader("Cache-Control", "no-cache"); // 缓存策略为不缓存
resp.setHeader("program", "no-cache");
// 把图片写给浏览器
ImageIO.write(image, "jpg", resp.getOutputStream());
}
// 设置验证码获取的随机数
private String makeNum(){
Random random = new Random();
String num = random.nextInt(999999) + "";
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 6-num.length(); i++){
sb.append("0");
}
num = sb.toString() + num;
return num;
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
效果如图:
- 使用Response对象(方法)实现网页重定向(本小节唯一的重点)
本小节以 用户表单提交网页 实验实现重定向的功能(展示),其步骤如下:-
写出前端界面1,让用户提交表单,该界面指向Servlet程序,接受用户表单。
-
写出Servlet程序接受前端界面1用户提交的表单,并对表单进行处理。同时 重定向到 前端界面2.
-
前端界面2显示用户已经登录成功。
-
前端界面1,即(项目包)主网页:
-
Servlet程序接受前端界面1用户提交的表单,并对表单进行处理同时 重定向到 前端界面2:
-
前端界面2显示用户已经登录成功:
-
网页整体登录效果如下:
-
6.7 HttpServletRequest对象(类)
Request对象通常用于拿到用户的输入,即拿到前端信息。
6.7.1 获取前端传递的参数(用户提交的表单)
本案例依然通过三个网页(程序)实现,其中2两个前端页面和1个Servlet程序。
- 写出前端界面1,让用户提交表单,该界面指向Servlet程序,接受用户表单。
(注意action后的文件定位,写死前缀${pageContext.request.contextPath})
<%--
Created by IntelliJ IDEA.
User: 青梧成林
Date: 2022/7/27
Time: 11:20
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录</title>
</head>
<body>
<h1>登录</h1>
<div style="text-align: center">
<form action="${pageContext.request.contextPath}/login" method="post">
用户名:<input type="text" name="username"> <br>
密码: <input type="password" name="password"> <br>
爱好:
<input type="checkbox" name="hobbies" value="梅琳娜">梅琳娜
<input type="checkbox" name="hobbies" value="菈妮">菈妮
<input type="checkbox" name="hobbies" value="菲雅">菲雅
<input type="checkbox" name="hobbies" value="大树守卫">大树守卫
<input type="checkbox" name="hobbies" value="爱蜜莉雅">爱蜜莉雅
<br>
<input type="submit">
</form>
</div>
</body>
</html>
- 写出Servlet程序接受前端界面1用户提交的表单,并对表单进行处理。同时 请求转发到 前端界面2.
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 设置请求和响应的编码集
resp.setCharacterEncoding("utf-8");
req.setCharacterEncoding("utf-8");
// 使用请求request拿到前端送回的信息
String username = req.getParameter("username");
String password = req.getParameter("password");
String [] hobbies = req.getParameterValues("hobbies");
// 打印拿到的信息
System.out.println("=======================================");
System.out.println(username);
System.out.println(password);
System.out.println(Arrays.toString(hobbies));
System.out.println("=======================================");
// 通过 请求转发 到另一个前端页面,注意,是转发不是重定向。
// 在 请求转发 语句中,"/"即代表了当前项目(位置),仅仅使用"/"+目标jsp文件即可。
req.getRequestDispatcher("/success.jsp").forward(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 使用doGet即可
doGet(req, resp);
}
}
- 前端界面2显示用户已经登录成功。
<%--
Created by IntelliJ IDEA.
User: 青梧成林
Date: 2022/7/27
Time: 11:45
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>提交成功</h1>
</body>
</html>
- 效果如下
页面用户提交表单
提交成功
后台拿到信息
7. Cookie,Session
Cookie和Session都是用于保存用户(访问)信息的技术。
7.1 会话
会话 维基百科定义:
In computer science, in particular networking, a session is a temporary and interactive information interchange between two or more communicating devices,
or between a computer and user (see login session). A session is established at a certain point in time, and then torn down - brought to an end - at some later point.
An established communication session may involve more than one message in each direction. A session is typically stateful,
meaning that at least one of the communicating parties needs to hold current state information and save information about the session history in order to be able to communicate,
as opposed to stateless communication, where the communication consists of independent requests with responses.
- 在CS学科尤其是网络中,会话是两个或多个通讯设备之间的临时的信息交互行为。
- 最常见的会话是浏览器访问,从打开到关闭的这个过程称为(一次)会话。其中可以点击很多超链接,访问多个web资源,
会话的引导问题:一个网站怎么证明你来过?
- 服务器发给客户端(浏览器)一个 信件 ,客户端后续访问 **带上信件 **即可。 Cookie
- 服务器对你的访问进行** 登记**,下次客户端再次访问时进行 **信息匹配 **。 Session
7.2 保存会话的两种技术
** (1)Cookie **
- 客户端技术(响应, 请求)
** (2)Session **
- 服务器技术,利用这个技术可以保存用户的会话信息,我们可以把信息储存在Session中。
7.3 Cookie
- Cookie是服务器发给浏览器的信息认证,上面保存了用户(浏览器)信息。
- 如前面所述,Cookie是Servlet对象,由 请求 向服务器拿到Cookie对象。
- Cookie对象是一个数组,可能返回多个值。
Cookie[] cookies = req.getCookies();
7.4 Session(重点)
-
什么是Session?
- Session是Servlet对象,由请求req.getSession从服务器端拿到。
- 服务器会给每一个用户(即浏览器)创建一个Session对象;
- 一个Session独占一个浏览器,只要浏览器没有关闭,这个Session就一直存在;
- 用户登录之后,整个网站都可以访问。即Session的作用是保存用户信息。
-
Session和Cookie的区别
- Cookie是把用户的数据写给用户的浏览器,在浏览器中保存(即客户端保存),可以保存多个Cookie。
- Session是把对象的数据写在用户独占的Session中,由服务器(服务端保存),只保存重要的信息,减少服务器资源浪费。
8. JSP
Java Server Pages JSP: java服务器页面,和Servlet一样,用于动态web技术。
特点:
- JSP整体与HTML高度相似;
- JSP中可以嵌入Java代码,为用户提供动态数据;
8.1 JSP原理
思路:JSP是怎么执行的?
- JSP本质上也是一种Servlet,它是从Servlet继承而来。
- 服务器内部会生成一个jsp文件夹,以IDEA内使用Tomcat为例:
Tomcat内jsp文件夹路径如下:apache-tomcat\work\Catalina\localhost\ROOT\org\apache\jsp
10. MVC框架
什么是MVC?:Model, View, Controller, 即模型,视图,控制器。
10.1 早期框架模型
- 在MVC框架出现之前的早些年,用户直接访问控制层(Controller:Servlet),控制层可以直接操作数据库:
- 其结构示意简图如下:
架构问题与解决方案:
- Servlet-->CRUD-->数据库,即Servlet直接在数据库上进行操作。
此时Servlet内实现的操作有:处理请求、响应、视图跳转、处理JDBC、处理业务代码、处理逻辑代码...
导致Servlet中代码臃肿,不利于维护。 - 解决方案:没什么问题是加一层架构解决不了的!!!
10.2 MVC框架
MCV框架图如下:
(1)Model层
- 业务处理:业务逻辑,由service实现
- 数据持久层:CRUD,由Dao层实现
(2)View层
- 显示页面,即展示数据
- 提供链接发起Servlet请求(a, for, img, ...)
(3)Controller层
- 接受用户的请求:(req:请求参数,Session信息...)
- 交给业务层处理对应的代码
- 控制视图的跳转,即返回不同的页面。
典型的业务流程:
登录 ---> 接收用户的登录请求 ---> 处理用户的请求(即获取用户登录的参数,username,password等) --->
交给业务层处理登录业务(判断信息是否正确等) ---> Dao层查询用户名和密码是否正确 ---> 数据库交互
11. Filter过滤器(重点!)
过滤器的基本属性:
- Filter过滤器:以某种要求过滤网站的数据(包含需求和响应的双向数据)
- 按照需求可设置多个(层)过滤器,每个过滤器的功能不同,各司其职。如过滤浏览器垃圾请求、处理中文乱码问题等
- 不同的过滤器有不同的过滤目标(对象即servlet文件之类的),在web.xml中设置filter的对象范围。
- 过滤器(整体)的作用是双向的,既可以过滤来自Web服务器的请求,也可以过滤来自Servlet的响应。
- 过滤器的生命周期:Filter在服务器创建初期即被调用,访问(filter)目标网页时实现具体功能,服务器关闭时即被销毁。
11.1 Filter的工程实现
(1)新建Maven项目,并补充web架构。
- 新建普通Maven项目没有web文件夹
- 增加框架支持
(2)创建package并写出过滤器程序,即Filter类文件
-
类继承Filter,一定要选javax.servlet,别搞错。
-
报红,必须重载3个方法
-
重载方法int()、doFilter()、destroy()
-
正常了
(3)创建需要被过滤的Servlet文件
- 存在中文输出的未设置编码格式的Servlet文件。
(4)web.xml配置Servlet与Filter
-
注册Servlet,配置两个映射url路径,方便检验filter的效果。
-
注册Filter,并注明作用范围。这里我们选择/servlet/*,即/servlet路径下所有程序。
<filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>com.zfy.filter.CharacterEncodingFilter</filter-class> </filter> <!-- <url-pattern>内为该 过滤器 要过滤的网址url,即servlet的mapping映射 --> <!-- /servlet/* 即/servlet下的所有路径均为过滤器的作用范围 --> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/servlet/*</url-pattern> </filter-mapping>
-
使用路径 /show 访问,为未过滤路径,所以出现中文乱码。
-
使用路径 /servlet/show访问,为过滤路径,解决乱码问题。
12. 监听器
- 监听器是用于监听行为的发生,种类非常广泛。
- 常见的监听器有:鼠标(点击)监听器、窗口监听器、人数监听器
- 这里以网页访问人数监听器为例实现监听器程序。
12.1 监听器程序的实现,以网页访问人数为例
(1)编写监听器程序
-
继承自HttpSessionListener,重载两个方法:sessionCreated()、sessionDestroyed()。
-
编写监听的具体操作
(2)配置监听器,无需表明作用范围,自动施加在所有servlet程序上
-
监听器配置代码
<listener> <listener-class>com.zfy.listener.OnlineCountListener</listener-class> </listener>
(3)前端页面展示结果
-
写对应的前端页面
-
用两种浏览器访问url页面,前端结果如下:
-
两种浏览器对应的不同SessionID:
Z. 本章遇到的错误
1. MAVEN子工程发布在tomcat上时没有lib文件夹
问题描述:发布在tomcat上后,子工程的target中文件夹内仅有war文件,其余文件均缺失,而父工程中的target中会出现classes文件夹,其中包含有对应的class文件。
排错1:让子工程中的target中出现正常的文件夹分布。
解决办法:
在tomcat配置中找Before launch,把里面的全删除,然后加号选择Run Maven Goal ,目录选择父文件目录,命令为:clean packgae,之后重启tomcat
此步之后子工程target文件分布正常:
排错2:
此时index.jsp可以正常访问,但是servlet跳转依旧提示错误404.原因为WEB-INF中没有lib文件夹,即子工程中jar包未能发布到tomcat上。
经过查找,在父工程的pom中,对添加依赖的jar包添加了scope标签,导致子工程在发布到tomcat时无法获取到父工程中jar包的信息。
解决办法:
将父项目pom.xml中依赖的
<!-- <scope>provided</scope> -->
<!-- 设置作用域为provided,表明该包旨在编译和测试时使用,会导致子工程发布到tomcat时无法获取父工程jar包信息。-->
参考链接:https://blog.csdn.net/x2027290/article/details/118718710
2. 创建的maven子项目webapp文件夹无蓝点(非服务器文件夹)##
根据我的观察,IDEA创建maven子工程时有几率创建失败/文件残缺/功能失效。
此时可以人为修补,也可以删除项目,重新进入IDEA再次创建。
webapp文件夹修复链接如下:
参考链接:https://blog.csdn.net/carolineme/article/details/107716709
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人