首页 > 其他分享 >深入理解Servlet

深入理解Servlet

时间:2024-09-16 11:19:45浏览次数:14  
标签:java String Servlet 理解 深入 import servlet response

引言

  Servlet是一门很古老的技术了,网上很多人说没必要学了,可以直接学习Springboot+vue进行开发。但Servlet作为‌Java EE规范的一部分,是这些框架的基础。掌握Servlet对于理解现代Web开发框架至关重要。‌当然每个人有自己独特的见解,写这篇文章方便自己回顾,也希望能帮助到他人。如文章中有描述错误,欢迎大家指正!

相关资源下载

mysql驱动jar包官网下载: https://dev.mysql.com/downloads/connector/j/
mysql驱动jar包下载教程: https://blog.51cto.com/u_15858035/11005824
tomcat官网下载: https://tomcat.apache.org/
tomcat下载教程: https://blog.csdn.net/qq_42257666/article/details/105701914
jakartaee帮助文档官网下载: https://jakarta.ee/zh/specifications/
jakartaee下载教程: https://blog.csdn.net/yuanhong55/article/details/133868639

什么是Servlet?(以下内容来自百度百科)

  Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容。
  狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。Servlet运行于支持Java的应用服务器中。从原理上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。
  最早支持Servlet标准的是JavaSoft的Java Web Server,此后,一些其它的基于Java的Web服务器开始支持标准的Servlet。

快速入门(从0到1搭建)

  1. 新建项目
    在这里插入图片描述

  2. 新建模块
    在这里插入图片描述

  3. 为模块添加web框架支持
    在这里插入图片描述

  4. 创建工件
    在这里插入图片描述

  5. 为模块添加依赖(刚才下载的tomcat中有这个依赖)
    在这里插入图片描述

  6. idea配置tomcat
    在这里插入图片描述

  7. 部署工件
    在这里插入图片描述

  8. 实现Servlet接口
    在这里插入图片描述

  9. 在service方法中编写响应代码
    在这里插入图片描述

  10. 在web.xml中编写Servlet配置信息
    在这里插入图片描述

  11. 启动项目在浏览器访问
    在这里插入图片描述

到这里,第一个demo程序便写完了。

了解Servlet接口

Servlet接口有五个方法

void init(ServletConfig config) throws ServletException;      由servlet容器调用,向servlet指示servlet正在投入服务。

void service(ServletRequest request, ServletResponse response) throws ServletException, IOException;       由servlet容器调用,以允许servlet响应请求。

ServletConfig getServletConfig();       返回一个ServletConfig对象,其中包含此servlet的初始化和启动参数。

String getServletInfo();        返回有关servlet的信息,如作者、版本和版权。

void destroy();         由servlet容器调用,向servlet指示servlet正在停止服务。

方法执行顺序测试
在这里插入图片描述

多次访问demo然后结束程序,控制台输出结果
在这里插入图片描述

由此结果可得结论:

  1. init方法最先执行,且只执行一次
  2. service方法执行次数与用户访问次数有关
  3. 当程序结束之前,tomcat会调用这个servlet的destroy方法
既然Demo这整个Servlet都由tomcat服务器来调用, 那这个demo对象也是由tomcat来创建,我们能不能自己创建创建一个?

在这里插入图片描述

浏览器访问
在这里插入图片描述

控制台输出
在这里插入图片描述

当浏览器发送请求后,才出现了以上控制台输出,说明:

  1. tomcat默认不会创建servlet对象,而且servlet对象只会创建一次,是单例的,但是我们可以手动创建servlet对象,所以是一种假单例。
  2. 执行顺序(无参构造、init、service)
  3. 我们可以创建servlet对象,但是似乎没什么用,而且还有一种风险
    • 当我们写了一个有参构造函数,不写无参,项目会启动失败(tomcat要调用无参构造函数)

所以我们应该避免自己去写构造函数,如果有初始化参数,可以放在init方法中(init方法先于service方法执行)

