如何獲取注解中的值_如何在運行時利用注解信息

注解( annontation )是 Java 1.5 之后引入的一個為程序添加元數據的功能。注解本身并不是魔法,只是在代碼里添加了描述代碼自身的信息,至于如何理解和使用這些信息,則需要專門的解析代碼來負責。

本文首先介紹注解的基本知識,包括注解的分類和運用時的領域知識。隨后,給出一個通過的在運行時解析注解的框架代碼,介紹處理注解的一般思路。最后,通過現實世界里使用注解的例子,來加深對注解的實用性方面的認識。

注解的基本知識

注解作為程序中的元數據,其本身的性質也被其上的注解所描述。

剛剛我們提到,理解和使用注解信息,需要專門的解析代碼。其中,Java 的編譯器和虛擬機也包含解析注解信息的邏輯,而它們判斷一個注解的性質,就是依賴注解之上的元注解。

能夠注解一個注解的注解就是元注解,Java 本身能夠識別的元注解有以下幾個。

@Retention

Retention 注解的相關定義如下

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

首先我們看到它自己也被幾個元注解包括自身所注解,因此在注解的源頭有一個類似于自舉的概念,最終觸發自舉的是編譯器和源代碼中的先驗知識。

再看到 Retention 注解的值,是一個注解保留性質的枚舉,包括三種情況。

  1. SOURCE 表示注解信息僅在編譯時保留,在編譯之后就被丟棄,這樣的注解為代碼的編譯提供原信息。例如常用的 @Override 注解就提示 Java 編譯器進行重寫方法的檢查。
  2. CLASS 表示注解信息保留在字節碼中,但在運行時不可見。這是注解的默認行為,如果定義注解時沒有使用 Retention 注解顯式表明保留性質,默認的保留性質就是這個。
  3. RUNTIME 表示注解信息在運行時可見,當然,也就必須保留在字節碼中。

SOURCE 標注的注解通常稱為編譯期注解,Lombok 項目提供大量的編譯期注解,以幫助開發者簡寫自己的代碼。例如 @Setter 注解注解在類上時,在編譯期由 Lombok 的注解處理器處理,為被注解的類的每一個字段生成 Setter 方法。

編譯期的注解需要專門的注解處理器來處理,并且在編譯時指定處理器的名字提示編譯期使用該處理器進行處理。技術上說,編譯期處理注解和運行時處理注解完全是兩個概念的事情。本文主要介紹運行時處理注解的技術,關于編譯期處理注解的資料,可以參考這篇 ANNOTATION PROCESSING 101 的文章以及 Lombok 的源碼。

CLASS 性質雖然是默認的保留性質,但實際使用中幾乎沒有采用這一保留性質的。準確需要這一性質的情形應該是某些專門的字節碼處理框架,大多數時候使用這一性質的注解僅僅是在編譯期使用,使用 SOURCE 足以,且使用 SOURCE 還可以減少字節碼文件的大小。

本文介紹運行時處理注解的技術,所有在運行時可見的注解都需要顯式地標注 @Retention(RetentionPolicy.RUNTIME) 注解。CLASS 和 RUNTIME 性質的注解都會出現在字節碼中。編譯器將注解信息寫成字節碼時,通過為 CLASS 性質的注解賦予 RuntimeInvisibleAnnotations 屬性,為 RUNTIME 性質的注解賦予 RuntimeVisibleParameterAnnotations 來提示虛擬機在運行時加載的時候區別對待。

運行時,我們可以調用被注解對象的相應方法取得其上的注解,具體手段在【注解解析的框架代碼】一節中介紹。

@Target

上一節最后我們提到,注解有不同的注解對象,這正是 Target 注解加入的元數據,其定義如下

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {ElementType[] value();
}public enum ElementType {TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE,ANNOTATION_TYPE,PACKAGE,TYPE_PARAMETER,TYPE_USE,MODULE
}

Target 元注解的信息解釋了一個注解能夠被注解在什么位置上,或者說能夠接受該注解的對象集合。一個注解可以有多種類型的注解對象,所有這些對象類型存在 ElementType 枚舉中。

大多數枚舉值的含義就是字面含義,值得一提的取值包括

  • TYPE 在 Java 中指類、接口、注解或者枚舉類
  • TYPE_PARAMETER 在 Java 1.8 中被引入,指的是泛型中的類型參數
  • TYPE_USE 在 Java 1.8 中被引入,指的是所有可以出現類型的位置,具體參考 Java 語言標準的對應章節

