SpringSecurity6 | 委派筛选器代理和过滤器链代理

news2024/11/27 4:01:46

在这里插入图片描述

✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉
🍎个人主页:Leo的博客
💞当前专栏: Java从入门到精通
✨特色专栏: MySQL学习
🥭本文内容:SpringSecurity6 | 委派筛选器代理和过滤器链代理
🖥️个人小站 :个人博客,欢迎大家访问
📚个人知识库: Leo知识库,欢迎大家访问

文章目录

    • 1.前言
    • 2.剖析DelegatingFilterProxy
      • 2.1DelegatingFilterProxy概述
      • 2.类的结构
      • 3.类的属性
    • 3.DelegatingFilterProxy原理
      • 3.1init
      • 3.2dofilter
      • 3.3destroy
    • 4.DelegatingFilterProxy作用
    • 5.FilterChainProxy
      • 5.1FilterChainProxy概述
      • 5.2FilterChainProxy的作用
    • 6.SecurityFilterChain概述
    • 6.参考文献
    • 7.总结

学习参考 :

  • 讲师:孙帅老师
  • 课程:孙哥说SpringSecurity6

image-20231030235443828

1.前言

大家好,我是Leo哥🫣🫣🫣,上一节我们简单回顾了一下关于Servlet原生过滤器以及简单认识了SpringSecurity中的一些过滤器。但是底层SpringSecurity是如何维护这些过滤器,并通过这些过滤器是如果拦截我们的客户端请求的,我们都还只是停留在表层,今天就让我们去深入了解一下我们今天得主角—委派筛选器代理 DelegatingFilterProxy。好了,话不多说让我们开始吧😎😎😎。

2.剖析DelegatingFilterProxy

2.1DelegatingFilterProxy概述

Spring 提供了一个名为 DelegatingFilterProxyFilter 实现,允许在 Servlet 容器的生命周期和 Spring 的 ApplicationContext 之间建立桥梁。Servlet容器允许通过使用自己的标准来注册 Filter 实例,但它不知道 Spring 定义的 Bean。你可以通过标准的Servlet容器机制来注册 DelegatingFilterProxy,但将所有工作委托给实现 FilterSpringBean

image-20231107195435608

简单来说,DelegatingFilterProxy就是一个对于servlet filter的代理,用这个类的好处主要是通过Spring容器来管理servlet filter的生命周期,

  • 还有就是如果filter中需要一些Spring容器的实例,可以通过spring直接注入,
  • 另外读取一些配置文件这些便利的操作都可以通过Spring来配置实现。

本质上来说DelegatingFilterProxy就是一个Filter,其间接实现了Filter接口,但是 在doFilter中其实调用的从Spring 容器中获取到的代理Filter的实现类delegate。

2.类的结构

先看下 DelegatingFilterProxy 类的继承链

image-20231107200106838

通过类图,可以看到 DelegatingFilterProxy 继承了 Filter 接口,最终生成了一个过滤器。

3.类的属性

我们通过IDEA打开我们之前的项目,双击Shift键,去查找他的源码,我们来简单看一下他的源码。

image-20231107200323099

根据上面代码,看到了熟悉的 targetBeanNametargetFilterLifecycle 参数。看到这些参数,不知道大家有没有想到之前我们最开始学习JavaWeb的时候,最初始配置的过滤器,就是在一个web.xml文件中进行双标签配置,为了让大家更清楚的明白,我们下面写一个简单的xml配置。

    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>
            org.springframework.web.filter.DelegatingFilterProxy
        </filter-class>
        <init-param>
            <param-name>targetBeanName</param-name>
            <param-value>springSecurityFilterChain</param-value>
        </init-param>
        <init-param>
            <param-name>targetFilterLifecycle</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>

简单讲下这些参数的作用:

  • targetBeanName: 字符串

targetBeanName 参数是在 DelegatingFilterProxy 的构造函数中使用的一个字符串参数。它用于指定要代理的目标过滤器的 bean 名称。

