• Filter&Listener11_Filter1


    Filter:过滤器

    1. 概念

      * 生活中的过滤器:净水器,空气净化器,土匪

      * web中的过滤器:当访问服务器的资源时,过滤器可以将请求拦截下来,完成一些特殊的功能。

      * 过滤器的作用:一般用于完成通用的操作。如:登录验证、统一编码处理、敏感字符过滤...

    2. 快速入门

      a. 步骤:

        1. 定义一个类,实现接口Filter

        2. 复写方法

        3. 配置拦截路径

          1. web.xml

          2. 注解

      更改Filter模板:File-Settings-Editor-File and Code Templates-Other-Web-Java code templates-Filer Annotated Class.java,在这儿更改一下Filter类的脚本,新建的时候就能一键填充部分内容。

      b. 代码:

        注解配置拦截路径

      @WebFilter("/*") //访问所有资源之前都会执行该过滤器

      @WebFilter("/demo.jsp") //访问demo.jsp之前会执行该过滤器

    package cn.itcast.web.filter;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import java.io.IOException;
    
    /**
     * 过滤器快速入门程序
     */
    @WebFilter("/*")//访问所有资源之前都会执行该过滤器
    public class FilterDemo1 implements Filter {
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
    
        }
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            System.out.println("filterDemo1被执行了...");
    
            //放行
            filterChain.doFilter(servletRequest,servletResponse);
        }
    
        @Override
        public void destroy() {
    
        }
    }

      index.jsp

    <%--
      Created by IntelliJ IDEA.
      User: ajing
      Date: 2022/2/28
      Time: 21:16
      To change this template use File | Settings | File Templates.
    --%>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
      <head>
        <title>$Title$</title>
      </head>
      <body>
        index.jsp...
      </body>
    </html>

      如果不写filterChain.doFilter(servletRequest,servletResponse);那么浏览器访问http://localhost/day11/index.jsp,idea控制台输出:filterDemo1被执行了...,但是浏览器未输出index.jsp...,因为被拦截了;

      写了filterChain.doFilter(servletRequest,servletResponse);那么浏览器访问http://localhost/day11/index.jsp时,idea控制台会输出:filterDemo1被执行了...,同时浏览器会输出index.jsp...

    3. web.xml配置拦截路径

     web包中新建路径WEB-INF,WEB-INF下新建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>demo2</filter-name>
            <filter-class>cn.itcast.web.filter.FilterDemo2</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>demo2</filter-name>
            <url-pattern>/*</url-pattern> <!-- 拦截路径 -->
        </filter-mapping>
        
    </web-app>
    package cn.itcast.web.filter;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import java.io.IOException;
    
    /**
     * 过滤器快速入门程序
     */
    public class FilterDemo2 implements Filter {
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
    
        }
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            System.out.println("filterDemo2被执行了...");
    
            //放行
            filterChain.doFilter(servletRequest,servletResponse);
        }
    
        @Override
        public void destroy() {
    
        }
    }

      浏览器访问http://localhost/day11/index.jsp,检查idea控制台输出:filterDemo2被执行了...,同时浏览器展示:index.jsp。

    4. 过滤器执行流程

      a. 执行过滤器

      b. 执行放行后的资源

      c. 回来执行过滤器放行代码下边的代码

    package cn.itcast.web.filter;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import java.io.IOException;
    
    @WebFilter("/*")
    public class FilterDemo3 implements Filter {
        public void destroy() {
        }
    
        public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
    
            //对request对象请求消息增强
            System.out.println("filterDemo3执行了...");
            //放行
            chain.doFilter(req, resp);
            //对response对象的响应消息增强
            System.out.println("filterDemo3回来了...");
        }
    
        public void init(FilterConfig config) throws ServletException {
    
        }
    
    }

      index.jsp

    <%--
      Created by IntelliJ IDEA.
      User: ajing
      Date: 2022/2/28
      Time: 21:16
      To change this template use File | Settings | File Templates.
    --%>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
      <head>
        <title>$Title$</title>
      </head>
      <body>
        index.jsp...
      <%
        System.out.println("index.jsp......");
      %>
      </body>
    </html>

       浏览器访问http://localhost/day11/index.jsp,检查浏览器输出:index.jsp...,同时idea控制台输出结果:

       

        可以看出来执行顺序,先执行打印filterDemo3执行了...,然后执行放行,去执行index.jsp,输出到浏览器及idea控制台,执行完后执行放行下方的代码,打印:filterDemo3回来了...

    5. 过滤器生命周期方法

     a. init:在服务器启动后,会创建Filter对象,然后调用init方法。只执行一次。用于加载资源

     b. doFilter:每一次请求被拦截资源时,会执行。执行多次

     c. destroy:在服务器关闭后,Filter对象被销毁。如果服务器是正常关闭,则会执行destroy方法。只执行一次。用于释放资源

    package cn.itcast.web.filter;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import java.io.IOException;
    
    @WebFilter("/*")
    public class FilterDemo4 implements Filter {
    
        /**
         * 每一次请求被拦截资源时,会执行。执行多次
         * @param req
         * @param resp
         * @param chain
         * @throws ServletException
         * @throws IOException
         */
        public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
            System.out.println("doFilter...");
            chain.doFilter(req, resp);
        }
    
        /**
         * 在服务器启动后,会创建Filter对象,然后调用init方法。只执行一次。用于加载资源
         * @param config
         * @throws ServletException
         */
        public void init(FilterConfig config) throws ServletException {
            System.out.println("init...");
        }
    
        /**
         * 在服务器关闭后,Filter对象被销毁。如果服务器是正常关闭,则会执行destroy方法。只执行一次。用于释放资源
         */
        public void destroy() {
            System.out.println("destroy...");
        }
    
    }

      tomcat启动成功后,检查idea控制台输出:init...。

      浏览器访问http://localhost/day11/index.jsp,检查输出doFilter...,再请求一次http://localhost/day11/index.jsp,继续输出doFilter...。

      tomcat关闭服务后,检查idea控制台输出:destroy...

    6. 过滤器配置详解

      a. 拦截路径配置:

        1. 具体资源路径: /index.jsp 只有访问index.jsp资源时,过滤器才会被执行。访问其他资源不会执行过滤器

        2. 拦截目录: /user/* 访问/user下的所有资源时,过滤器都会被执行

        3. 后缀名拦截: *.jsp 访问所有后缀名为jsp资源时,过滤器都会被执行

        4. 拦截所有资源:/* 访问所有资源时,过滤器都会被执行

    package cn.itcast.web.filter;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import java.io.IOException;
    
    @WebFilter("/index.jsp")
    public class FilterDemo5 implements Filter {
        public void destroy() {
        }
    
        public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
            System.out.println("filterDemo5...");
            chain.doFilter(req, resp);
        }
    
        public void init(FilterConfig config) throws ServletException {
    
        }
    
    }

      浏览器访问http://localhost/day11/index.jsp,idea控制台会输出:filterDemo5...,访问http://localhost/day11/hello.jsp,过滤器不会被执行,idea控制台不会输出内容。

    package cn.itcast.web.filter;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import java.io.IOException;
    
    @WebFilter("/user/*")
    public class FilterDemo5 implements Filter {
        public void destroy() {
        }
    
        public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
            System.out.println("filterDemo5...");
            chain.doFilter(req, resp);
        }
    
        public void init(FilterConfig config) throws ServletException {
    
        }
    
    }
    package cn.itcast.web.servlet;
    
    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;
    
    @WebServlet("/user/findAllServlet")
    public class ServletDemo1 extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            System.out.println("findAllServlet...");
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doPost(request, response);
        }
    }
    package cn.itcast.web.servlet;
    
    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;
    
    @WebServlet("/user/updateServlet")
    public class ServletDemo2 extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            System.out.println("updateServlet...");
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doPost(request, response);
        }
    }

      浏览器访问http://localhost/day11/user/findAllServlet,检查idea控制台输出:filterDemo5...及findAllServlet...。浏览器访问http://localhost/day11/user/updateServlet,检查idea控制台输出filterDemo5...及updateServlet...。访问http://localhost/day11/index.jsp,检查idea控制台不会输出:filterDemo5...

    package cn.itcast.web.filter;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import java.io.IOException;
    
    @WebFilter("*.jsp")
    public class FilterDemo5 implements Filter {
        public void destroy() {
        }
    
        public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
            System.out.println("filterDemo5...");
            chain.doFilter(req, resp);
        }
    
        public void init(FilterConfig config) throws ServletException {
    
        }
    
    }

      浏览器访问http://localhost/day11/index.jsp,idea控制台会输出:filterDemo5...。访问http://localhost/day11/user/findAllServlet,过滤器不会被执行,idea控制台不会输出内容。

      b. 拦截方式配置:资源被访问的方式

        注解配置:

          * 设置dispatcherTypes属性(数组)

            1. REQUEST:默认值。浏览器直接请求资源
            2. FORWARD:转发访问资源
            3. INCLUDE:包含访问资源
            4. ERROR:错误跳转资源
            5. ASYNC:异步访问资源

        web.xml配置

          设置<dispatcher></dispatcher>标签即可

          取值同上面5个,如:<dispatcher>REQUEST</dispatcher>

    package cn.itcast.web.filter;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import java.io.IOException;
    
    //浏览器直接请求资源时,该过滤器会被执行
    @WebFilter(value = "/index.jsp",dispatcherTypes = DispatcherType.REQUEST)
    public class FilterDemo6 implements Filter {
        public void destroy() {
        }
    
        public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
            System.out.println("filterDemo6...");
            chain.doFilter(req, resp);
        }
    
        public void init(FilterConfig config) throws ServletException {
    
        }
    
    }
    package cn.itcast.web.servlet;
    
    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;
    
    @WebServlet("/user/updateServlet")
    public class ServletDemo2 extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            System.out.println("updateServlet...");
    
            //转发到index.jsp
            request.getRequestDispatcher("/index.jsp").forward(request,response);
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doPost(request, response);
        }
    }

      浏览器直接访问http://localhost/day11/index.jsp,idea控制台会输出:filterDemo6...。

      浏览器访问http://localhost/day11/user/updateServlet,idea控制台不会输出内容。

    package cn.itcast.web.filter;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import java.io.IOException;
    
    
    //只有转发访问index.jsp时,该过滤器才会被执行
    @WebFilter(value = "/index.jsp",dispatcherTypes = DispatcherType.FORWARD)
    public class FilterDemo6 implements Filter {
        public void destroy() {
        }
    
        public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
            System.out.println("filterDemo6...");
            chain.doFilter(req, resp);
        }
    
        public void init(FilterConfig config) throws ServletException {
    
        }
    
    }

      浏览器访问http://localhost/day11/user/updateServlet,idea控制台会输出:filterDemo6...。

      浏览器访问http://localhost/day11/index.jsp,idea控制台不会输出内容。

    package cn.itcast.web.filter;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import java.io.IOException;
    
    
    //浏览器直接请求index.jsp或者转发访问index.jsp时,该过滤器都会被执行
    @WebFilter(value = "/index.jsp",dispatcherTypes = {DispatcherType.REQUEST,DispatcherType.FORWARD})
    
    public class FilterDemo6 implements Filter {
        public void destroy() {
        }
    
        public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
            System.out.println("filterDemo6...");
            chain.doFilter(req, resp);
        }
    
        public void init(FilterConfig config) throws ServletException {
    
        }
    
    }

      浏览器直接访问http://localhost/day11/index.jsp,idea控制台会输出:filterDemo6...。

      浏览器访问http://localhost/day11/user/updateServlet,idea控制台会输出:filterDemo6...。

    package cn.itcast.web.filter;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import java.io.IOException;
    
    
    @WebFilter(value = "/*",dispatcherTypes = {DispatcherType.REQUEST,DispatcherType.FORWARD})
    public class FilterDemo6 implements Filter {
        public void destroy() {
        }
    
        public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
            System.out.println("filterDemo6...");
            chain.doFilter(req, resp);
        }
    
        public void init(FilterConfig config) throws ServletException {
    
        }
    
    }

      浏览器直接访问http://localhost/day11/index.jsp,idea控制台会输出:filterDemo6...。

       浏览器访问http://localhost/day11/user/updateServlet,idea控制台输出结果:

       

    6. 过滤器链(配置多个过滤器)

      a. 执行顺序:如果有两个过滤器:过滤器1和过滤器2

        1. 过滤器1

        2. 过滤器2

        3. 资源执行

        4. 过滤器2

        5. 过滤器1

      b. 过滤器先后顺序问题:

        1. 注解配置:按照类名的字符串比较规则比较,值小的先执行

          * 如: AFilter 和 BFilter,AFilter就先执行了。

        2. web.xml配置: <filter-mapping>谁定义在上边,谁先执行

    package cn.itcast.web.filter;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import java.io.IOException;
    
    @WebFilter("/*")
    public class FilterDemo7 implements Filter {
        public void destroy() {
        }
    
        public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
            System.out.println("filterDemo7执行了。。。");
            chain.doFilter(req, resp);
            System.out.println("filterDemo7回来了。。。");
    
        }
    
        public void init(FilterConfig config) throws ServletException {
    
        }
    
    }
    package cn.itcast.web.filter;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import java.io.IOException;
    
    @WebFilter("/*")
    public class FilterDemo8 implements Filter {
        public void destroy() {
        }
    
        public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
            System.out.println("filterDemo8执行了。。。");
    
            chain.doFilter(req, resp);
    
            System.out.println("filterDemo8回来了。。。");
    
        }
    
        public void init(FilterConfig config) throws ServletException {
    
        }
    
    }

      浏览器访问http://localhost/day11/index.jsp,检查idea控制台输出结果(7比8小所以FilterDemo7先执行):

      

    7. 案例:登录验证

      需求:

        1. 访问day10_case案例的资源。验证其是否登录

        2. 如果登录了,则直接放行。

        3. 如果没有登录,则跳转到登录页面,提示"您尚未登录,请先登录"。

       分析:

        

       代码:

        login.jsp

    <%--
      Created by IntelliJ IDEA.
      User: ajing
      Date: 2022/2/26
      Time: 20:42
      To change this template use File | Settings | File Templates.
    --%>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>login</title>
        <script>
            window.onload = function(){
                document.getElementById("img").onclick = function () {
                    this.src="/day10/checkCodeServlet?time="+ new Date().getTime();
                }
            }
        </script>
    
        <style>
            div{
                color: red;
            }
        </style>
    </head>
    <body>
    
        <form action="/day10/loginServlet" method="post">
            <table>
                <tr>
                    <td>用户名</td>
                    <td><input type="text" name="username"></td>
                </tr>
                <tr>
                    <td>密码</td>
                    <td><input type="text" name="password"></td>
                </tr>
                <tr>
                    <td>验证码</td>
                    <td><input type="text" name="checkCode"></td>
                </tr>
                <tr>
                    <td colspan="2"><img id="img" src="/day10/checkCodeServlet"></td>
                </tr>
                <tr>
                    <td colspan="2"><input type="submit" value="登录"></td>
                </tr>
            </table>
        </form>
    
        <div><%=request.getAttribute("cc_error") == null ? "":request.getAttribute("cc_error")%></div>
        <div><%=request.getAttribute("login_error") == null ? "" : request.getAttribute("login_error")%></div>
    
    </body>
    </html>
    package cn.itcast.servlet;
    
    import cn.itcast.dao.UserDao;
    import cn.itcast.domain.User;
    import org.apache.commons.beanutils.BeanUtils;
    
    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 javax.servlet.http.HttpSession;
    import java.io.IOException;
    import java.lang.reflect.InvocationTargetException;
    import java.util.Map;
    
    @WebServlet("/loginServlet")
    public class LoginServlet extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            //1. 设置request编码
            request.setCharacterEncoding("utf-8");
            //2. 获取参数Map
            Map<String, String[]> map = request.getParameterMap();
    
            //3. 创建User对象
            User loginUser = new User();
            UserDao dao = new UserDao();
            try {
                BeanUtils.populate(loginUser,map);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
    
            //3. 先获取生产的验证码
            HttpSession session = request.getSession();
            String checkCode_session = (String) session.getAttribute("checkCode_session");
    
            //删除session中存储的验证码
            session.removeAttribute("checkCode_session");
    
            //3.先判断验证码是否正确
            if(checkCode_session != null && checkCode_session.equalsIgnoreCase(map.get("checkCode")[0])){
                //忽略大小写比较
                //验证码正确
                //判断用户名和密码是否一致
    
                //4. 调用UserDao的login方法
                User user = dao.login(loginUser);
    
                if(user != null){
                    //登录成功
                    //存储信息:用户信息
                    session.setAttribute("user",user.getUsername());
    
                    //重定向到success.jsp
                    response.sendRedirect(request.getContextPath()+"/success.jsp");
                }else {
                    //登录失败
                    //存储提示信息到request
                    request.setAttribute("login_error","用户名或密码错误");
                    //转发到登录页面
                    request.getRequestDispatcher("/login.jsp").forward(request,response);
                }
            }else{
                //验证码不一致
                //存储提示信息到request
                request.setAttribute("cc_error","验证码错误");
                //转发到登录页面
                request.getRequestDispatcher("/login.jsp").forward(request,response);
            }
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doPost(request, response);
        }
    }

        success.jsp

    <%--
      Created by IntelliJ IDEA.
      User: ajing
      Date: 2022/2/26
      Time: 21:15
      To change this template use File | Settings | File Templates.
    --%>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
    
        <h1><%=request.getSession().getAttribute("user")%>,欢迎您</h1>
    
    </body>
    </html>
    package cn.itcast.filter;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import javax.servlet.http.HttpServletRequest;
    import java.io.IOException;
    
    /**
     * 登录验证的过滤器
     */
    @WebFilter("/*")
    public class LoginFilter implements Filter {
        public void destroy() {
        }
    
        public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
           
            //0. 强制转换
            HttpServletRequest request = (HttpServletRequest) req;
            
            //1. 获取资源请求路径
            String uri = request.getRequestURI();
    
            //2.判断是否包含登录相关的资源路径,要注意排除掉css/js/图片/验证码等资源
            if(uri.contains("/login.jsp")|| uri.contains("/loginServlet")|| uri.contains("/css")|| uri.contains("/js")|| uri.contains("/fonts")|| uri.contains("/checkCodeServlet")){
                //包含,用户想登陆。放行
                chain.doFilter(req, resp);
            }else{
                //不包含,需要验证用户是否登录
                //3.从获取session中获取user
                Object user = request.getSession().getAttribute("user");
                if(user!=null){
                    //登录了,放行
                    chain.doFilter(req, resp);
                }else{
                    //没有登录,跳转登录页面
                    request.setAttribute("login_error","您尚未登录,请先登录");
                    request.getRequestDispatcher("/login.jsp").forward(request,resp);
                }
            }              
        }
    
        public void init(FilterConfig config) throws ServletException {
    
        }
    
    }
  • 相关阅读:
    循环队列操作
    让测试人员参与软件设计
    Oracle之初探
    关注LoadRunner脚本回放日志中的Warning信息
    性能测试工具CurlLoader
    『原创』网站测试计划模板
    LoadRunner如何监控Linux下的系统资源
    搭建Linux学习环境安装CentOS5.4
    Linux下搭建Tomcat服务器
    性能测试分析之带宽瓶颈的疑惑
  • 原文地址:https://www.cnblogs.com/ajing2018/p/15938375.html
Copyright © 2020-2023  润新知