Java注解學習記錄

目錄

一、為什么要學注解?

二、注解是什么?

三、為什么要使用注解?

四、注解的作用

五、注解的分類

5.1 元注解

@Retention(/ r??ten?(?)n /) ★★★★★

@Target ★★★★★

@Inherited(/ ?n?her?t?d /)?★★

@Repeatable(/ r??pi?t?bl /)?★★

@Documented(/?d?kjument?d /)?★

5.2 標準注解

5.3 自定義注解

六、使用反射操作注解

七、注解的底層實現-動態代理

八、總結:注解工作流程


一、為什么要學注解?

????????在日常開發中,基本都是在使用別人定義或是各種框架的注解,比如Spring框架中常用的一些注解:@Controller、@Service、@RequestMapping,以此來實現某些功能,但是卻不知道如何實現的,所以如果想學習這些框架的實現原理,那么注解就是我們必知必會的一個點。其次,可以利用注解來自定義一些實現,比如在某個方法上加一個自定義注解,就可以實現方法日志的自動記錄打印,這樣也可以展現足夠的逼格。所以如果你想走上人生巔峰,更好的利用框架,又或者想要高一點的逼格,從團隊中突出,那么學習注解都是前提。


二、注解是什么?

????????在Java中注解其實就是寫在接口、類、屬性、方法上的一個標簽,或者說是一個特殊形式的注釋,注解在代碼運行時是可以被反射讀取并進行相應的操作,而如果沒有使用反射或者其他檢查,那么注解是沒有任何真實作用的,也不會影響到程序的正常運行結果。

? ????????舉個例子:@Override就是一個注解,它的作用是告訴閱讀者(開發人員、編譯器)這個方法重寫了父類的方法,對于開發人員只是一個標志,而編譯器則會多做一些事情,編譯器如果發現方法標注了這個注解,就會檢查這個方法到底是不是真的覆寫了父類的方法,如果沒有那就是在欺騙他的感情,甭廢話,編譯時直接給你報個錯,不留情面的那種。而如果不添加@Override注解,程序也是可以正常運行的,不過缺乏了靜態的檢查,本來是想覆寫父類的hello方法的,卻寫成了he110方法,這就會有些尷尬了。

在spring框架中的注解會影響到程序的運行,是因為spring內部使用反射操作了對應的注解。

?????????上面的說法是為了方便理解的,那么下面來個稍微正式一點的:注解是提供一種為程序元素設置元數據的方法,理解起來還是一樣的,程序元素就是指接口、類、屬性、方法,這些都是屬于程序的元素,那啥叫元數據呢?

????????元數據就是描述數據的數據(data about data),舉個簡單的例子,系統上有一個sm.png文件,這個文件才是我們真正需要的數據本身,而這個文件的屬性則可以稱之為sm.png的元數據,是用來描述png文件的創建時間、修改時間、分辨率等信息的,這些信息無論是有還是沒有都不影響它作為圖片的性質,都可以使用圖片軟件打開。

  • 元數據是添加到程序元素如方法、字段、類和包上的額外信息,注解就是一種載體形式
  • 注解不能直接干擾程序代碼的運行

三、為什么要使用注解?

? ????????以Spring為例,早期版本的Spring是通過XML文件的形式對整個框架進行配置的,一個縮減版的配置文件如下

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"><!-- 配置事物管理器 --><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/></bean><!-- 配置注解驅動事物管理 --><tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

????????在xml文件中可以定義Spring管理的Bean、事物切面等,話說當年非常流行xml配置的。優點呢就是整個項目的配置信息集中在一個文件中,從而方便管理,是集中式的配置。缺點也顯而易見,當配置信息非常多的時候,配置文件會變得越來越大不易查看管理,特別是多人協作開發時會導致一定的相互干擾。

? ????????現在都提倡解耦、輕量化或者說微小化,那么注解就順應了這一需求,各個包或模塊在內部方法或類上使用注解即可實現指定功能,而且使用起來灰常方便,簡單易懂。缺點呢就是不方便統一管理,如果需要修改某一類功能,則需要整體搜索逐個修改,是分散式的存在各個角落。

