SpringMVC(三):请求流程处理

news2024/11/25 10:03:25

一、引言:

如下是我画的一个简单的SpringMVC的请求流程图,接下来会通过请求流程图去进行源码分析。
在这里插入图片描述

  • [1 ] 当我们客户端发送请求时,Servlet会进行请求的解析,然后交给DispatcherServlet进行统一分发。
  • [2] DispatcherServlet会根据我们的请求路径去寻找对应的HandlerMapping,并返回一个HandlerExecutionChain(如果没有寻找到也就会返回404)

HandlerExecutionChain: 请求处理链,包括请求处理器和处理器拦截器等。它的作用是在请求处理过程中,按照一定的顺序调用各个处理器,确保在请求处理器执行前后,能够按照需要进行预处理和后处理。

  • [3] 根据请求的HandlerMapping去适配HandlerAdapter。
  • [4] DispatcherServlet 调用拦截器的PreHandl,这个是在之前方法之前调用。
  • [5] DispatcherServlet执行我们请求路径的方法,请求完成后返回一个返回ModelAndView。
  • [6] DispatcherServlet将ModelAndView传给ViewReslover视图解析器,ViewReslover解析后返回具体的View视图(JSP / HTML)。
  • [7] DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)。
  • [8] DispatcherServlet响应用户,用户看到界面和数据。

源码分析:

  1. 当我们接收到请求时,先执行FrameworkServlet里面的Service方法
 protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 		// 解析请求
        HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
        if (httpMethod != HttpMethod.PATCH && httpMethod != null) {
        // 在执行HttpServlet的service()方法
            super.service(request, response);
        } else {
            this.processRequest(request, response);
        }

    }
  1. 在执行HttpServlet的service()方法

这里主要看你是什么请求,post请求就走dopost方法,get请求就走doget方法

    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       。。。。。。
        } 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);
        }

    }
  1. 调用processRequest()方法
    protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        long startTime = System.currentTimeMillis();
        Throwable failureCause = null;
        LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
        LocaleContext localeContext = this.buildLocaleContext(request);
        RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes);
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
        this.initContextHolders(request, localeContext, requestAttributes);

        try {
        // 在调用doService方法
            this.doService(request, response);
        } catch (IOException | ServletException var16) {
            failureCause = var16;
            throw var16;
        } catch (Throwable var17) {
            failureCause = var17;
            throw new NestedServletException("Request processing failed", var17);
        } finally {
            this.resetContextHolders(request, previousLocaleContext, previousAttributes);
            if (requestAttributes != null) {
                requestAttributes.requestCompleted();
            }

            this.logResult(request, response, (Throwable)failureCause, asyncManager);
            this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause);
        }

    }
  1. doService()方法
    进行请求处理的前置准备。
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        this.logRequest(request);
        Map<String, Object> attributesSnapshot = null;
        if (WebUtils.isIncludeRequest(request)) {
            attributesSnapshot = new HashMap();
            Enumeration<?> attrNames = request.getAttributeNames();

            label116:
            while(true) {
                String attrName;
                do {
                    if (!attrNames.hasMoreElements()) {
                        break label116;
                    }

                    attrName = (String)attrNames.nextElement();
                } while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));

                attributesSnapshot.put(attrName, request.getAttribute(attrName));
            }
        }
		// 把spring容器,相关一些信息存入request当中
        request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());
        request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
        request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
        request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource());
        // 重定向,转发
        if (this.flashMapManager != null) {
            FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
            if (inputFlashMap != null) {
                request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
            }

            request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
            request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
        }

        RequestPath previousRequestPath = null;
        if (this.parseRequestPath) {
            previousRequestPath = (RequestPath)request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE);
            ServletRequestPathUtils.parseAndCache(request);
        }

        try {
        	// 核心方法
            this.doDispatch(request, response);
        } finally {
            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {
                this.restoreAttributesAfterInclude(request, attributesSnapshot);
            }

            if (this.parseRequestPath) {
                ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request);
            }

        }

    }