常見的 Override 注解只能注解在方法上,Spring 框架中的 Component 注解只能注解在類型上。SuppressWarnings 注解能注解在除了本地變量和類型參數以外的幾乎所有地方,Spring 框架中的 Autowired 注解也能注解在字段、構造器、方法參數和注解等多種位置上。

@Inherited

Inherited 主要用來標注注解在類繼承關系之間的傳遞關系。它本身不攜帶自定義信息,僅作為一個布爾信息存在,即是或者不是 Inherited 的注解。

標注 Inherited 元注解的注解,標注在某個類型上時,其子類也默認視為標注此注解。或者換個方向說,獲取某個類的注解時,會遞歸的搜索其父類的注解,并獲取其中標注 Inherited 元注解的注解。注意,標注 Inherited 元注解的注解在子類上也標注時,子類上的注解優先級最高。

技術上說,可以通過 getAnnotationsgetDeclaredAnnotations 的區別來獲取確切標注在當前類型上的注解和按照上面描述的方法查找的注解。另一個值得強調的是這種繼承僅發生在類的繼承上,實現接口并不會導致標注 Inherited 元注解的注解的傳遞。

值得注意的是,注解本身是不能繼承的。為了實現類似繼承的效果,開發者們從基于原型的繼承找到靈感,采用本節后續將講到的組合注解技術來達到注解繼承的目的。

@Repeatable

Repeatable 注解在 Java 1.8 中被引入,主要是為了解決相同的注解只能出現一次的情況下,為了表達實際中需要的相同注解被標注多次的邏輯,開發者不得不首先創建出一個容器注解,然后使用者在單個和多個注解的情況下分別使用基礎注解和容器注解的繁瑣邏輯。具體例子如下

@ComponentScan(basePackages = "my.package")
class MySimpleConfig { }@ComponentScans({ @ComponentScan(basePackages = "my.package") @ComponentScan(basePackages = "my.another.package")
})
class MyCompositeConfig { }

有了 Repeatable 注解,從注解處理方,代碼不會精簡,仍然需要分開處理兩種注解類型,但是使用方就可以精簡代碼。例如上面 MyCompositeConfig 的標注可以變為

@ComponentScan(basePackages = "my.package") 
@ComponentScan(basePackages = "my.another.package")
class MyCompositeConfig { }

對應的注解定義為

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {// ...
}@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface ComponentScans {ComponentScan[] value();
}

對于注解的處理方,重復注解會在背后由 Java 編譯器轉化為容器注解的形式傳遞。就上面的例子而言,無論有沒有 Repeatable 注解,MyCompositeConfig 在獲取注解時,都會獲取到 ComponentScans 注解及其 ComponentScan[] 形式的元數據信息。

值得注意的是,重復注解和容器注解不能同時存在,即在標記了 @Repeatable(ComponentScans.class) 之后,ComponentScansComponentScan 不能同時標注同一個對象。

@Documented

這個注解沒有太多好說的,注解信息在生成文檔時默認是不會留存的。如果使用此注解標注某個注解,那么被標注的注解注解的對象的文檔會顯示它被對應的注解所標注。

組合注解

嚴格來說,組合注解是一種設計模式而不是語言特性。

由于注解無法繼承,例如 Spring 框架中具有 "is-a" 關系的 Service 注解和 Component 注解,無法通過繼承將 Service 定義為 Component 的特例。但是在實際使用的時候,又確實有表達這樣 "is-a" 關系的需求。

在框架代碼中,無法窮盡對下游項目擴展注解實質上的繼承關系的情況,但是又需要支持下游項目自定義框架注解的擴展。如何將下游項目自定義的注解和框架注解之間的繼承關系表達出來,就是一個技術上實際的需求。

為了解決這個問題,開發者們注意到在注解設計之初,留下了注解能夠標注注解的路徑。這一路徑使得我們可以采用一種類似基于原型的繼承的方式,通過遞歸獲取注解上的注解來追溯注解的鏈條,從而類似原型鏈上找父類的方式找到當前注解邏輯上繼承的注解。

這一技術在 Spring 框架中被廣泛使用,例如 Service/Repository/Controller 等注解組合了 Component 注解,從而在下一節的注解解析的框架代碼中能夠作為 Component 的某種意義上的子注解被識別,同時在需要時取出繼承的注解的元數據信息。