DelegatingFilterProxy 接收到请求时,它将查找 Spring 容器中与 targetBeanName 参数匹配的目标过滤器的 bean。然后,DelegatingFilterProxy 将委托实际的过滤工作给找到的目标过滤器实例。

通过使用 targetBeanName 参数,你可以指定要使用的目标过滤器的 bean 名称,而不需要直接引用目标过滤器的类或实例。

示例二中,我们将 DelegatingFilterProxy 配置为过滤器,并通过 <init-param> 元素指定了 targetBeanName 参数的值为 springSecurityFilterChain。这意味着 DelegatingFilterProxy 将在 Spring 容器中查找名为 springSecurityFilterChain 的目标过滤器的 bean,并将实际的过滤工作委托给它。

请注意,springSecurityFilterChain 应该替换为实际的目标过滤器的 bean 名称。该名称必须与 Spring 容器中定义的目标过滤器的 bean 名称匹配。

  • targetFilterLifecycle: 布尔

targetFilterLifecycle 参数是在 DelegatingFilterProxy 的构造函数中使用的一个布尔值参数。它用于指定是否由 DelegatingFilterProxy 负责管理目标过滤器的生命周期。

如果将 targetFilterLifecycle 参数设置为 true,则 DelegatingFilterProxy 将负责调用目标过滤器的 init()destroy() 方法。这意味着 DelegatingFilterProxy 将在容器启动时自动调用目标过滤器的 init() 方法,并在容器关闭时调用目标过滤器的 destroy() 方法。

如果将 targetFilterLifecycle 参数设置为 false,则 DelegatingFilterProxy 将不会管理目标过滤器的生命周期。这意味着你需要手动调用目标过滤器的 init()destroy() 方法,确保它们在适当的时候被调用。

默认情况下,targetFilterLifecycle 参数被设置为 false,即 DelegatingFilterProxy 不会管理目标过滤器的生命周期。如果你希望 DelegatingFilterProxy 管理目标过滤器的生命周期,你可以将 targetFilterLifecycle 参数设置为 true

在上面的代码示例中,通过配置将 targetFilterLifecycle 设置为 true,以便让 DelegatingFilterProxy 管理目标过滤器的生命周期。

3.DelegatingFilterProxy原理

DelegatingFilterProxy 作为一个过滤器,过滤器的基本声明周期当然不会少。标准过滤器函数三大件:init, doFilter, destroy,这三个函数也是我们最关心的函数,至于 DelegatingFilterProxy 的奥秘也是通过这几个函数体现出来,当然最重要的还是 init, doFilter。下面我们就通过源码的方式一一去查看DelegatingFilterProxy他的生命周期。

3.1init

init 函数定义在 DelegatingFilterProxy 所继承的抽象类 GenericFilterBean 中。

@Override
public final void init(FilterConfig filterConfig) throws ServletException {
        Assert.notNull(filterConfig, "FilterConfig must not be null");

        this.filterConfig = filterConfig;

        // Set bean properties from init parameters.
        PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);
        if (!pvs.isEmpty()) {
                try {
                        BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
                        ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());
                        Environment env = this.environment;
                        if (env == null) {
                                env = new StandardServletEnvironment();
                        }
                        bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, env));
                        initBeanWrapper(bw);
                        bw.setPropertyValues(pvs, true);
                }
                catch (BeansException ex) {
                        String msg = "Failed to set bean properties on filter '" +
                                        filterConfig.getFilterName() + "': " + ex.getMessage();
                        logger.error(msg, ex);
                        throw new ServletException(msg, ex);
                }
        }

        // Let subclasses do whatever initialization they like.
        // 重点关注:🔥🔥🔥🔥🔥🔥
        initFilterBean();

        if (logger.isDebugEnabled()) {
                logger.debug("Filter '" + filterConfig.getFilterName() + "' configured for use");
        }
}

image-20231107202119949

接着看下 initFilterBean 的实现,此函数实现定义为 DelegatingFilterProxy 中。

image-20231107202444609

