SpringBoot擴展——日志管理!

Spring Boot擴展

在Spring Boot中可以集成第三方的框架如MyBatis、MyBatis-Plus和RabbitMQ等統稱為擴展。每一個擴展會封裝成一個集成,即Spring Boot的starter(依賴組件)。starter是一種非常重要的機制,不需要煩瑣的配置,開發者只需要在項目的依賴中加入starter依賴,Spring Boot就能根據依賴信息自動掃描到要加載的信息并啟用相應的默認配置。starter的出現讓開發者不再需要查找各種依賴庫及相應的配置。所有stater模塊都遵循著約定成俗的默認配置,并允許自定義配置,即遵循“約定大于配置”的原則。

常用的starter及其說明如表6.1所示。

表6.1 Spring Boot的starter列表

日志管理

項目的日志主要包括系統日志、應用程序日志和安全日志。運維人員和項目開發人員可以通過日志了解服務器軟/硬件信息,檢查配置過程中的錯誤及錯誤發生的原因。通過分析日志還可以了解服務器的負荷、性能安全性,從而及時采取措施解決發生的問題。

因此,項目人員需要在系統開發和運行時保存日志。關于什么時候保存日志有以下幾個要點:

(1)系統初始化時:記錄系統和服務的啟動參數。在核心模塊初始化過程中會依賴一些關鍵配置項,根據參數不同提供不同的服務,記錄當前的參數有助于發生錯誤后排除問題。

(2)系統提示異常:代碼中的異常捕獲機制,此類異常的錯誤級別非常高,是系統在告知開發人員需要關注的錯誤信息。一般用WARN或者ERROR級別來記錄當前的錯誤日志。

(3)業務流程預期不符:記錄與正常流程不同的業務參數,如外部參數不正確、未知的請求信息等。

(4)系統核心角色和組件的關鍵動作的記錄:包括核心業務的日志記錄,INFO級別的日志記錄,保存微服務各服務節點交互的數據日志記錄、系統核心數據表的增、刪、改操作的日志記錄,以及核心組件運行情況的日志記錄等。

(5)第三方服務遠程調用:對第三方的服務調用需要保存調用前后的日志記錄,方便在發生錯誤時排查問題。

常用的日志框架

在Java項目開發過程中,最簡單的方式是使用System.out.println打印日志,但這種方式有很多缺陷,如I/O瓶頸,而且不利于日志的統一管理。目前市面上有很多日志組件可以集成到Spring Boot中,它們能夠快速地實現不同級別的日志分類,以及在不同的時間進行保存。常用的日志框架有以下幾個:

1. JUL簡介

JUL即java.util.logging.Logger,是JDK自帶的日志系統,從JDK 1.4開始出現。其優點是系統自帶,缺點是相較于其他的日志框架來說功能不夠強大。

2. Apache Commons Logging簡介

Apache Commons Logging是Apache提供的一個通用日志API,可以讓程序不再依賴于具體的日志實現工具。Apache Commons Logging包中還對其他日志工具(包括Log4j、JUL)進行了簡單的包裝,可以讓應用程序在運行時直接將Apache Commons Logging適配到對應的日志實現工具中。

提示:Apache Common Logging通過動態查找機制,在程序運行時會自動找出真正使用的日志庫。這一點與SLF4J不同,SLF4J是在編譯時靜態綁定真正的Log實現庫。

3. Log4j簡介

Log4j是Ceki Gülcü實現出來的,后來捐獻給Apache,又被稱為Log4j1.x,它是Apache的開放源代碼項目。在系統中使用Log4j,可以控制日志信息輸送的目的地是控制臺、文件及數據庫等,還可以自定義每一條日志的輸出格式,通過定義每一條日志信息的級別,還可以控制日志的生成過程。

Log4j主要是由Loggers(日志記錄器)、Appender(輸出端)和Layout(日志格式化器)組成。其中:

Logger用于控制日志的輸出級別與是否輸出日志;

Appender用于指定日志的輸出方式(輸出到控制臺、文件等);Layout用于控制日志信息的輸出格式。

Log4j有7種不同的log級別,按照等級從低到高依次為TRACE、DEBUG、INFO、WARN、ERROR、FATAL和OFF。如果配置為OFF級別,表示關閉log。