5.doDispatch()核心方法处理

  protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            try {
                ModelAndView mv = null;
                Exception dispatchException = null;

                try {
                    processedRequest = this.checkMultipart(request);
                    multipartRequestParsed = processedRequest != request;
                    // 寻找对应查找对应请求路径,是否存在,返回对象或者方法
                    mappedHandler = this.getHandler(processedRequest);
                    if (mappedHandler == null) {
                        this.noHandlerFound(processedRequest, response);
                        return;
                    }

                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                    String method = request.getMethod();
                    boolean isGet = HttpMethod.GET.matches(method);
                    if (isGet || HttpMethod.HEAD.matches(method)) {
                        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                        if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                            return;
                        }
                    }

                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }

                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }

                    this.applyDefaultViewName(processedRequest, mv);
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                } catch (Exception var20) {
                    dispatchException = var20;
                } catch (Throwable var21) {
                    dispatchException = new NestedServletException("Handler dispatch failed", var21);
                }

                this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
            } catch (Exception var22) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
            } catch (Throwable var23) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
            }

        } finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            } else if (multipartRequestParsed) {
                this.cleanupMultipart(processedRequest);
            }

        }
    }

处理HandlerMapping

方法调用流程图如下
在这里插入图片描述

mappedHandler = this.getHandler(processedRequest);
在这里就是寻找对应请求是否存在,比如我是用RequestMapping注解进行请求的。如果请求不存在就直接返回404了

    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        if (this.handlerMappings != null) {
            Iterator var2 = this.handlerMappings.iterator();
			// 遍历DisparchServlect.properties文件中的HandlerMapping
			// BeanNameUrlHandlerMapping 优先级最高
			// RequestMappingHandlerMapping
			// RouterFunctionMapping
            while(var2.hasNext()) {
            // 使用策略模式
                HandlerMapping mapping = (HandlerMapping)var2.next();
                HandlerExecutionChain handler = mapping.getHandler(request);
                if (handler != null) {
                    return handler;
                }
            }
        }

        return null;
    }
 

mapping.getHandler(request);

  public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        Object handler = this.getHandlerInternal(request);
        if (handler == null) {
            handler = this.getDefaultHandler();
        }
        }

getHandlerInternal()方法

   protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        String lookupPath = this.initLookupPath(request);
        this.mappingRegistry.acquireReadLock();

        HandlerMethod var4;
        try {
        // 这里就是在我们mappingRegistry中取出对应的请求方法进行返回
            HandlerMethod handlerMethod = this.lookupHandlerMethod(lookupPath, request);
            var4 = handlerMethod != null ? handlerMethod.createWithResolvedBean() : null;
        } finally {
            this.mappingRegistry.releaseReadLock();
        }

        return var4;
    }

适配HandlerAdapter:

方法调用流程图如下
在这里插入图片描述

HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
这里也是通过DispatcherServlet.properties文件中获取HandlerAdapter,根据我们不同请求的HandlerMapping去适配对应的HandlerAdapter。

因为如果我们实现Controller接口他相应的请求就是BeanNameUrlHandlerMapping。而这个请求他在处理我们HandlerMapping时,是只需要返回一个对象参数在进行接口回调即可。

@Component("/test")
public class Test implements Controller {

    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        return null;
    }
}

而我们如果是使用RequstMapping注解呢,该注解是制定某个方法进行请求,而不是指定每个类请求。所以这个请求处理我们HandlerMapping时,返回的就是一个方法,那么他接收请求处理的方式又不一样。所以这时候就需要我们的HandlerAdapter来进行适配。

源码分析
getHandlerAdapter(Object handler)

    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        if (this.handlerAdapters != null) {
            Iterator var2 = this.handlerAdapters.iterator();
		// 1.HttpRequestHandlerAdapter
		// 2.SimpleControllerHandlerAdapter
		// 3.RequestMappingHandlerAdapter
		// 4.HandlerFunctionAdapter
            while(var2.hasNext()) {
                HandlerAdapter adapter = (HandlerAdapter)var2.next();
                if (adapter.supports(handler)) {
                    return adapter;
                }
            }
        }

如果你是BeanNameUrlHandlerMapping 请求,那么就会适配SimpleControllerHandlerAdapter,调用supports方法。看你当前handler 是否实现Controller接口

  public boolean supports(Object handler) {
        return handler instanceof Controller;
    }