可以看到在DelegatingFilterProxy我们找到了关于initFilterBean的实现,我们来简单解读一下。

  1. synchronized (this.delegateMonitor):使用synchronized关键字对delegateMonitor进行同步,以确保在多线程环境下只有一个线程可以进入临界区。这样做是为了避免多个线程同时执行初始化操作而导致的竞态条件和数据不一致问题。
  2. if (this.delegate == null):检查delegate对象是否已经被初始化,如果没有,则继续执行初始化操作。
  3. if (this.targetBeanName == null):如果没有指定目标bean的名称,就使用过滤器的名称作为目标bean的名称。
  4. this.targetBeanName = getFilterName():通过getFilterName()方法获取过滤器的名称,并将其作为目标bean的名称。
  5. WebApplicationContext wac = findWebApplicationContext():调用findWebApplicationContext()方法来查找并获取Spring根应用程序上下文。这个上下文是用来获取bean实例的,因此在初始化delegate之前需要先获取它。
  6. if (wac != null) { this.delegate = initDelegate(wac); }:如果找到了Spring根应用程序上下文,则调用initDelegate方法对delegate进行初始化。这个initDelegate方法可能会根据具体需求做一些初始化工作,比如创建并配置delegate对象。

总的来说,这段代码的作用是在过滤器初始化时,尽早地初始化delegate对象。通过同步机制和延迟初始化策略,确保在多线程环境下安全地进行初始化操作,并尽可能地提前准备好delegate对象以供后续使用。

那么如何尽早的初始化delegate对象呢,这个initDelegate方法又做了什么呢,我们接着往下解读。

image-20231107202928322

  1. String targetBeanName = getTargetBeanName();:通过getTargetBeanName方法获取目标bean的名称,这个名称在之前的初始化过程中已经确定。
  2. Assert.state(targetBeanName != null, "No target bean name set");:使用断言确保目标bean的名称不为空,如果为空则抛出异常。这样可以在代码中明确地表达出对目标bean名称的依赖性,以避免潜在的空指针异常。
  3. Filter delegate = wac.getBean(targetBeanName, Filter.class);:通过Spring的应用程序上下文(wac)根据目标bean的名称获取对应的Filter实例。这里利用了Spring的IoC容器来管理和获取Filter实例。
  4. if (isTargetFilterLifecycle()) { delegate.init(getFilterConfig()); }:如果目标Filter需要进行生命周期初始化(例如调用init方法),则调用delegateinit方法并传入过滤器的配置信息。这是为了确保目标Filter在需要时能够正确地进行初始化工作。
  5. return delegate;:返回初始化后的delegate对象,该对象已经准备好被使用了。

总的来说,这个initDelegate方法的主要作用是根据目标bean的名称从Spring的应用程序上下文中获取对应的Filter实例,并根据需要进行生命周期的初始化工作。这样就能够在initFilterBean方法中及时地准备好delegate对象,以供后续的过滤器处理流程使用。

这一下就非常清晰了。

3.2dofilter

doFilter 函数实现定义为 DelegatingFilterProxy 中,我们直接看源码。

image-20231107203046568

我们进行解读:

  1. Filter delegateToUse = this.delegate;:首先将当前已初始化的delegate对象赋给delegateToUse,这里使用了委托模式,即通过delegateToUse来执行具体的过滤器操作。
  2. if (delegateToUse == null) { ... }:检查delegateToUse是否为null,如果为null则表示delegate对象尚未初始化,需要进行延迟初始化。
  3. synchronized (this.delegateMonitor) { ... }:使用synchronized关键字对delegateMonitor进行同步,以确保在多线程环境下只有一个线程可以进入临界区。这样做是为了避免多个线程同时执行初始化操作而导致的竞态条件和数据不一致问题。
  4. delegateToUse = this.delegate;:再次将当前已初始化的delegate对象赋给delegateToUse,因为在同步块外部可能已经有其他线程初始化了delegate对象。
  5. if (delegateToUse == null) { ... }:再次检查delegateToUse是否为null,因为在同步块外部可能已经有其他线程初始化了delegate对象。
  6. WebApplicationContext wac = findWebApplicationContext();:查找并获取Spring根应用程序上下文。如果没有找到上下文,则抛出异常,这是为了确保能够获取到必要的上下文信息用于后续的初始化操作。
  7. delegateToUse = initDelegate(wac);:调用initDelegate方法对delegateToUse进行初始化。这里利用了之前定义的initDelegate方法来完成初始化工作。
  8. this.delegate = delegateToUse;:将初始化后的delegateToUse对象赋给delegate,以便在以后的处理中可以直接使用已经初始化好的delegate对象。
  9. invokeDelegate(delegateToUse, request, response, filterChain);:调用invokeDelegate方法,让已经准备好的delegateToUse对象执行实际的过滤操作。这样就实现了通过代理对象来执行具体过滤器的功能。

