Servlet三小时速成

news2024/11/15 6:14:34

Servlet三小时速成

实例驱动的速成教程。自己敲一遍的话入门还是没问题的。如有错误请读着多多包涵。

Serlet的前辈:CGI 通用网关接口

CGI通过调用外部程序来处理HTTP请求,对于每个请求都会启动一个新的进程。

这就导致了许多问题,首先是请求数量的受限,不可能无限地启动进程,调用外部程序的做法也会延长处理请求的时间;另外,CGI极其依赖平台的语言(如C、C++、perl等等)

Servlet的优势

servlet很好地解决了CGI的问题。

Web容器面对多个请求的情况,使用线程来替代进程,降低了通信成本,也因此提高了性能和稳定性;另外,Servlet由JVM管理,背靠的是Java语言,移植性很好,平台依赖性不高。

速通Servlet API

主要是两个软件包:image-20241103090030353

关于servlet和httpServlet的区别:

Servlet 是其中一个接口,定义了所有 servlet 的基本功能和方法(如 init()service()destroy())。

HttpServletServlet 接口的抽象类,专门为处理 HTTP 请求而设计,扩展了 Servlet 的功能。实际用的比较多。

javax.servlet

API 文档速查:javax.servlet (Java™ EE 8 Specification APIs)

常用接口
接口描述
AsyncContext表示在 ServletRequest 上启动的异步操作的执行上下文的类。
AsyncListener当在添加了该监听器的 ServletRequest 上启动的异步操作完成、超时或出现错误时,将通知的监听器。
Filter过滤器是一个对象,用于对请求资源(如 servlet 或静态内容)或对响应资源进行过滤操作,或同时对两者进行过滤。
FilterChainFilterChain 是一个由 servlet 容器提供给开发者的对象,提供对过滤请求资源的调用链的视图。
FilterConfig过滤器配置对象,servlet 容器在初始化时用于传递信息给过滤器。
FilterRegistration通过此接口可以进一步配置过滤器。
FilterRegistration.Dynamic通过此接口可以进一步配置通过 ServletContextaddFilter 方法注册的过滤器。
ReadListener此类表示一个回调机制,将在 HTTP 请求数据可被读取而不阻塞时通知实现类。
Registration通过此接口可以进一步配置 ServletFilter
Registration.Dynamic通过此接口可以进一步配置通过 ServletContextaddServletaddFilter 方法分别注册的 ServletFilter
RequestDispatcher定义一个接收客户端请求并将其发送到服务器上任何资源(如 servlet、HTML 文件或 JSP 文件)的对象。
Servlet定义所有 servlets 必须实现的方法。
ServletConfigservlet 配置对象,servlet 容器在初始化时用于传递信息给 servlet
ServletContainerInitializer允许库/运行时在 web 应用程序的启动阶段被通知,并根据需要进行程序化注册 servletsfilterslisteners 的接口。
ServletContext定义 servlet 用于与其 servlet 容器通信的一组方法,例如获取文件的 MIME 类型、分发请求或写入日志文件。
ServletContextAttributeListener接收有关 ServletContext 属性更改的通知事件的接口。
ServletContextListener接收有关 ServletContext 生命周期更改的通知事件的接口。
ServletRegistration通过此接口可以进一步配置 Servlet
ServletRegistration.Dynamic通过此接口可以进一步配置通过 ServletContextaddServlet 方法注册的 Servlet
ServletRequest定义一个对象,用于向 servlet 提供客户端请求信息。
ServletRequestAttributeListener接收有关 ServletRequest 属性更改的通知事件的接口。
ServletRequestListener接收有关进入和离开 web 应用程序范围的请求的通知事件的接口。
ServletResponse定义一个对象,以帮助 servlet 向客户端发送响应。
SessionCookieConfig可用于配置用于会话跟踪目的的 cookie 的各种属性的类。
SingleThreadModelJava Servlet API 2.4 起已弃用,没有直接替代品。
WriteListener回调通知机制,向开发者信号可在不阻塞的情况下写入内容。
常用类
描述
AsyncEvent当在 ServletRequest 上启动的异步操作(通过调用 ServletRequest.startAsync()ServletRequest.startAsync(ServletRequest, ServletResponse))完成、超时或产生错误时触发的事件。
GenericFilter定义一个通用的、协议无关的过滤器。
GenericServlet定义一个通用的、协议无关的 servlet。
HttpConstraintElementHttpConstraint 注解值的 Java 类表示。
HttpMethodConstraintElementHttpMethodConstraint 注解值的 Java 类表示。
MultipartConfigElementMultipartConfig 注解值的 Java 类表示。
ServletContextAttributeEvent关于 web 应用程序的 ServletContext 属性更改的通知事件类。
ServletContextEvent这是关于 web 应用程序的 servlet 上下文更改的通知事件类。
ServletInputStream提供用于从客户端请求中读取二进制数据的输入流,包括一个高效的 readLine 方法,用于逐行读取数据。
ServletOutputStream提供用于向客户端发送二进制数据的输出流。
ServletRequestAttributeEvent这是关于 servlet 请求中属性更改的通知事件类。
ServletRequestEvent此类事件指示 ServletRequest 的生命周期事件。
ServletRequestWrapper提供 ServletRequest 接口的方便实现,开发者可以通过子类化来适配请求到一个 servlet。
ServletResponseWrapper提供 ServletResponse 接口的方便实现,开发者可以通过子类化来适配来自一个 servlet 的响应。
ServletSecurityElementServletSecurity 注解值的 Java 类表示。
枚举
枚举描述
DispatcherType过滤器调度类型的枚举。
SessionTrackingMode会话跟踪模式的枚举。
异常
异常描述
ServletException定义一个通用异常,servlet 在遇到困难时可以抛出该异常。
UnavailableException定义一个异常,servlet 或过滤器抛出该异常以指示其永久或暂时不可用。

