SpringBoot核心框架之AOP詳解

SpringBoot核心框架之AOP詳解

一、AOP基礎
1.1 AOP概述
  • AOP:Aspect Oriented Programming(面向切面編程,面向方面編程),其實就是面向特定方法編程。
    • 場景:項目部分功能運行較慢,定位執行耗時較長的業務方法,此時就需要統計每一個業務的執行耗時。
    • 思路:給每個方法在開始前寫一個開始計時的邏輯,在方法結束后寫一個計時結束的邏輯,然后相減得到運行時間。

思路是沒問題的,但是有個問題,一個項目是有很多方法的,如果挨個增加邏輯代碼,會相當繁瑣,造成代碼的臃腫,所以可以使用AOP編程,將計時提出成一個這樣的模板:

  1. 獲取方法運行開始時間
  2. 運行原始方法
  3. 獲取方法運行結束時間,計算執行耗時

原始方法就是我們需要計算時間的方法,并且可以對原始方法進行增強,其實這個技術就是用到了我們在Java基礎部分學習的動態代理技術

實現:動態代理是面向切面編程最主流的實現。而SpringAOP是Spring框架的高級技術,旨在管理bean對象的過程中,主要是通過底層的動態代理機制,對特點的方法進行編程。

1.2 AOP快速入門

統計各個業務層方法執行耗時

  1. 導入依賴:在pom.xml中導入AOP的依賴。

    org.springframework.boot spring-boot-starter-aop
  2. 編寫AOP程序:針對于特定方法根據業務需要進行編程。

    @Slf4j // 日志
    @Component // 將當前類交給spring管理
    @Aspect // 聲明這是一個AOP類
    public class TimeAspect {

    @Around(“execution(* com.example.service..(…))”)
    // @Around:表示這是一個環繞通知。
    // “execution(* com.example.service..(…))”:切入點表達式,它定義了哪些方法會被這個環繞通知所攔截。這個后面會詳細講解。
    // execution(* …):表示攔截執行的方法。
    // * com.example.service..(…):表示攔截 com.example.service 包下所有類的所有方法(* 表示任意字符的通配符)。
    // …:表示方法可以有任意數量和類型的參數。
    public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {
    // ProceedingJoinPoint是 Spring AOP 中的一個接口,在使用環繞通知時需要
    // 它繼承自 JoinPoint 接口,并添加了 proceed() 方法。
    // 這個方法是 AOP 代理鏈執行的關鍵部分,它允許你在切面中執行自定義邏輯后繼續執行原始方法。

       // 1. 記錄開始時間long start = System.currentTimeMillis();// 2. 調用原始方法Object result = joinPoint.proceed(); // 執行被通知的方法。如果不調用 proceed(),被通知的方法將不會執行。// 3. 記錄結束時間,計算耗時long end = System.currentTimeMillis();// getSignature():返回當前連接點的簽名。log.info(joinPoint.getSignature()+"方法執行耗時:{}ms",end - start);return result;
    

    }
    }

  3. 查看結果在這里插入圖片描述

這樣我們就完成了,一個AOP的小例子,但是AOP的功能遠不能這些,他還有更多的實用的功能。比如:記錄操作日志:可以記錄誰什么時間操作了什么方法,傳了什么參數,返回值是什么都可以很方便的實現。還有比如權限控制,事務管理等等。

我們來總結一下AOP的優勢

  1. 代碼無侵入
  2. 減少重復代碼
  3. 提高開發效率
  4. 維護方便
1.3. AOP核心概念

連接點:JoinPoint,可以被連接點控制的方法(暗含方法執行時的信息)。 在此例中就是需要被計算耗時的業務方法。
通知:Advice,指那些重復的邏輯,也就是共性功能(最終體現為一個方法)。在此例中就是計算耗時的邏輯代碼。
切入點:PointCut,匹配連接點的條件,通知僅會在切入點方法執行時被應用。在此例中就是com.example.service 包下所有類的所有方法。
切面:Aspect,描述通知與切入點的對應關系(通知+切入點)。在此例中就是TimeAspect方法。
目標對象:Target,通知所應用的對象。在此例中就是通知com.example.service 包下所有類的所有方法。

