`
lukesun1987
  • 浏览: 5026 次
  • 性别: Icon_minigender_1
  • 来自: 北京
最近访客 更多访客>>
社区版块
存档分类
最新评论

过滤器浅谈

阅读更多
过滤器的生命周期:
1、实例化
通过web.xml配置,web容器启动时就会加载过滤器,实例化只会实例化一次。Servlet3.0规范中新增了@WebFilter的方式,这种方式用于讲一个类声明为过滤器,该注解将会在部署时被web容器处理,容器将根据具体的属性配置将相应的类部署为过滤器。
2、初始化
调用过滤器的init(),这是过滤器的初始化方法,web容器创建过滤器实例后将调用这个方法。这个方法中可以读取web.xml文件中过滤器的参数。
3、过滤方法
每次请求都会调用过滤器的doFilter(),这个方法完成实际的过滤操作。这个是过滤器的核心方法,当用户请求访问与过滤器关联的URL时,web容器将先调用过滤器的doFilter()。FilterChain参数可以调用chain.doFilter(),将请求传递给下一个过滤器(或目标资源),或利用转发,重定向将请求转发到其他资源。
4、销毁
当web容器在销毁过滤器实例前调用destroy(),在这个方法中可以释放过滤器占用的资源。

过滤器的分类:
指定过滤器的转发模式,对应filter中的dispatchTypes属性,具体属性值包括以下5个,其中Servlet2.5支持REQUEST、FORWARD、INCLUDE、ERROR,Servlet3.0新增ASYNC
1、REQUEST
用户直接访问页面时,web容器将会调用过滤器,过滤器的默认种类。
2、FORWARD
目标资源是通过RequestDispatch的forward访问时,该过滤器将被调用。
3、INCLUDE
目标资源是通过RequestDispatch的include访问时,该过滤器将被调用。
4、ERROR
目标资源是通过声明式异常处理机制调用时,过滤器将被调用。
5、ASYNC
支持异步处理

过滤器示例:
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_3_1.xsd"
         version="3.1">

    <filter>
        <filter-name>loginFilter</filter-name>
        <filter-class>com.pumpkin.filter.LoginFilter</filter-class>
        <init-param>
            <param-name>unfilteredPaths</param-name>
            <param-value>login.jsp;failure.jsp;login</param-value>
        </init-param>
    </filter>
    <filter>
        <filter-name>EncodingFilter</filter-name>
        <filter-class>com.pumpkin.filter.EncodingFilter</filter-class>
        <init-param>
            <param-name>charset</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>loginFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>EncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <servlet>
        <servlet-name>LoginServlet</servlet-name>
        <servlet-class>com.pumpkin.servlet.LoginServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>LoginServlet</servlet-name>
        <url-pattern>/login</url-pattern>
    </servlet-mapping>

    <welcome-file-list>
        <welcome-file>login.jsp</welcome-file>
    </welcome-file-list>

</web-app>


login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Filter实例</title>
</head>
<body>
<%
    System.out.println("===login.jsp===");
%>
<form action="login" method="post">
    用户名:<input type="text" name="username"/>
    <br/>
    密码:<input type="password" name="password"/>
    <br/>
    <input type="submit" value="提交"/>
</form>
</body>
</html>


success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>成功页面</title>
</head>
<body>
<%
    System.out.println("===success.jsp===");
%>
${username},登陆成功!
</body>
</html>


failure.jsp
<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2017/6/17
  Time: 22:47
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>失败页面</title>
</head>
<body>
<%
    System.out.println("===failure.jsp===");
%>
登录失败!
</body>
</html>


LoginServlet.java
package com.pumpkin.servlet;

import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;


public class LoginServlet extends HttpServlet {
    public LoginServlet() {
        super();
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("===LoginServlet===doGet===");
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("===LoginServlet===doPost===start===");
        String username =  req.getParameter("username");
        String password =  req.getParameter("password");
        System.out.println("===username===" + username + "===");

        if ("admin".equals(username) && "admin".equals(password)) {
            HttpSession session = req.getSession();
            session.setAttribute("username",username);
            resp.sendRedirect(req.getContextPath() + "/success.jsp");
        }else {
            resp.sendRedirect(req.getContextPath() + "/failure.jsp");
        }
        System.out.println("===LoginServlet===doPost===end===");
    }
}


LoginFilter.java
package com.pumpkin.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.logging.Filter;


public class LoginFilter implements javax.servlet.Filter {
    private FilterConfig config;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("===LoginFilter===init===");
        config = filterConfig;
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("===LoginFilter===doFilter===start===");
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        HttpSession session = request.getSession();

        String unfilteredPaths = config.getInitParameter("unfilteredPaths");
        if (unfilteredPaths != null) {
            String[] strArray = unfilteredPaths.split(";");
            for (int i = 0; i < strArray.length; i++) {
                if (strArray[i] == null || "".equals(strArray[i])) {
                    continue;
                }
                if (request.getRequestURI().indexOf(strArray[i]) != -1) {
                    filterChain.doFilter(request, response);
                    return;
                }
            }
        }

        if (session.getAttribute("username") != null) {
            filterChain.doFilter(request, response);
        } else {
            response.sendRedirect("login.jsp");
        }
        System.out.println("===LoginFilter===doFilter===end===");
    }

    @Override
    public void destroy() {
        System.out.println("===LoginFilter===destroy===");
    }
}


EncodingFilter.java
package com.pumpkin.filter;

import com.sun.net.httpserver.Filter;
import com.sun.net.httpserver.HttpExchange;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import java.io.IOException;


public class EncodingFilter implements javax.servlet.Filter{
    private FilterConfig config;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("===EncodingFilter===init===");
        config = filterConfig;
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("===EncodingFilter===doFilter===start===");

        String charset = config.getInitParameter("charset");
        if (charset == null) {
            charset = "UTF-8";
        }
        servletRequest.setCharacterEncoding(charset);
        filterChain.doFilter(servletRequest,servletResponse);

        System.out.println("===EncodingFilter===doFilter===end===");
    }

    @Override
    public void destroy() {
        System.out.println("===EncodingFilter===destroy===");
    }
}


Servlet3.0特性:
Servlet3.0支持注解的方式注册servlet和filter,我们不需要在web.xml中配置servlet和filter,只需要在servlet和filter类中用@方式注入即可,代码如下:

LoginServlet.java
package com.pumpkin.servlet;

import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;


@WebServlet(name = "LoginServlet",urlPatterns = "/login")
public class LoginServlet extends HttpServlet {
    public LoginServlet() {
        super();
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("===LoginServlet===doGet===");
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("===LoginServlet===doPost===start===");
        String username =  req.getParameter("username");
        String password =  req.getParameter("password");
        System.out.println("===username===" + username + "===");

        if ("admin".equals(username) && "admin".equals(password)) {
            HttpSession session = req.getSession();
            session.setAttribute("username",username);
            resp.sendRedirect(req.getContextPath() + "/success.jsp");
        }else {
            resp.sendRedirect(req.getContextPath() + "/failure.jsp");
        }
        System.out.println("===LoginServlet===doPost===end===");
    }
}


LoginFilter.java
package com.pumpkin.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.logging.Filter;


@WebFilter(filterName = "Filter1_LoginFilter", value = {"/*"}, initParams = {@WebInitParam(name = "unfilteredPaths", value = "login.jsp;failure.jsp;login")})
public class LoginFilter implements javax.servlet.Filter {
    private FilterConfig config;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("===LoginFilter===init===");
        config = filterConfig;
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("===LoginFilter===doFilter===start===");
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        HttpSession session = request.getSession();

        String unfilteredPaths = config.getInitParameter("unfilteredPaths");
        if (unfilteredPaths != null) {
            String[] strArray = unfilteredPaths.split(";");
            for (int i = 0; i < strArray.length; i++) {
                if (strArray[i] == null || "".equals(strArray[i])) {
                    continue;
                }
                if (request.getRequestURI().indexOf(strArray[i]) != -1) {
                    filterChain.doFilter(request, response);
                    return;
                }
            }
        }

        if (session.getAttribute("username") != null) {
            filterChain.doFilter(request, response);
        } else {
            response.sendRedirect("login.jsp");
        }
        System.out.println("===LoginFilter===doFilter===end===");
    }

    @Override
    public void destroy() {
        System.out.println("===LoginFilter===destroy===");
    }
}


EncodingFilter.java
package com.pumpkin.filter;

import com.sun.net.httpserver.Filter;
import com.sun.net.httpserver.HttpExchange;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import java.io.IOException;


@WebFilter(filterName = "Filter0_EncodingFilter", value = {"/*"}, initParams = {@WebInitParam(name = "charset", value = "UTF-8")})
public class EncodingFilter implements javax.servlet.Filter{
    private FilterConfig config;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("===EncodingFilter===init===");
        config = filterConfig;
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("===EncodingFilter===doFilter===start===");

        String charset = config.getInitParameter("charset");
        if (charset == null) {
            charset = "UTF-8";
        }
        servletRequest.setCharacterEncoding(charset);
        filterChain.doFilter(servletRequest,servletResponse);

        System.out.println("===EncodingFilter===doFilter===end===");
    }

    @Override
    public void destroy() {
        System.out.println("===EncodingFilter===destroy===");
    }
}


LoginServlet.java
@WebServlet(name = "LoginServlet",urlPatterns = "/login")

这样的配置等同web.xml中的配置
<servlet>
	<servlet-name>LoginServlet</servlet-name>
	<servlet-class>com.pumpkin.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
	<servlet-name>LoginServlet</servlet-name>
	<url-pattern>/login</url-pattern>
</servlet-mapping>


LoginFilter.java
@WebFilter(filterName = "Filter1_LoginFilter", value = {"/*"}, initParams = {@WebInitParam(name = "unfilteredPaths", value = "login.jsp;failure.jsp;login")})

这样的配置等同web.xml中的配置
<filter>
	<filter-name>loginFilter</filter-name>
	<filter-class>com.pumpkin.filter.LoginFilter</filter-class>
	<init-param>
		<param-name>unfilteredPaths</param-name>
		<param-value>login.jsp;failure.jsp;login</param-value>
	</init-param>
</filter>

<filter-mapping>
	<filter-name>loginFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>


EncodingFilter.java
@WebFilter(filterName = "Filter0_EncodingFilter", value = {"/*"}, initParams = {@WebInitParam(name = "charset", value = "UTF-8")})

这样的配置等同web.xml中的配置
<filter>
	<filter-name>EncodingFilter</filter-name>
	<filter-class>com.pumpkin.filter.EncodingFilter</filter-class>
	<init-param>
		<param-name>charset</param-name>
		<param-value>UTF-8</param-value>
	</init-param>
</filter>

<filter-mapping>
	<filter-name>EncodingFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>


Servlet3.0过滤器执行顺序:
Servlet3.0之前Filter过滤的顺序是由用户在web.xml中配置的顺序决定的,如我们的程序会先执行LoginFilter,再执行encodingFilter。
在3.0之后新增@WebFilter注解,当使用注解配置多个Filter时,用户无法控制其执行顺序,此时Filter过滤的顺序是按照Filter的类名来控制的,按自然排序的规则。LoginFilter.java 和 EncodingFilter.java 这两个文件里面分别是“用户登录过滤器”和“字符集过滤器”,因为这两个文件的首字母E排L之前,导致每次执行的时候都是先执行“字符集过滤器”再执行“用户登录过滤器”,所以我们现在修改两个文件的名称分别为
Filter0_EncodingFilter.java
Filter1_LoginFilter.java
这样就能先执行“用户登录过滤器”再执行“字符集过滤器”。
分享到:
评论

相关推荐

    基于Java的SaaS OA协同办公毕设(源码+使用文档)

    系统概述 SaaS OA协同办公系统通常包括以下几个关键组件: 用户界面(UI):提供用户交互界面,用于任务管理、日程安排、文档共享等。 后端服务:处理业务逻辑,如用户认证、数据管理、服务集成等。 数据库:存储用户数据、任务数据、文档数据等。 服务层:提供业务逻辑服务,如权限管理、工作流程等。 集成API:与其他系统集成,如邮件服务、短信服务等。 主要功能 用户认证与管理:用户登录、权限分配、用户资料管理。 任务管理:创建、分配、跟踪和归档任务。 日程管理:安排会议、提醒事件、查看日历。 文档管理:上传、下载、共享和版本控制文档。 协同工作:实时编辑文档、团队讨论、任务协作。 技术架构 Java:作为主要的编程语言。 Spring Boot:用于快速开发基于Java的后端服务。 Apache Shiro或Spring Security:用于安全和认证。 Thymeleaf或JSF:用于构建Java Web应用的用户界面。 数据库:如MySQL、PostgreSQL或MongoDB。 开发优势 实用性:解决企业日常办公需求,提高工作效率。 技术先进:使用当前流行的Java技术栈和框架。

    虎年春节送祝福微信小程序源码下载/新版UI/支持多种流量主

    虎年春节送祝福微信小程序源码下载,新版UI支持多种流量主,这是一款网友用以前发过的一款端午送祝福改的一款小程序。 里面的背景图包括祝福语都已经修改成与虎年相关的内容了,总体来说找的背景图还是可以的,不过有些地方和细节小编也给完善了一下。 然后小编测试的时候发现还没有流量主,所以小编也给加了几个流量主进去,到时候大家直接替换流量主的ID就可以了。 另外支持更多小程序推荐,拥有独立的推荐界面 PS:进入送祝福的按钮,部分机型是在老虎的帽子那里,部分是在金元宝那里

    智能车竞赛介绍&竞赛案例&智能车开发技术&技术项目.docx

    智能车竞赛是一个涉及人工智能、机器人技术和工程学的跨学科竞技活动。在这类比赛中,参赛者通常需要设计、构建和编程一辆能够自主行驶的智能车,并使其在给定的赛道上完成特定任务或挑战。以下是一些智能车竞赛的介绍、案例、技术和项目: 1. 智能车竞赛介绍: 智能车竞赛是一种比赛形式,旨在促进人工智能、机器人技术等领域的发展与创新。参赛者通过设计和编程智能车,挑战其在复杂环境中的自主感知、决策和行动能力。 2. 竞赛案例: RoboCup: 国际机器人世界杯大赛,包括足球比赛、救援比赛等多个项目,旨在推动机器人技术的发展与应用。 Formula Student Driverless: 一种大学生工程师团队间的比赛,要求参赛车辆自主完成赛道上的行驶和各种任务。 DARPA Urban Challenge: 由美国国防高级研究计划局(DARPA)主办的自动驾驶车辆竞赛,要求车辆在城市环境中完成一系列任务。 3. 智能车开发技术: 感知技术: 使用传感器(如摄像头、激光雷达、超声波传感器等)感知周围环境,获取路况和障碍物信息。 决策与规划技术: 基于感知系统获取的信息,采用不同的算法进行决策,包括路

    奇异谱分析的matlab程序(包括部分实验结果).zip

    该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示

    基于微信小程序的家庭记账本(后端接口ssm框架实现)-毕设项目

    毕业设计基于微信小程序的家庭记账本(后端接口ssm框架实现)-毕设项目.zip 个人经导师指导并认可通过的高分设计项目,评审分98分。主要针对计算机相关专业的正在做毕设的学生和需要项目实战练习的学习者,也可作为课程设计、期末大作业。 项目介绍: 家庭记账本项目, 前端为微信小程序,后端接口为ssm框架实现,项目包含源码、数据库毕业设计基于微信小程序的家庭记账本(后端接口ssm框架实现)-毕设项目.zip 个人经导师指导并认可通过的高分设计项目,评审分98分。主要针对计算机相关专业的正在做毕设的学生和需要项目实战练习的学习者,也可作为课程设计、期末大作业。 项目主要功能: 这是一个基于微信小程序的毕设项目,实现了家庭记账本功能。后端采用SSM(Spring、SpringMVC、MyBatis)框架进行接口开发。主要功能包括收支记录管理、分类统计、账户管理等,旨在方便用户跟踪和分析家庭财务状况。特点在于便捷的移动端使用体验,结合微信小程序的特性,实现随时随地记账。技术栈涵盖Java后端开发和微信小程序前端,为用户提供了轻量级的财务管理和数据分析工具。

    机器视觉:5G工业的“眼睛”

    机器视觉:5G工业的“眼睛”

    2024嵌入式面试资料IO模型

    2024嵌入式面试资料IO模型提取方式是百度网盘分享地址

    网络安全相关的密码学、网络攻防、安全分析等教程.docx

    网络安全相关的密码学、网络攻防、安全分析等教程.docx

    某三层流水别墅建筑施工说明,材料做法表A2.dwg

    某三层流水别墅建筑施工说明,材料做法表A2.dwg

    基于stm32的英语单词学习设备

    基于stm32的英语单词学习设备

    HIT 软件工程实验二 第一次迭代

    HIT 软件工程实验二 第一次迭代

    Python基于Tensorflow的手势识别系统源码+数据资料+说明文档.zip

    数据收集和预处理:收集手势数据集,包括不同手势的图像或视频。然后对这些数据进行预处理,例如裁剪、调整大小和标准化,以便于输入到神经网络模型中。 神经网络模型:使用TensorFlow构建一个深度学习模型来学习手势的特征并进行分类。常见的模型包括卷积神经网络(CNN)、循环神经网络(RNN)或其变体,用于处理图像序列的长期依赖性。 训练模型:使用收集的手势数据集对神经网络模型进行训练。通过将标记好的手势图像作为输入,并提供相应的标签(手势类型),来调整模型的参数以最小化预测误差。 模型评估:评估训练好的模型在测试数据集上的性能。这包括计算准确率、精确度、召回率等指标,以了解模型对手势识别的整体效果。 实时手势识别:将训练好的模型部署到实际系统中,例如智能手机应用程序或摄像头监测系统,以实时地识别人类手势。 模型优化:根据实时手势识别的反馈和用户反馈,对模型进行优化和改进,以提高识别准确率和性能。 综上所述,基于TensorFlow的手势识别系统可以帮助识别和理解人类手势的动作,广泛应用于虚拟现实、智能交互、手势控制等领域。

    汽车智能工厂报告:智能工厂降本增效,自动化设备迎来稳健发展

    汽车智能工厂报告:智能工厂降本增效,自动化设备迎来稳健发展

    使用VC++实现的类似C#下List<Key,Value>形态的数组字典

    可以同时通过index或者Key来对数组元素进行查询。在VC++ 2022下的示例: List<std::string, int> list; list.add("one", 1); auto element1 = list.getByIndex(1); auto element2 = list.getByKey("two"); for (const auto& element : list) { std::cout << "Element: " << element << std::endl; }

    某三层流水别墅建筑施工图暖-施工图.dwg

    某三层流水别墅建筑施工暖-施工图.dwg

    2024年铁基非晶合金行业分析报告.pptx

    行业报告

    整站程序亿网家教网站管理程序 v2.0-ywjj-php

    [整站程序]亿网家教网站管理程序 v2.0_ywjj-php

    优化BP神经网络(GWO-BP)灰狼算法

    优化BP神经网络(GWO-BP)【灰狼算法】

    2024嵌入式大厂面经赛科世纪面试题

    2024嵌入式大厂面经赛科世纪面试题提取方式是百度网盘分享地址

    2024嵌入式面试资料如何看芯片数据手册

    2024嵌入式面试资料如何看芯片数据手册提取方式是百度网盘分享地址

Global site tag (gtag.js) - Google Analytics