那么假设我们有一个Servlet需要连接mysql数据库,有一些初始化参数,我们可以怎么做呢?

这个时候我们可以使用ServletConfig这个对象了。一个Servlet对应一个ServletConfig。
在<init-param></init-param>标签中配置的参数可以被ServletConfig获取到
在这里插入图片描述

使用ServletConfig获取初始化信息
在这里插入图片描述

了解ServletConfig接口

ServletConfig中有四个方法

String getServletName();        返回此servlet实例的名称。

ServletContext getServletContext();     返回对调用方正在其中执行的ServletContext的引用。

String getInitParameter(String key);    获取具有给定名称的初始化参数的值。

Enumeration<String> getInitParameterNames();    返回servlet初始化参数的名称,作为String对象的枚举,如果servlet没有初始化参数,则返回空枚举。

实践
在这里插入图片描述

由于init方法和destroy方法只会执行一次, 那么我们应该重点关注service方法,因为每发一次请求,就会调用这个方法。

void service(ServletRequest request, ServletResponse response) throws ServletException, IOException;

那么既然我们着重于service方法,而init方法是按需使用,我们能不能专注于service方法的编写呢?

当然可以。
Servlet接口有一个适配器GenericServlet,该类实现了Servlet、ServletConfig、Serializable接口。它将service方法设为抽象方法,我们只需要继承该抽象类,然后重写service方法便可以进行web开发。
那么以后如果有初始化参数,我们可以按需重写init方法
重写时我们发现有两个init方法,这是怎么回事,看看源码

public void init(ServletConfig config) throws ServletException {
    this.config = config;
    this.init();
}

public void init() throws ServletException {
}
那么哪个是我们需要重写的呢?

可以看出上面的那个init方法是GenericServlet实现Servlet接口中的方法。
里面将形参config交给了成员变量config来保管,在源码下面我们发现

public ServletConfig getServletConfig() {
    return this.config;
}

这个方法提供了ServletConfig对象,这表明GenericServlet将config保存了下来,并且可以供子类使用。
如果我们重写这个有参的init的方法,便破坏了GenericServlet结构,而有参的init方法之后调用了无参的init方法,所以我们重写无参的init方法也是能实现效果的。
所以如果重写init方法,我们应该重写GenericServlet中无参的init方法。

继承GenericServlet进行web开发
import java.io.IOException;
import java.io.PrintWriter;

public class Servlet01 extends GenericServlet {
    @Override
    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8"); //设置响应信息格式
        PrintWriter out = response.getWriter();
        out.print("基于GenericServlet进行web开发");
    }
}

编写servlet配置信息

<servlet>
    <servlet-name>servlet01</servlet-name>
    <servlet-class>com.hzh.linux.web.Servlet01</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>servlet01</servlet-name>
    <url-pattern>/servlet01</url-pattern>
</servlet-mapping>

启动项目,浏览器访问
在这里插入图片描述

由此可见, 继承GenericServlet抽象类来开发确实是我们更专注于service方法的编写。

那么有没有更简单的开发方式呢?我们是基于http协议的web程序

当然。tomcat中有针对http协议的类(HttpServlet)
HttpServlet继承了GenericServlet,实现了里面的service方法。
看看源码:

public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
    HttpServletRequest request;
    HttpServletResponse response;
    try {
        request = (HttpServletRequest)req;
        response = (HttpServletResponse)res;
    } catch (ClassCastException var6) {
    throw new ServletException(lStrings.getString("http.non_http"));
    }

    this.service(request, response);
}

在源码中我们可以看到 ServletRequest 被强转成了 HttpServletRequest , ServletResponse 被强制成了 HttpServletResponse。
查看相关源码,可知 HttpServletRequest 继承了 ServletRequest
HttpServletResponse 继承了 ServletResponse。

那么为什么可以直接强转供后续使用呢?

因为我们做的就是基于http协议的web开发。