1.4. AOP的執行流程

因為SpringAOP是基于動態代理實現的,所有在方法運行時就會先為目標對象基于動態代理生成一個代理對象,為什么說AOP可以增強方法,就是因為有一個代理方法,然后在AOP執行時,Spring就會將通知添加到代理對象的方法前面,也就是記錄開始時間的那個邏輯代碼,然后調用原始方法,也就是需要計時的那個方法,此時代理對象已經把原始方法添加到代理對象里面了,然后執行調用原始方法下面的代碼,在此例中就是計算耗時的那部分,AOP會把這部分代碼添加到代理對象的執行方法的下面,這樣代理對象就完成了對目標方法的增強,也就是添加了計時功能,最后在程序運行時自動注入的也就不是原來的對象,而是代理對象了,不過這些都是AOP自動完成,我們只需要編寫AOP代碼即可。

二、AOP進階
2.1. AOP支持的通知類型

通知類型:

  1. 環繞通知(Around Advice) 重點!!!:
  • 使用 @Around 注解來定義。
  • 包圍目標方法的執行,可以在方法執行前后執行自定義邏輯,并且可以控制目標方法的執行。
  • 通過 ProceedingJoinPoint 參數的 proceed() 方法來決定是否執行目標方法。
  1. 前置通知(Before Advice)
  • 使用 @Before 注解來定義。
  • 在目標方法執行之前執行,無論方法是否拋出異常,都會執行。
  • 不能阻止目標方法的執行。
  1. 后置通知(After Advice) 也叫最終通知
  • 使用 @After 注解來定義。
  • 在目標方法執行之后執行,無論方法是否拋出異常,都會執行。
  • 通常用于資源清理工作
  • 返回通知(After Returning Advice) 了解
  • 使用 @AfterReturning 注解來定義。
  • 在目標方法成功執行之后執行,即沒有拋出異常時執行。
  • 可以獲取方法的返回值。
  1. 異常通知(After Advice)了解
  • 使用 @AfterThrowing 注解來定義。
  • 在目標方法拋出異常后執行。
  • 可以獲取拋出的異常對象。

注意事項:

  1. 環繞通知需要自己調用joinPoint.proceed()來讓原始方法執行,其他通知則不需要。

  2. 環繞通知的返回值必須是Object,來接受原始方法的返回值。

    @Slf4j
    @Component
    @Aspect
    public class MyAspect {

    // 因為示例中的切入點都是一樣的,所以不用寫多次切入表達式,創建一個方法即可。
    // 此方法也可在其他AOP需要切入點的地方使用。
    @Pointcut("execution(* com.example.service.impl.DeptServiceImpl.*(..))")
    public void pt(){}// 前置通知
    @Before("pt()")
    public void Before(){log.info("before ...");
    }// 環繞通知
    @Around("pt()")
    public Object Around(ProceedingJoinPoint joinPoint) throws Throwable {log.info("around after ...");// 調用原始方法Object proceed = joinPoint.proceed();log.info("around after ...");return proceed;
    }// 后置通知
    @After("pt()")
    public void After(){log.info("after ...");
    }// 返回通知
    @AfterReturning("pt()")
    public void Returning(){log.info("returning ...");
    }// 異常通知
    @AfterThrowing("pt()")
    public void Throwing(){log.info("throwing ...");
    }
    

    }

2.2. 多個通知之間的執行順序

當有多個切面的切入點都匹配到了目標方法,目標方法運行時,多個通知方法都會執行。那么順序是怎么的呢?
我們先創建三個AOP程序,分別給他們創建一個前置通知和后置通知,然后啟動程序觀察他們的輸出情況。