Log4j支持兩種格式的配置文件:properties和XML。

4. Logback簡介

Logback是由log4j的創立者Ceki Gülcü設計,是Log4j的升級版。

Logback當前分成3個模塊:logback-core、logback- classic和logbackaccess。logback-core是另外兩個模塊的基礎模塊。logback-classic是Log4j的一個改良版本,目前依然建議在生產環境中使用。

5. Log4j2簡介

Log4j2也是由log4j的創立者Ceki Gulcu設計的,它是Log4j 1.x和Logback的改進版。在項目中使用Log4j2作為日志記錄的組件,在日志的吞吐量和性能方面比log4j 1.x提高了10倍,并可以解決一些死鎖的Bug,配置也更加簡單、靈活。

6. SLF4J

SLF4J是對所有日志框架制定的一種規范、標準和接口,并不是一個具體框架。因為接口并不能獨立使用,需要和具體的日志框架配合使用(如Log4j2、Logback)。使用接口的好處是,當項目需要更換日志框架時,只需要更換jar和配置,不需要更改相關的Java代碼,SLF4J相當于Java設計模式的門面模式。目前項目的開發中多使用SLF4J+Logback或者SLF4J+Log4J2的組合方式來記錄日志。

日志的輸出級別

日志的輸出是分級別的,不同的日志級別在不同的場合打印不同的日志。常見的日志級別有以下4個:DEBUG:該級別的日志主要輸出與調試相關的內容,主要在開發、測試階段輸出。DEUBG日志應盡可能詳細,開發者會把各類詳細信息記錄到DEBUG里,起到調試的作用,包括參數信息、調試細節信息、返回值信息等,方便在開發、測試階段出現問題或者異常時對問題進行分析和修改。

INFO:該級別的日志主要記錄系統關鍵信息,用來保留系統正常工作期間的關鍵信息指標。開發者可以將初始化系統配置、業務狀態變化信息或者用戶業務流程中的核心處理記錄到INFO日志中,方便運維及錯誤回溯時進行場景復現。當在項目完成后,一般會把項目日志級別從DEBUG調成INFO,對于不需要再調試的日志,將通過INFO級別的日志記錄這個應用的運行情況,如果出現問題,根據記錄的INFO級別的日志來排查問題。

WARN:該級別的日志主要輸出警告性質的內容,這類日志可以預知問題的發生,如某個方法入參為空或者參數的值不滿足運行方法的條件時。在輸出WARN級別的日志時應輸出詳盡的提示信息,方便開發者和運維人員對日志進行分析。

ERROR:該級別主要指系統錯誤信息,如錯誤、異常等。例如,在catch中抓獲的網絡通信和數據庫連接等異常,若異常對系統的整個流程影響不大,可以輸出WARN級別的日志。在輸出ERROR級別的日志時,要記錄方法入參和方法執行過程中產生的對象等數據,在輸出帶有錯誤和異常對象的數據時,需要將該對象全部記錄,方便后續的Bug修復。

日志的等級由低到高分別是DEBUG<INFO<WARN<ERROR,日志記錄一般會記錄設置級別及其以下級別的日志。例如,設置日志的級別為INFO,則系統會記錄INFO和DEBUG級別的日志,超過INFO級別的日志不會記錄。

綜上所述,在項目中保存好日志有以下好處:

打印調試:用日志記錄變量或者邏輯的變化,方便進行斷點調試。

問題定位:程序出現異常后可根據日志快速定位問題所在,方便后期解決問題。

用戶行為日志:記錄用戶的關鍵操作行為。重要系統邏輯日志記錄:方便以后問題的排查和記錄。

實戰:日志管理之使用AOP記錄日志

本小節將新建一個項目,實現使用日志組件和Spring的AOP記錄所有Controller入參的功能,本次使用SLF4J+log4j2的方式實現日志的記錄。

(1)新建一個項目spring-extend-demo,在pom.xml中添加Web、Log4j2、SLF4J和AOP的依賴坐標,具體如下:

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0

https://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

<parent>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-parent</artifactId>

<version>2.3.10.RELEASE</version>

<relativePath/> <!-- lookup parent from repository -->

</parent>

<groupId>com.example</groupId>

