Servlet三小时速成
Servlet三小时速成
实例驱动的速成教程。自己敲一遍的话入门还是没问题的。如有错误请读着多多包涵。
Serlet的前辈:CGI 通用网关接口
CGI通过调用外部程序来处理HTTP请求,对于每个请求都会启动一个新的进程。
这就导致了许多问题,首先是请求数量的受限,不可能无限地启动进程,调用外部程序的做法也会延长处理请求的时间;另外,CGI极其依赖平台的语言(如C、C++、perl等等)
Servlet的优势
servlet很好地解决了CGI的问题。
Web容器面对多个请求的情况,使用线程来替代进程,降低了通信成本,也因此提高了性能和稳定性;另外,Servlet由JVM管理,背靠的是Java语言,移植性很好,平台依赖性不高。
速通Servlet API
主要是两个软件包:
关于servlet和httpServlet的区别:
Servlet
是其中一个接口,定义了所有 servlet 的基本功能和方法(如init()
、service()
、destroy()
)。
HttpServlet
是Servlet
接口的抽象类,专门为处理 HTTP 请求而设计,扩展了Servlet
的功能。实际用的比较多。
javax.servlet
API 文档速查:javax.servlet (Java™ EE 8 Specification APIs)
常用接口
接口 | 描述 |
---|---|
AsyncContext | 表示在 ServletRequest 上启动的异步操作的执行上下文的类。 |
AsyncListener | 当在添加了该监听器的 ServletRequest 上启动的异步操作完成、超时或出现错误时,将通知的监听器。 |
Filter | 过滤器是一个对象,用于对请求资源(如 servlet 或静态内容)或对响应资源进行过滤操作,或同时对两者进行过滤。 |
FilterChain | FilterChain 是一个由 servlet 容器提供给开发者的对象,提供对过滤请求资源的调用链的视图。 |
FilterConfig | 过滤器配置对象,servlet 容器在初始化时用于传递信息给过滤器。 |
FilterRegistration | 通过此接口可以进一步配置过滤器。 |
FilterRegistration.Dynamic | 通过此接口可以进一步配置通过 ServletContext 的 addFilter 方法注册的过滤器。 |
ReadListener | 此类表示一个回调机制,将在 HTTP 请求数据可被读取而不阻塞时通知实现类。 |
Registration | 通过此接口可以进一步配置 Servlet 或 Filter 。 |
Registration.Dynamic | 通过此接口可以进一步配置通过 ServletContext 的 addServlet 或 addFilter 方法分别注册的 Servlet 或 Filter 。 |
RequestDispatcher | 定义一个接收客户端请求并将其发送到服务器上任何资源(如 servlet 、HTML 文件或 JSP 文件)的对象。 |
Servlet | 定义所有 servlets 必须实现的方法。 |
ServletConfig | servlet 配置对象,servlet 容器在初始化时用于传递信息给 servlet 。 |
ServletContainerInitializer | 允许库/运行时在 web 应用程序的启动阶段被通知,并根据需要进行程序化注册 servlets 、filters 和 listeners 的接口。 |
ServletContext | 定义 servlet 用于与其 servlet 容器通信的一组方法,例如获取文件的 MIME 类型、分发请求或写入日志文件。 |
ServletContextAttributeListener | 接收有关 ServletContext 属性更改的通知事件的接口。 |
ServletContextListener | 接收有关 ServletContext 生命周期更改的通知事件的接口。 |
ServletRegistration | 通过此接口可以进一步配置 Servlet 。 |
ServletRegistration.Dynamic | 通过此接口可以进一步配置通过 ServletContext 的 addServlet 方法注册的 Servlet 。 |
ServletRequest | 定义一个对象,用于向 servlet 提供客户端请求信息。 |
ServletRequestAttributeListener | 接收有关 ServletRequest 属性更改的通知事件的接口。 |
ServletRequestListener | 接收有关进入和离开 web 应用程序范围的请求的通知事件的接口。 |
ServletResponse | 定义一个对象,以帮助 servlet 向客户端发送响应。 |
SessionCookieConfig | 可用于配置用于会话跟踪目的的 cookie 的各种属性的类。 |
SingleThreadModel | 自 Java Servlet API 2.4 起已弃用,没有直接替代品。 |
WriteListener | 回调通知机制,向开发者信号可在不阻塞的情况下写入内容。 |
常用类
类 | 描述 |
---|---|
AsyncEvent | 当在 ServletRequest 上启动的异步操作(通过调用 ServletRequest.startAsync() 或 ServletRequest.startAsync(ServletRequest, ServletResponse) )完成、超时或产生错误时触发的事件。 |
GenericFilter | 定义一个通用的、协议无关的过滤器。 |
GenericServlet | 定义一个通用的、协议无关的 servlet。 |
HttpConstraintElement | HttpConstraint 注解值的 Java 类表示。 |
HttpMethodConstraintElement | HttpMethodConstraint 注解值的 Java 类表示。 |
MultipartConfigElement | MultipartConfig 注解值的 Java 类表示。 |
ServletContextAttributeEvent | 关于 web 应用程序的 ServletContext 属性更改的通知事件类。 |
ServletContextEvent | 这是关于 web 应用程序的 servlet 上下文更改的通知事件类。 |
ServletInputStream | 提供用于从客户端请求中读取二进制数据的输入流,包括一个高效的 readLine 方法,用于逐行读取数据。 |
ServletOutputStream | 提供用于向客户端发送二进制数据的输出流。 |
ServletRequestAttributeEvent | 这是关于 servlet 请求中属性更改的通知事件类。 |
ServletRequestEvent | 此类事件指示 ServletRequest 的生命周期事件。 |
ServletRequestWrapper | 提供 ServletRequest 接口的方便实现,开发者可以通过子类化来适配请求到一个 servlet。 |
ServletResponseWrapper | 提供 ServletResponse 接口的方便实现,开发者可以通过子类化来适配来自一个 servlet 的响应。 |
ServletSecurityElement | ServletSecurity 注解值的 Java 类表示。 |
枚举
枚举 | 描述 |
---|---|
DispatcherType | 过滤器调度类型的枚举。 |
SessionTrackingMode | 会话跟踪模式的枚举。 |
异常
异常 | 描述 |
---|---|
ServletException | 定义一个通用异常,servlet 在遇到困难时可以抛出该异常。 |
UnavailableException | 定义一个异常,servlet 或过滤器抛出该异常以指示其永久或暂时不可用。 |
javax.servlet.http
接口
接口 | 描述 |
---|---|
HttpServletMapping | 允许在运行时发现当前 HttpServletRequest 的 HttpServlet 被调用的方式。 |
HttpServletRequest | 扩展 ServletRequest 接口,为 HTTP servlet 提供请求信息。 |
HttpServletResponse | 扩展 ServletResponse 接口,提供发送 HTTP 响应的特定功能。 |
HttpSession | 提供一种方法,以跨多个页面请求或网站访问识别用户,并存储关于该用户的信息。 |
HttpSessionActivationListener | 绑定到会话的对象可以监听会话钝化和激活的容器事件通知。 |
HttpSessionAttributeListener | 用于接收 HttpSession 属性更改的通知事件的接口。 |
HttpSessionBindingListener | 使对象在绑定到会话或从会话中解除绑定时接收到通知。 |
HttpSessionContext (已弃用) | 出于安全原因,自 Java™ Servlet API 2.1 起已弃用,无替代方案。 |
HttpSessionIdListener | 用于接收 HttpSession ID 更改通知事件的接口。 |
HttpSessionListener | 用于接收 HttpSession 生命周期更改通知事件的接口。 |
HttpUpgradeHandler | 封装升级协议处理的接口。 |
Part | 表示通过 multipart/form-data POST 请求接收的部分或表单项的类。 |
PushBuilder | 构建要推送的请求。 |
WebConnection | 封装升级请求连接的接口。 |
类
类 | 描述 |
---|---|
Cookie | 创建一个 cookie,小量信息由 servlet 发送至 Web 浏览器,浏览器保存该信息,并在稍后将其发送回服务器。 |
HttpFilter | 提供一个抽象类,可被子类化以创建适合网站的 HTTP 过滤器。 |
HttpServlet | 提供一个抽象类,可被子类化以创建适合网站的 HTTP servlet。 |
HttpServletRequestWrapper | 提供 HttpServletRequest 接口的便捷实现,开发者可以通过子类化适配请求到 Servlet。 |
HttpServletResponseWrapper | 提供 HttpServletResponse 接口的便捷实现,开发者可以通过子类化适配来自 Servlet 的响应。 |
HttpSessionBindingEvent | 当对象绑定或解绑到会话时,或者当在会话中任何属性被绑定、解绑或替换时,发送给实现 HttpSessionBindingListener 的对象或 HttpSessionAttributeListener 的事件。 |
HttpSessionEvent | 表示 web 应用程序中会话更改通知事件的类。 |
HttpUtils (已弃用) | 自 Java™ Servlet API 2.3 起已弃用。 |
枚举
枚举 | 描述 |
---|---|
MappingMatch | Servlet 映射类型的枚举。 |
第一个Servlet实例:学会创建Servlet并重写doGet和doPost方法
FirstServlet.java
package com.niko;
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 FirstServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//能在URL中携带参数,但是数据量有限,能直接在网页中访问,处理 HTTP GET 请求。一般用于从服务器获取数据。请求参数以 URL 查询字符串的形式附加在 URL 后面。
//1.这是在终端输出
System.out.println("It's my First Servlet!");
//2.这是在网页输出,写入到的是writer中的输出
PrintWriter out = resp.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title>Servlet FirstServlet</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>Servlet FirstServlet</h1>");
out.println("</body>");
out.println("</html>");
//3.尝试获取请求中的参数
String username= req.getParameter("username");
String password= req.getParameter("password");
out.println("username:"+username);
out.println("password:"+password);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//参数在请求体中,处理 HTTP POST 请求。通常用于向服务器发送数据,尤其是提交表单或上传文件。
}
}
了解了控制台输出和response输出的区别、doGet方法和doPost方法,并一共完成三个任务:
- 控制台输出
- 网页输出
- 获取请求中的参数
第二个Servlet实例:学习Servlet的生命周期
一个Servlet的生命周期可以简单地概括为:
创建(init)→ 服务(service)→销毁(destory)
对应了servlet提供的三个同名方法,我们可以在自己的Servlet中重写这三个方法。
创建一个SecondServlet.java
package com.niko;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet(name = "secondServlet", urlPatterns = {"/secondServlet"})
public class SecondServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html");
PrintWriter out = resp.getWriter();
out.println("hello!");
String username= req.getParameter("username");
String password= req.getParameter("password");
//对传入的参数进行一些判断的逻辑操作
if(username.equals("admin") && password.equals("admin")){
out.println("密码正确!");
}else{
out.println("密码错误!");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
@Override
public void init() throws ServletException {
//要注意这个方法只在servlet初始化时被调用一次,后续再有请求不会再调用,除非销毁重建
System.out.println("init...");
}
@Override
public void destroy() {
//这个方法在关闭Tomcat服务器时被调用,很显然服务器关闭了,也要销毁所有的servlet
super.destroy();
System.out.println("done...");
}
//发现了这两个service方法,两者都存在的情况下好像先调用了后者,并且一个是protected,一个是public
/*
* void service(ServletRequest req,ServletResponse response)方法是由tomcat自动调用,
* 它将接收的客户端请求转交给HttpServlet中的另一个protected void service(HttpServletRequest req,HttpServletResponse res)方法,
* 此保护类型的service方法再把将请求分发给doPost()、doGet()方法进行下一步处理。
* 所以我们完全可以重写受保护的service()方法来代替doPost()和doGet()方法。
* */
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("request coming!");
System.out.println("Processing...");
}
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
System.out.println("request coming?");
System.out.println("Processing。。。");
}
}
观察控制台输出,可以看到只有第一次访问网页(发送请求)后控制台输出了 init 信息,只有在tomcat服务器关闭后 才输出了destory信息。
第三个Servlet实例:网页访问次数计数与数据库登录练习
网页访问次数是通过doGet
方法触发的,因此相关的逻辑应该在doGet
方法中实现,用到了一个全局静态变量Count
,只在类加载时被初始化,也就是调用init
方法; 数据库的登录暂时先使用传统JDBC流程来实现,放在doPost
方法中,使用API调试工具postman来发送post请求进行测试,预计应该判断输入的密码和用户名是否正确,并给出对应的判断信息。
对POST请求来说,参数可以通过请求体*(body)或URL查询字符串(query string)*传递。虽然规范上,POST请求的参数通常在请求体中,但为了测试方便,有时也能够通过URL传递参数。
ThirdServlet.java
package com.niko;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.*;
@WebServlet(name = "ThirdServlet", urlPatterns = {"/thirdServlet"})
public class ThirdServlet extends HttpServlet {
static int Count;
@Override
public void init() throws ServletException {
Count=0;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
PrintWriter out = resp.getWriter();
Count++;
out.println(
"<html>\n"+
"<body>\n"+
"<h1>"+"访问次数:"+ Count+"</h1>\n>"+
"</body>\n"+
"</html>"
);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
//设置响应内容,已经包含了resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/HTML;charset=UTF-8");
PrintWriter out = resp.getWriter();
String username = req.getParameter("username");
String password = req.getParameter("password");
//进行JDBC操作
String user="root";
String pass="Aa@123456789";
String jdbcUrl="jdbc:mysql://localhost:3306/servlet?useUnicode=true&characterEncoding=utf-8";
String driver="com.mysql.jdbc.Driver";
Connection conn=null;
PreparedStatement ps=null;
ResultSet rs=null;
String queryDb="select * from user where username=? and password=?";
try{
Class.forName(driver);
conn = DriverManager.getConnection(jdbcUrl,user,pass);
ps = conn.prepareStatement(queryDb);
ps.setString(1,username);
ps.setString(2,password);
rs=ps.executeQuery();
//判断结果集是否存在
if(!rs.isBeforeFirst()){
out.println("登录失败-------->error");
}
else{
out.println("登录成功-------->success");
}
} catch (ClassNotFoundException | SQLException e) {
throw new RuntimeException(e);
}
}
}
这是采用Body方式传入的参数。直接写在URL里也可以,但是不安全,涉及敏感数据不能使用。
第四个Servlet实例:注册与登录逻辑处理
这是个比较有挑战的项目了,但是仍然是能够顺利完成的,解决的关键在于实现多个jsp页面的跳转和数据的流向问题。
一共使用到了两个servlet文件和四个jsp文件:
LoginServlet.java
package com.niko.login;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
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.sql.*;
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
private static final String JDBC_URL = "jdbc:mysql://localhost:3306/servlet?useUnicode=true&characterEncoding=utf-8&useSSL=false";
private static final String USER = "root"; //自己的数据库用户
private static final String PASSWORD = "Aa@123456789"; //自己的数据库密码
private static final String DRIVER_CLASS = "com.mysql.jdbc.Driver";
private static final String QUERY_SQL = "select * from user where username = ? and password = ?";
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
PrintWriter out = resp.getWriter();
String username = req.getParameter("username");
String password = req.getParameter("password");
//进行JDBC操作
try {
Class.forName(DRIVER_CLASS);
Connection conn= DriverManager.getConnection(JDBC_URL,USER,PASSWORD);
if(isUserExist(conn,username,password)){//跳转逻辑在这
req.getRequestDispatcher("success.jsp").forward(req, resp);
}else{
req.getRequestDispatcher("error.jsp").forward(req, resp);
}
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
private boolean isUserExist(Connection conn, String username, String password) throws SQLException {
try (PreparedStatement ps = conn.prepareStatement(QUERY_SQL)) {
ps.setString(1, username);
ps.setString(2, password);
try (ResultSet rs = ps.executeQuery()) {
return rs.isBeforeFirst();
}
}
}
}
RegisterServlet.java
package com.niko.login;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
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.sql.*;
@WebServlet("/registerServlet")
public class RegisterServlet extends HttpServlet {
private static final String JDBC_URL = "jdbc:mysql://localhost:3306/servlet?useUnicode=true&characterEncoding=utf-8&useSSL=false";
private static final String USER = "root";
private static final String PASSWORD = "Aa@123456789";
private static final String DRIVER_CLASS = "com.mysql.jdbc.Driver";
private static final String INSERT_SQL = "INSERT INTO user(username, password) VALUES(?, ?)";
private static final String QUERY_SQL = "SELECT * FROM user WHERE username=? AND password=?";
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
PrintWriter out = resp.getWriter();
String username = req.getParameter("username");
String password = req.getParameter("password");
try {
Class.forName(DRIVER_CLASS);
try (Connection conn = DriverManager.getConnection(JDBC_URL, USER, PASSWORD)) {
if (isUserExist(conn, username, password)) {
req.getRequestDispatcher("/error.jsp").forward(req, resp);
} else {
int count = registerUser(conn, username, password);
out.println("影响了" + count + "行数据");
req.getRequestDispatcher("/success.jsp").forward(req, resp);
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace(out); // 输出到响应中
} catch (SQLException e) {
e.printStackTrace(out); // 输出到响应中
}
}
private boolean isUserExist(Connection conn, String username, String password) throws SQLException {
try (PreparedStatement ps = conn.prepareStatement(QUERY_SQL)) {
ps.setString(1, username);
ps.setString(2, password);
try (ResultSet rs = ps.executeQuery()) {
return rs.isBeforeFirst();
}
}
}
private int registerUser(Connection conn, String username, String password) throws SQLException {
try (PreparedStatement ps = conn.prepareStatement(INSERT_SQL)) {
ps.setString(1, username);
ps.setString(2, password);
return ps.executeUpdate();
}
}
}
两个servlet的大致框架是一致的,除去固定的JDBC步骤之外,Login的逻辑是先获取请求中的数据(使用getParameter
)并将这些数据和数据库中的数据比对,如果查询到了对应的表条目,则返回登录成功(跳转到success),否则返回失败(跳转到error);
Register的逻辑类似,获取数据,接着查询数据库中是否已经存在相同数据,如果查询到了,则返回注册失败(跳转到error),如果没有查询到,则更新数据库数据(Insert一条数据,并且跳转到success),注册成功。
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录</title>
</head>
<body>
<h2>用户登录</h2>
<form action="/ServletDemo/loginServlet" method="post">
<div>
<label for="username">用户名:</label>
<input type="text" id="username" name="username" required>
</div>
<div>
<label for="password">密码:</label>
<input type="password" id="password" name="password" required>
</div>
<div>
<input type="submit" value="登录">
</div>
<div>
<input type="button" value="注册" οnclick="location.href='/ServletDemo/register.jsp'">
</div>
</form>
</body>
</html>
error.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>注册</title>
</head>
<body>
<h1>失败!</h1>
</body>
</html>
success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>注册</title>
</head>
<body>
<h1>成功!</h1>
<div>
<input type="button" value="返回登录界面" οnclick="location.href='/ServletDemo/index.jsp'">
</div>
</body>
</html>
register.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>注册</title>
</head>
<body>
<h2>用户注册</h2>
<form action="/ServletDemo/registerServlet" method="post">
<div>
<label for="username">用户名:</label>
<input type="text" id="username" name="username" required>
</div>
<div>
<label for="password">密码:</label>
<input type="password" id="password" name="password" required>
</div>
<div>
<input type="submit" value="提交">
</div>
</form>
</body>
</html>
这四个jsp文件比较简单,但还是值得学习的,需要要求有一些HTML的知识。
比如使用了表单<form>
来提交给对应的Servlet(这里要注意上下文路径,如果错误则不能成功跳转),使用了<div>
这个分块的标签来区分“文本+输入框”的用户名块和密码块,使用了<label>
标签显示文本并聚焦到关联的属性(id),使用了<input>
标签显示两个能传入数据的输入框(type是submit
,type改为button
则会变成一个按钮,如果是按钮的话可以指定跳转路径)。
可以利用这些标签实现更为复杂的逻辑。
第五个Servlet实例:学习JSP的九个隐含对象和EL表达式
JSP隐含对象
JSP隐式对象是JSP容器为每个页面提供的Java对象,开发者可以直接使用它们而不用显式声明。
对象 | 描述 |
---|---|
request | HttpServletRequest类的实例,代表 HTTP 请求的对象,包含客户端发送到服务器的信息,如表单数据、URL参数等。 |
response | HttpServletResponse类的实例,代表 HTTP 响应的对象,用于向客户端发送数据和响应。 |
out | JspWriter类的实例,用于向客户端输出文本内容的对象,通常用于生成HTML。 |
session | HttpSession类的实例,代表用户会话的对象,可用于存储和检索用户特定的数据,跨多个页面。 |
application | ServletContext类的实例,代表 Web 应用程序的上下文,可以用于存储和检索全局应用程序数据。 |
config | ServletConfig类的实例,包含有关当前 JSP 页面的配置信息,例如初始化参数。 |
pageContext | PageContext类的实例,提供对JSP页面所有对象以及命名空间的访问 |
page | 类似于 Java 类中的 this 关键字,代表当前 JSP 页面的实例,可以用于调用页面的方法。 |
exception | exception 类的对象,代表发生错误的 JSP 页面中对应的异常对象,用于处理 JSP 页面中的异常情况,可用于捕获和处理页面中发生的异常。 |
可以看到有很多熟悉的面孔,比如request、response、out……这些对象可以直接在JSP页面中使用,
JSP的EL表达式
EL表达式提供".
“和”[]
"两种运算符来存取数据。两者一般通用。
${user.name}
${user[“name”]}
如果有特殊符号或者要通过变量取值,则只能用后者
${user.first-name}错误写法
${user[“first-name”]}正确写法
通过变量动态取值:
${user[param]}
EL表达式预定义的11个对象
其中斜体的是没有JSP隐含对象与之对应的,
除了pageContext对象是PageContext类型,其余都是 Map类型!!!
对象 | 说明 |
---|---|
pageContext | 提供页面级别的上下文信息,允许访问 JSP 页面的属性和方法。 |
param | 获取HTTP请求参数的单个值,返回一个字符串。 |
paramValues | 获取HTTP请求参数的多个值,返回一个字符串数组。 |
header | 获取HTTP请求头的单个值。 |
headerValues | 获取HTTP请求头的多个值,返回一个字符串数组。 |
cookie | 获取HTTP请求中的单个Cookie。 |
initParam | 获取Web应用的初始化参数,从web.xml文件中读取的参数值。 |
pageScope | 访问页面范围内的属性,只能在当前页面中访问。 |
requestScope | 访问请求范围内的属性,可以用于在请求的整个生命周期内访问。 |
sessionScope | 访问会话范围内的属性,可以跨多个请求访问。 |
applicationScope | 访问应用范围内的属性,所有用户都可以访问。 |
代码例子
用到一个FourthServlet
和一个tryEL.jsp
FourthServlet.java
package com.niko;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
@WebServlet(name = "fourthServlet",
urlPatterns = "/ELexample",
initParams = {
@WebInitParam(name = "appName", value = "My Application")
})
public class FourthServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置请求属性
request.setAttribute("requestAttribute", "This is a request attribute");
// 设置会话属性
HttpSession session = request.getSession();
session.setAttribute("sessionAttribute", "This is a session attribute");
// 设置应用属性
getServletContext().setAttribute("applicationAttribute", "This is an application attribute");
// 设置Cookie
Cookie cookie = new Cookie("user", "John_Doe");
response.addCookie(cookie);
// 转发到 JSP 页面
request.getRequestDispatcher("/tryEL.jsp").forward(request, response);
}
}
tryEL.jsp
<%--
Created by IntelliJ IDEA.
User: Star
Date: 2024/11/6
Time: 下午3:03
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>EL表达式训练</title>
</head>
<body>
我们知道JSP有九个可以直接操作的隐含对象:request、response、out、page、pageContext、config、exception、session、application
接下来学习EL表达式!EL表达式也有11个预定义的对象:pageContext、param、paramValues、initParam、header、headerValues、cookie、pageScope、requestScope、applicationScope、sessionScope
<hr>
<h2>EL 表达式示例</h2>
<p>请求属性: ${requestScope.requestAttribute}</p>
<p>会话属性: ${sessionScope.sessionAttribute}</p>
<p>应用属性: ${applicationScope.applicationAttribute}</p>
<p>初始化参数: ${initParam.appName}</p>
<p>Cookie: ${cookie.user}</p>
<p>请求参数:</p>
<ul>
<li>单个参数: ${param.someParam}</li>
<li>多个参数:
<c:forEach var="value" items="${paramValues['multiParam']}">
${value} <br>
</c:forEach>
</li>
</ul>
<p>请求头:</p>
<p>User-Agent: ${header['User-Agent']}</p>
<p>Accept:
<c:forEach var="value" items="${headerValues['Accept']}">
${value} <br>
</c:forEach>
</p>
</body>
</html>
最后访问URL:http://localhost:8080/ServletDemo/ELexample?someParam=value1&multiParam=value1&multiParam=value2
效果如下:
学到的几个知识点:
-
initParam
需要在web.xml
中配置,使用注解也可以。表示的是单个Servlet中的参数。而context-param
针对的是整个项目,而且似乎并不能使用注解配置,必须在web.xml
中配置。它们两者都采用的是键值对的方式,都是name+value
<context-param> <param-name>appName</param-name> <param-value>My Web Application</param-value> </context-param>
-
attribute
都是通过对应对象的setter
来设置的 -
<hr>
标签用于输出横向线条,用于分割
第六个Servlet实例:过滤器Filter、简单的设计模式以及MVC设计架构的思路训练
什么是Filter
?它的作用可以看作是请求处理过程中的“拦截器”或“中间件”,在请求到达 Servlet
之前,或者响应返回客户端之前,进行一些预处理或后处理。
正式地说,Filter
是 Java Web 应用程序中的一个重要组件,它用于对请求和响应进行预处理或后处理。Filter
通常用于实现一些跨多个 Servlet
的功能,如日志记录、权限校验、数据过滤、请求修改等。
原先的Filter是一个接口,我们想要定义自己的过滤器类,就得先实现接口中的方法,一共有三个:
init()
负责初始化Filter
doFilter()
值得注意的是有一个参数叫做FilterChain
,它也有一个doFilter
方法,是用于将请求传递给下一个Filter
对象或者Servlet
destroy()
负责Filter
相关的销毁工作
在类的前面使用一个注解@WebFilter
,注解参数可以包括Filter
的名字,已经要拦截的请求的URL模式(如果实际的和这个不匹配,就意味着当前的URL请求不会经过该Filter
)。当然也可以在web.xml中配置,下面是一个例子
<filter>
<filter-name>authFilter</filter-name>
<filter-class>com.niko.filter.AuthFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>authFilter</filter-name>
<url-pattern>/*</url-pattern> <!-- 拦截所有请求 -->
</filter-mapping>
这是最终的一个Filter示例,用于确认是否已经登录和记录日志:
MyFilter.java
package com.niko.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
@WebFilter(filterName = "MyFilter",urlPatterns = "/*")
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// 获取请求信息
String method = httpRequest.getMethod();
String requestURI = httpRequest.getRequestURI();
String clientIP = httpRequest.getRemoteAddr();
// 1. 身份验证
// if (!isAuthenticated(httpRequest)) {
// // 用户未登录,重定向到登录页面
// httpResponse.sendRedirect(httpRequest.getContextPath() + "/login.jsp");
// return;
// }
// 2.记录请求日志
System.out.println("Request at " + new Date() + " - URL: " + httpRequest.getRequestURL());
// 继续执行下一个过滤器或者目标 Servlet
chain.doFilter(request, response);
}
@Override
public void destroy() {
Filter.super.destroy();
}
// 判断用户是否已经认证(通过会话中保存的用户信息判断)
private boolean isAuthenticated(HttpServletRequest request) {
Object user = request.getSession().getAttribute("user");
return user != null; // 如果会话中有用户信息,说明已登录
}
}
更多知识,可以查阅Servlet 编写过滤器 | 菜鸟教程
第七个Servlet实例:监听器Listener
Web三大组件:
-
Servlet
-
Filter
-
Listener
监听器可以 监听 JavaWeb 中的三大域对象:HttpServletRequest、HttpSession、ServletContext
(创建和销毁),一旦被监视的对象发生相应的变化,应该采取相应的操作。
相应的,Listener有三个接口类:HttpSessionListener、ServletRequestListener、ServletContextListener
其中的方法名一般是“监听对象+Initialized/Created/Destroyed
”,用于表示监听对象的销毁和重建时进行的动作。
建立MyListener.java
package com.niko.listener;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
@WebListener
public class MyListener implements ServletContextListener {
/**
* @param sce
* 你可以在这里初始化一些全局资源,比如数据库连接池、日志等
* 比如:加载配置文件、初始化数据库连接池等
*/
@Override
public void contextInitialized(ServletContextEvent sce) {
// 获取ServletContext对象
ServletContext context = sce.getServletContext();
// 存储信息到 ServletContext
context.setAttribute("appMessage", "Web应用已启动,欢迎访问我们的应用!");
context.setAttribute("appName", "Niko");
context.setAttribute("appVersion", "1.0.0");
System.out.println("Web应用已启动!");
String appName = (String) sce.getServletContext().getAttribute("appName");
String appVersion=sce.getServletContext().getAttribute("appVersion").toString();
System.out.println("应用名: " + appName);
System.out.println("版本号: " + appVersion);
}
/**
* @param sce
* 在Web应用关闭时调用
* 在这里可以进行资源清理工作,比如关闭数据库连接池、清理缓存等
*/
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("Web应用已关闭!");
}
}
件、初始化数据库连接池等
*/
@Override
public void contextInitialized(ServletContextEvent sce) {
// 获取ServletContext对象
ServletContext context = sce.getServletContext();
// 存储信息到 ServletContext
context.setAttribute("appMessage", "Web应用已启动,欢迎访问我们的应用!");
context.setAttribute("appName", "Niko");
context.setAttribute("appVersion", "1.0.0");
System.out.println("Web应用已启动!");
String appName = (String) sce.getServletContext().getAttribute("appName");
String appVersion=sce.getServletContext().getAttribute("appVersion").toString();
System.out.println("应用名: " + appName);
System.out.println("版本号: " + appVersion);
}
/**
* @param sce
* 在Web应用关闭时调用
* 在这里可以进行资源清理工作,比如关闭数据库连接池、清理缓存等
*/
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("Web应用已关闭!");
}
}
---
至此,Servlet应用的全貌暂且算是过了一遍,在实际开发或者是应试过程中,不要止步于此,多多阅读源码、查阅文档,灵活地利用这些机制(生命周期、过滤器、监听器、作用域……)去完成自己想完成的项目吧。