? ????????這里擴充一下,Spring注解替代了之前Spring xml文件,是不是說Spring的xml也是一種元數據呢?對的,Spring的配置文件xml也是元數據的一種表現形式。不過xml的方式是集中式的元數據,不需要和代碼綁定的,而注解是一種分散式的元數據設置方式。


四、注解的作用

????????根本來說注解就是一個注釋標簽。開發者的視角可以解讀出這個類/方法/屬性的作用以及該怎么使用,而從框架的視角則可以解析注解本身和其屬性實現各種功能,編譯器的角度則可以進行一些預檢查(@Override)和抑制警告(@SuppressWarnings)等。

  • 作為特定標記,用于告訴編譯器一些信息
  • 編譯時動態處理,如動態生成代碼
  • 運行時動態處理,作為額外信息的載體,如獲取注解信息

五、注解的分類

? 通常來說注解分為以下三類

  • 元注解 – Java內置的注解,標明該注解的使用范圍、生命周期等。
  • 標準注解 – Java提供的基礎注解,標明過期的元素,標明是復寫父類方法的方法,標明抑制警告。
  • 自定義注解 – 第三方定義的注解,含義和功能由第三方來定義和實現。

5.1 元注解

????????用于定義注解的注解,通常用于注解的定義上,標明該注解的使用范圍、生效范圍等。元XX 都代表最基本最原始的東西,因此,元注解就是最基本不可分解的注解,我們不能去改變它只能使用它來定義自定義的注解元注解包含以下五種: @Retention、@Target、@Documented、@Inherited、@Repeatable,其中最常用的是@Retention和@Target下面分別介紹一下這五種元注解。

@Retention(/ r??ten?(?)n /) ★★★★★

????????中文翻譯為保留的意思,標明自定義注解的生命周期

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {RetentionPolicy value();
}

? ????????從編寫Java代碼到運行主要周期為:源文件→ Class文件 → 運行時數據,@Retention則標注了自定義注解的信息要保留到哪個階段,分別對應的value取值為:SOURCE →CLASS→RUNTIME

  • SOURCE 源代碼java文件,生成的class文件中就沒有該信息了
  • CLASS class文件中會保留注解,但是jvm加載運行時就沒有了
  • RUNTIME 運行時,如果想使用反射獲取注解信息,則需要使用RUNTIME,反射是在運行階段進行反射的
    ?

value取值為:SOURCE

value取值為:CLASS

各個生命周期的用途:

  • Source:一個最簡單的用法,就是自定義一個注解例如@ThreadSafe,用來標識一個類時線程安全的,就和注釋的作用一樣,不過更引人注目罷了。
  • Class:這個有啥用呢?個人覺得主要是起到標記作用,還沒有做實驗,例如標記一個@Proxy,JVM加載時就會生成對應的代理類。
  • Runtime:反射實在運行階段執行的,那么只有Runtime的生命周期才會保留到運行階段,才能被反射讀取,也是我們最常用的。
@Target ★★★★★

????????中文翻譯為目標,描述自定義注解的使用范圍,允許自定義注解標注在哪些Java元素上(類、方法、屬性、局部屬性、參數…)

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {ElementType[] value();
}

????????value是一個數組,可以有多個取值,說明同一個注解可以同時用于標注在不同的元素上。value的取值如下

說明
TYPE類、接口、注解、枚舉
FIELD屬性
MEHOD方法
PARAMETER方法參數
CONSTRUCTOR構造函數
LOCAL_VARIABLE局部變量(如循環變量、catch參數)
ANNOTATION_TYPE注解
PACKAGE
TYPE_PARAMETER泛型參數 jdk1.8
TYPE_USE任何元素 jdk1.8

????????示例:自定義一個注解@RetentionTest,使用@Target注解定義該注解只能在類和方法上使用,使用在屬性上時會提示錯誤。

@Inherited(/ ?n?her?t?d /)?★★

? ? ? ? 標志是否可以被標注類的子類繼承被@Inherited修飾的注解是具有繼承性的在自定義的注解標注到某個類時,該類的子類會繼承這個自定義注解

????????注意:只有當子類繼承父類的時候,注解才會被繼承。類實現接口,或者接口繼承接口,都是無法獲得父接口上的注解聲明的。

????????正確的示例如下(通過反射獲取注解)

@Repeatable(/ r??pi?t?bl /)?★★

