主要内容
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>