<artifactId>spring-extend-demo</artifactId>

<version>0.0.1-SNAPSHOT</version>

<name>spring-extend-demo</name>

<description>Demo project for Spring Boot</description>

<properties>

<java.version>11</java.version>

</properties>

<dependencies>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter</artifactId>

<exclusions>

<exclusion>

<groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter

logging</artifactId>

</exclusion>

</exclusions>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-test</artifactId>

<scope>test</scope>

<exclusions>

<exclusion>

<groupId>org.junit.vintage</groupId>

<artifactId>junit-vintage-engine</artifactId>

</exclusion>

</exclusions>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-log4j2</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-aop</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

<exclusions>

<exclusion>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter

logging</artifactId>

</exclusion>

</exclusions>

</dependency>

</dependencies>

<build>

<plugins>

<plugin>

<groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId>

</plugin>

</plugins>

</build>

</project>

添加完依賴后,可以查看項目的依賴庫,部分依賴庫如圖6.1和圖6.2所示,當前項目中已經引入了SLG4J和Log4j2依賴。

圖6.2 SLF4J的依賴

(2)在resources目錄下新建一個log4j2.xml配置文件,配置日志的記錄如下:

<?xml version="1.0" encoding="UTF-8"?>

<!--

Configuration后面的配置,用于設置log4j2內部的信息輸出,可以不設置。當設置成

trace時可以看到log4j2內部的各種詳細輸出。

-->

<!--

monitorInterval:Log4j能夠自動檢測、修改配置文件,并設置間隔秒數。

-->

<configuration status="error" monitorInterval="30">

<!--先定義所有的Appender-->

<appenders>

<!--這個輸出控制臺的配置-->

<Console name="Console" target="SYSTEM_OUT">

<!--控制臺只輸出level及以上級別的信息(onMatch),其他的直接拒絕

(onMismatch)-->

<ThresholdFilter level="trace" onMatch="ACCEPT"

onMismatch="DENY"/>

<!--輸出日志的格式-->

<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level

%class{36} %L%M - %msg%xEx%n"/>

</Console>

<!--文件會打印出所有信息,該日志在每次運行程序時會自動清空,由append屬性

決定,適合臨時測試用-->

<File name="log" fileName="log/test.log" append="false">

<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level

%class{36} %L%M - %msg%xEx%n"/>

</File>

<!-- 打印出所有的信息,如果大小超過size,則超出部分的日志會自動存入按年

份-月份建立的文件夾下面并進行壓縮作為存檔-->

<RollingFile name="RollingFile" fileName="D:/log/log.log"

filePattern="D:/log/log-${date:yyyy-MM}/log-

%d{MM-dd-yyyy}-%i.log">

<PatternLayout pattern="%d{yyyy-MM-dd 'at' HH:mm:ss z}

%-5level%class{36} %L %M - %msg%xEx%n"/> <!-- 如果一個文件超過50 MB,就會生成下一個日志文件 -->

<SizeBasedTriggeringPolicy size="50MB"/>

<!-- 如不設置DefaultRolloverStrategy屬性,則默認同一文件夾下最多

有7個文件,這里設置為20 -->

<DefaultRolloverStrategy max="20"/>

</RollingFile>

</appenders>

<!--定義logger,只有定義了logger并引入上面配置的Appender,當前的Appender

才會生效-->

<loggers>

<!--建立一個默認用戶的logger,將其作為日志記錄的根配置-->

<root level="info">

<appender-ref ref="RollingFile"/>

<appender-ref ref="Console"/>

</root>

</loggers>

</configuration>

(3)配置保存成功后,每天會根據前面的配置生成一個日志文件,一個日志文件的最大容量為50MB,超過50MB就再新建一個日志文件。新建一個Web入口類HelloController,代碼如下:

package com.example.springextenddemo.controller;

import com.example.springextenddemo.vo.UserVO;

import org.springframework.web.bind.annotation.*;

@RestController

public class HelloController {

@GetMapping("/hi")

public String hi(@RequestParam("name")String name){

return "hi "+name;

}

@PostMapping("/hi-post")

public String hiPost(@RequestBody UserVO userVO){ return "hi-post "+userVO;

}

}

Hellocontroller中的參數接收實體類UserVO如下:

package com.example.springextenddemo.vo;

import java.util.StringJoiner;

public class UserVO {

private String name;

private String address;

private int age;

//省略GET和SET方法

}

(4)新建一個AOP類記錄日志:

package com.example.springextenddemo.aop;

import org.aspectj.lang.JoinPoint;

import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.Signature;

import org.aspectj.lang.annotation.Around;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Pointcut;

import org.aspectj.lang.reflect.MethodSignature;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.core.annotation.Order;

import org.springframework.stereotype.Component;

import org.springframework.web.servlet.mvc.method.annotation.Extended

ServletRequestDataBinder;

import javax.servlet.http.HttpServletResponseWrapper;import java.util.HashMap;

import java.util.Map;

/**

* 第一個執行

*/

@Order(1)

/**

* aspect 切面

*/

@Aspect

@Component

public class RequestParamLogAop {

private static final Logger log =

LoggerFactory.getLogger(RequestParamLogAop.class);

/**

* Controller層切點

*/

@Pointcut("execution (*

com.example.springextenddemo.controller..*.*(..))")

public void controllerAspect() {

}

/**

* 環繞通知

*

* @param joinPoint

* @throws Throwable

*/

@Around(value = "controllerAspect()")

public Object around(ProceedingJoinPoint joinPoint) throws

Throwable {

Signature signature = joinPoint.getSignature();

methodBefore(joinPoint,signature);

Object result = joinPoint.proceed();

methodAfterReturn(result, signature);

return result;

} /**

* 方法執行前執行

*

* @param joinPoint

* @param signature

*/

private void methodBefore(JoinPoint joinPoint, Signature

signature) {

//在兩個數組中,參數值和參數名的個數和位置是一一對應的

Object[] objs = joinPoint.getArgs();

// 參數名

String[] argNames = ((MethodSignature)

signature).getParameterNames();

Map<String, Object> paramMap = new HashMap<String, Object>();

for (int i = 0; i < objs.length; i++) {

if (!(objs[i] instanceof ExtendedServletRequestDataBinder)

&& !(objs[i] instanceof

HttpServletResponseWrapper)) {

paramMap.put(argNames[i], objs[i]);

}

}

log.info("請求前-方法:{} 的請求參數:{}", signature, paramMap);

}

/**

* 方法執行后的返回值

*/

private void methodAfterReturn(Object result, Signature signature)

{

log.info("請求后-方法:{} 的返回參數是:{}", signature, result);

}

}

(5)新建一個Spring Boot啟動類:

package com.example.springextenddemo;

import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication

public class SpringExtendDemoApplication {

public static void main(String[] args) {

SpringApplication.run(SpringExtendDemoApplication.class,

args);

}

}

(6)修改配置文件application.properties,將日志配置文件添加到Spring Boot中:

logging.config=classpath:log4j2.xml

(7)啟動項目,可以在控制臺看到新的日志格式,如圖6.3所示。

在瀏覽器中訪問localhost:8080/hi?name= cc,結果如圖6.4所示。

再訪問localhost:8080/hi-post,結果如圖6.5所示。

查看日志的配置目錄,打開D:\log可以看到日志文件,如圖6.6所示,日志內容如圖6.7所示,控制臺的輸出日志和保存日志文件內容一樣,如圖6.8所示。

圖6.7 log日志內容

圖6.8 控制臺打印的日志

過AOP簡單地完成了對所有Controller入口的請求參數的記錄,這個功能一般在項目中必須要有,請求入參必須進行記錄,以方便問題的回溯。

實戰:日志管理之自定義Appender

上面定義的日志配置使用的是Log4j2自帶的日志Appender,在Log4j2中常用的Appender如表6.2所示,它們有不同的功能。

表6.2 Log4j2常用的Appender

在項目開發中可以直接使用上面的Appender,也可以自定義一個Appender。下面完成一個自定義的Appender,在打印的日志前面加上自定義的內容,完成自定義日志的開發。

(1)新建一個Appeder的實現類,此類需要繼承自類AbstractAppender,代碼如下:

package com.example.springextenddemo.appender;

import org.apache.logging.log4j.core.Filter;

import org.apache.logging.log4j.core.Layout;

import org.apache.logging.log4j.core.LogEvent;