3.3destroy

destroy 函数实现定义为 DelegatingFilterProxy 中。

image-20231107203459005

这里就没什么好说的了DelegatingFilterProxy在这里就像一个甩锅的大爷,根本不需要他去干活,他直接就把其交给委托过滤器来做了。

4.DelegatingFilterProxy作用

上面我们已经了解了关于DelegatingFilterProxy流程,以及简单看了一下他的源码。这里我们来简单总结一下。

作用: 实现把Servlet容器中的Filter 同Spring 容器中的 bean 关联起。是Servlet容器和Spring 的 ApplicationContext 之间的桥梁。

  1. 委托管理:DelegatingFilterProxy允许将对Servlet规范中的Filter接口的调用委托给Spring应用程序上下文中的一个或多个过滤器Bean。这些过滤器Bean可以由Spring的IoC容器进行管理,并且可以利用Spring的依赖注入等特性来进行配置和定制。
  2. 集成Spring特性:通过DelegatingFilterProxy,可以将Spring的AOP、事务管理等特性集成到Servlet规范的过滤器中。这样能够更好地利用Spring框架的各种功能来处理Web应用程序中的过滤逻辑。
  3. 延迟初始化:DelegatingFilterProxy支持延迟初始化和懒加载,它可以在需要时动态地将请求委托给具体的过滤器Bean,而不需要预先在web.xml中配置具体的过滤器类。这样可以避免在应用启动时立即创建所有过滤器实例,从而提高了系统的性能和资源利用率。
  4. 安全性:DelegatingFilterProxy可以被配置为要求认证的请求使用安全通道,从而提供一定程度的安全保障。

原生的Filter运行在Servlet容器里边也就是Tomcat服务器当中,而Spring的所书写的过滤器属于Spring工厂。Spring工厂中的过滤器是没有办法拦截Http请求并进行干预的,但是原生Filter就可以做到直接拦截Http请求并进行干预,就比如DelegatingFilterProxy,所以借助它,Spring当中的Filter就可以过滤和干预Http请求了。

5.FilterChainProxy

5.1FilterChainProxy概述

在上面的源码环节中,我们已经简单的认识到了FilterChainProxy就是一个过滤器链代理。那具体什么是FilterChainProxy呢?

  1. 统一的安全过滤器链管理
    • FilterChainProxy负责统一管理多个SecurityFilterChain,每个SecurityFilterChain都代表一组安全过滤器,用于处理特定模式的请求。这样可以在一个应用中同时支持多个安全策略,或者针对不同的URL模式使用不同的安全过滤器链。
  2. 根据请求匹配合适的安全过滤器链
    • 当收到一个HTTP请求时,FilterChainProxy会根据请求的URL和其他条件来选择合适的SecurityFilterChain,然后按照SecurityFilterChain中定义的顺序执行其中的安全过滤器。这样可以确保请求能够得到正确的安全处理。
  3. 灵活的安全策略配置
    • FilterChainProxy允许开发人员通过配置来定义多个SecurityFilterChain,以适应不同的安全需求。这种灵活性使得可以针对不同的URL模式或特定的安全需求定制化安全过滤器链。
  4. 核心的安全过滤器调度器
    • 在Spring Security框架中,FilterChainProxy可以看作是核心的安全过滤器调度器,它负责将请求交给正确的安全过滤器链进行处理,从而实现认证、授权、会话管理等安全操作。