如果你是RequestMappingHandlerMapping请求,那么就会适配AbstractHandlerMethodAdapter,调用supports方法。看你当前handler 是否是一个方法。

   public final boolean supports(Object handler) {
        return handler instanceof HandlerMethod && this.supportsInternal((HandlerMethod)handler);
    }

前置拦截器处理:

这里会去循环遍历interceptorList里面的拦截器,然后根据特定拦截器执行前置方法。如果方法返回false,则流程结束。

if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
      }

List<HandlerInterceptor> interceptorList;

执行方法handle():

  1. 创建binderFactory 作用于@InitBinder注解的方法
  2. 创建modelFactory 创建model对象,作用于@ModelAttribute
  3. 设置方法参数解析器,返回值解析器。 这些解析器都是在初始化HandlerAdapter时会进行创建加载。
  4. 初始化model对象,在设置model的其他属性值。
  5. 调用invokeAndHandle执行方法(1.用适配器去解析相应的方法参数解析器;2.执行方法;3.用适配器去适配结果返回值处理器)
  6. 返回ModelAndView
 public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
 	// 获取方案参数
        Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
        if (logger.isTraceEnabled()) {
            logger.trace("Arguments: " + Arrays.toString(args));
        }

        return this.doInvoke(args);
    }

    protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
     // 获取当前方法有那些参数
        MethodParameter[] parameters = this.getMethodParameters();
        if (ObjectUtils.isEmpty(parameters)) {
            return EMPTY_ARGS;
        } else {
            Object[] args = new Object[parameters.length];

            for(int i = 0; i < parameters.length; ++i) {
                MethodParameter parameter = parameters[i];
                parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
                args[i] = findProvidedArgument(parameter, providedArgs);
                if (args[i] == null) {
                   // 获取相应的参数解析器
                    if (!this.resolvers.supportsParameter(parameter)) {
                        throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
                    }

                    try {
                    // 对该参数进行赋值
                        args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
                    } catch (Exception var10) {
                        if (logger.isDebugEnabled()) {
                            String exMsg = var10.getMessage();
                            if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
                                logger.debug(formatArgumentError(parameter, exMsg));
                            }
                        }

                        throw var10;
                    }
                }
            }

            return args;
        }
    }

视图渲染():

mappedHandler.applyPostHandle(processedRequest, response, mv);
这里源码就不在进行分析了,主要就是通过执行完方法返回的ModelAndView进行适配视图解析器viewResolver,最后在进行视图的渲染。


方法总体调用图:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) 
							throws Exception {
		...
		//1、根据URL(当然不一定非得是URL)匹配到一个处理器
		mappedHandler = getHandler(processedRequest);
		if (mappedHandler == null) {
			// 若匹配不到Handler处理器,就404了
			noHandlerFound(processedRequest, response);
			return;
		}

		//2、从HandlerExecutionChain里拿出Handler(注意是Object类型哦~ )然后找到属于它的适配器
		HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
		...
		//3、执行作用在此Handler上的所有拦截器的Pre方法
		if (!mappedHandler.applyPreHandle(processedRequest, response)) {
			return;
		}
		//4、真正执行handle方法(也就是你自己书写的逻辑方法),得到一个ModelAndView
		mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

		//5、视图渲染
		applyDefaultViewName(processedRequest, mv);
		
		//6、执行拦截器的post方法(可见它是视图渲染完成了才会执行的哦~)
		mappedHandler.applyPostHandle(processedRequest, response, mv);
		...
		//7、执行拦截器的afterCompletion方法(不管抛出与否)
	}


总结:

1.客户端发起请求,请求被DispatcherServlet接收。
2.DispatcherServlet把请求交给HandlerMapping解析器进行映射,得到匹配的Controller。
3.Controller接收到请求后进行处理,把处理结果放到Model中返回给DispatcherServlet。
4.DispatcherServlet把Model交给ViewResolver视图解析器,找到对应的视图。
5.视图渲染处理,最终生成HTML页面返回给客户端。


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

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

相关文章

北京君正案例:超能面板PRO采用4英寸IPS超清多彩屏,值不值得买?