import org.apache.logging.log4j.core.appender.AbstractAppender;

import org.apache.logging.log4j.core.config.plugins.Plugin;

import org.apache.logging.log4j.core.config.plugins.PluginAttribute;import org.apache.logging.log4j.core.config.plugins.PluginElement;

import org.apache.logging.log4j.core.config.plugins.PluginFactory;

import org.apache.logging.log4j.core.layout.PatternLayout;

import java.io.Serializable;

/**

* 自定義實現Appender

* @Plugin注解:在log4j2.xml配置文件中使用,指定的Appender Tag

*/

@Plugin(name = "myAppender", category = "Core", elementType =

"appender",

printObject = true)

public class MyLog4j2Appender extends AbstractAppender {

String printString;

/**

*構造函數 可自定義參數 這里直接傳入一個常量并輸出

*

*/

protected MyLog4j2Appender(String name, Filter filter, Layout<?

extends Serializable> layout,

String printString) {

super(name, filter, layout);

this.printString = printString;

}

/**

* 重寫append()方法:在該方法里需要實現具體的邏輯、日志輸出格式的設置

* 自定義實現輸出

* 獲取輸出值:event.getMessage().toString()

* @param event

*/

@Override

public void append(LogEvent event) {

if (event != null && event.getMessage() != null) {

//格式化輸出

System.out.print("自定義appender"+printString + ":" +

getLayout().toSerializable(event));

}

} /**

* 接收log4j2-spring.xml中的配置項

* @PluginAttribute 是XML節點的attribute值,如<book name="sanguo">

</book>,這里的name是attribute

* @PluginElement 表示XML子節點的元素,例如:

* <book name="sanguo">

* <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l -

%m%n"/>

* </book>

* 其中,PatternLayout是{@link Layout}的實現類

*/

@PluginFactory

public static MyLog4j2Appender createAppender(

@PluginAttribute("name") String name,

@PluginElement("Filter") final Filter filter,

@PluginElement("Layout") Layout<? extends Serializable>

layout,

@PluginAttribute("printString") String printString) {

if (name == null) {

LOGGER.error("no name defined in conf.");

return null;

}

//默認使用 PatternLayout

if (layout == null) {

layout = PatternLayout.createDefaultLayout();

}

//使用自定義的Appender

return new MyLog4j2Appender(name, filter, layout, printString);

}

@Override

public void start() {

System.out.println("log4j2-start方法被調用");

super.start();

}

@Override

public void stop() {

System.out.println("log4j2-stop方法被調用"); super.stop();

}

}

重寫的start()方法為初始時調用,在數據庫入庫、連接緩存或者MQ時,可以在這個方法里進行初始化操作。stop()方法是在項目停止時調用,用來釋放資源。

(2)將之前項目中的日志配置文件log4j.xml修改為log4j.xm.bak,再新建一個自定義的Appender的log4j2的配置文件。注意,自定義的Appender的名稱要和Java代碼中的Appender的名字相同,其配置文件的內容如下:

<?xml version="1.0" encoding="UTF-8"?>

<configuration status="INFO" monitorInterval="30"

packages="com.example.

springextenddemo">

<!--定義Appenders-->

<appenders>

<myAppender name="myAppender" printString=":start log:">

<!--輸出日志的格式-->

<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l -

%m%n"/>

</myAppender>

</appenders>

<!--自定義logger,只有定義了logger并引入appender,appender才會生效-->

<loggers>

<!--spring和mybatis的日志級別為info-->

<logger name="org.springframework" level="INFO"></logger>

<logger name="org.mybatis" level="INFO"></logger>

<!-- 如果在自定義包中設置為INFO,則可以看見輸出的日志不包含debug輸出了

-->

<logger name="com.example.springextenddemo" level="INFO"/>

<root level="all">

<appender-ref ref="myAppender"/>

</root> </loggers>

</configuration>

(3)重新啟動項目,在瀏覽器中訪問http://localhost:8080/hi?name=cc,可以看到控制臺顯示的自定義日志如圖6.9所示。日志前已經加上了前綴自定義appender:start log,達到了本次自定義Appender的目的。

圖6.9 自定義Appender輸出

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

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

相關文章