下面给出Spring官方给出的解释:

Spring Security 的 Servlet 支持包含在 FilterChainProxy 中。FilterChainProxy 是 SpringSecurity 提供的一个特殊的 Filter,允许通过 SecurityFilterChain 委托给许多 Filter 实例。由于 FilterChainProxy 是一个Bean,它通常被包裹在 DelegatingFilterProxy 中。

我们通过一张图来更清晰地认识他吧。

image-20231107204747766

5.2FilterChainProxy的作用

SecurityFilterChain 中的 Security Filter 通常是Bean,但它们是用 FilterChainProxy 而不是 DelegatingFilterProxy 注册的。与直接向Servlet容器或 DelegatingFilterProxy 注册相比,FilterChainProxy 有很多优势。首先,它为 Spring Security 的所有 Servlet 支持提供了一个起点。由于这个原因,如果你试图对 Spring Security 的 Servlet 支持进行故障诊断,在 FilterChainProxy 中添加一个调试点是一个很好的开始。

其次,由于 FilterChainProxy 是 Spring Security 使用的核心,它可以执行一些不被视为可有可无的任务。 例如,它清除了 SecurityContext 以避免内存泄漏。它还应用Spring Security的 HttpFirewall 来保护应用程序免受某些类型的攻击。

此外,它在确定何时应该调用 SecurityFilterChain 方面提供了更大的灵活性。在Servlet容器中,Filter 实例仅基于URL被调用。 然而,FilterChainProxy 可以通过使用 RequestMatcher 接口,根据 HttpServletRequest 中的任何内容确定调用。

最主要的就是把请求传递给一个或者多个SecurityFilterChain示例进行认证或授权,并且能够需要时进行重定向和返回错误信息。

我们基于上面的图,来分析一下,一个客户端请求到Servlet中,其中到底是如何经过层层过滤器的。

首先客户端发起一个Http请求,这个请求会经过原生过滤器Filter-1,此时我们的FilterChain会通过**doFilter()**方法进行放心。之后这个请求会通过Filter-1传递到第二个原生过滤器,也就是我们的DelegatingFilterProxy。此时会搭建一个前往Spring工厂中的桥梁,但是我们并不能直接去通过这个桥梁访问我们的SecurityFilterChain,而是还需要通过桥梁中的另一个过滤器,也就是我们的过滤器链代理FilterChainProxy,他会将我们的请求重定向到SecurityFilterChain中。然后回基于匹配的规则,一个一个去执行Filter。执行完之后,会进行返回,原路返回到我们的FilterChainProxy这个过滤器链代理中。于是由原生过滤器接着向下面过滤器继续请求,如果下面没有过滤器了,则直接到Web资源了。

6.SecurityFilterChain概述

SecurityFilter 并不是直接放在 Web 项目的原生 FilterChain 中,而是通过一个FlterChainProxy来统管理

FilterChainProxySecurityFilterChain 嵌入到 Web项目的原生过滤器链中DelegatingFilterProxy 把 FilterChainProxy 整合到原生的过滤器链中

FilterChainProxy顶层管理者,统一管理 SecurityFilter和 SecurityFllterChain过涉器链。

当请求到达 FilterChainProxy 时,会根据当前请求匹配SecurityFilterChain,然后将请求依次转发给 SecurityFilterChain 中的 SecurityFilter中。

6.参考文献

  • https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/filter/DelegatingFilterProxy.html
  • https://springdoc.cn/spring-security/servlet/architecture.html

7.总结

以上便是本文的全部内容,本人才疏学浅,文章有什么错误的地方,欢迎大佬们批评指正!我是Leo,一个在互联网行业的小白,立志成为更好的自己。

如果你想了解更多关于Leo,可以关注公众号-程序员Leo,后面文章会首先同步至公众号。

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

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

相关文章

世微 AP2400 宽电压降压恒流驱动IC 过EMC认证线路方案