清晨&#xff0c;窗帘自动拉开&#xff0c;悦耳音乐缓缓响起&#xff0c;面包机、咖啡机自动工作&#xff0c;开启新一天。离家时&#xff0c;一键关掉所有灯光和家电&#xff0c;节能安全&#xff0c;手机上便可查看家里设备状态&#xff0c;不用担心门没锁、灯没关等问题。下…

ClickHouse入门详解

ClickHouse基础部分详解一、ClickHouse简介二、ClickHouse单机版安装2.1、ClickHouse安装前准备环境2.2、ClickHouse单机安装三、ClickHouse数据类型四、ClickHouse的表引擎一、ClickHouse简介 对于其他乱起八糟的简介&#xff0c;我就不写了&#xff0c;只写干货. ClickHouse总…

Lumen6 /laravel 框架路由请求实现token验证

版本 Lumen6.0 中文文档&#xff1a;https://learnku.com/docs/lumen/5.7/cache/2411 实现功能效果 1、使用缓存存储用户token 2、从请求头head 中获取用户token 3、返回指定的认证失败结构体 4、对指定的接口路由做身份验证 第一步&#xff1a;解除注释 注意&#xff1…

QML控件--Container

文章目录一、控件基本信息二、控件说明三、属性成员四、成员函数一、控件基本信息 Import Statement: import QtQuick.Controls 2.14 Since: Qt 5.7 Inherits: Control Inherited By: DialogButtonBox, MenuBar, SwipeView, and TabBar 二、控件说明 Container&#xff08;容…

网络安全之从原理看懂 XSS

01、XSS 的原理和分类 跨站脚本攻击 XSS(Cross Site Scripting)&#xff0c;为了不和层叠样式表(Cascading Style Sheets&#xff0c;CSS)的缩写混淆 故将跨站脚本攻击缩写为 XSS&#xff0c;恶意攻击者往 Web 页面里插入恶意 Script 代码&#xff0c;当用户浏览该页面时&…

【产品设计】删除确认文案,猛男落泪

使用各种系统时&#xff0c;都有各种删除操作&#xff0c;用户在删除时&#xff0c;很少关注文案写了什么&#xff0c;但这个文案往往让产品经理们殚精竭虑。怎么样才能写出合格的删除确认文案呢&#xff1f; 使用各种系统的时候&#xff0c;都有各种删除操作&#xff0c;作为用…

substrate中打印调试信息的多种方式详解

目录1. 获取substrate-node-template代码2. 添加一个用于测试的pallet至依赖到pallets目录3. log方式来输出信息3.1 将log依赖添到cargo.toml文件3.2 log-test/src/lib.rs修改call方法3.3 polkadot.js.调用测试函数do_something_log_test4. printable trait方式来输出信息4.1 首…

在 Rainbond 上使用在线知识库系统zyplayer-doc

zyplayer-doc 是一款适合企业和个人使用的WIKI知识库管理工具&#xff0c;提供在线化的知识库管理功能&#xff0c;专为私有化部署而设计&#xff0c;最大程度上保证企业或个人的数据安全&#xff0c;可以完全以内网的方式来部署使用它。 当然也可以将其作为企业产品的说明文档…

2023“认证杯”数学中国数学建模赛题浅析

2023年认证杯”数学中国数学建模如期开赛&#xff0c;本次比赛与妈杯&#xff0c;泰迪杯时间有点冲突。因此&#xff0c;个人精力有限&#xff0c;有些不可避免地错误欢迎大家指出。为了大家更方便的选题&#xff0c;我将为大家对四道题目进行简要的解析&#xff0c;以方便大家…

4.redis-主从复制

01-主从复制概述 单机redis存在的问题 ①硬盘故障, 导致数据丢失, redis不好用;②内存容量受限制, 单台服务器内存扩展有上限, 内存数据超过该上线, 该如何处理. 解决方案 1个master可以有多个slave, 1个slave只能对应1个master master: 主机, 可以读也可以写, 主要负责写. …

MySQL运维24-SHOW ENGINE INNODB STATUS解析