注解解析的框架代碼

Java 語言提供的方法

注解解析最基礎的手段是通過 Java 語言本身提供的方法。哪怕是其他框架增強注解解析的功能,最終也需要依賴基本方法的支持。

運行時獲取注解信息,可想而知是通過反射的手段來獲取的。Java 為被注解的元素定義了一個 AnnotatedElement 的接口,通過這一接口的方法可以在運行時取得被注解元素之上的注解。該接口的實現類是運行時通過反射拿到的元素里面能夠被注解的類。

我們先看到這一接口提供的方法。

public interface AnnotatedElement {<T extends Annotation> T getAnnotation(Class<T> annotationClass);Annotation[] getAnnotations();<T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass);Annotation[] getDeclaredAnnotations();<T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass);<T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass);boolean isAnnotationPresent(Class<? extends Annotation> annotationClass);
}

這些方法沒必要一個一個講,其實可以簡單地分成兩類

  • 獲取被注解對象上聲明的注解,即 getDeclaredAnnotations 系列的方法
  • 獲取被注解對象所擁有的注解,即 getAnnotations 系列的方法,比起上一類,額外包括 @Inherited 的注解

最后 isAnnotationPresent 方法僅僅是一個判斷標簽式注解的簡易方法,內容只有一行。

default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {return getAnnotation(annotationClass) != null;
}

我們可以通過 Java 語言自身的 AnnotationSupport#getIndirectlyPresent 方法來看看怎么用這套基礎支持解析注解。

private static <A extends Annotation> A[] getIndirectlyPresent(Map<Class<? extends Annotation>, Annotation> annotations,Class<A> annoClass
) {Repeatable repeatable = annoClass.getDeclaredAnnotation(Repeatable.class);if (repeatable == null)return null;  // Not repeatable -> no indirectly present annotationsClass<? extends Annotation> containerClass = repeatable.value();Annotation container = annotations.get(containerClass);if (container == null)return null;// Unpack containerA[] valueArray = getValueArray(container);checkTypes(valueArray, container, annoClass);return valueArray;
}

以上這段代碼是在 Java 1.8 引入 Repeatable 注解后,由于默認的會將重復的 Repeatable 的注解在獲取時直接合并成容器注解,為了提供一個方便的按照基礎注解來獲取注解信息的手段提供的方法。

我們看到,傳入的內容包括一個根據 Class 對象查找實現類對象的映射,這個是被注解類所取得的所擁有的注解的類到實例的字典,不用過多關注。另一方面 annoClass 則是我們想要獲取的基礎注解的類。

例如,annoClass 為上面提過的 Spring 的 ComponentScan 類,對于僅注解了 ComponentScans 的類來說,以 ComponentScan.class 作為參數調用 getDeclaredAnnotationsByType 方法一路走到上面這個方法里,代碼邏輯將會看到 ComponentScan 標注了 @Repeatable(ComponentScans.class) 注解,從而在 annotations 映射里查找 ComponentScans 注解的信息,并將它轉換為 ComponentScan 的數組返回。

Spring 解析注解的方案

Spring 解析注解的核心是 MergedAnnotation 接口及相關的工具類。

Spring 框架重度使用了注解來簡化開發的復雜度。對于具體的某一個或某幾個注解,圍繞它展開的代碼散布在其邏輯鏈條的各處。但是,Spring 的注解處理的特別之處就在于它定義了 MergedAnnotation 接口,并支持了基于組合注解和 AliasFor 的注解增強機制。

AliasFor 注解的解析非常簡單,就是查看當前注解或者 targetAnnotation 注解里面相應名稱的注解。在 5.2.7.RELEASE 版本中,其解析邏輯基本在 AnnotationTypeMapping#resolveAliasTarget 方法里,最終組裝出來的 AnnotationTypeMapping 對象能夠在獲取屬性值的時候顯示處理了 AliasFor 之后的屬性值。

下面我們展開說一下如何遞歸解析組合注解。

為了支持前面提到的組合注解,即注解上的注解的遞歸查找,Spring 中提供了 AnnotationUtils#findAnnotation 系列方法來做查詢,區別于 AnnotationUtils#getAnnotation 的單層查找。

Spring 對這個查找邏輯的演化花了很多心思。

