09.AOP-尚硅谷Spring零基礎入門到進階,一套搞定spring6全套視頻教程(源碼級講解)

現有代碼缺陷
針對帶日志功能的實現類,我們發現有如下缺陷:

  • 對核心業務功能有干擾,導致程序員在開發核心業務功能時分散了精力
  • 附加功能分散在各個業務功能方法中,不利于統一維護
    解決思路
    解決核心:解耦。把附加功能從業務功能代碼中抽取出來。
    困難
    解決問題的困難:要抽取的代碼在方法內部,靠以前把子類中的重復代碼抽取到父類的方式沒法解決。所以需要引
    入新的技術。

代理模式

概念

介紹
二十三種設計模式中的一種,屬于結構型模式。它的作用就是通過提供一個代理類,讓我們在調用目標方法的時
候,不再是直接對目標方法進行調用,而是通過代理類間接調用。讓不屬于目標方法核心邏輯的代碼從目標方法中
剝離出來一一解耦。調用目標方法時先調用代理對象的方法,減少對目標方法的調用和打擾,同時讓附加功能能夠
集中在一起也有利于統一維護。
相關術語
,代理:將非核心邏輯剝離出來以后,封裝這些非核心邏輯的類、對象、方法。
·目標:被代理“套用”了非核心邏輯代碼的類、對象、方法。

場景模擬

  1. 聲明計算器接口Calculator,包含加減乘除的抽象方法

    package org.example;  public interface Calculator {  public int add(int i, int j);  public int sub(int i, int j);  public int mul(int i, int j);  public int div(int i, int j);  
    }
    
  2. 寫一個實現Calculator業務的實現類

    package org.example;
    public class CalculatorImpl implements Calculator {@Overridepublic int add(int i, int j) {int result = i + j;System.out.println("result=" + result);return result;}@Overridepublic int sub(int i, int j) {int result = i - j;System.out.println("result=" + result);return result;}@Overridepublic int mul(int i, int j) {int result = i * j;System.out.println("result=" + result);return result;}@Overridepublic int div(int i, int j) {int result = i / j;System.out.println("result=" + result);return result;}
    }
    
  3. 寫一個實現Calculator業務的帶有日志功能的實現類

    package org.example;  
    public class CalculatorLogImpl implements Calculator {@Overridepublic int add(int i, int j) {System.out.println("計算開始,i=" + i + "j=" + j);int result = i + j;System.out.println("計算結束,i=" + i + "j=" + j + "result=" + result);System.out.println("result=" + result);return result;}@Overridepublic int sub(int i, int j) {System.out.println("計算開始,i=" + i + "j=" + j);int result = i - j;System.out.println("計算結束,i=" + i + "j=" + j + "result=" + result);System.out.println("result=" + result);return result;}@Overridepublic int mul(int i, int j) {System.out.println("計算開始,i=" + i + "j=" + j);int result = i * j;System.out.println("計算結束,i=" + i + "j=" + j + "result=" + result);System.out.println("result=" + result);return result;}@Overridepublic int div(int i, int j) {System.out.println("計算開始,i=" + i + "j=" + j);int result = i / j;System.out.println("計算結束,i=" + i + "j=" + j + "result=" + result);System.out.println("result=" + result);return result;}
    }
    

靜態代理

靜態代理確實實現了解耦,但是由于代碼都寫死了,完全不具備任何的靈活性。就拿日志功能來說,將來其
他地方也需要附加日志,那還得再聲明更多個靜態代理類,那就產生了大量重復的代碼,日志功能還是分散
的,沒有統一管理。
提出進一步的需求:將日志功能集中到一個代理類中,將來有任何日志需求,都通過這一個代理類來實現。
這就需要使用動態代理技術了。

動態代理

使用java.lang.reflect.Proxy類實現動態代理
官方示例代碼

InvocationHandler handler = new MyInvocationHandler(...);  
Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),  new class<?>[]{Foo.class},  handler);

創建一個代理工廠類