产品描述 AP2400 是一款 PWM 工作模式,外围简单、外驱功率管&#xff0c;适用于 5-100V 输入的高精度降压 LED 恒流驱动芯片。外 驱 MOS&#xff0c;输出电流可达 6A。 AP2400 可实现三段功能切换&#xff0c;通过 MODE1/2/3 切换三种功能模式&#xff1a;全亮&#xff0c; 半亮…

电子式电表和智能电表哪个更适合家用?

随着科技的发展&#xff0c;家用电力设备也在不断升级。电子式电表和智能电表作为两种常见的电表类型&#xff0c;究竟哪个更适合家用呢&#xff1f;今天&#xff0c;小编将会从多个角度进行全面分析&#xff0c;帮助大家做出明智的选择。 一、工作原理及准确性比较 1.电子式电…

vue 点击滑动到页面指定位置(点击下滑滚动)的功能

需求 点击页面上的 文字 滑动到页面指定位置 三种方法 document.getElementById(show).scrollIntoView() // 默认滚动至节点置顶document.getElementById(show).scrollIntoView(false) // 默认滚动至节点显示document.getElementById(show).scrollIntoView({ behavior: &quo…

【电路笔记】-并联RLC电路分析

并联RLC电路分析 文章目录 并联RLC电路分析1、概述2、AC的行为3、替代配置3.1 带阻滤波器3.2 带通滤波器 4、总结 电子器件三个基本元件的串联行为已在我们之前的文章系列 RLC 电路分析中详细介绍。 在本文中&#xff0c;介绍了另一种称为并联 RLC 电路的关联。 在第一部分中&a…

MySQL事务机制,事务与并发

mysql应对并发设立了四种事务机制&#xff1a; READ UNCOMMITEDREAD COMMITZEDREPEATABLE READSERIALIZABLE 以上按照串行程度排序 READ UNCOMMITED只要有人修改&#xff0c;我都能读到&#xff0c;不论是不是commited&#xff0c;但这有问题&#xff0c;万一A修改了&#xf…

钓鱼识别视频AI算法,让智慧水务更上一层楼

智慧水务已经成为现代水务行业的发展趋势&#xff0c;通过一系列的技术升级&#xff0c;实现智能化、高效化的水资源管理。其中&#xff0c;钓鱼识别视频AI算法的应用&#xff0c;更是为安全防线提供了强大的技术支持。本文将围绕智慧水务技术升级和钓鱼识别视频AI算法展开讨论…

考研顺序表的初始化、销毁、打印、封装、增删改查代码看这一篇就够了

目录 题目 头文件&#xff1a; SeqList.c 文件 销毁函数 封装函数&#xff0c;动态扩容 尾插法 打印函数 头插法 尾删法 头删法 指定位置插入 指定下标位置删除 按值查找下标 Test.c测试类 题目 // SeqList.h #pragma once #include <stdio.h> #include <…

不得不学的“职场高手秘籍”,有这3张表,做工作进度管理必备!

在职场中&#xff0c;进度管理是非常重要的一环&#xff0c;无论是完成自己的工作还是协调团队的工作&#xff0c;都需要对进度进行有效的管理&#xff0c;以确保工作能够顺利进行&#xff0c;达成既定目标。 工作进度表 工作管理/自动统计/图表分析/可编辑修改 在制定工作计划…

挑战100天 AI In LeetCode Day04(热题+面试经典150题)

挑战100天 AI In LeetCode Day04&#xff08;热题面试经典150题&#xff09; 一、LeetCode介绍二、LeetCode 热题 HOT 100-62.1 题目2.2 题解 三、面试经典 150 题-63.1 题目3.2 题解 一、LeetCode介绍 LeetCode是一个在线编程网站&#xff0c;提供各种算法和数据结构的题目&am…

11月15-19日,第二十五届高交会等你来

11月15—19日&#xff0c;第二十五届中国国际高新技术成果交易会&#xff08;以下简称“高交会”&#xff09;将在深圳会展中心&#xff08;福田展区&#xff09;和深圳国际会展中心&#xff08;宝安展区&#xff09;两馆同时举行。 本届高交会以“激发创新活力提升发展质量”…

【CSDN 每日一练 ★☆☆】【链表】删除排序链表中的重复元素