在最新的 Spring 5.2.7.RELEASE 版本中,這兩個方法都對 AnnotatedElement 構造了 MergedAnnotation 實例,在最終查找的時候通過不同的謂詞策略來做篩選。構造 MergedAnnotation 實例的過程經由幾個工廠函數之后構造出一個 TypeMappedAnnotations 的實例,調用其上的 get 方法構造出實際的 MergedAnnotation 對象,這個對象就是對要查找的注解遞歸查找的結果。

相關邏輯為了定制各種策略變得非常復雜,我們從 4.3.8.RELEASE 版本入手,查看在復雜的定制引入之前,這一查找過程核心邏輯的實現框架。

Annotation[] anns = clazz.getDeclaredAnnotations();
for (Annotation ann : anns) {if (ann.annotationType() == annotationType) {return (A) ann;}
}
for (Annotation ann : anns) {if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) {A annotation = findAnnotation(ann.annotationType(), annotationType, visited);if (annotation != null) {return annotation;}}
}

無論后期代碼演化得再復雜,其核心還是一個遞歸查找的過程,也就是以上的代碼。

  1. 首先,獲取當前的類上的注解,注意這里的類可以是一個注解類,如果此次獲取的注解就包含了我們要查找的注解,那么直接返回。
  2. 如果沒有包含,對剛才取得的注解遞歸的查找。注意這里有一個類似于深度優先搜索的 visited 集合。這是因為有些注解可以以自己為目標,導致出現遞歸查找的自環。典型的例如 Java 自帶的元注解 Retention 也被自己所注解。
  3. 如果深度優先搜索窮盡之后沒有得到結果,則返回空。

可以看到,上面的邏輯中對 Repeatable 和 Inherited 等元注解的復雜組合情況沒有定制的邏輯,而是采用了一些默認的硬編碼策略。

最新版本的 Spring 之所以變得相當復雜,有一部分代碼量是為了解決搜索的不同策略以及跟進新版 Java 的注解特性。另一部分,注意到上述邏輯在獲取注解時沒有關心 AliasFor 注解的邏輯,在早期版本中這是由 AnnotationUtils 中的一個全局靜態映射來管理的。在最新版本中,產生 MergedAnnotation 時將構造并維護一個本地的 alias 映射。

現實世界的注解解析

上一節介紹了處理注解的兩個通用套路,背后的思想是基礎的注解信息獲取和遞歸的注解信息獲取。本節我們將從現實世界的注解解析入手,介紹實際項目里面特定的注解是如何被解析的。

Flink

@RpcTimeout

Flink 采用類似 RMI 的方式來進行遠程調用,為了避免無限阻塞,方法調用時可以傳遞一個超時參數。本地攔截遠端調用的動作時,從方法的簽名中反射取得標注 RpcTimeout 的參數,將它作為超時參數傳遞到實際的方法調用過程中,以在超過限定時間時返回超時異常而非阻塞等待遠端調用的返回。

取得標注 RpcTimeout 的參數的邏輯代碼展開如下

final Annotation[][] parameterAnnotations = method.getParameterAnnotations();for (int i = 0; i < parameterAnnotations.length; i++) {for (Annotation annotation : parameterAnnotations[i]) {if (annotation.annotationType().equals(RpcTimeout.class)) {if (args[i] instanceof Time) {return (Time) args[i];} else {throw new RuntimeException(/* ... */)}}}
}return defaultTimeout;

可以看到,是針對先驗知識能得知的可能出現該注解的位置進行遍歷獲取。其實,所有的注解解析代碼都遵循這樣的模式,這也是最基礎的模式。

JUnit 4

@Test

JUnit 4 測試框架的用戶最熟悉的就是 Test 注解了。不同于上一節提到的基礎解析和遞歸解析,JUnit 4 的 Test 注解有一個特殊的場景需要支持,即在獲取當前類的所有待測試方法時,獲取到父類中的 Test 標注的方法。

這是因為我們常常把相似的測試的配置和基礎測試方法抽成抽象基類,在根據不同的實現場景實現不同的測試子類。雖然類似的功能可以用 Parameterized Runner 和 Parameter 注解來實現,但是 Parameter 的方案只能支持參數化字段,如果測試方法是有和沒有的區別而不是參數的不同,子類是比使用 Parameter 向量并加入 Enable 開關更好的解決方案。

總之,JUnit 4 支持查找父類中標注 Test 的其他方法,此邏輯實現如下。