????????是否可以重復標注。這個注解其實是一個語法糖,jdk1.8之前也是有辦法進行重復標注的,就是使用數組屬性(自定義注解會講到)。下面給一個例子,雖然我們標注的是多個@MyAnnotation,其實會給我們返回一個@MyAnnotations,相當于是Java幫我們把重復的注解放入了一個數組屬性中,所以只是一個語法糖而已。

@Documented(/?d?kjument?d /)?★

????????是否在生成的JavaDoc文檔中體現,被標注該注解后,生成的javadoc中,會包含該注解,這里就不做演示了。

5.2 標準注解

標準注解有一下三個

  • @Override 標記一個方法是覆寫父類方法
  • @Deprecated 標記一個元素為已過期,避免使用
  • ? 支持的元素類型為:CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE
  • @SuppressWarnings 不輸出對應的編譯警告
@SuppressWarnings(value = {"unused", "rawtypes"})
public class StandardTest extends Parent {@Overridepublic void hello() {System.out.println("StandardTest hello");}@Deprecatedpublic void walk() {}
}
5.3 自定義注解

????????注解定義格式

public @interface 注解名 {修飾符 返回值 屬性名() 默認值;修飾符 返回值 屬性名() 默認值;
}

? ????????首先注解的修飾符一般是public的,定義注解一般都是要給三方使用的,不是public的又有什么意義呢?定義的類型使用@interface,可以猜出來和接口是有一些說不清道不明的關系的,其實注解就是一個接口,在程序運行時,JVM會為其生成對應的代理類

? ????????然后內部的定義,這個有點四不像,說是方法吧它還有一個默認值,說它是屬性吧它的后面還加了一個括號,我個人還是喜歡稱之為帶默認返回值的接口方法,通過后面的學習我們會進一步認識它的真面目。內部的修飾符只能是public的,即使不寫也默認是public的,因為它本質上就是一個接口,而接口方法的默認訪問權限就是pubilc的

????????? 注解是不能繼承也不能實現其他類或接口的,本身就是一個元數據了,確實沒什么必要。

返回值支持的類型如下

  • 基本類型 int float boolean byte double char logn short
  • String
  • Class
  • Enum
  • Annotation
  • 以上所有類型的數組類型

????????定義一個簡單的接口示例

// 保留至運行時
@Retention(RetentionPolicy.RUNTIME)
// 可以加在方法或者類上
@Target(value = {ElementType.TYPE,ElementType.METHOD})
public @interface RequestMapping {public String method() default "GET";public String path();public boolean required();
}

????????接下來我們來看下它到底是不是一個接口,首先編譯一下該注解javac RequestMapping.java生成對應的RequestMapping.class文件,然后對其進行反編譯,輸出如下

// ...
①public interface RequestMapping extends java.lang.annotation.Annotation//...②public abstract java.lang.String method();descriptor: ()Ljava/lang/String;flags: ACC_PUBLIC, ACC_ABSTRACTAnnotationDefault:③default_value: s#7public abstract java.lang.String path();descriptor: ()Ljava/lang/String;flags: ACC_PUBLIC, ACC_ABSTRACTpublic abstract boolean required();descriptor: ()Zflags: ACC_PUBLIC, ACC_ABSTRACT
}
//...

① 從這里可以看到,注解的本質就是一個接口,并且繼承了java.lang.annotation.Annotation

② ③這里驗證了上面所說的,內部的定義其實就是一個帶默認值的方法


六、使用反射操作注解

? ????????反射可以獲取到Class對象,進而獲取到Constructor、Field、Method等實例,點開源碼結構發現Class、Constructor、Field、Method等均實現了AnnotatedElement接口,AnnotatedElement接口的方法如下

// 判斷該元素是否包含指定注解,包含則返回true
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
// 返回該元素上對應的注解,如果沒有返回null
<T extends Annotation> T getAnnotation(Class<T> annotationClass);
// 返回該元素上的所有注解,如果沒有任何注解則返回一個空數組
Annotation[] getAnnotations();
// 返回指定類型的注解,如果沒有返回空數組
T[] getAnnotationsByType(Class<T> annotationClass)
// 返回指定類型的注解,如果沒有返回空數組,只包含直接標注的注解,不包含inherited的注解
T getDeclaredAnnotation(Class<T> annotationClass)
// 返回指定類型的注解,如果沒有返回空數組,只包含直接標注的注解,不包含inherited的注解
T[] getDeclaredAnnotationsByType
// 返回該元素上的所有注解,如果沒有任何注解則返回一個空數組,只包含直接標注的注解,不包含inherited的注解
Annotation[] getDeclaredAnnotations();