// MyAspect2
@Slf4j
@Component
@Aspect
public class MyAspect2 {@Before("execution(* com.example.service.impl.DeptServiceImpl.*(..))")public void befor(){log.info("befor2 ...");}@After("execution(* com.example.service.impl.DeptServiceImpl.*(..))")public void after(){log.info("after2 ...");}
}
// MyAspect3
@Slf4j
@Component
@Aspect
public class MyAspect3 {@Before("execution(* com.example.service.impl.DeptServiceImpl.*(..))")public void befor(){log.info("befor3 ...");}@After("execution(* com.example.service.impl.DeptServiceImpl.*(..))")public void after(){log.info("after3 ...");}
}
// MyAspect4
@Slf4j
@Component
@Aspect
public class MyAspect4 {@Before("execution(* com.example.service.impl.DeptServiceImpl.*(..))")public void befor(){log.info("befor4 ...");}@After("execution(* com.example.service.impl.DeptServiceImpl.*(..))")public void after(){log.info("after4 ...");}
}// 輸出結果com.example.aop.MyAspect2                : befor2 ...com.example.aop.MyAspect3                : befor3 ...com.example.aop.MyAspect4                : befor4 ...com.example.aop.MyAspect4                : after4 ...com.example.aop.MyAspect3                : after3 ...com.example.aop.MyAspect2                : after2 ...// 然后我們把MyAspect2改成MyAspect5,但輸出內容不變,我們來看一下輸出結果com.example.aop.MyAspect3                : befor3 ...com.example.aop.MyAspect4                : befor4 ...com.example.aop.MyAspect5                : befor2 ...com.example.aop.MyAspect5                : after2 ...com.example.aop.MyAspect4                : after4 ...com.example.aop.MyAspect3                : after3 ...
2.2.1 默認情況:

執行順序是和類名有關系的,對于目標方法前的通知字母越靠前的越先執行,目標方法后的通知則相反,字母越靠前的越晚執行,這和Filter攔截器的規則是一樣的。

2.2.2 也可以使用注解的方式指定順序。使用@Order(數字)加在切面類上來控制順序。

目標方法前的通知:數字小的先執行。
目標方法后的通知:數字小的后執行。

@Slf4j
@Component
@Aspect@Order(10)public class MyAspect3 {...
}
2.3. 切入點表達式

切入點表達式:描述切入點方法的一種表達式。
作用:主要決定項目中哪些方法需要加入通知。
常見形式:

  • execution(…):根據方法的簽名來匹配。
  • @annotation:根據注解匹配。
2.3.1 execution(…)

execution主要是通過方法的返回值,類名,包名,方法名,方法參數等信息來匹配,語法為:
execution(訪問修飾符? 返回值 包名.類名.方法名(方法參數) throws 異常)

其中帶 ? 的表示可以省略的部分

  • 訪問修飾符:可省略(比如:public private …)

  • 包名.類名:可省略 但不推薦

  • throws 異常:可省略 (注意是方法上聲明可拋出的異常,不是實際拋出的異常)

    // 完整的寫法:
    @Before(“execution(public void com.example.service.impl.DeptServiceImpl.add(java.lang.Integer))”)
    public void befor(){

    }

可以使用通配符描述切入點

  • 單個獨立的任意符號,可以通配任意返回值,包括包名,類名,方法名,任意一個參數,也可以通配包,類,方法名的一部分。

    @After(“execution(* com..service..add*(*))”)

  • 多個連續的任意符號,可以通配任意層級的包,或任意類型,任意個數的參數。

    @After(“execution(* com.example…DeptService.*(…))”)

  • 根據業務的需要,也可以使用 且(&&),或(||),非(!)來組合切入點表達式。

    @After(“execution(* com.example…DeptService.(…)) || execution( com.example.service.DeptService.*(…))”)

2.3.2 @annotation:用于匹配標識有特定注解的方法

語法:@annotation(注解的全類名)

先新建一個注解:

@Retention(RetentionPolicy.RUNTIME)  // 用來描述有效時間,RUNTIMW:在運行時有效
@Target(ElementType.METHOD) // 用來說明這個注解可以運行在哪里, METHOD:方法上
public @interface MyLog {
}

在目標方法上添加注解

@MyLog
@Override
public void delete(Integer id) {deptMapper.delect(id); // 根據id刪除部門
}
@MyLog
@Override
public void add(Dept dept) {dept.setCreateTime(LocalDateTime.now());dept.setUpdateTime(LocalDateTime.now());deptMapper.add(dept);
}

在切入點表達式以注解的方式進行

@After("@annotation(com.example.aop.MyLog)")
public void after(){...
}
3.3. 連接點