package org.example;  import lombok.val;  import javax.print.attribute.standard.JobKOctets;  
import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.Method;  
import java.lang.reflect.Proxy;  public class ProxyFactory {  Object target;  public ProxyFactory(Object target) {  this.target = target;  }  public Object getProxy() {  
/*      有三個參數  第一個參數:CLassLoader:加載動態生成代理類的來加載器  第二個參數:CLass[]interfaces:目錄對象實現的所有接口cLass類型數組  第三個參數:InvocationHandler:設置代理對象實現目標對象方法的過程*/  ClassLoader cLassLoader = target.getClass().getClassLoader();  Class[] classes = target.getClass().getInterfaces();  InvocationHandler invocationHandler = new InvocationHandler() {  @Override  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  //調用方法前日志  System.out.println("[動態代理][調用前日志]" + method.getName() + "參數:" + args);  //調用目標方法  Object result = method.invoke(target, args);  //調用方法后日志  System.out.println("[動態代理][調用后日志]" + method.getName() + "參數:" + args);  return result;  }  };  return Proxy.newProxyInstance(cLassLoader, classes, invocationHandler);  }  
}

編寫測試類

@Test  
public void calculatorTest(){  ProxyFactory proxyFactory=new ProxyFactory(new CalculatorImpl());  Calculator proxy=(Calculator) proxyFactory.getProxy();  proxy.add(1,1);  
}

輸出結果

[動態代理][調用前日志]add參數:[Ljava.lang.Object;@7d0587f1
result=2
[動態代理][調用后日志]add參數:[Ljava.lang.Object;@7d0587f1

基于注解的AOP

動態代理分類:JDK動態代理和cglib動態代理
JDK動態代理生成接口實現類代理對象
cglib動態代理繼承被代理的目標類,生成子類代理對象,不需要目標類實現接口

  • 有接口可以使用JDK動態代理和cblib動態代理
  • 沒有接口只能使用cblib動態代理
    Aspect:是AOP思想的一種實現。本質上是靜態代理,將代理邏輯“織入"被代理的目標類編譯得到的字節碼
    文件,所以最終效果是動態的。weaver就是織入器。Spring只是借用了Aspect)中的注解。

使用AOP步驟

  1. 引入aop相關依賴

    <!--spring aop依賴-->  
    <dependency>  <groupId>org.springframework</groupId>  <artifactId>spring-aop</artifactId>  <version>6.0.2</version>  
    </dependency>  
    <!--spring aspects依賴-->  
    <dependency>  <groupId>org.springframework</groupId>  <artifactId>spring-aspects</artifactId>  <version>6.0.2</version>  
    </dependency>
    
  2. 創建目標資源

    1. 接口

      package com.example.annoAOP;  public interface Calculator {  public int add(int i, int j);  public int sub(int i, int j);  public int mul(int i, int j);  public int div(int i, int j);  
      }
      
    2. 實現類

      package com.example.annoAOP;  import org.springframework.stereotype.Component;  @Component  
      public class CalculatorImpl implements Calculator {  @Override  public int add(int i, int j) {  int result = i + j;  System.out.println("result=" + result);  return result;  }  @Override  public int sub(int i, int j) {  int result = i - j;  System.out.println("result=" + result);  return result;  }  @Override  public int mul(int i, int j) {  int result = i * j;  System.out.println("result=" + result);  return result;  }  @Override  public int div(int i, int j) {  int result = i / j;  System.out.println("result=" + result);  return result;  }  
      }
      

第三步創建切面類

  1. 創建bean.xml,使用AOP約束,開啟AOP功能和掃描功能

    <?xml version="1.0" encoding="UTF-8"?>  
    <beans xmlns="http://www.springframework.org/schema/beans"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xmlns:context="http://www.springframework.org/schema/context"  xmlns:aop="http://www.springframework.org/schema/aop"  xsi:schemaLocation="http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans.xsd    http://www.springframework.org/schema/context    http://www.springframework.org/schema/context/spring-context.xsd    http://www.springframework.org/schema/aop  http://www.springframework.org/schema/aop/spring-aop.xsd">  <!-- 開啟組件掃描           -->  <context:component-scan base-package="com.example"/>  <!--開啟aspectj自動代理,為目標對象生成代理-->  <aop:aspectj-autoproxy></aop:aspectj-autoproxy>  
    </beans>
    
  2. 創建LogAscept類,增加一個方法的前置切入點

    package com.example.annoAOP;  import org.aspectj.lang.annotation.Aspect;  
    import org.aspectj.lang.annotation.Before;  
    import org.springframework.stereotype.Component;  @Aspect//表明這是一個AOP文件  
    @Component//讓IoC進行管理  
    public class LogAspect {  //設置切入點和通知類型  //通知類型:  // 前置   @Before(value="切入點表達式")  // 返回   @AfterReturning    // 異常   @AfterThrowing    // 后置   @After()    // 環繞   @Around()    //切入點表達式寫法:execution(權限修飾 方法返回值 方法所在全類名.方法名 (參數列表))  //execution:固定語法  //權限修飾:這里寫*表示權限修飾符和返回值任意  //方法所在全類名:寫*表示任意包名;寫*...表示包名任意同時包層次深度任意  //類名用*號代替表示類名任意,部分用*代替,如*Service,表示匹配以Service結尾的列或接口  //方法名:用*號代替表示方法名任意;部分用*代替,如get*,表示匹配以get開頭的方法  //參數列表可以使用(...)形式表示參數列表任意  @Before(value = "execution(public int com.example.annoAOP.CalculatorImpl.add (int,int))")  public void beforeAdd() {  System.out.println("[前置通知][add()]計算開始");  }  
    }
    

    方法表達式寫法:

    在這里插入圖片描述

  3. 創建測試方法

    @Test
    public void testAOPAdd(){ApplicationContext applicationContext=new ClassPathXmlApplicationContext("bean.xml");Calculator calculator=applicationContext.getBean(Calculator.class);calculator.add(1,1);
    }
    
  4. 輸出結果

    [前置通知][add()]計算開始
    result=2
    

通知類型

  • 前置通知:在被代理的目標方法前執行
  • 返回通知:在被代理的目標方法成功結束后執行(壽終正寢)
  • 異常通知:在被代理的目標方法異常結束后執行(死于非命)
  • 后置通知:在被代理的目標方法最終結束后執行(蓋棺定論)
  • 環繞通知:使用try.catch.finally結構圍繞整個被代理的目標方法,包括上面四種通知對應的所有位置
    修改LogAspect類,添加五種通知方法
package com.example.annoAOP;  import org.aspectj.lang.JoinPoint;  
import org.aspectj.lang.ProceedingJoinPoint;  
import org.aspectj.lang.annotation.*;  
import org.springframework.stereotype.Component;  @Aspect//表明這是一個AOP文件  
@Component//讓IoC進行管理  
public class LogAspect {  //前置通知  @Before(value = "execution(* com.example.annoAOP.CalculatorImpl.* (..))")  public void beforeMethod(JoinPoint joinPoint) {  String MethodName = joinPoint.getSignature().getName();  Object[] args = joinPoint.getArgs();  System.out.println("[前置通知][CalculatorImpl.MethodName=" + MethodName + "()");  System.out.println("Args[]=" + args);  }  //后置通知  @After(value = "execution(* com.example.annoAOP.CalculatorImpl.* (..))")  public void afterMethod(JoinPoint joinPoint) {  String MethodName = joinPoint.getSignature().getName();  Object[] args = joinPoint.getArgs();  System.out.println("[后置通知][CalculatorImpl.MethodName=" + MethodName + "()");  System.out.println("Args[]=" + args);  }  //返回通知  @AfterReturning(value = "execution(* com.example.annoAOP.CalculatorImpl.* (..))", returning = "result")  public void afterReturnMethod(JoinPoint joinPoint, Object result) {  String MethodName = joinPoint.getSignature().getName();  System.out.println("[返回通知][CalculatorImpl.MethodName=" + MethodName + "()");  System.out.println("[返回通知]result=" + result);  }  //異常通知  @AfterThrowing(value = "execution(* com.example.annoAOP.CalculatorImpl.* (..))", throwing = "exp")  public void afterThrowing(JoinPoint joinPoint, Throwable exp) {  String MethodName = joinPoint.getSignature().getName();  System.out.println("[異常通知][CalculatorImpl.MethodName=" + MethodName + "()");  System.out.println(exp);  }  //環繞通知  @Around("execution(* com.example.annoAOP.CalculatorImpl.* (..))")  //ProceedingJoinPoint繼承JoinPoint,比JoinPoint功能更強大,可以更好的調用目標方法  public Object around(ProceedingJoinPoint joinPoint) {  Object result = null;  try {  System.out.println("環繞通知-目標方法執行前");  result = joinPoint.proceed();  System.out.println("環繞通知-目標方法執行后");  } catch (Throwable throwable) {  System.out.println("環繞通知-目標方法執行異常");  } finally {  System.out.println("環繞通知-目標方法執行完成");  }  return result;  }  
}

輸出結果

環繞通知-目標方法執行前
[前置通知][CalculatorImpl.MethodName=add()
Args[]=[Ljava.lang.Object;@62727399
result=2
[返回通知][CalculatorImpl.MethodName=add()
[返回通知]result=2
[后置通知][CalculatorImpl.MethodName=add()
Args[]=[Ljava.lang.Object;@62727399
環繞通知-目標方法執行后
環繞通知-目標方法執行完成

編寫測試方法,使測試方法引發異常

@Test  
public void testAOPexp(){  ApplicationContext applicationContext=new ClassPathXmlApplicationContext("bean.xml");  Calculator calculator=applicationContext.getBean(Calculator.class);  calculator.div(1,0);  
}

運行結果

環繞通知-目標方法執行前
[前置通知][CalculatorImpl.MethodName=div()
Args[]=[Ljava.lang.Object;@4d9ac0b4
[異常通知][CalculatorImpl.MethodName=div()
java.lang.ArithmeticException: / by zero
[后置通知][CalculatorImpl.MethodName=div()
Args[]=[Ljava.lang.Object;@4d9ac0b4
環繞通知-目標方法執行異常
環繞通知-目標方法執行完成[之后是異常報錯信息]

重用切入點

  1. 定義一個切入點

    	package com.example.annoAOP;
    @Pointcut(value = "execution(* com.example.annoAOP.CalculatorImpl.* (..))")  
    public void pointCut() {}
    
  2. 使用切入點

    1. 內部使用切入點

      @After(value = "pointCut")  
      public void afterMethod(JoinPoint joinPoint) {  String MethodName = joinPoint.getSignature().getName();  Object[] args = joinPoint.getArgs();  System.out.println("[后置通知][CalculatorImpl.MethodName=" + MethodName + "()");  System.out.println("Args[]=" + args);  
      }  
      
    2. 外部使用切入點

      @After(value = "com.example.annoAOP.pointCut")  
      public void afterMethod(JoinPoint joinPoint) {  String MethodName = joinPoint.getSignature().getName();  Object[] args = joinPoint.getArgs();  System.out.println("[后置通知][CalculatorImpl.MethodName=" + MethodName + "()");  System.out.println("Args[]=" + args);  
      }  
      

切面的優先級

相同目標方法上同時存在多個切面時,切面的優先級控制切面的內外嵌套順序。

  • 優先級高的切面:外面
  • 優先級低的切面:里面
    使用@Order注解可以控制切面的優先級:
  • @Order(較小的數):優先級高
  • @Order(較大的數):優先級低

XML形式配置AOP

  1. 創建新包xmlaop,復制上文接口、實現類、AOP配置類

  2. 刪除LogAspect類的@Aspect注解和AOP注解

  3. 新建XmlAop.xml配置文件

    <?xml version="1.0" encoding="UTF-8"?>  
    <beans xmlns="http://www.springframework.org/schema/beans"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xmlns:context="http://www.springframework.org/schema/context"  xmlns:aop="http://www.springframework.org/schema/aop"  xsi:schemaLocation="http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans.xsd    http://www.springframework.org/schema/context    http://www.springframework.org/schema/context/spring-context.xsd    http://www.springframework.org/schema/aop    http://www.springframework.org/schema/aop/spring-aop.xsd">  <!-- 開啟組件掃描           -->  <context:component-scan base-package="com.example.xmlAOP"/>  <!--配置AOP-->  <aop:config>  <!-- 配置切面類       -->  <aop:aspect ref="logAspect">  <!-- 配置切入點       -->  <aop:pointcut id="cutpoint" expression="execution(* com.example.xmlAOP.CalculatorImpl.* (..))"/>  <!-- 配置方法執行前通知       -->  <aop:before method="beforeMethod" pointcut-ref="cutpoint"/>  <!-- 配置方法執行后通知       -->  <aop:after method="afterMethod" pointcut-ref="cutpoint"/>  <!-- 配置方法返回后通知       -->  <aop:after-returning method="afterReturnMethod" pointcut-ref="cutpoint" returning="result"/>  <!-- 配置環繞通知       -->  <aop:around method="around" pointcut-ref="cutpoint"/>  <!-- 配置異常通知       -->  <aop:after-throwing method="afterThrowing" pointcut-ref="cutpoint" throwing="exp"/>  </aop:aspect>  </aop:config>  </beans>
    
  4. 編寫測試方法

    @Test  
    public void testXML_AOP(){ApplicationContext applicationContext=new ClassPathXmlApplicationContext("XmlAop.xml");//本項目存在兩個Calculator,需要注意使用的是哪個Calculator類com.example.xmlAOP.Calculator calculator=applicationContext.getBean(com.example.xmlAOP.Calculator.class);calculator.add(1,1);
    }
    
  5. 輸出結果

[前置通知][CalculatorImpl.MethodName=add()
Args[]=[Ljava.lang.Object;@eda25e5
環繞通知-目標方法執行前
result=2
環繞通知-目標方法執行后
環繞通知-目標方法執行完成
[返回通知][CalculatorImpl.MethodName=add()
[返回通知]result=2
[后置通知][CalculatorImpl.MethodName=add()
Args[]=[Ljava.lang.Object;@eda25e5

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/45007.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/45007.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/45007.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

glm-4 聯網搜索 api 測試

今天測試了一下 glm-4 的聯網搜索 web_search tool 調用&#xff0c;發現了 web_search 的網頁檢索返回結果中幾個比較詭異的事情&#xff0c;特此記錄&#xff1a; 有些檢索結果沒有 icon、link、media 字段&#xff0c;但從內容上看確實是聯網搜索出來的結果&#xff0c;不知…

從零開始讀RocketMq源碼(三)Broker存儲Message流程解析

目錄 前言 準備 消息載體CommitLog 文件持久化位置 源碼解析 broker消息對象MessageExtBrokerInner 異步存儲message CommitLog的真相 創建MappedFile文件 加入異步刷盤隊列 Message異步存儲MappedByteBuffer 總結 前言 在面試中我們經常會聽到這樣的回答&#x…

國產化趨勢下源代碼數據防泄密的信創沙盒的方案分享

隨著國產化的大力推進&#xff0c;越來越多的企事業單位在逐步替換Windows、Linux等操作系統的使用。那么什是國產化了&#xff1f;國產化是指在產品或服務中采用國內自主研發的技術和標注&#xff0c;替代過去依賴的他國的產品和服務&#xff0c;國產化又被稱之為“信創”&…

GitLab CI/CD實現項目自動化部署

1 GitLab CI/CD介紹 GitLab CI/CD 是 GitLab 中集成的一套用于軟件開發的持續集成&#xff08;Continuous Integration&#xff09;、持續交付&#xff08;Continuous Delivery&#xff09;和持續部署&#xff08;Continuous Deployment&#xff09;工具。這套系統允許開發團隊…

vue里實現點擊按鈕回到頁面頂部功能,博客必備!

效果 步驟 1-標簽結構 動態綁定樣式style&#xff0c;監聽點擊事件&#xff0c;后續控制opacity透明度。和滾動距離 <div class"toTop" :style"dynamicStyles" click"toTop"><!--<i class"fa fa-arrow-up"></i>…

Django ORM中的F 對象

F 對象非常強大&#xff0c;可以在查詢和更新操作中進行復雜的字段間運算。 假設我們有一個包含商品信息的模型 Product&#xff1a; from django.db import modelsclass Product(models.Model):name models.CharField(max_length100)price models.DecimalField(max_digits…

MySQL向自增列插入0失敗問題

問題 在一次上線時&#xff0c;發現通過腳本添加的狀態表中&#xff0c;待提交的狀態不正確&#xff0c;本來應該是0&#xff0c;線上是101。 原因 默認情況下&#xff0c;MySQL對應自增列&#xff0c;認為0和null等價&#xff08;因為mysql認為0不是最佳實踐不推薦使用&…

超簡單的通配證書簽發工具,免費,無需安裝任何插件到本地

常見的acme.sh 或者 lego等工具需要配置&#xff0c;安裝不靈活&#xff0c;續簽需要配置計劃任務&#xff0c;簽發單域名證書或者通配證書需要不同的指令和配置&#xff0c;繁瑣&#xff0c;如果自己程序想要對接簽發證書的api有的不支持&#xff0c;有的用起來繁瑣。 最近發…

[手機Linux PostmarketOS]三, Alpine Linux命令使用

Alpine Linux 一些常用的指令&#xff1a; 添加國內源下載鏈接&#xff1a; 編譯以下文件&#xff0c;添加鏈接進去&#xff1a; sudo vi /etc/apk/repositories##清華源&#xff1a; https://mirror.tuna.tsinghua.edu.cn/alpine/latest-stable/main https://mirror.tuna.tsi…

【VIVADO SDK調試遇到DataAbortHandler】

問題 SDK調試遇到DataAbortHandler問題。 運行后不顯示結果&#xff0c;debug模式下發現進入DataAbortHandler異常函數。程序中存在大數組。 原因:SDK默認的堆棧為1024bytes,需要將堆棧調大。 修改方法&#xff1a; 解決:對application中src下的lscript.ld雙擊&#xff0c;…

android 添加一個水平線

在Android中&#xff0c;添加一個水平線通常可以通過幾種方式實現&#xff0c;最常見的是使用View組件或者自定義的Drawable。下面是一個簡單的例子&#xff0c;展示如何在布局文件中添加一個水平線&#xff1a; 使用View組件 在你的布局XML文件中&#xff0c;你可以添加一個…

Linux 程序卡死的特殊處理

一、前言 Linux環境。 我們在日常編寫的程序中&#xff0c;可能會出現一些細節問題&#xff0c;導致程序卡死&#xff0c;即程序沒法正常運行&#xff0c;界面卡住&#xff0c;也不會閃退... 當這種問題出現在客戶現場&#xff0c;那就是大問題了。。。 當我們暫時還無法排…

Python如何調用C++

ctypes 有以下優點: Python內建&#xff0c;不需要單獨安裝Python可以直接調用C/C 動態鏈接庫(.dll 或 .so)在Python一側&#xff0c;不需要了解 c/c dll 內部的工作方式提供了 C/C 數據類型與Python類型的相互映射&#xff0c;以及轉換&#xff0c;包括指針類型。 在使用cty…

如何定量選擇孔銷基準?-DTAS來幫你!

在當今快速發展的工程領域&#xff0c;公差仿真的作用日漸重要&#xff0c;在公差仿真中&#xff0c;基準體系的選擇對于最終結果更是至關重要。基準體系不同可能導致仿真過程中的參數計算、誤差分析以及最終的工程設計都有所不同。基準體系作為評估和比較的參照&#xff0c;直…

Suricata引擎二次開發之命中規則定位

二開背景 suricata是一款高性能的開源網絡入侵檢測防御引擎&#xff0c;旨在檢測、預防和應對網絡中的惡意活動和攻擊。suricata引擎使用多線程技術&#xff0c;能夠快速、準確地分析網絡流量并識別潛在的安全威脅&#xff0c;是眾多IDS和IPS廠商的底層規則檢測模塊。 前段時間…

強制升級最新系統,微軟全面淘汰Win10和部分11用戶

說出來可能不信&#xff0c;距離 Windows 11 正式發布已過去整整三年時間&#xff0c;按理說現在怎么也得人均 Win 11 水平了吧&#xff1f; 然而事實卻是&#xff0c;三年時間過去 Win 11 占有率僅僅突破到 29%&#xff0c;也就跳起來摸 Win 10 屁股的程度。 2024 年 6 月 Wi…

【Linux】磁盤性能壓測-FIO工具

一、FIO工具介紹 fio&#xff08;Flexible I/O Tester&#xff09;是一個用于評估計算機系統中 I/O 性能的強大工具。 官網&#xff1a;fio - fio - Flexible IO Tester 注意事項&#xff01; 1、不要指定文件系統名稱&#xff08;如/dev/mapper/centos-root)&#xff0c;避…

react啟用mobx @decorators裝飾器語法

react如果沒有經過配置&#xff0c;直接使用decorators裝飾器語法會報錯&#xff1a; Support for the experimental syntax ‘decorators’ isn’t currently enabled 因為react默認是不支持裝飾器語法&#xff0c;需要做一些配置來啟用裝飾器語法。 step1: 在 tsconfig.js…

【學術會議征稿】第三屆能源互聯網及電力系統國際學術會議(ICEIPS 2024)

第三屆能源互聯網及電力系統國際學術會議&#xff08;ICEIPS 2024&#xff09; 2024 3rd International Conference on Energy Internet and Power Systems 能源互聯網是實現新一代電力系統智能互動、開放共享的重要支撐技術之一&#xff0c;也是提升能源調度效率&#xff0…

SQL 存儲過程

SQL&#xff08;Structured Query Language&#xff09;的存儲過程&#xff08;Stored Procedure&#xff09;是一組為了完成特定功能的SQL語句集&#xff0c;它經編譯后存儲在數據庫中&#xff0c;用戶通過指定存儲過程的名字并給它傳遞參數&#xff08;如果有的話&#xff09…