主要内容
-
Servlet 重点
-
MVC 重点
-
Filter 重点
章节目标
-
掌握 Servlet 的作用
-
掌握 Servlet 的生命周期
-
掌握 JSP 的本质
-
掌握 MVC 的设计思想
-
掌握 Filter 的作用及使用场景
第一节 Servlet
1. Servlet 概念
Servlet 是在服务器上运行的能够对客户端请求进行处理,并返回处理结果的程序
2. Servlet 体系结构
2.1 Servlet 接口
//Servlet对象的初始化,Servlet 对象初始化后才能处理请求,由 Servlet 容器调用
public void init(ServletConfig config) throws ServletException;
//获取Servlet配置信息
public ServletConfig getServletConfig();
//处理客户端的请求,由 Servlet 容器调用
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
//返回有关 Servlet 的信息,比如作者、版本和版权
public String getServletInfo();
//销毁Servlet,由 Servlet 容器调用
public void destroy();
2.2 ServletConfig 接口
//获取Servlet的实例名称
public String getServletName();
//返回正在执行的Servlet所在的上下文对象
public ServletContext getServletContext();
//获取Servlet中给定名称的初始化参数
public String getInitParameter(String name);
//获取Servlet中所有的初始化参数
public Enumeration<String> getInitParameterNames();
2.3 Servlet 案例
-
编写Servlet
package com.qf.jsp.servlet; import javax.servlet.*; import java.io.IOException; import java.util.Enumeration; public class LoginServlet implements Servlet { private ServletConfig servletConfig; @Override public void init(ServletConfig config) throws ServletException { this.servletConfig = config; String servletName = config.getServletName(); System.out.println("Servlet 实例的名称:" + servletName); //获取Servlet中所有的初始化参数 Enumeration<String> initParameterNames = config.getInitParameterNames(); while (initParameterNames.hasMoreElements()) { String initParameterName = initParameterNames.nextElement(); //获取Servlet中给定名称的初始化参数 String initParameterValue = config.getInitParameter(initParameterName); System.out.println("Servlet 初始化参数 " + initParameterName + ":" + initParameterValue); } } @Override public ServletConfig getServletConfig() { return servletConfig; } @Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { System.out.println("Servlet 处理请求"); } @Override public String getServletInfo() { return "Login Servlet"; } @Override public void destroy() { System.out.println("Servlet 销毁"); } }
-
配置 web.xml
<?xml version="1.0" encoding="UTF-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"> <display-name>ServletApp</display-name> <!--Servlet 实例配置--> <servlet> <!-- Servlet 实例的名称--> <servlet-name>loginServlet</servlet-name> <!--Servlet 的类型--> <servlet-class>com.qf.jsp.servlet.LoginServlet</servlet-class> <init-param> <!--初始化参数的名称--> <param-name>characterEncoding</param-name> <!--初始化参数的值--> <param-value>UTF-8</param-value> </init-param> </servlet> <!--Servlet 实例与请求地址的映射配置--> <servlet-mapping> <!-- Servlet 实例的名称--> <servlet-name>loginServlet</servlet-name> <!-- Servlet 匹配的请求地址--> <url-pattern>/login</url-pattern> </servlet-mapping> <!--session 配置--> <session-config> <!--超时时间配置--> <session-timeout>30</session-timeout> </session-config> </web-app>
-
获取初始化参数信息
@Override public void init(ServletConfig config) throws ServletException { this.servletConfig = config; String servletName = config.getServletName(); System.out.println("Servlet 实例的名称:" + servletName); //获取Servlet中所有的初始化参数 Enumeration<String> initParameterNames = config.getInitParameterNames(); while (initParameterNames.hasMoreElements()){ String initParameterName = initParameterNames.nextElement(); //获取Servlet中给定名称的初始化参数 String initParameterValue = config.getInitParameter(initParameterName); System.out.println("Servlet 初始化参数 " + initParameterName + ":" + initParameterValue); } }
-
编写 index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <!DOCTYPE HTML> <html> <head> <title>登录页面</title> </head> <body> <form action="login" method="post"> <div> <input type="text" name="username"> </div> <div> <input type="password" name="password"> </div> <div> <input type="submit" value="登录"> </div> </form> </body> </html>
-
启动服务器,进行登录操作,查看控制台信息
控制台打印信息中并没有打印 "Servlet 销毁" 信息,由此可以得出:Servlet 处理完了请求后,并没有销毁。
-
关闭服务器,查看控制台信息
由此可以得出:在 Tomcat 关闭之前,Servlet 被销毁
-
结论
Servlet 在第一次接收请求时,由容器(如 Tomcat)创建实例,紧接着就由容器调用该 Servlet 的 init
方法完成初始化,然后由容器调用该 Servlet 的 service
方法进行请求处理,请求处理完成后,Servlet 并不会消亡, 而是跟随容器共存亡,在容器关闭之前,由容器调用 Servlet 的 destroy
方法进行销毁
-
JSP 本质
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports {
}
由此可以得出:JSP 的本质就是Servlet,只是 JSP 注重的是页面内容的展示,而Servlet注重的是业务逻辑的处理。
3. 请求处理与响应
3.1 体系结构
3.2 请求接口
-
ServletRequest 接口常用方法
//从请求中获取给定属性名对应的属性值
Object getAttribute(String attributeName);
//将给定的属性值以给定的属性名存储在请求中
void setAttribute(String attributeName, Object attributeVaue);
//从请求中将给定的属性名移除
void removeAttribute(String attributeName);
//获取请求中存储的所有属性名
Enumeration<String> getAttributeNames();
//从请求中获取给定参数名对应的参数值(参数值是单个数据)
String getParameter(String parameterName);
//从请求中获取给定参数名对应的参数值(参数值是多个数据)
String[] getParameterValues(String parameterName);
//从请求中获取所有的参数名
Enumeration<String> getParameterNames();
//从请求中获取所有的参数名和参数值形成的映射
Map<String, String[]> getParameterMap();
//从请求中获取字符集编码
String getCharacterEncoding();
//设置请求的字符集编码
void setCharacterEncoding(String charset) throws UnsupportedEncodingException;
//从请求中获取字符流,该字符流只能读取请求体中的数据信息,与下面的 getInputStream 方法只能二选一
BufferedReader getReader() throws IOException;
//从请求中获取字节流,该字节流只能读取请求体中的数据信息
ServletInputStream getInputStream() throws IOException;
//从请求中获取当前Servlet所在的上下文对象
ServletContext getServletContext();
//从请求中获取请求转发的对象
RequestDispatcher getRequestDispatcher(String path);
-
用法
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("请求的字符集编码需要在读取请求信息之前进行设置,否则,设置的字符集编码格式将不生效");
System.out.println("请求字符集编码:" + servletRequest.getCharacterEncoding());
servletRequest.setCharacterEncoding("UTF-8");
System.out.println("请求字符集编码:" + servletRequest.getCharacterEncoding());
BufferedReader reader = servletRequest.getReader();
System.out.println("开始读取请求参数信息");
String line;
while ((line = reader.readLine()) != null){
System.out.println(line);
}
System.out.println("请求参数信息读取完毕");
System.out.println("================");
System.out.println("开始获取请求中存储的数据");
Enumeration<String> attributeNames = servletRequest.getAttributeNames();
while (attributeNames.hasMoreElements()){
String attributeName = attributeNames.nextElement();
Object attributeValue = servletRequest.getAttribute(attributeName);
System.out.println(attributeName + " => " + attributeValue);
}
System.out.println("请求中存储的数据获取完毕");
System.out.println("================");
System.out.println("方式一:开始获取请求参数信息");
Enumeration<String> parameterNames = servletRequest.getParameterNames();
while (parameterNames.hasMoreElements()){
String parameterName = parameterNames.nextElement();
String parameterValue = servletRequest.getParameter(parameterName);
System.out.println(parameterName + " => " + parameterValue);
}
System.out.println("方式一:请求参数信息获取完毕");
System.out.println("================");
System.out.println("方式二:开始获取请求参数信息");
Map<String, String[]> parameterMap = servletRequest.getParameterMap();
parameterMap.forEach((k, values) -> System.out.println(k + " => " + Arrays.toString(values)));
System.out.println("方式二:请求参数信息获取完毕");
System.out.println("请求所使用的上下文路径:" + servletRequest.getServletContext().getContextPath());
}
-
POST请求测试
-
GET 请求测试
-
结论
使用GET方式发送的请求,只能通过getParameter 方法获取;使用POST方式发送的请求,只能使用流来获取。这是因为使用GET方式发送的请求,参数在URL地址中,解析这些参数的时候将其存放在一个Map集合中,因此可以直接获取。而POS方式发送的请求,参数在请求体中,这部分内容只能通过流来读取,然后再进行处理。
3.3 响应接口
-
ServletResponse 接口常用方法
//获取响应的字符集编码
String getCharacterEncoding();
//设置响应的字符集编码
void setCharacterEncoding(String charset);
//获取响应的内容类型
String getContentType();
//设置响应的内容类型
void setContentType(String contentType);
//获取输出流,主要用于下载文件
ServletOutputStream getOutputStream() throws IOException;
//获取打印流,主要用于向页面传输信息
PrintWriter getWriter() throws IOException;
-
用法(在 service 方法最后追加如下代码)
System.out.println();
System.out.println();
System.out.println("===========================================");
System.out.println("响应的字符集编码:" + servletResponse.getCharacterEncoding());
servletResponse.setCharacterEncoding("UTF-8");
System.out.println("响应的字符集编码:" + servletResponse.getCharacterEncoding());
System.out.println("响应的内容类型:" + servletResponse.getContentType());
servletResponse.setContentType("text/html;charset=utf-8");
System.out.println("响应的内容类型:" + servletResponse.getContentType());
PrintWriter writer = servletResponse.getWriter();
writer.print("登录请求已处理");
writer.flush();
writer.close();
-
测试
3.4 HTTP 请求和响应
-
HttpServletRequest 接口常用方法
//从请求中获取Cookie信息 Cookie[] getCookies(); //从请求中获取给定请求头名称对应的属性值 String getHeader(String headerName); //从请求中获取所有的请求头名称 Enumeration<String> getHeaderNames(); //获取请求的方式:GET、POST、PUT、DELETE等 String getMethod(); //从请求中获取上下文路径 String getContextPath(); //从请求中获取session HttpSession getSession(); //获取请求地址 String getRequestURI();
-
HttpServletResponse 接口常用方法
//添加客户端存储的Cookie信息
void addCookie(Cookie cookie);
//返回错误状态及错误信息
void sendError(int status, String errorMsg) throws IOException;
//返回错误状态
void sendError(int status) throws IOException;
//重定向至新的资源
void sendRedirect(String redirectURL) throws IOException;
//设置响应头信息
void setHeader(String headerName, String headerValue);
//添加响应头信息
void addHeader(String headerName, String headerValue);
//设置响应状态
void setStatus(int status);
-
HttpServlet常用方法(支持 HTTP 协议的 Servlet)
//对父类抽象方法的实现,该方法是对HTTP协议的交互信息的实现,调用的是下面的 service 方法
void service(ServletRequest req,ServletResponse res);
//HTTP协议的交互信息的实现,该方法主要针对不同的请求方式进行处理。GET请求会调用 doGet 方法处理,
//POST请求会调用 doPost 处理, PUT请求会调用 doPut 方法处理, DELETE请求会调用 doDelete 方法处理
void service(HttpServletRequest req, HttpServletResponseres);
//GET请求处理
void doGet(HttpServletRequestreq,HttpServletResponse res);
//POST请求处理
void doPost(HttpServletRequestreq,HttpServletResponse res);
//PUT请求处理
void doPut(HttpServletRequestreq,HttpServletResponse res);
//DELETE请求处理
void doDelete(HttpServletRequestreq,HttpServletResponse res);
-
用法
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;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Map;
public class RegisterServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("开始获取请求头信息");
Enumeration<String> headerNames = req.getHeaderNames();
while (headerNames.hasMoreElements()){
String headerName = headerNames.nextElement();
String headerValue = req.getHeader(headerName);
System.out.println(headerName + " => " + headerValue);
}
System.out.println("请求头信息获取完毕");
System.out.println("请求方式:" + req.getMethod());
System.out.println("请求地址:" + req.getRequestURI());
System.out.println("请求的上下文路径:" + req.getContextPath());
System.out.println("==================");
System.out.println("开始读取请求参数");
Map<String, String[]> parameterMap = req.getParameterMap();
parameterMap.forEach((k, values) -> System.out.println(k + "=>" + Arrays.toString(values)));
System.out.println("请求参数读取完毕");
System.out.println();
System.out.println();
System.out.println("=========================");
System.out.println("响应开始");
resp.setContentType("text/html;charset=utf-8");
PrintWriter writer = resp.getWriter();
writer.print("注册请求已经处理");
writer.flush();
writer.close();
}
}
<!-- register.jsp -->
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE HTML>
<html>
<head>
<title>注册页面</title>
</head>
<body>
<form action="createUser" method="post">
<div>
<input type="text" name="username">
</div>
<div>
<input type="password" name="password">
</div>
<div>
<input type="submit" value="注册">
</div>
</form>
</body>
</html>
<!-- web.xml 中添加如下配置 -->
<servlet>
<servlet-name>registerServlet</servlet-name>
<servlet-class>com.qf.jsp.servlet.RegisterServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>registerServlet</servlet-name>
<url-pattern>/createUser</url-pattern>
</servlet-mapping>
-
测试
查看控制台信息
3.5 Servlet 交互流程
4. ServletContext
4.1 常用方法
//获取上下文路径
String getContextPath();
//获取给定相对路径对应的绝对路径
String getRealPath(String path);
//获取上下文初始化参数中给定参数名对应的参数值
String getInitParameter(String parameterName);
//获取上下文初始化参数中所有的参数名
Enumeration<String> getInitParameterNames();
//获取上下文存储的数据中给定属性名对应的属性值
Object getAttribute(String attributeName);
//获取上下文存储的数据中所有的属性名
Enumeration<String> getAttributeNames();
//将给定的属性值使用给定的属性名存储在上下文中
void setAttribute(String attributeName, Object attributeValue);
//从上下文存储的数据中将给定的属性名移出
void removeAttribute(String attributeName);
4.2 用法
-
配置 web.xml
<context-param> <param-name>characterEncoding</param-name> <param-value>UFT-8</param-value> </context-param>
-
修改 RegisterServlet
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("开始读取上下文参数信息");
ServletContext servletConte
xt = config.getServletContext();
Enumeration<String> initParameterNames = servletContext.getInitParameterNames();
while (initParameterNames.hasMoreElements()){
String initParameterName = initParameterNames.nextElement();
String initParameterValue = servletContext.getInitParameter(initParameterName);
System.out.println( initParameterName + " => " + initParameterValue);
}
}
-
测试
查看控制台信息
第二节 MVC
1. 什么是 MVC
模型-视图-控制器(MVC模式)是一种非常经典的软件架构模式,在UI框架和UI设计思路中扮演着非常重要的角色。从设计模式的角度来看,MVC模式是一种复合模式,它将多个设计模式在一种解决方案中结合起来,用来解决许多设计问题。MVC模式把用户界面交互分拆到不同的三种角色中,使应用程序被分成三个核心部件:Model(模型)、View(视图)、Control(控制器)
-
模型:模型持有所有的数据、状态和程序逻辑。模型独立于视图和控制器。
-
视图:用来呈现模型。视图通常直接从模型中取得它需要显示的状态与数据。对于相同的信息可以有多个不同的显示形式或视图。
-
控制器:位于视图和模型中间,负责接受用户的输入,将输入进行解析并反馈给模型
MVC模式将它们分离以提高系统的灵活性和复用性,不使用MVC模式,用户界面设计往往将这些对象混在一起。MVC模式实现了模型和视图的分离,使得其具有以下优点:
-
一个模型提供不同的多个视图表现形式,也能够为一个模型创建新的视图而无须重写模型。一旦模型的数据发生变化,模型将通知有关的视图,每个视图相应地刷新自己。
-
模型可复用。因为模型是独立于视图的,所以可以把一个模型独立地移植到新的平台工作。
-
提高开发效率。在开发界面显示部分时,仅仅需要考虑的是如何布局一个好的用户界面;开发模型时,仅仅要考虑的是业务逻辑和数据维护,这样能使开发者专注于某一方面的开发,提高开发效率。
2. JSP 中的 MVC
在 JSP 中 Servlet 扮演的是控制器, JSP 页面扮演的是视图,Java Bean 扮演的是模型。
案例: 将用户信息呈现在页面上
-
编写视图 user.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>显示用户信息</title>
</head>
<body>
<a href="showUserInfo">显示用户信息</a>
</body>
</html>
-
编写模型 User
package com.qf.jsp.pojo;
public class User {
private String username;
private String name;
private String sex;
private int age;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", name='" + name + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
'}';
}
}
-
编写控制器 UserInfoServlet
package com.qf.jsp.servlet;
import com.qf.jsp.pojo.User;
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 UserInfoServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
User user = new User();
user.setUsername("admin");
user.setName("管理员");
user.setSex("男");
user.setAge(20);
resp.setContentType("text/html;charset=utf-8");
resp.setCharacterEncoding("UTF-8");
PrintWriter writer = resp.getWriter();
//告知视图需要展示的模型
writer.print(user.toString());
writer.flush();
writer.close();
}
}
-
配置 web.xml
<servlet>
<servlet-name>userInfoServlet</servlet-name>
<servlet-class>com.qf.jsp.servlet.UserInfoServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>userInfoServlet</servlet-name>
<url-pattern>/showUserInfo</url-pattern>
</servlet-mapping>
第三节 过滤器
1. 什么是过滤器
过滤器的概念过滤器是一个服务器端的组件,可以拦截客户端的请求和响应信息并对这些信息进行过滤。
2. 过滤器体系结构
-
启动服务器,访问 user.jsp,然后测试
2.1 Filter接口
//过滤器初始化
default void init(FilterConfig filterConfig) throws ServletException {
}
//过滤操作,与协议无关
void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException;
//过滤器销毁
default void destroy() {
}
2.2 FilterConfig 接口
//获取过滤器实例的名称
String getFilterName();
//获取Servlet上下文
ServletContext getServletContext();
//从过滤器初始化配置中获取给定属性名对应的属性值
String getInitParameter(String parameterName);
//获取过滤器初始化配置中所有的属性名
Enumeration<String> getInitParameterNames();
2.3 案例
使用过滤器完成中文乱码处理
-
编写过滤器 CharacterEncodingFilter
package com.qf.jsp.filter;
import javax.servlet.*;
import java.io.IOException;
public class CharacterEncodingFilter implements Filter {
private String characterEncoding;
@Override
public void init(FilterConfig config) throws ServletException {
System.out.println("过滤器初始化");
this.characterEncoding = config.getInitParameter("characterEncoding");
}
@Override
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
System.out.println("过滤器过滤操作");
request.setCharacterEncoding(characterEncoding);
response.setCharacterEncoding(characterEncoding);
//让过滤器链中的其他过滤器执行,这行代码必不可少,否则,无法进入后面的Servlet执行
chain.doFilter(request, response);
}
@Override
public void destroy() {
System.out.println("过滤器销毁");
}
}
-
在 web.xml 中配置过滤器
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>com.qf.jsp.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>characterEncoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<!-- * 标识通配符,匹配所有-->
<url-pattern>/*</url-pattern>
</filter-mapping>
-
测试
-
结论
2.4 HttpFilter 抽象类
//重写无协议过滤器操作,调用下面支持HTTP协议请求过滤操作的方法
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {}
//HTTP协议请求过滤操作的方法
protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {}
2.5 案例
使用过滤器完成登录超时处理
-
编写过滤器
package com.qf.jsp.filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
public class TimeoutFilter extends HttpFilter {
@Override
protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpSession session = request.getSession();
Object username = session.getAttribute("username");
//session中没有数据了,说明session已经过期了,当前的session是一个新的session
if(username == null){
//首页的地址 就是上下文路径
String homePageUrl = request.getContextPath();
if("".equalsIgnoreCase(homePageUrl)){//上下文路径为空字符串时给一条斜杠即可
homePageUrl = "/";
}
response.sendRedirect(homePageUrl);
} else {
//让过滤器链中的其他过滤器执行,这行代码必不可少,否则,无法进入后面的Servlet执行
chain.doFilter(request, response);
}
}
}
-
在 web.xml 中配置过滤器
<filter>
<filter-name>timeoutFilter</filter-name>
<filter-class>com.qf.jsp.filter.TimeoutFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>timeoutFilter</filter-name>
<!-- * 标识通配符,匹配所有-->
<url-pattern>/*</url-pattern>
</filter-mapping>