在Spring中使用JoinPoint抽象了連接點,用它可以獲取方法執行時的相關信息,如目標類目,方法名,方法參數等。

  • 對于環繞通知(@around),獲取連接點信息只能使用ProceedingJoinPoint

  • 對于其他四種通知,獲取連接點信息只能使用JoinPoint,他是ProceedingJoinPoint的父類型。

    // 我們只在環繞通知中演示,因為API都是相同的
    @Component
    @Aspect
    @Slf4j
    public class MyAspect5 {

    @Pointcut("@annotation(com.example.aop.MyLog)")
    public void pt(){}@Before("pt()")
    public void before(JoinPoint joinPoint){log.info("before ...");
    }
    @Around("pt()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {log.info("around ... before");// 1. 獲取目標對象的類名log.info("目標對象的類名:"+joinPoint.getTarget().getClass().getName());// 2. 獲取目標方法的方法名log.info("目標方法的方法名"+joinPoint.getSignature().getName());// 3. 目標方法運行時傳入的參數log.info("目標方法運行時傳入的參數"+ Arrays.toString(joinPoint.getArgs())); // 數組不能直接輸出// 4. 放行,目標方法執行Object object = joinPoint.proceed();// 5. 獲取目標方法的返回值log.info("目標方法的返回值"+ object);log.info("around ... after");return object;
    }
    

    }
    // 查看結果
    com.example.aop.MyAspect5 : around … before
    com.example.aop.MyAspect5 : 目標對象的類名:com.example.service.impl.DeptServiceImpl
    com.example.aop.MyAspect5 : 目標方法的方法名select
    com.example.aop.MyAspect5 : 目標方法運行時傳入的參數[1]
    com.example.aop.MyAspect5 : before …
    com.example.aop.MyAspect5 : 目標方法的返回值[Dept(id=1, name=學工部, createTime=2023-11-30T13:55:55, updateTime=2023-11-30T13:55:55)]
    com.example.aop.MyAspect5 : around … after

三、AOP案例
3.1. 分析

需求:將項目中的增、刪、改、相關接口的操作日志記錄到數據庫表中

  • 操作日志包含:操作人,操作時間,執行方法的全類名,執行方法名,方法運行時的參數,返回值,方法運行時長。
    思路分析:
  • 需要對方法添加統一的功能,使用AOP最方便,并且需要計算運行時長,所以使用 環繞通知
  • 因為增刪改的方法名沒有規則,所以使用注解的方式寫切入表達式
    步驟:
    • 準備:
      • 案例中引入AOP的起步依賴
      • 設計數據表結構,并且引入對應的實體類
    • 編碼:
      • 自定義注解:@Log
      • 定義切面類,完成記錄操作日志的邏輯代碼
3.2. 開始干活

3.2.1. 創建數據庫:

create table operate_log
(id            int unsigned primary key auto_increment comment 'ID',operate_user  int unsigned comment '操作人ID',operate_time  datetime comment '操作時間',class_name    varchar(100) comment '操作的類名',method_name   varchar(100) comment '操作的方法名',method_params varchar(1000) comment '方法參數',return_value  varchar(2000) comment '返回值',cost_time     bigint comment '方法執行耗時, 單位:ms'
) comment '操作日志表';

3.2.2. 引入依賴

<!-- AOP-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency><!-- fastJSON  阿里巴巴提供的轉JSON的工具-->
<!-- 因為返回值是一個json的,但數據庫表需要的是字符串,所以使用此工具將json轉換成String -->
<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>2.0.7</version>
</dependency>

3.2.3. 新建實體類

@Data
@NoArgsConstructor
@AllArgsConstructor
public class OperateLog {private Integer id; //IDprivate Integer operateUser; //操作人IDprivate LocalDateTime operateTime; //操作時間private String className; //操作類名private String methodName; //操作方法名private String methodParams; //操作方法參數private String returnValue; //操作方法返回值private Long costTime; //操作耗時
}

3.2.4. 新建Mapper層