// TestClass#scanAnnotatedMembers
for (Class<?> eachClass : getSuperClasses(clazz)) {for (Method eachMethod : MethodSorter.getDeclaredMethods(eachClass)) {addToAnnotationLists(new FrameworkMethod(eachMethod), methodsForAnnotations);}// ensuring fields are sorted to make sure that entries are inserted// and read from fieldForAnnotations in a deterministic orderfor (Field eachField : getSortedDeclaredFields(eachClass)) {addToAnnotationLists(new FrameworkField(eachField), fieldsForAnnotations);}
}// TestClass#addToAnnotationLists
for (Annotation each : member.getAnnotations()) {Class<? extends Annotation> type = each.annotationType();List<T> members = getAnnotatedMembers(map, type, true);T memberToAdd = member.handlePossibleBridgeMethod(members);if (memberToAdd == null) {return;}if (runsTopToBottom(type)) {members.add(0, memberToAdd);} else {members.add(memberToAdd);}
}

其實也很簡單,在初始化 TestClass 時遍歷測試的候選類及其父類的所有方法和字段,將它們的注解信息存在一個注解類型到被注解對象的列表的映射中。后續需要查找的時候從該映射查找,即可查找到標注對應注解的所有方法或字段。

@RunWith

另一個常見的注解是 RunWith 注解,用于標注運行測試時采用自定義的 Runner 實現。其代碼如下

for (Class<?> currentTestClass = testClass; currentTestClass != null;currentTestClass = getEnclosingClassForNonStaticMemberClass(currentTestClass)) {RunWith annotation = currentTestClass.getAnnotation(RunWith.class);if (annotation != null) {return buildRunner(annotation.value(), testClass);}
}

可以看到,是從內到外層層查找的形式。注意這里沒有去查找父類的 RunWith 注解,這是由于 RunWith 注解本身被 @Inherited 所標注,調用 Java 提供的基礎方法獲取類的注解時已經做了相應的處理。

Spring

@SpringBootApplication

SpringBootApplication 可以說是最好的解釋 Spring 中重度使用組合注解的例子了。對于這一注解的解析,我們甚至不需要或者說不能列舉出任何解析代碼,因為 SpringBootApplication 從來沒有作為它自己被解析。該注解的定義如下

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {@AliasFor(annotation = EnableAutoConfiguration.class)Class<?>[] exclude() default {};@AliasFor(annotation = EnableAutoConfiguration.class)String[] excludeName() default {};@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")String[] scanBasePackages() default {};@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")Class<?>[] scanBasePackageClasses() default {};@AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;@AliasFor(annotation = Configuration.class)boolean proxyBeanMethods() default true;
}

這里有兩件事情值得關注,分別對應介紹 Spring 的注解解析框架的時候指出的 Spring 的兩個關鍵的增強

  1. 實際使用 SpringBootApplication 時,Spring 框架的解析代碼是通過 findAnnotation 查找其組合的注解來實現具體功能的。
  2. SpringBootApplication 通過 AliasFor 支持用戶在使用該注解時覆蓋其所組合的注解的屬性。

從這里我們也看出,組合注解僅僅是一種形式上相關聯的組合,與任一形式的繼承不同,不會以某種形式繼承屬性。

@Autowired

Autowired 可以說是 Spring 框架中使用最為廣泛的注解之一了,它和 Value 注解以及 JSR-330 的 Inject 注解一起組成了注入 Bean 的核心手段。

Autowired 的處理邏輯在 AutowiredAnnotationBeanPostProcessor 中,即 Bean 被創造和加載之后的一個后處理邏輯或者成為裝飾邏輯。其中涉及到 Autowired 等注解的地方主要是篩選出需要為目標注入 Bean 的候選。

首先,在初始化的時候,會將對應的 Autowired 系列注解保存到 autowiredAnnotationTypes 集合字段中。

隨后,當 Bean 處理框架調用后處理邏輯時,調用后處理器的 findAutowiringMetadata 方法,通過標記型注解找到需要 Autowired 的候選。整個過程通過反射將被 Autowired 注解的對象及 Autowired 注解中持有的是否必須( required )的信息保存到 InjectElement 中。

再之后,對獲取到的所有 InjectElement 調用 inject 方法進行注入。根據不同的被注入對象,注入的邏輯有所不同。例如,對于字段的注入,由 AutowiredFieldElement 對象處理,從 BeanFactory 中根據依賴關系初始化 Bean 并將 Bean 賦值給字段。