该方法最后调用了自己的service方法,再看看源码:

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String method = req.getMethod();
    long lastModified;
    if (method.equals("GET")) {
        lastModified = this.getLastModified(req);
        if (lastModified == -1L) {
            this.doGet(req, resp);
        } else {
            long ifModifiedSince;
            try {
                ifModifiedSince = req.getDateHeader("If-Modified-Since");
            } catch (IllegalArgumentException var9) {
                ifModifiedSince = -1L;
            }

            if (ifModifiedSince < lastModified / 1000L * 1000L) {
                this.maybeSetLastModified(resp, lastModified);
                this.doGet(req, resp);
            } else {
                resp.setStatus(304);
            }
        }
    } else if (method.equals("HEAD")) {
        lastModified = this.getLastModified(req);
        this.maybeSetLastModified(resp, lastModified);
        this.doHead(req, resp);
    } else if (method.equals("POST")) {
        this.doPost(req, resp);
    } else if (method.equals("PUT")) {
        this.doPut(req, resp);
    } else if (method.equals("DELETE")) {
        this.doDelete(req, resp);
    } else if (method.equals("OPTIONS")) {
        this.doOptions(req, resp);
    } else if (method.equals("TRACE")) {
        this.doTrace(req, resp);
    } else {
        String errMsg = lStrings.getString("http.method_not_implemented");
        Object[] errArgs = new Object[]{method};
        errMsg = MessageFormat.format(errMsg, errArgs);
        resp.sendError(501, errMsg);
    }

}

从源码我们大概可以看出,这个service方法首先获取了请求方式,然后根据不同的请求方式执行不同的方法。并且没匹配上会向前端响应错误
HttpServlet是一个抽象类,并未存在抽象方法,那么我们继承之后应该怎么做呢?

  1. 可以直接重写 void service(HttpServletRequest req, HttpServletResponse resp) 这个方法
    • 但是这样可以享受不到一些报错信息,需要自己给浏览器响应相关报错信息
  2. 我们可以直接重写里面对应不同请求方式所执行的函数。如: doGet、doPost等。
测试

我们编写Servlet02继承HttpServlet进行开发
Servlet02.java

package com.hzh.linux.web;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;

public class Servlet02 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        out.print("继承HttpServlet进行web开发");
    }
}

web.xml中相关配置

<servlet>
    <servlet-name>servlet02</servlet-name>
    <servlet-class>com.hzh.linux.web.Servlet02</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>servlet02</servlet-name>
    <url-pattern>/servlet02</url-pattern>
</servlet-mapping>

浏览器访问
在这里插入图片描述

如何实现多Servlet数据共享

其实我们在前面多次看到了 ServletContext 这个类,这个类相当于web.xml,可以实现多Servlet数据共享。
在 web.xml 中配置如下信息:

<context-param>
    <param-name>username</param-name>
    <param-value>root</param-value>
</context-param>
<context-param>
    <param-name>password</param-name>
    <param-value>123456</param-value>
</context-param>

在Servlet01和Servlet02中获取全局信息
Servlet01.java

package com.hzh.linux.web;

import jakarta.servlet.*;

import java.io.IOException;
import java.io.PrintWriter;

public class Servlet01 extends GenericServlet {
    @Override
    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        ServletContext application = this.getServletContext();
        String username = application.getInitParameter("username");
        String password = application.getInitParameter("password");

        PrintWriter out = response.getWriter();
        out.print(username + ": " + password);
    }
}

Servlet02.java

package com.hzh.linux.web;

import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;

public class Servlet02 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        ServletContext application = this.getServletContext();
        String username = application.getInitParameter("username");
        String password = application.getInitParameter("password");

        PrintWriter out = response.getWriter();
        out.print(username + ": " + password);
    }
}

浏览器访问结果:
在这里插入图片描述

在这里插入图片描述