這就說明以上元素均可以通過反射獲取該元素上標注的注解。

個完整的示例

// package-info.java
@AnyAnnotation(order = 0, desc = "包")
package demo.annotation.reflect;// AnyAnnotation.java
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.PACKAGE, ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.FIELD,ElementType.LOCAL_VARIABLE, ElementType.PARAMETER})
public @interface AnyAnnotation {int order() default 0;String desc() default "";
}// ReflectAnnotationDemo.java
@AnyAnnotation(order = 1, desc = "我是類上的注釋")
public class ReflectAnnotationDemo {@AnyAnnotation(order = 2, desc = "我是成員屬性")private String name;@AnyAnnotation(order = 3, desc = "我是構造器")public ReflectAnnotationDemo(@AnyAnnotation(order = 4, desc = "我是構造器參數") String name) {this.name = name;}@AnyAnnotation(order = 45, desc = "我是方法")public void method(@AnyAnnotation(order = 6, desc = "我是方法參數") String msg) {@AnyAnnotation(order = 7, desc = "我是方法內部變量") String prefix = "I am ";System.out.println(prefix + msg);}public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException {Class<ReflectAnnotationDemo> clazz = ReflectAnnotationDemo.class;// 獲取包上的注解,聲明在package-info.java文件中Package packagee = Package.getPackage("demo.annotation.reflect");printAnnotation(packagee.getAnnotations());// 獲取類上的注解Annotation[] annotations = clazz.getAnnotations();printAnnotation(annotations);// 獲取成員屬性注解Field name = clazz.getDeclaredField("name");Annotation[] annotations1 = name.getAnnotations();printAnnotation(annotations1);//獲取構造器上的注解Constructor<ReflectAnnotationDemo> constructor = clazz.getConstructor(String.class);AnyAnnotation[] annotationsByType = constructor.getAnnotationsByType(AnyAnnotation.class);printAnnotation(annotationsByType);// 獲取構造器參數上的注解Parameter[] parameters = constructor.getParameters();for (Parameter parameter : parameters) {Annotation[] annotations2 = parameter.getAnnotations();printAnnotation(annotations2);}// 獲取方法上的注解Method method = clazz.getMethod("method", String.class);AnyAnnotation annotation = method.getAnnotation(AnyAnnotation.class);printAnnotation(annotation);// 獲取方法參數上的注解Parameter[] parameters1 = method.getParameters();for (Parameter parameter : parameters1) {printAnnotation(parameter.getAnnotations());}// 獲取局部變量上的注解/*** 查了一些資料,是無法獲取局部變量的注解的,且局部變量的注解僅保留到Class文件中,運行時是沒有的。* 這個更多是給字節碼工具使用的,例如lombok可以嵌入編譯流程,檢測到有對應注解轉換成相應的代碼,* 而反射是無法進行操作的。當然也可以利用asm等工具在編譯器完成你要做的事情*/}public static void printAnnotation(Annotation... annotations) {for (Annotation annotation : annotations) {System.out.println(annotation);}}
}

@AnyAnnotation的Retention中的生命周期改為SOURCE/CLASS試試,這時就獲取不到任何注解信息了哦


七、注解的底層實現-動態代理

首先準備一下測試代碼,如下

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Learn {// 默認值為"default"public String name() default "default";// 必須填寫public int age();
}
@Learn(age = 18)
public class LearnAnnotationReflect {// 獲取LearnAnnotationReflect類的Class對象public static void main(String[] args) {Class<LearnAnnotationReflect> reflectClass = LearnAnnotationReflect.class;// 判斷LearnAnnotationReflect類是否有Learn注解if (!reflectClass.isAnnotationPresent(Learn.class)) {return;}// 獲取LearnAnnotationReflect類的Learn注解Learn learn = reflectClass.getAnnotation(Learn.class);// 輸出Learn注解的name屬性System.out.println(learn.name());// 輸出Learn注解的age屬性System.out.println(learn.age());}
}

????????在System.out.println(learn.name());打一個斷點,以Debug模式運行,查看learn這個對象到底是什么

? ????????從上面的截圖可以看出,jdk為Learn生成了一個叫$Proxy1的代理對象,并且包含了一個內部成員AnnotationIvocationHandler,接下來就是調用$Proxy1.name()進行獲取name的值,那么我們來看下$Proxy1到底是一個什么樣的對象,在jdk8中可以添加JVM參數-Dsun.misc.ProxyGenerator.saveGeneratedFiles來保存代理類,更高版本可以使用-Djdk.proxy.ProxyGenerator.saveGeneratedFiles=true來保存代理類。在Idea中的設置方法如下

???????重新運行程序,就會發現在項目根目錄多了如下類,其中$Proxy1就是Learn注解對應的代理類

當我們調用Learn.name()時,其實就是調用這個代理類的name方法,如下