javax.servlet.http

接口
接口描述
HttpServletMapping允许在运行时发现当前 HttpServletRequestHttpServlet 被调用的方式。
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 起已弃用。
枚举
枚举描述
MappingMatchServlet 映射类型的枚举。

第一个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方法,并一共完成三个任务:

  1. 控制台输出
  2. 网页输出
  3. 获取请求中的参数

第二个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);
        }
    }
}

image-20241104124758803

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这是采用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对象,开发者可以直接使用它们而不用显式声明。

对象描述
requestHttpServletRequest类的实例,代表 HTTP 请求的对象,包含客户端发送到服务器的信息,如表单数据、URL参数等。
responseHttpServletResponse类的实例,代表 HTTP 响应的对象,用于向客户端发送数据和响应。
outJspWriter类的实例,用于向客户端输出文本内容的对象,通常用于生成HTML。
sessionHttpSession类的实例,代表用户会话的对象,可用于存储和检索用户特定的数据,跨多个页面。
applicationServletContext类的实例,代表 Web 应用程序的上下文,可以用于存储和检索全局应用程序数据。
configServletConfig类的实例,包含有关当前 JSP 页面的配置信息,例如初始化参数。
pageContextPageContext类的实例,提供对JSP页面所有对象以及命名空间的访问
page类似于 Java 类中的 this 关键字,代表当前 JSP 页面的实例,可以用于调用页面的方法。
exceptionexception 类的对象,代表发生错误的 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

效果如下:

image-20241106193341095

学到的几个知识点:

  • 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应用的全貌暂且算是过了一遍,在实际开发或者是应试过程中,不要止步于此,多多阅读源码、查阅文档,灵活地利用这些机制(生命周期、过滤器、监听器、作用域……)去完成自己想完成的项目吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2240666.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Qt主线程把数据发给子线程,主线程会阻塞吗

演示&#xff1a; #include <QCoreApplication> #include <QThread> #include <QObject> #include <QDebug>// 子线程类 class Worker : public QObject {Q_OBJECT public slots:void processData(int data) {qDebug() << "Processing dat…

「QT」文件类 之 QTemporaryFile 临时文件类

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「QT」QT5程序设计&#x1f4da;全部专栏「Win」Windows程序设计「IDE」集成开发环境「UG/NX」BlockUI集合「C/C」C/C程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「UG/NX」NX定制…

探索Python的HTTP利器:Requests库的神秘面纱

文章目录 **探索Python的HTTP利器&#xff1a;Requests库的神秘面纱**一、背景&#xff1a;为何选择Requests库&#xff1f;二、Requests库是什么&#xff1f;三、如何安装Requests库&#xff1f;四、Requests库的五个简单函数使用方法1. GET请求2. POST请求3. PUT请求4. DELET…

【Java语言】String类

在C语言中字符串用字符可以表示&#xff0c;可在Java中有单独的类来表示字符串&#xff08;就是String&#xff09;&#xff0c;现在我来介绍介绍String类。 字符串构造 一般字符串都是直接赋值构造的&#xff0c;像这样&#xff1a; 还可以这样构造&#xff1a; 图更能直观的…

jmeter常用配置元件介绍总结之线程组

系列文章目录 安装jmeter jmeter常用配置元件介绍总结之线程组 1.线程组(用户)1.1线程组1.1.setUp线程组和tearDown线程组1.2.Open Model Thread Group(开放模型线程组)1.3.bzm - Arrivals Thread Group(到达线程组)1.4.jpgc - Ultimate Thread Group(终极线程组)1.5.jpgc - St…

电工电子原理笔记

这一篇手记会记录我硬件开发过程中遇到的一些底层电学原理&#xff0c;并且结合实际场景作为“例题”&#xff08;出于篇幅和保密考虑会进行部分简化&#xff09;。 叠加定理 基本介绍 在线性电路中&#xff0c;任一支路的电流&#xff08;或电压&#xff09;可以看成是电路…

Python与其他语言比较·练习题 --- 《跟着小王学Python》

Python与其他语言比较练习题 — 《跟着小王学Python》 《跟着小王学Python》 是一套精心设计的Python学习教程&#xff0c;适合各个层次的学习者。本教程从基础语法入手&#xff0c;逐步深入到高级应用&#xff0c;以实例驱动的方式&#xff0c;帮助学习者逐步掌握Python的核心…

STM32 使用 STM32CubeMX HAL库实现低功耗模式