文章目录1、SHOW ENGINE INNODB STATUS概述2、信号量&#xff08;Semaphores&#xff09;2.1、信号量信息示例2.2、信号量信息说明2.3、知识点&#xff1a;CPU自旋(SPIN)2.3、信号量中的OS WAIT ARRAY INFO3、死锁3.1、死锁信息示例3.2、死锁信息说明4、外键冲突4.1、外键冲突信…

elementUI实现selecttree自定义下拉框树形组件

elementUI有select组件也有tree组件&#xff0c;但是就是没有下拉框和tree组件的结合体&#xff0c;那么这次我们就自定义一个。 效果图 引入组件 <select-tree ref"selectTree" treeChange"treeChangeFun" :dataArray"orgList" :value"…

【网络安全】文件上传漏洞及中国蚁剑安装

文件上传漏洞描述中国蚁剑安装1. 官网下载源码和加载器2.解压至同一目录并3.安装4.可能会出现的错误文件上传过程必要条件代码示例dvwa靶场攻击示例1.书写一句话密码进行上传2. 拼接上传地址3.使用中国蚁剑链接webshell前端js绕过方式服务端校验请求头中content-type黑名单绕过…

《花雕学AI》22:一种让AI模拟虚拟角色方法,足以更多创造力的ChatGPT角色扮演

一、什么是ChatGPT的角色扮演&#xff1f; ChatGPT是一种基于GPT-3模型的人机对话技术&#xff0c;它可以实现自然语言和计算机之间的交互。ChatGPT的角色扮演指的是让模型扮演一个虚构的人物&#xff0c;与用户进行设定好的对话。 例如&#xff0c;您可以让ChatGPT扮演一个关…

一文打通锁升级(偏向锁,轻量级锁,重量级锁)

前置知识&#xff1a;synchronized 在JavaSE1.6以前&#xff0c;synchronized都被称为重量级锁。但是在JavaSE1.6的时候&#xff0c;对synchronized进行了优化&#xff0c;引入了偏向锁和轻量级锁&#xff0c;以及锁的存储结构和升级过程&#xff0c;减少了获取锁和释放锁的性能…

Hbase1.1:Hbase官网、Hbase定义、Habse结构、Hbase依赖框架、Hbase整合框架

这里写自定义目录标题Hbase官网Hbase特点&#xff1a;大Hbase定义Habse结构Hbase依赖框架hadoopHbase整合框架PhoenixHiveHbase官网 Hbase官网地址 HBase是Hadoop database&#xff0c;一个分布式、可扩展的大数据存储。 当您需要对大数据进行随机、实时读/写访问时&#xf…

记录网关zuul处理跨域/XSS问题

一&#xff0c;疑问 1.之前遇到跨域问题是在NG中解决的&#xff0c;添加跨域请求头和域名配置。那么与网关处理跨域问题关系是什么&#xff0c;NG处理了&#xff0c;为什么还需要在网关中处理 二&#xff0c;前置知识 zuul概念与原理 zuul 的概念和原理 - 知乎 Zuul工作原…

接触过的第一台电脑-90年代的x86与如今的树莓派

#勤写标兵挑战赛#最早接触电脑是在幼儿园&#xff0c;那时候电脑下象棋都要输入命令行的。后来小学时候有了电脑课&#xff0c;要穿鞋套的。满满的回忆。那个时代电子产品更新很慢的&#xff0c;一台电脑可以用5年&#xff0c;286-386-486-586。486电脑的特点是&#xff1a;处理…

python数据分析-matplotlib散点图-条形图的绘制以及完整方法归纳02

matplotlib的基本使用02一.散点图的绘制二.散点图绘图步骤及案例解析1.导入模块2.设置散点图所有字符的字体样式3.编写主体代码4.主题代码解析5.图形展示三.条形图的绘制四.条形图案例展示1.导入模块五.绘制条形图完整代码六.条形图展示七.多个条形图展示1.结果展示八.总结一.散…

工业交换机与商业交换机区别对比

摘要&#xff1a;工业交换机和商业交换机在功能方面差距较小&#xff0c;性能方面差别很大。最主要的差别是在对各类环境的适应性上&#xff0c;包括在温度、湿度、盐雾环境、振动环境、恶劣电磁干扰环境、海拔等各类环境下稳定可靠提供功能服务的能力。当然工业交换机在一些告…