這一套邏輯支持了 Bean 注入最常用的字段注入的功能,以及運行配置方法的功能。

Autowired 注解還能被用在參數和構造函數上,其中參數上的標注目前僅用于在 JUnit Jupiter 框架測試時使用,而構造函數的標注廣泛替代了直接標注字段的用法,其代碼路徑存在于 AbstractAutowireCapableBeanFactory 創建 Bean 實例的時候。從結果來說,標注在構造函數的 Autowired 能夠將參數對應類型的 Bean 作為構造函數的實參,調用構造函數以構造出對象。

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

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

相關文章

mysql5.7運行按鈕_MySQL 5.7.* 啟動問題

mysql-5.7.10-winx64啟動 mysqld.exe&#xff0c;報錯&#xff1a;mysqld: Could not create or access the registry key needed for the MySQL applicationto log to the Windows EventLog. Run the application with sufficientprivileges once to create the key, add the …

控制元素的div屬性

1、需求分析 改變元素的寬、高、顏色、顯示、重置等屬性。 2、技術分析 基礎的css、html、js 3、詳細分析 如圖&#xff0c;單擊按鈕&#xff0c;改變元素屬性: 3.1 HTML部分 根據視圖不難發現&#xff0c;內容分兩大不分:按鈕欄和效果圖&#xff0c;所以設置兩個div。 <…

使用JMeter和Yourkit進行REST / HTTP服務的性能分析

我的上一篇文章描述了如何使用JMeter完成異步REST / HTTP服務的壓力測試或負載測試。 但是&#xff0c;運行這樣的測試通常表明被測系統不能很好地應對不斷增加的負載。 現在的問題是如何找到瓶頸&#xff1f; 深入研究代碼以檢測可疑部分可能是另一種選擇。 但是考慮到潛在的…

EasyUI 加載時需要顯示和隱藏 panel(面板)內容破版問題