STM32 使用 HAL 库的低功耗模式测试使用 ...... 矜辰所致前言 上次画了一个 STM32L010F4 最小系统的板子&#xff0c;也做了一些基本测试&#xff0c;但是最重要的低功耗一直拖到现在&#xff0c;以前在使用 STM32L151 的时候用标准库做过低功耗的项目&#xff0c;现在都使…

接口自动化代码编写规范

命名规范 文件命名&#xff1a;测试文件应该以 test_ 开头&#xff0c;以 _test.py 结尾&#xff0c;例如&#xff1a;test_my_module_test.py。 函数命名&#xff1a;测试函数应该以 test_ 开头&#xff0c;描述清楚被测试的功能或行为&#xff0c;使用下划线分隔单词&#…

ESLint 使用教程(四):ESLint 有哪些执行时机?

前言 ESLint 作为一个静态代码分析工具&#xff0c;可以帮助我们发现和修复代码中的问题&#xff0c;保持代码风格的一致性。然而&#xff0c;ESLint的最佳实践不仅仅在于了解其功能&#xff0c;更在于掌握其执行时机。本文将详细介绍ESLint在不同开发阶段的执行时机&#xff…

Leetcode 存在重复元素II

这段代码的算法思想可以用以下步骤来解释&#xff1a; 算法思想 使用哈希表&#xff08;HashMap&#xff09;存储每个元素的索引&#xff1a; 遍历数组 nums 时&#xff0c;使用一个 HashMap 来记录每个元素的值和它的索引位置。这样可以快速查找之前出现过的相同元素的索引。…

1111111111待修改--大流量分析(三)-BUUCTF

总结摘要 题目来来源URL https://buuoj.cn/challenges#%E5%A4%A7%E6%B5%81%E9%87%8F%E5%88%86%E6%9E%90%EF%BC%88%E4%B8%89%EF%BC%89 答题过程 这道题是看大佬写着说查找phpinfo&#xff0c;我现在也不知道为什么能够一下子就定位到这里了 这里先按照phpinfo进行&#xff…

在Docker环境下为Nginx配置HTTPS

前言 配置HTTPS已经成为网站部署的必要步骤。本教程将详细介绍如何在Docker环境下为Nginx配置HTTPS&#xff0c;使用自签名证书来实现加密通信。虽然在生产环境中建议使用权威CA机构颁发的证书&#xff0c;但在开发测试或内网环境中&#xff0c;自签名证书是一个很好的选择。 …

Python →爬虫实践

爬取研究中心的书目 现在&#xff0c;想要把如下网站中的书目信息爬取出来。 案例一 耶鲁 Publications | Yale Law School 分析网页&#xff0c;如下图所示&#xff0c;需要爬取的页面&#xff0c;标签信息是“<p>”&#xff0c;所以用 itemssoup.find_all("p&…

expo5.2运行web报错Cannot find module ‘react‘

修改app.json中的web output 配置为 ‘single’ 可以解决 expo run web 这个错误问题 "web": {"bundler": "metro","output": "single","favicon": "./assets/images/favicon.png"},相关链接&#xff1…

信号保存和信号处理

目录 信号保存中重要的概念 内核中信号的保存 对sigset_t操作的函数 对block&#xff0c;pendding&#xff0c;handler三张表的操作 sigpromask ​编辑 sigpending 是否有sighandler函数呢&#xff1f; 案例 信号处理 操作系统是如何运行的&#xff1f; 硬件中断 …

C#从入门到放弃

C#和.NET的区别 C# C#是一个编程语言 .NET .NET是一个在window下创建程序的框架 .NET框架不仅局限于C#,它还可以支持很多语言 .NET包括了2个组件&#xff0c;一个叫CLR(通用语言运行时)&#xff0c;另一个是用来构建程序的类库 CLR 用C写一个程序&#xff0c;在一台8688的机器…

STM32 HAL 矩阵按键(轮询方式)

1、简介 最近准备做个门禁系统,首先通过4x4矩阵按键实现密码的设定,查看网上资料完成了4x4矩阵按键的初步使用,整理一个傻瓜式操作方便后续的使用与复习。 2、实物图 3、STM32CubeMX配置 4、KEY.C /******************************************************************…

linux网络编程9——无锁队列

文章目录 无锁队列1. 无锁队列原理1.1 多线程并发控制策略介绍1.2 无锁队列概念1.3 无锁队列的分类1.3.1 以生产者消费者数量划分1.3.2 以底层数据结构划分1.3.3 侵入式与非侵入式链表队列 1.4 无锁队列应用场景 2. 无锁队列的实现2.1 MPSCQueue2.2 rte_ring 学习参考 无锁队列…

【电子设计】按键LED控制与FreeRTOS

1. 安装Keilv5 打开野火资料,寻找软件包 解压后得到的信息 百度网盘 请输入提取码 提取码:gfpp 安装526或者533版本都可以 下载需要的 F1、F4、F7、H7 名字的 DFP pack 芯片包 安装完 keil 后直接双击安装 注册操作,解压注册文件夹后根据里面的图示步骤操作 打开说明 STM…