【CSDN 每日一练 ★☆☆】【链表】删除排序链表中的重复元素 链表 递归 题目 存在一个按升序排列的链表&#xff0c;给你这个链表的头节点 head &#xff0c;请你删除所有重复的元素&#xff0c;使每个元素 只出现一次 。 返回同样按升序排列的结果链表。 示例 示例 1&am…

如何给WSL2缩减硬盘(即减小虚拟大小)?

如何给WSL2缩减硬盘&#xff08;即减小虚拟大小&#xff09;&#xff1f; 1.软件环境⚙️&#x1f50d;2.问题描述&#x1f50d;&#x1f421;3.解决方法&#x1f421;&#x1f914;4.结果预览&#x1f914; 1.软件环境⚙️ Windows10 教育版64位 WSL 2 Ubuntu 20.04 &#x1f…

linux循环继续fordodone数值处理和脚本的追踪调试

格式 for &#xff08;&#xff08;初始值&#xff1a;限制值&#xff1b;步长&#xff09;&#xff09; do 程序段 done 注意点&#xff1a;$(()) 数值运算 $()命令 ${}取值 sh [-nvx] *.sh -n 不执行脚本&#xff0c;检查语法错误-常用 -v 执行之前&#xff0c;将…

野火霸天虎 STM32F407 学习笔记_3 尝试寄存器映射方式点亮 LED 灯

新建工程 寄存器方式 要命啊&#xff0c;一看名字我就不想试。寄存器新建不得麻烦死。 哎算了为了学习原理&#xff0c;干了。 我们尝试自己写一个寄存器的库函数来引用。 首先我们需要引用 st 官方启动文件 stmf4xx.s&#xff0c;具体用途后面章节再展开讲解。然后我们自…

一种libuv实现websockets服务的解决方案

方法是libuv用多事件循环来驱动。说起来容易&#xff0c;做起来还是比下面的方法更容易&#xff1a; 上图是某位网友的方法代表子大部分网络资料。此方法对部署不友好&#xff0c;因为软件仓库提供的libwebsockets是不能用了。如何简化部署&#xff0c;利用好现有的软件仓库呢&…

ubuntu 安装 zsh、ohmyzsh并配置必要插件

下述记录是完成全部操作后回忆记录得来&#xff0c;或有不准确。我只记录安装中确实用到的指令&#xff0c;参考资料中有扩展内容&#xff0c;记录如下&#xff1a; ubuntu使用zsh终端并安装nerd font字体——nerd font字体不太好安装&#xff0c;使用fonts-powerline替代。 Ub…

基于SpringBoot+Vue的点餐管理系统

基于springbootvue的点餐平台网站系统的设计与实现~ 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBootMyBatisVue工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 菜品详情 个人中心 订单 管理员界面 菜品管理 摘要 点餐管理系统是一种用…

【Mac开发环境搭建】安装HomeBrew、HomeBrew安装Docker、Docker安装Mysql5.7和8

文章目录 HomeBrew安装相关命令安装包卸载包查询可用的包更新所有包更新指定包查看已经安装的包查看包的信息清理包查看brew的版本更新brew获取brew的帮助信息 Brew安装DockerDocker常用命令镜像相关查看已经拉取的所有镜像删除镜像 容器相关停止运行容器启动容器重启容器删除容…

uniapp+uview2.0+vuex实现自定义tabbar组件

效果图 1.在components文件夹中新建MyTabbar组件 2.组件代码 <template><view class"myTabbarBox" :style"{ backgroundColor: backgroundColor }"><u-tabbar :placeholder"true" zIndex"0" :value"MyTabbarS…

【Linux】:文件系统

文件系统 一.认识硬件-磁盘1.磁盘的物理构成2.磁盘的存储构成3.逻辑结构 二.文件系统 文件内容属性&#xff0c;前面我们所说的文件操作都是针对以打开的文件&#xff0c;那么未打开的文件呢&#xff1f;当然是在磁盘上储存着&#xff0c;接下来谈谈它是如何储存的。 一.认识硬…