@Mapper
public interface OperateLogMapper {//插入日志數據@Insert("insert into operate_log (operate_user, operate_time, class_name, method_name, method_params, return_value, cost_time) " +"values (#{operateUser}, #{operateTime}, #{className}, #{methodName}, #{methodParams}, #{returnValue}, #{costTime});")void insert(OperateLog log);
}

3.2.5. 新建注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Log {
}

3.2.6. 定義切面類,完成記錄操作日志的邏輯代碼

@Component
@Aspect
@Slf4j
public class LogAspect {@Autowiredprivate HttpServletRequest request;@Autowiredprivate OperateLogMapper operateLogMapper;@Around("@annotation(com.example.anno.Log)")public Object recordLog(ProceedingJoinPoint joinPoint) throws Throwable {//操作人ID    因為jwt令牌有登錄人信息,所以解析jwt令牌就可以
//        String token = request.getHeader("token");
//        Claims claims = JwtUtils.parseJWT(token);
//        Integer user = (Integer) claims.get("id");//  使用鏈式編程   ↓↓↓Integer user = (Integer) JwtUtils.parseJWT(request.getHeader("token")).get("id");//操作時間LocalDateTime optionTime = LocalDateTime.now();//操作類名String className = joinPoint.getTarget().getClass().getName();//操作方法名String methodName = joinPoint.getSignature().getName();//操作方法參數String args = Arrays.toString(joinPoint.getArgs());long start = System.currentTimeMillis(); // 記錄方法開始運行時間// 調用原始方法Object result = joinPoint.proceed();long end = System.currentTimeMillis(); // 記錄方法結束運行時間//操作方法返回值String returnValue = JSONObject.toJSONString(result);//操作耗時long costTime = end - start;// 記錄操作日志OperateLog operateLog = new OperateLog(null, user, optionTime, className, methodName, args, returnValue, costTime);operateLogMapper.insert(operateLog);log.info("AOP記錄操作日志:{}", operateLog);return result;}
}

3.2.7. 給需要記錄的方法上面添加自定義的注解

// 這里就不一一展示了    
/*** 根據id刪除部門*/@Log@DeleteMapping("/{id}")public Result delete(@PathVariable Integer id){log.info("根據id刪除部門:{}",id);deptService.delete(id);return Result.success();}/*** 添加部門*/@Log@PostMappingpublic Result add(@RequestBody Dept dept){log.info("添加部門{}",dept);deptService.add(dept);return Result.success();}
3.3. 查看結果

剛剛進行了部門的增刪改以及員工的增刪改操作,我們查看數據庫,看有沒有被記錄。

1,1,2024-10-27 20:20:23,com.example.controller.DeptController,delete,[15],"{""code"":1,""msg"":""success""}",40
2,1,2024-10-27 20:20:45,com.example.controller.DeptController,add,"[Dept(id=null, name=測試部, createTime=null, updateTime=null)]","{""code"":1,""msg"":""success""}",5
3,1,2024-10-27 20:21:00,com.example.controller.EmpController,sava,"[Emp(id=null, username=測試, password=null, name=測試, gender=1, image=, job=1, entrydate=2024-10-20, deptId=16, createTime=null, updateTime=null)]","{""code"":1,""msg"":""success""}",6
4,1,2024-10-27 20:23:01,com.example.controller.DeptController,add,"[Dept(id=null, name=1, createTime=null, updateTime=null)]","{""code"":1,""msg"":""success""}",8
5,1,2024-10-27 20:23:18,com.example.controller.DeptController,delete,[17],"{""code"":1,""msg"":""success""}",12

在這里插入圖片描述

完全符合要求!!!!!!

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

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

相關文章

【RK3588嵌入式圖形編程】-SDL2-構建模塊化UI

構建模塊化UI 文章目錄 構建模塊化UI1、概述2、創建UI管理器3、嵌套組件4、繼承5、多態子組件6、總結在本文中,將介紹如何使用C++和SDL創建一個靈活且可擴展的UI系統,重點關注組件層次結構和多態性。 1、概述 在前面的文章中,我們介紹了應用程序循環和事件循環,這為我們的…

第四屆圖像、信號處理與模式識別國際學術會議(ISPP 2025)

重要信息 會議官網&#xff1a;www.icispp.com 會議時間&#xff1a;2025年3月28-30日 會議地點&#xff1a;南京 簡介 由河海大學和江蘇大學聯合主辦的第四屆圖像、信號處理與模式識別國際學術會議&#xff08;ISPP 2025) 將于2025年3月28日-30日在中國南京舉行。會議主…

低代碼與開發框架的一些整合[2]

1.分析的項目資源說明 經過近期的的不斷分析與運行對比&#xff0c;最終把注意力集中在了以下幾個框架&#xff1a; 01.dibootdiboot.diboot: 寫的更少, 性能更好 -> 為開發人員打造的低代碼開發平臺。Mybatis-plus關聯查詢&#xff0c;關聯無SQL&#xff0c;性能高10倍&a…

Spring Boot 中事務的用法詳解

引言 在 Spring Boot 中&#xff0c;事務管理是一個非常重要的功能&#xff0c;尤其是在涉及數據庫操作的業務場景中。Spring 提供了強大的事務管理支持&#xff0c;能夠幫助我們簡化事務的管理和控制。本文將詳細介紹 Spring Boot 中事務的用法&#xff0c;包括事務的基本概…

Java面試——Tomcat

優質博文&#xff1a;IT_BLOG_CN 一、Tomcat 頂層架構 Tomcat中最頂層的容器是Server&#xff0c;代表著整個服務器&#xff0c;從上圖中可以看出&#xff0c;一個Server可以包含至少一個Service&#xff0c;用于具體提供服務。Service主要包含兩個部分&#xff1a;Connector和…

第4章 信息系統架構(三)

4.3 應用架構 應用架構的主要內容是規劃出目標應用分層分域架構&#xff0c;根據業務架構規劃目標應用域、應用組和目標應用組件&#xff0c;形成目標應用架構邏輯視圖和系統視圖。從功能視角出發&#xff0c;闡述應用組件各自及應用架構整體上&#xff0c;如何實現組織的高階…

python小項目編程-中級(1、圖像處理)

目錄 圖像處理 實現 測試 unittest pytest 圖像處理 實現界面化操作&#xff0c;使用PIL庫實現簡單的圖像處理功能&#xff0c;如縮放&#xff08;設置縮放比例&#xff09;、旋轉和濾鏡、對比度調整、亮度調整、灰度圖、二值化圖&#xff08;二值圖如果使用的是彩色圖片需…

【Leetcode 每日一題】2209. 用地毯覆蓋后的最少白色磚塊

問題背景 給你一個下標從 0 0 0 開始的 二進制 字符串 f l o o r floor floor&#xff0c;它表示地板上磚塊的顏色。 f l o o r [ i ] floor[i] floor[i] 為 ‘0’ 表示地板上第 i i i 塊磚塊的顏色是 黑色 。 f l o o r [ i ] floor[i] floor[i] 為’1’ 表示地板上第 i …

Docker 性能優化指南

Docker 提供了強大的容器化功能&#xff0c;能夠幫助開發者在不同的環境中構建、測試和部署應用。然而&#xff0c;隨著容器化應用的不斷增長&#xff0c;Docker 容器可能會面臨一些性能瓶頸&#xff0c;影響其運行效率、資源占用和擴展能力。為了確保容器在生產環境中的高效運…

2025 WE DAY品牌日| 天璇II WE X7 Pro充電樁震撼發布,能效電氣開啟充電革命

隨著新能源產業的迅猛發展,充電樁作為電動汽車能量補給的重要基礎設施,正在成為市場關注的焦點。能效電氣作為充電樁領域的佼佼者,專注于研發高效、智能的充電解決方案,為電動汽車的普及與可持續發展鋪設了堅實的基礎。 2025年2月21日,能效電氣在深圳盛大舉辦了以“以創新 引未…

< OS 有關 > Ubuntu 24 SSH 服務器更換端口 in jp/us VPSs

原因&#xff1a; 兩臺 VPS 的 ssh 端口一直被密碼重試&#xff0c; us 這臺已經封了 632, jp 這臺兩周前清過一次 sqlite3 數據&#xff0c;現在贊到 1008 Fail2Ban 是使用 sqlite3 來記錄&#xff0c;數據量大后&#xff0c;硬盤的 I/O 會飆升&#xff0c;我有寫過一個 app…

MATLAB學習之旅:數據插值與曲線擬合

在MATLAB的奇妙世界里,我們已經走過了一段又一段的學習旅程。從基礎的語法和數據處理,到如今,我們即將踏入數據插值與曲線擬合這片充滿魅力的領域。這個領域就像是魔法中的藝術創作,能夠讓我們根據現有的數據點,構建出更加豐富的曲線和曲面,從而更好地理解和描述數據背后…

若依-@Excel新增注解numberFormat

Excel注解中原本的scale會四舍五入小數&#xff0c;導致進度丟失 想要的效果 顯示的時候保留兩個小數真正的數值是保留之前的數值 還原過程 若以中有一個專門的工具類&#xff0c;用來處理excel的 找到EXCEL導出方法exportExcel()找到writeSheet,寫表格的方法找到填充數據的方法…

LeetCode 熱題 100_搜索二維矩陣(64_74_中等_C++)(二分查找)(暴力破解法;Z字形查找;一次二分查找)

LeetCode 熱題 100_搜索二維矩陣&#xff08;64_74&#xff09; 題目描述&#xff1a;輸入輸出樣例&#xff1a;題解&#xff1a;解題思路&#xff1a;思路一&#xff08;暴力破解法&#xff09;&#xff1a;思路二&#xff08;Z字形查找&#xff09;&#xff1a;思路三&#x…

從CNN到Transformer:遙感影像目標檢測的技術演進(礦產勘探、精準農業、城市規劃、林業測量、軍事目標識別和災害評估等)

在遙感影像分析領域&#xff0c;目標檢測一直是研究熱點之一。隨著高分辨率對地觀測系統的不斷發展&#xff0c;遙感影像的分辨率和數據量呈爆發式增長&#xff0c;如何高效、準確地從海量數據中提取有用信息&#xff0c;成為了一個亟待解決的問題。近年來&#xff0c;深度學習…

【rt-thread】rt-thread 控制 led 的兩種方式

1. pin設備 #define LED_PIN 3int led(void) {rt_uint8_t count;rt_pin_mode(LED_PIN, PIN_MODE_OUTPUT); for(count 0 ; count < 10 ;count){ rt_pin_write(LED_PIN, PIN_HIGH);rt_kprintf("led on, count : %d %d\r\n", count, rt_pin_read(LED_PIN));…

Excell 代碼處理

文章目錄 Excell 代碼處理cvc格式xlsl格式小結 Excell 代碼處理 有時候要對excell進行分析&#xff0c;或者數據的導入導出&#xff0c;這個時候如果可以用代碼讀寫分析操作那么會方便很多 cvc格式 CSV&#xff08;Comma-Separated Values&#xff0c;逗號分隔值&#xff09;是…

新手小白如何挖掘cnvd通用漏洞之存儲xss漏洞(利用xss釣魚)

視頻教程和更多福利在我主頁簡介或專欄里 &#xff08;不懂都可以來問我 專欄找我哦&#xff09; 如果對你有幫助你可以來專欄找我&#xff0c;我可以無償分享給你對你更有幫助的一些經驗和資料哦 目錄&#xff1a; 一、XSS的三種類型&#xff1a; 二、XSS攻擊的危害&#x…

代碼隨想錄算法【Day52】

Day51 101. 孤島的總面積 思路 從周邊找到陸地然后 通過 dfs或者bfs 將周邊靠陸地且相鄰的陸地都變成海洋&#xff0c;然后再去重新遍歷地圖 統計此時還剩下的陸地 代碼 #include <iostream> #include <vector> using namespace std; int dir[4][2] {-1, 0, …

Python開源項目月排行 2024年12月

#2024年12月2025年1月21日1DeepSeek-Coder-V2一個開源的專家混合&#xff08;MoE&#xff09;代碼語言模型&#xff0c;其在代碼特定任務中的性能可與GPT4-Turbo相媲美。具體而言&#xff0c;DeepSeek-Coder-V2是在DeepSeek-V2的一個中間檢查點上進一步預訓練的&#xff0c;增加…