    public final String name() throws  {try {return (String)super.h.invoke(this, m3, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}

代理類的name方法中主要是調用h的invoke方法傳入當前對象,以及m3這個方法元素,m3如下

m3 = Class.forName("demo.annotation.runtime.Learn").getMethod("name");

? ????????在5.3講解的內容時,我們反編譯了注解的class文件,知道在編譯注解時,實際上編譯為了一個接口,接口中定義了想干的屬性的方法。

? 那么基本的流程我們就可以梳理出來了:

  1. 通過反射我們可以獲取對應元素上的注解@Learn,前面說過注解本質是一個接口,也就是獲取到了Learn接口的代理對象。
  2. Learn代理對象提供了相應的同名方法,內部聲明了原注解的相應方法Method,如method3
  3. 之后通過代理對象父類的h成員屬性,也就是AnnotationInvocationHandler去執行invoke方法
  4. AnnotationInvocationHandler在初始化時,會包含一個memberValues的map,key就是方法名,value就是對應的屬性值,在invoka內部通過Method的name從memberValues中獲取到對應的值并返回

?? 接下來我們來看下AnnotationInvocationHandler中的invoke方法相關信息,如下

// 當前注解類型
private final Class<? extends Annotation> type;
// 當前注解的相關屬性集合,key是方法名,value是對應的值
private final Map<String, Object> memberValues;// jdk會將對應的屬性信息傳過來
AnnotationInvocationHandler(Class<? extends Annotation> var1, Map<String, Object> var2) {this.type = var1;this.memberValues = var2;}public Object invoke(Object var1, Method var2, Object[] var3) {// 方法名String var4 = var2.getName();// 參數類型Class[] var5 = var2.getParameterTypes();// 如果是equals方法,則調用對應方法if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {return this.equalsImpl(var3[0]);// 不是equals方法,卻有參數,說明是錯誤的,注解的方法是不允許有參數的} else if (var5.length != 0) {throw new AssertionError("Too many parameters for an annotation method");} else {// 定義一個變量var7 默認值-1byte var7 = -1;// 不同的方法賦予var7不同的值switch(var4.hashCode()) {case -1776922004:if (var4.equals("toString")) {var7 = 0;}break;case 147696667:if (var4.equals("hashCode")) {var7 = 1;}break;case 1444986633:if (var4.equals("annotationType")) {var7 = 2;}}// 返回對應方法的處理switch(var7) {case 0:return this.toStringImpl();case 1:return this.hashCodeImpl();case 2:return this.type;// 默認方法, 也就是我們自定義的屬性方法default:// 從map集合中獲取對應的值Object var6 = this.memberValues.get(var4);if (var6 == null) {throw new IncompleteAnnotationException(this.type, var4);} else if (var6 instanceof ExceptionProxy) {throw ((ExceptionProxy)var6).generateException();} else {if (var6.getClass().isArray() && Array.getLength(var6) != 0) {var6 = this.cloneArray(var6);}return var6;}}}}

八、總結:注解工作流程

  • 通過鍵值對的形式為注解屬性賦值
  • 編譯器檢查注解的使用范圍 (將注解信息寫入元素屬性表 attribute)
  • 運行時JVM將單個Class的runtime的所有注解屬性取出并最終存入map里
  • 創建AnnotationInvocationHandler實例并傳入前面的map
  • JVM使用JDK動態代理為注解生成代理類,并初始化處理器
  • 調用invoke方法,通過傳入方法名返回注解對應的屬性值

面向大佬編程:Java注解入門到精通,這一篇就夠了_java 注解-CSDN博客

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

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

相關文章

43.安卓逆向2-補環境-使用unidbg(使用Smali語法調用方法和使用方法地址調用方法)

免責聲明&#xff1a;內容僅供學習參考&#xff0c;請合法利用知識&#xff0c;禁止進行違法犯罪活動&#xff01; 內容參考于&#xff1a;圖靈Python學院 工具下載&#xff1a; 鏈接&#xff1a;https://pan.baidu.com/s/1bb8NhJc9eTuLzQr39lF55Q?pwdzy89 提取碼&#xff1…

【Kubernetes知識點問答題】Pod 調度

1. 如何將特定 Pod 調度到指定的節點&#xff1f;可以使用下列方法中的任何一種來選擇 K8s 對特定 Pod 的調度&#xff1a;① 與節點標簽匹配的 nodeSelector&#xff1a;在 Pod 的規范中使用 nodeSelector 字段來指定節點標簽&#xff0c;以便將 Pod 調度到具有特定標簽的節點…

wordpress顯示時間日期的幾種常見的方式

在WordPress中&#xff0c;顯示時間日期有多種常見方式&#xff0c;包括使用默認設置、模板標簽、插件等&#xff0c;以下是詳細介紹&#xff1a; 使用默認設置 WordPress的默認設置允許你在文章列表中顯示文章的發布時間。登錄到WordPress后臺&#xff0c;在“設置”中找到“…

基于飛算JavaAI實現布隆過濾器防止緩存穿透:原理、實踐與全流程解析

引言&#xff1a;當緩存失效時&#xff0c;系統如何避免“雪崩式崩潰”&#xff1f; 在互聯網高并發場景中&#xff08;如電商秒殺、社交平臺熱點新聞&#xff09;&#xff0c;緩存是提升系統性能的核心手段——將頻繁訪問的數據&#xff08;如商品詳情、用戶信息&#xff09;存…

DeepResearch開源與閉源方案對比

在這個AI不再只是聊天工具的時代&#xff0c;"深度研究"已經成為大語言模型&#xff08;LLM&#xff09;的一項新能力。先進的LLM不再只是給出快速的一次性回答&#xff0c;而是可以像研究助手一樣工作——搜索網上信息&#xff0c;調用各種工具&#xff08;比如搜索…

UniApp 頁面傳參方式詳解

在 UniApp 開發中&#xff0c;頁面間參數傳遞是核心功能之一。以下是 8 種常用的傳參方式&#xff0c;每種方式都有其適用場景和特點&#xff1a;一、URL 拼接傳參&#xff08;最常用&#xff09; 適用場景&#xff1a;簡單數據傳遞&#xff0c;如 ID、狀態值等基礎類型數據 實…

音頻分類標注工具

pyqt 分類標注工具&#xff1a;import glob import sys import json import os from PyQt5.QtWidgets import (QApplication, QMainWindow, QTableWidget, QTableWidgetItem,QSplitter, QVBoxLayout, QWidget, QPushButton, QRadioButton,QButtonGroup, QLabel, QHBoxLayout, Q…

云計算-Kubernetes+Istio 實現金絲雀發布:流量管理、熔斷、流量鏡像、ingreess、污點及pv案例實戰

介紹 在微服務架構中,如何安全、高效地實現服務發布與流量管理是保障業務穩定性的核心挑戰。金絲雀發布(Canary Release)、灰度發布等策略通過精細化的流量控制,可有效降低新版本上線風險, Istio 作為主流的服務網格(Service Mesh)工具。 此次Istio 在 Kubernetes 集群…

12.web api 3

定時器-間歇函數

ComfyUI進階:EchoMimic插件全解析,讓靜態肖像實現音頻驅動的精準口型動畫

在數字內容創作中&#xff0c;讓靜態肖像“開口說話”并做出自然表情&#xff0c;是提升交互感與沉浸感的關鍵。傳統動畫制作需專業人員逐幀調整口型與表情&#xff0c;成本高且效率低。ComfyUI的EchoMimic插件通過音頻驅動技術&#xff0c;實現了“輸入音頻→自動生成匹配口型…

鏈式前向星、vector存圖

場景設定 想象你是一個社交達人&#xff0c;要記錄你和所有朋友的關系&#xff08;這就是“圖”&#xff09;。每個朋友是一個節點&#xff0c;關系是一條邊。你需要快速回答&#xff1a;“我有哪些朋友&#xff1f;”&#xff08;遍歷鄰居&#xff09;。方式1&#xff1a;鏈式…

YAML 中定義 List 的幾種方式

在 YAML 配置文件中定義 List 并在 Spring 應用中注入是非常常見的操作&#xff0c;下面詳細介紹具體寫法和注入方式。一、YAML 中定義 List 的幾種方式1. 縮進式寫法&#xff08;推薦&#xff09;最常用的方式&#xff0c;通過短橫線 - 加空格表示列表項&#xff1a;yaml# app…

C# 反射和特性(自定義特性)

自定義特性 你或許已經注意到了&#xff0c;應用特性的語法和之前見過的其他語法有很大不同。你可能會覺得特 性是一種完全不同的結構類型&#xff0c;其實不是&#xff0c;特性只是一種特殊的類。 有關特性類的一些要點如下。 用戶自定義的特性類叫作自定義特性。所有特性類都…

科目二的四個電路

一.K21電動機單連續運轉接線(帶點動控制)1.電路圖2.主線路這可很明了,是一條直線,從上接到下就OK了,然后從熱繼電器出來,接到SB3按鈕的常閉觸點上接著往下走一端接到SB2的常閉觸點上,接著往下走&#xff0c;走到接觸器的線圈上,從L2借一條火線出來,從熔斷器的上端接入,另一端接…

【位運算】查詢子數組最大異或值|2693

本文涉及知識點 位運算、狀態壓縮、枚舉子集匯總 3277. 查詢子數組最大異或值 給你一個由 n 個整數組成的數組 nums&#xff0c;以及一個大小為 q 的二維整數數組 queries&#xff0c;其中 queries[i] [li, ri]。 對于每一個查詢&#xff0c;你需要找出 nums[li…ri] 中任…

HTML DOM 方法

HTML DOM 方法 引言 HTML DOM&#xff08;文檔對象模型&#xff09;是HTML文檔的編程接口&#xff0c;它允許開發者通過JavaScript來操作HTML文檔中的元素。DOM 方法是DOM編程的核心&#xff0c;它提供了豐富的操作手段來改變網頁的結構、樣式和行為。本文將詳細介紹HTML DOM中…

w嵌入式分享合集68

自己的原文哦~ https://blog.51cto.com/whaosoft/14133002 一、一鍵開關機電路的設計方案 方案一&#xff1a;電路圖 一鍵開關機電路分析如下&#xff1a; 電路工作流程如下&#xff1a; Key按下瞬間&#xff0c;Q2、Q1導通&#xff0c;7805輸入電壓在8.9V左右&…

FFmpeg QoS 處理

FFmpeg 中的 QoS (服務質量) 處理主要關注于實時流媒體傳輸中的時序控制、丟幀策略和網絡適應等方面。以下是 FFmpeg 中 QoS 相關的關鍵機制和配置方法。1. 基本 QoS 機制丟幀策略 (Frame Dropping)cAVDictionary *options NULL; av_dict_set(&options, "framedrop&q…

TexStudio中的Latex,PDFLatex,XeLatex和LuaLatex的區別

多種LaTeX編譯器一、多種LaTeX編譯器 1.1 PDFLaTeX&#xff08;1994年&#xff09; 默認、最常用的引擎。 輸入文件通常是 ASCII 或 UTF-8 編碼&#xff08;但中文需要 CJK 宏包或 ctex 宏包支持&#xff09;。 字體選擇受限&#xff1a;只能使用 TeX 自帶的字體或者 Type 1…

容器化部署:用Docker封裝機器翻譯模型與服務詳解

文章目錄一、機器翻譯容器化的技術棧選型1.1 為什么需要容器化MT模型&#xff1f;1.2 基礎鏡像選擇對比1.3 典型依賴分層方案1.4 性能對比&#xff08;容器化 vs 原生部署&#xff09;二、關鍵部署模式2.1 輕量級API服務封裝2.2 模型熱更新策略三、Docker鏡像構建3.1 編寫Docke…