【JSON-To-Video】AI智能體開發:為視頻圖片元素添加動效(滑入、旋轉、滑出),附代碼

各位朋友們&#xff0c;大家好&#xff01; 今天要教大家如何在 JSON - To - Video 中為視頻內圖片元素添加滑入、旋轉、滑出的動效。 如果您還不會封裝制作自己的【視頻工具插件】&#xff0c;歡迎查看之前的教程&#xff01; AI智能體平臺&#xff0c;如何封裝自定義短視頻…

Spring Boot(九十二):Spring Boot實現連接不上數據庫就重啟服務

場景: 在線上部署時,若服務器因斷電等原因意外重啟,項目及其依賴的數據庫服務通常需要配置為自動啟動。此時,如果數據庫服務啟動較慢或失敗,Spring Boot 項目會因無法建立數據庫連接而啟動失敗。 需求: 為確保項目啟動成功,需要讓 Spring Boot 項目等待數據庫服務完全就…

Debian配置Redis主從、哨兵

前言 Redis的下載安裝可參考Centos安裝配置Redis6.x&#xff0c;Centos和Debian的步驟基本類似&#xff0c;或自行在網上搜索相關資料 注意&#xff1a;遠程連接需放開相應端口 主從 搭建一個一主二從的主從模式 處理conf文件 #進入redis所在目錄 cd /tools/redis/redis6 …

虛實交融:數字孿生如何重塑交通與公路勘察設計的未來

當每一條道路、每一座橋梁、每一盞信號燈都在數字世界獲得“永生副本”&#xff0c;交通系統從被動響應邁入主動預演的紀元 一、數字孿生的核心定義&#xff1a;超越鏡像的動態認知引擎 數字孿生&#xff08;Digital Twin&#xff09;并非簡單的三維可視化模型&#xff0c;而是…

vector模擬實現中的迭代器失效問題

首先來看一組代碼&#xff1a; iterator insert(iterator pos, const T& x) {// 擴容if (_finish _end_of_storage){size_t len pos - _stare;reserve(capacity() 0 ? 4 : capacity() * 2);pos _stare len;}iterator end _finish - 1;while (end > pos){*(end…

java 設計模式_行為型_22模板模式

22.模板模式 模板方法&#xff08;Template Method&#xff09;作為Java的設計模式之一&#xff0c;一個詞概括其優勢特點那就是&#xff1a;抽象步驟 首先我們應該抽出共通的東西做一個父類&#xff08;Base類&#xff09;&#xff0c;其次具體的蛋糕制作由子類進一步實現&…

隨記:在springboot中websocket的使用

我現在有兩種方法 第一種&#xff1a;使用java封裝的這個包下的javax.websocket.* 先配置這個配置類 import com.alibaba.nacos.common.utils.CollectionUtils; import org.springframework.stereotype.Component;import javax.websocket.HandshakeResponse; import javax.w…

技術文章大綱:SpringBoot自動化部署實戰

技術文章大綱&#xff1a;SpringBoot自動化部署實戰 概述 自動化部署的背景與意義SpringBoot在現代化部署中的優勢常見自動化部署工具與方案概覽&#xff08;Jenkins、Docker、K8s等&#xff09; 環境準備 基礎工具要求&#xff1a;JDK、Maven/Gradle、Git服務器環境配置&a…

FastAdmin按鈕類功能全解析 class 屬性定義不同的交互行為

在 FastAdmin 中&#xff0c;超鏈接的 class 屬性用于定義不同的交互行為和樣式。以下是常見 class 配置的用途和區別&#xff1a; btn-dialog 用于觸發彈出對話框行為。點擊帶有此 class 的鏈接或按鈕時&#xff0c;FastAdmin 會自動加載指定的 URL 內容并在模態框中顯示。通…

python3字典對象實現解析

文章目錄 前言Raymond的方案字典結構字典創建字典插入插入空字典PyDictKeysObject的創建設置索引存儲entry 插入非空字典調整大小字典查找聯合字典插入 字典查詢字典刪除 前言 本來以為python字典的實現就是一個哈希表的普通實現&#xff0c;所以在學習基本類型時沒去仔細研究…

Word2Vec介紹