1.當頁面加載的完成時,如果面板中的內容是未加載的如果把他的狀態從隱藏變為顯示會導致破版,頁面信息顯示不全 2.這時需要刷新面板$(#id).panel(open).panel(refresh); 3.它就會重新加載轉載于:https://www.cnblogs.com/eagle-xie/p/6892706.html

java中間件_90%的Java程序員,都扛不住這波消息中間件的面試四連炮!

概述大家平時也有用到一些消息中間件(MQ)&#xff0c;但是對其理解可能僅停留在會使用API能實現生產消息、消費消息就完事了。對MQ更加深入的問題&#xff0c;可能很多人沒怎么思考過。比如&#xff0c;你跳槽面試時&#xff0c;如果面試官看到你簡歷上寫了&#xff0c;熟練掌握…

python 取array并集_Python內置數據結構原理與性能簡易分析

ins ngladc文末左下方閱讀原文指向了本人博客鏈接&#xff0c;不含廣告。參考資料中的相關鏈接&#xff0c;可以在博客文章的最下方獲取。推薦蘋果手機用戶使用淺色模式觀看。前言 對于一些算法題&#xff0c;可以使用Python自帶的內置函數解決。但很多時候用就用了&#xff0c…

ae合成復制腳本_稀缺資源—這幾個AE腳本使用頻率很高,趕緊收藏吧!

「第442期」毫無疑問&#xff0c;AE已經成為目前制作短視頻比較主流的軟件&#xff0c;效果的多樣化深受很多創作者的喜愛。隨著對軟件的熟悉&#xff0c;越發覺得AE主要是基于多圖層控制的軟件。如果制作一些簡單的效果&#xff0c;幾個圖層幾個濾鏡就可以搞定&#xff0c;但如…

android activity and fragment活動周期

1.狀態 /* 每個活動一共有四種狀態 *&#xff1a;1。運行狀態&#xff0c;就是棧頂的那個 * 2。暫停狀態&#xff1a;就是不處于棧頂&#xff0c;但是依然可見&#xff0c;比如對話框下面的界面 * 3。停止狀態&#xff1a;不處于棧頂&#xff0c;并且不可見 * 4。銷毀狀態 * */…

html css基礎知識

1 這是自己學習html時候做的一些記錄&#xff0c;供大家參考 <!--2 塊和內聯3 塊元素:獨占一行的元素4 div p h ul5 div沒有任何語義&#xff0c;就是一個純粹的快元素6 就是為了方便布局7 …

番石榴的ListenableFuture

Guava中的ListenableFuture試圖為Future對象定義一致的API&#xff0c;以注冊完成回調。 通過在Future完成時添加回調的功能&#xff0c;我們可以異步有效地響應傳入的事件。 如果您的應用程序與許多將來的對象高度并發&#xff0c;我強烈建議您盡可能使用ListenableFuture 。 …

程序員的幸福感和頸椎病

脖子一直疼&#xff01; 去醫院檢查&#xff0c;拍片子的醫生在造影室里沖我喊&#xff1a; “小伙子&#xff0c;你多大年紀啦&#xff1f;” 我說&#xff1a;“我三十來歲&#xff0c;咋啦” 醫生說&#xff1a;“怎么這么年輕就得這種病啊&#xff01;” 我當時腿就有點軟&…

python實現詞語相似度計算分析_相似度計算的方法及Python實現

現實生活中&#xff0c;我們經常提到距離這個詞&#xff0c;本文談的相似度就是基于距離定義的&#xff0c;當兩個向量之間的距離特別小時&#xff0c;就說這倆個向量相似度高&#xff0c;反之相似度不高。所以&#xff0c;衡量相似度的指標就是距離度量。經常使用的相似度計算…

poll函數_I/O復用 - 三組I/O復用函數的比較

在之前的文章中 I/O復用 - epoll 和 I/O復用 - select&poll 中我們討論了三組I/O復用的系統調用&#xff0c;這3組系統調用都能同時監聽多個文件描述符。它們將等待由timeout參數指定的超時時間&#xff0c;直到一個或多個文件描述符上有事件發生時返回&#xff0c;返回值是…

HTML適應手機瀏覽器寬度

在網頁的<head>中增加以上這句話&#xff0c;可以讓網頁的寬度自動適應手機屏幕的寬度: <meta name"viewport" content"widthdevice-width, initial-scale1.0, minimum-scale0.5, maximum-scale2.0, user-scalableyes" /> <meta name&q…

css3畫圖那些事(三角形、圓形、梯形等)

閑來無事&#xff0c;寫寫圖形。當時鞏固一下css3吧.。前端小白&#xff0c;寫的不好還請前輩多指教。 三角形 { width: 0;height: 0;border-bottom: 140px solid red ;border-right: 70px solid transparent;border-left: 70px solid transparent; } 圓形 {width: 0px;height…

MyBatis教程– CRUD操作和映射關系–第1部分

CRUD操作 MyBatis是一個SQL Mapper工具&#xff0c;與直接使用JDBC相比&#xff0c;它極大地簡化了數據庫編程。 步驟1&#xff1a;創建一個Maven項目并配置MyBatis依賴項。 <project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema…

Java開發人員的升級之路

第一部分&#xff1a;對于參加工作一年以內的同學。恭喜你&#xff0c;這個時候&#xff0c;你已經擁有了一份Java的工作。這個階段是你成長極快的階段&#xff0c;而且你可能會經常加班。但是加班不代表你就可以松懈了&#xff0c;永遠記得我說的那句話&#xff0c;從你入行那…

docker 數據庫 mysql_在Docker中體驗數據庫之MySql

在上一篇在Docker中體驗數據庫之Mongodb之后&#xff0c;這次記錄一下在docker中安裝mysql。過程要比Mongodb麻煩一點……參考網址&#xff1a;https://dev.mysql.com/doc/refman/5.7/en/linux-installation-docker.htmlhttps://hub.docker.com/r/mysql/mysql-server/安裝過程如…

STL概覽——棧( stack )、隊列( queue )和優先級隊列( priority_queue)

棧&#xff08;stack&#xff09; stack是一種先進后出&#xff08;First In Last Out&#xff0c;FILO&#xff09;的數據結構&#xff0c;它只有一個口&#xff0c;平常在我們寫深度優先遍歷算法時&#xff0c;&#xff0c;就會用到棧&#xff0c;stack允許我們增加&#xff…

使用JMeter對異步HTTP / REST服務進行壓力/負載測試

盡管我一直在使用JMeter進行Web應用程序的壓力測試和負載測試好幾次&#xff0c;但我們還是花了一些時間才弄清楚如何使用該工具測試基于異步HTTP / REST的服務。 在我們這里&#xff0c;我是指一名程序員&#xff0c; Holger Staudacher &#xff0c;我很榮幸能與當前的一個項…