注意:servletcontext中保存的信息应尽量少且不经常改变
因为ServletContext只有一个,在多线程下对同一数据进行修改存在线程安全问题。

小试牛刀

学到这里,我们可以尝试实现一个登录功能。

设计并创建数据库表 t_user
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user`  (
  `id` int NOT NULL AUTO_INCREMENT,
  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

INSERT INTO `t_user` VALUES (1, 'root', '123456');
INSERT INTO `t_user` VALUES (2, 'linux', '123456');

SET FOREIGN_KEY_CHECKS = 1;

该表有三个字段:
id: 主键, 自增
username: 用户名
password: 密码
现在插入了两条数据

设计页面原型

login.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>login page</title>
</head>

<body>
    <form action="/oa/login" method="post">
        用户名: <input name="username" /><br />
        密码: <input name="password" type="password" /><br />
        <input type="submit" value="登录" />
    </form>
</body>

</html>
新建一个应用根路径为 /oa 的模块

将login.html放到web目录下,并创建LoginServlet
在web.xml中配置欢迎页与LoginServlet信息
配置完后目录结构如下:
在这里插入图片描述

启动项目,访问 http://localhost:8080/oa/login.html
在这里插入图片描述

导入mysql的驱动jar包:在WEB-INF目录下新建lib目录,将mysql的驱动jar包复制到此处

编写LoginServlet.java
package com.hzh.linux.web;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.*;

public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();

        String driver = "com.mysql.cj.jdbc.Driver";
        String url = "jdbc:mysql://localhost:3306/servlet?characterEncoding=utf-8&serverTimezone=Asia/Shanghai";
        String user = "root";   // 数据库账号
        String pwd = "123456";  // 数据库密码
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        try {
            Class.forName(driver);
            conn = DriverManager.getConnection(url, user, pwd);
            String sql = "select * from t_user where username = ? and password = ? ";
            ps = conn.prepareStatement(sql);
            ps.setString(1, username);
            ps.setString(2, password);
            rs = ps.executeQuery();
            if(rs.next()){
                // 登录成功
                out.print("登录成功");
            }else {
                //登录失败
                out.print("登录失败");
            }
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if(rs != null){
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(ps != null){
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(conn != null){
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

启动项目,输入正确的账号密码,浏览器显示登录成功

模拟学生信息管理功能,查看当前系统有啥问题

编写StudentManagerServlet

package com.hzh.linux.web;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;

public class StudentsManagerServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        out.print("姓名:张三   年龄:16<br/>姓名:李四  年龄:17");
    }
}

将LoginServlet中登录成功后的代码改为

if(rs.next()){
    // 登录成功
    response.sendRedirect("students");
}else {
    // 登录失败
    out.print("登录失败");
}

在web.xml中配置相关信息

<servlet>
    <servlet-name>studentsManagerServlet</servlet-name>
    <servlet-class>com.hzh.linux.web.StudentsManagerServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>studentsManagerServlet</servlet-name>
    <url-pattern>/students</url-pattern>
</servlet-mapping>

浏览器中登录成功后跳转成功
在这里插入图片描述

当前问题

正常逻辑应该是后台用户管理员登录成功后才可以访问学生信息管理页面。
但是我们发现不登录也能访问到。

解决方案 —— 使用HttpSession保持会话状态

http是一种无状态协议, 我们可以利用session来保持会话状态。
LoginServlet.java中修改代码如下

if(rs.next()){
    // 登录成功
    HttpSession session = request.getSession();
    session.setAttribute("username", username);
    response.sendRedirect("students");

}else {
    //登录失败
    out.print("登录失败");
}

StudentsManagerServlet.java中doGet方法添加如下代码

HttpSession session = request.getSession();
if(session == null || session.getAttribute("username") == null){
    response.sendRedirect("login");
}

这里我们没有设置session的过期时间,那有效时间是多少呢?
查看tomcat中conf目录下的web.xml文件
在这里插入图片描述

所以可知session默认30分钟

在项目web.xml中手动配置

<session-config>
    <session-timeout>60</session-timeout>
</session-config>

这样便增加了会话保持时间

现在便实现了用户只有登录后才能访问资源的效果,但仍存在问题。

假设是一个多功能的复杂系统,难道每次都要手写上述session相关代码吗?

当然不用,我们可以使用过滤器。

在web.xml中配置过滤器
<filter>
    <filter-name>myfilter</filter-name>
    <filter-class>com.hzh.linux.web.MyFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>myfilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
编写MyFilter.java
package com.hzh.linux.web;

import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;

import java.io.IOException;

public class MyFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        String path = request.getServletPath();
        if(!(path.equals("/login") || path.equals("/login.html"))){
            HttpSession session = request.getSession();
            if(session == null || session.getAttribute("username") == null){
                response.sendRedirect("login.html");
            }
        }

        // 以上访问Servlet之前的操作

        filterChain.doFilter(request, response);

        // 以下响应浏览器之前的操作
    }
}
接下来删除StudentsManagerServlet.java中相关session判断代码

至此,我们便完成了这个小系统的开发。

Servlet的注解式开发

@WebServlet注解源码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package jakarta.servlet.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebServlet {
    String name() default "";   // 相当于<servlet-name></servlet-name>

    String[] value() default {};    // 相当于<url-pattern></url-pattern>

    String[] urlPatterns() default {};  // 相当于<url-pattern></url-pattern>

    int loadOnStartup() default -1;     // 相当于<load-on-startup></load-on-startup>

    WebInitParam[] initParams() default {}; // 相当于多个<init-param></init-param>

    boolean asyncSupported() default false; // 相当于<async-supported></async-supported>

    String smallIcon() default "";  // 该Servlet的大图标

    String largeIcon() default "";      // 该Servlet的大图标

    String description() default "";    // 相当于<description></description>

    String displayName() default "";    // 相当于<display-name></display-name>
}
@WebFilter注解源码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package jakarta.servlet.annotation;

import jakarta.servlet.DispatcherType;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebFilter {
    String description() default "";    // description 属性用于提供过滤器的描述信息。

    String displayName() default "";    // 相当于<display-name></display-name>

    WebInitParam[] initParams() default {}; // 相当于多个<init-param></init-param>

    String filterName() default ""; // 相当于<filter-name></filter-name>

    String smallIcon() default "";  // 该过滤器的小图标

    String largeIcon() default "";  // 该过滤器的大图标

    String[] servletNames() default {}; // servletNames 属性用于指定过滤器将应用于哪些 Servlet。

    String[] value() default {};    // value 属性用于指定过滤器将应用到哪些 URL 模式或 Servlet 上。

    String[] urlPatterns() default {};  // 相当于<url-pattern></url-pattern>

    DispatcherType[] dispatcherTypes() default {DispatcherType.REQUEST};    // 用于指定过滤器应用的请求分发类型。

    boolean asyncSupported() default false; // 相当于<async-supported></async-supported>
}

改造上面那个小案例

@WebServlet("/students")
public class StudentsManagerServlet extends HttpServlet {...}

@WebServlet("/login")
public class LoginServlet extends HttpServlet {...}

@WebFilter("/*")
public class MyFilter implements Filter {}

使用上诉注解后便可以将web.xml中关于servlet相关配置和filter相关配置删除

不过建议在使用过滤器时不使用注解式开发。这样在修改过滤器路径时就不用重新编译整个项目了。而且一般过滤器的数量不会很多。

标签:java,String,Servlet,理解,深入,import,servlet,response
From: https://blog.csdn.net/m0_73431159/article/details/142299792

相关文章

  • Android HandlerThread Post后延迟7秒才执行的原因及解决方案|如何提高Android后台线程
    在Android开发中,HandlerThread是用于处理后台线程任务的常见工具。然而,有时我们会遇到这样的问题:当任务通过HandlerThread的post方法发送后,任务的执行时间会出现明显的延迟,比如7秒的延迟才执行任务。本文将深入分析这种问题的成因,探讨可能的影响因素,并提供多种优化方案,帮助开发者解......
  • 阅读周·深入浅出的Node.js | 代码测试,开发者掌握代码的行为和性能的极佳思路
    背景去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。没有计划的阅读,收效甚微。新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。这个“玩法”虽然常见且板正,但是有效。已读完书籍:《架构简洁之道》。当前阅读......
  • 深入理解redis删除策略和淘汰策略
    1、redis的删除策略Redis是一种内存级数据库,数据都存在内存中,但是针对于已经过期的数据,reids不会立刻删除只是会存储在 expires 中,当执行删除策略的时候,才会从expires中寻找对应的数据存储的地址,在存储空间中找到对应的数据进行删除。数据删除其实就是内存和CPU占用之间寻......
  • 深入理解Redis锁与Backoff重试机制在Go中的实现
    目录Redis锁的深入实现Backoff重试策略的深入探讨结合Redis锁与Backoff策略的高级应用具体实现结论在构建分布式系统时,确保数据的一致性和操作的原子性是至关重要的。Redis锁作为一种高效且广泛使用的分布式锁机制,能够帮助我们在多进程或分布式环境中同步访问共享资源。本文将深......
  • 《 C++ 修炼全景指南:十 》自平衡的艺术:深入了解 AVL 树的核心原理与实现
    摘要本文深入探讨了AVL树(自平衡二叉搜索树)的概念、特点以及实现细节。我们首先介绍了AVL树的基本原理,并详细分析了其四种旋转操作,包括左旋、右旋、左右双旋和右左双旋,阐述了它们在保持树平衡中的重要作用。接着,本文从头到尾详细描述了AVL树的插入、删除和查找操作,配......
  • AutoSar AP平台的SOMEIP文档的理解笔记
    前言前段时间,阅读了AutoSarAP的SOME/IP的标准文档(《SOME/IPProtocolSpecification.pdf》),并以PPT的图文并茂的形式做了理解笔记,内容主要是SOME/IP的协议规范,由SOME/IP报文格式和协议部分。1.SOMEIP报文格式1.1SOME/IP消息格式:头格式1.2SOME/IP头格式:RequestID(Clie......
  • 带你深入了解C语言指针(三)
    目录前言一、字符指针变量字符数组与常量字符串二、数组指针变量1.数组指针变量是什么2.数组指针变量怎么初始化3.数组指针怎么利用?三、二维数组传参四、函数指针变量1.函数指针变量的创建2.函数指针变量的使用3.typedef4.define和typedef的区别五、函数指针数......
  • Git缓冲区理解:`index`,`add`和`reset`,`staged`和`unstaged`
    在git里面,有一个叫index的区域,你把东西加到那里叫add,把东西再从哪里撤回来叫reset;已经在里面的我们形容它是staged,还没有加进去的我们形容它是unstaged。其实index区就是一个纯粹的缓冲区,也叫stagingarea,是正式提交之前给我们的一个缓冲,还有犹豫的余地。因为一旦正式commit提交......
  • 阅读周·深入浅出的Node.js | Node应用,产品化实践摸索,玩转进程
    背景去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。没有计划的阅读,收效甚微。新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。这个“玩法”虽然常见且板正,但是有效。已读完书籍:《架构简洁之道》。当前阅读......
  • 深入解析C++函数指针:掌握游戏开发中的关键技术
    深入解析C++函数指针:掌握游戏开发中的关键技术C++作为一门经典的编程语言,因其强大的性能和灵活性,被广泛应用于游戏开发。而函数指针作为C++中的一个重要概念,在游戏开发中更是扮演着不可或缺的角色。对于想要深入掌握C++并在游戏开发领域站稳脚跟的开发者来说,理解并灵活运用函数指......