前言 當今的大語言模型非常智能&#xff0c;但是你有沒有想過這些事情&#xff1a; 機器是怎么理解“國王”和“王后”之間的關系&#xff1f; “貓”和“狗”是怎么在 AI 中“相似以及區分”的&#xff1f; 文本又是怎么變成模型能讀懂的數字&#xff1f; 這一切&#xf…

Rsync+sersync實現數據實時同步(小白的“升級打怪”成長之路)

目錄 一、rsync部署 push推數據 1、編寫rsync配置文件 2、備份測試 3、檢驗結果 二、rsyncsersync 實現數據實時同步 1、安裝sersync服務 2、檢驗結果 pull拉取數據 1、編寫rsync配置文件 2、檢驗結果 三、腳本編寫 1、客戶端腳本編寫 2、服務器腳本編寫 一、rsy…

用 python 開發一個可調用工具的 AI Agent,實現電腦配置專業評價

在人工智能時代&#xff0c;AI Agent憑借其強大的任務處理能力&#xff0c;逐漸成為開發人員手中的得力工具。今天&#xff0c;我們就來一起動手&#xff0c;用Python打造一個能夠調用工具的AI Agent&#xff0c;實現根據電腦信息對電腦配置進行專業評價的功能。 一、項目創建…

WSL 安裝使用和常用命令

參考官方使用說明&#xff1a; https://learn.microsoft.com/zh-cn/windows/wsl/ 安裝wsl: wsl --install --no-distribution --no-distribution&#xff1a;安裝 WSL 時不要安裝分發版 更新 wsl: wsl --update 設置wsl 默認版本&#xff1a; wsl --set-default-version <…

720全景VR拍攝制作實戰教程

720全景VR拍攝制作實戰教程 720全景VR拍攝制作是近年來興起的一種沉浸式影像制作技術。它通過多角度拍攝&#xff0c;并將畫面拼接成一個全景視角&#xff0c;使觀眾獲得身臨其境的觀看體驗。本教程將帶你從準備階段到拍攝階段&#xff0c;再到后期處理階段&#xff0c;一步步…

什么真正的云原生開發?如何區別本地開發后部署到云端?

以下是關于云原生開發的深度解析&#xff0c;以及與本地開發后遷移上云的本質區別&#xff1a; 一、真正的云原生開發&#xff1a;從理念到實踐的全面革新 1. 定義與核心思想 云原生開發是一種以云計算能力為核心的架構設計和開發方法論&#xff0c;其本質是讓應用從誕生之初…

從代碼學習深度學習 - 詞的相似性和類比任務 PyTorch版

提示:文章寫完后,目錄可以自動生成,如何生成可參考右邊的幫助文檔 文章目錄 前言加載預訓練詞向量TokenEmbedding 類詳解預訓練詞向量簡介 (GloVe)具體含義總結建議應用預訓練詞向量詞相似度knn 函數get_similar_tokens 函數相似詞查找示例詞類比get_analogy 函數詞類比任務…

ubuntu 22.04 安裝部署elk(elasticsearch/logstash/kibana) 7.10.0詳細教程

安裝部署elk7.10.0詳細教程 一、安裝jdk 11環境二、安裝elasticsearch 7.10.0三、安裝kibana 7.10.0四、安裝logstash 7.10.0五、安裝ik7.10.0分詞六、開啟安全功能1. 開啟用戶名密碼登錄2. 開啟es安全加密通信3. 開啟Kibana安全功能 七、注意事項和常見錯誤八、其它操作及命令…

技術文章: 基板的吸水率

PCB基板或覆銅板的吸水率是一個重要的性能指標&#xff0c;它衡量了覆銅板在特定條件下&#xff08;通常是浸水后&#xff09;吸收水分的能力&#xff0c;通常用指定條件下吸水后與吸水前相比&#xff0c;質量增加的百分比來表示。當材料暴露扎起在潮濕空氣中或浸沒在水中時其抵…

九日集訓第三天

目錄 搜索旋轉排序數組 搜索旋轉排序數組|| 尋找旋轉排序中的數組最小值 爬樓梯 斐波那契數 第N個泰波那契數 差的絕對值為K的數對數目 猜數字 拿硬幣 山峰數組的峰頂索引 搜索旋轉排序數組 class Solution { public:int search(vector<int>& nums, int t…