Java注解技术

1. 注解的简介

从JDK 5开始,Java增加了对元数据(MetaData)的支持,也就是注解(Annotation)。

注解就是代码里的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。

注解提供了一种为程序元素(包、类、构造器、方法、成员变量等)设置元数据的方法,从某些方面来看,注解就像修饰符一样,可用于修饰包、类、构造器、方法、成员变量、参数、局部变量。

通过使用注解,开发人员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息,代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证或进行部署。

值得指出的是,如果希望让程序中的注解在编译时起作用,只有通过某种配套的工具对注解中的信息进行访问和处理。访问和处理注解的工具统称为APT,即Annotation Processing Tool。可以将此APT注解处理工具理解成一套用于获取并处理注解的程序代码。没有使用APT注解处理工具处理过的注解,在程序中是不会起任何作用的。

另外注解除了用来描述注解这一概念之外,它还表示一个接口。Java代码中,通过@interface定义的注解类型都默认实现了Annotation接口,通过反射获取的注解就是一个Annotation对象。

package java.lang.annotation;
public interface Annotation {// 返回注解对象的实际类型(即是哪个Annotation接口的实现类)Class<? extends Annotation> annotationType(); 
}

在Java代码中,对于可以接受注解的程序元素,需要实现AnnotatedElement接口。如Class(类)、Constructor(构造器)、Field(成员变量)、Method(成员方法)、Package(包),它们都实现了AnnotatedElement接口。

2. 自定义注解

2.1 语法

//定义注解类型@元注解
...
@元注解
public @interface 注解名称 {//定义成员变量成员变量类型 成员变量名() [default 默认值]成员变量类型 成员变量名() [default 默认值]...
}
  1. 使用@interface关键字定义注解类型

  2. 定义注解类型时,还可以通过一个或多个元注解进行修饰

  3. 注解类型可以定义成员变量,也可以不定义成员变量

  4. 成员变量的类型只能是:8种基本数据类型、String、Class、注解类型、枚举类型、以及这些类型的一维数组类型

  5. 成员变量可以通过default关键字设置默认值,设置了默认值的成员变量,在使用注解时可以省略不赋值。

注意: 如果定义注解时,定义了一个名为value的成员变量,并且使用此注解时只需要为此value成员变量赋值,即:

1. 只定义了成员变量`value`
2. 除了`value`之外的其他成员变量在定义时都设置了默认值

如上两种情况下,则使用该注解时可以直接在该注解后的圆括号里指定成员变量value的值,无需使用value=变量值的形式。

此时,若value表示的是一个数组类型,并且指定值时只需要指定一个元素值,则可以不用加花括号。

2.2 自定义注解示例

//1. 定义无成员变量的注解类型
public @interface Annotation0 {
}//2. 定义有成员变量的注解类型
public @interface Annotation1 {String name();int age();  String[] children();Class[] clazzes();
}//3. 定义有成员变量的注解类型,并给部分成员变量设置了默认值
public @interface Annotation2 {String name();int age();String city() default "shenzhen";
}//4. 定义存在一个value成员变量的注解。
//因为成员变量name设置了默认值,所有使用此注解时,可以只给value成员变量赋值
public @interface Annotation3 {String[] value();String name() default "19";
}//5. 定义被多个元注解修饰的注解类型
@Documented  //使用此元注解修饰的注解Annotation4将被javadoc工具提取成文档
@Target({ElementType.FIELD,ElementType.METHOD}) //使用此元注解修饰Annotation4后,Annotation4注解只能用来修饰指定的程序元素:成员变量和方法。
@Retention(RetentionPolicy.RUNTIME) //使用此元注解修饰的Annotation4,可以保留到运行时。
public @interface Annotation4 {String name();
}

2.3 使用注解示例

通过@ + 注解类型名 +赋值列表(可省略)的方法使用注解:

@Annotation0
public void method(){
}@Annotation1(name="zengk", age=26, children={"aa", "bb"}, clazzes={Object.class, TestAnnotation1.class})
public void method(){
}//没有对存在默认值的city成员变量赋值
@Annotation2(name="zengk", age=26)
public void method(){
}//@Annotation3(value={"xx","yy"})
@Annotation3({"xx","yyy"})
public void method(){
}//@Annotation3(value={"yy"}) 此方式也可以
//@Annotation3(value="yy") 此方式也可以
//@Annotation3({"yy"}) 此方式也可以
@Annotation3("yy")
public void method(){
}

3. 标记注解&元数据注解

根据注解在定义时是否有成员变量,可以把注解分为两类:

1. 标记注解: 没有定义成员变量的Annotation类型被称为标记。这种注解仅利用自身的存在与否来提供信息。如@Override

2. 元数据注解: 定义了成员变量的Annotation类型,因为他们可以接受更多的元数据,所以也被称为元数据注解。

3.1 元注解

元注解(Meta Annotation)就是用来修饰注解的注解

3.1.1 @Retention

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

@Retention只能用于修饰Annotation类型,用于指定被修饰的Annotation可以保留多长时间。@Retention元注解只定义了一个RetentionPolicy枚举类型的value成员变量,value成员变量的值只有如下三个:

RetentionPolicy.CLASS:编译器把注解信息记录在class文件中,但不会保留到运行时期。因此运行时不可获取注解信息。这是默认值。RetentionPolicy.RUNTIME:编译器把注解信息记录在class文件中,且会保留到运行时期。因此运行时可以通过反射获取注解信息。RetentionPolicy.SOURCE:注解信息只保留在源代码中,编译器直接丢弃这种注解信息。

如果需要通过反射获取某个注解的元数据,则必须使用@Retention(RetentionPolicy.RUNTIME)来修饰此注解类型。

3.1.2 @Target

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

@Target只能用于修饰Annotation类型,用于指定被修饰的Annotation能用于修饰哪些程序元素。@Target元注解只定义了一个ElementType[]数组类型的value成员变量(其中ElementType是枚举类型),value成员变量的值可以是如下几个:

ElementType.TYPE:	指定该策略的Annotation只能修饰类,接口(包括注解类型),枚举定义。
ElementType.FIELD:	指定该策略的Annotation只能修饰成员变量。
ElementType.METHOD:	指定该策略的Annotation只能修饰方法定义。
ElementType.PARAMETER:	指定该策略的Annotation只能修饰参数。
ElementType.CONSTRUCTOR:	指定该策略的Annotation只能修饰构造方法。
ElementType.LOCAL_VARIABLE:	指定该策略的Annotation只能修饰局部变量。
ElementType.ANNOTATION_TYPE:	指定该策略的Annotation只能修饰Annotation。
ElementType.PACKAGE:	指定该策略的Annotation只能修饰包定义。

3.1.3 @Documented

package java.lang.annotation;@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}

@Documented用于指定被该元注解修饰的Annotation类将被javadoc工具提取成文档,如果定义Annotation类时使用了@Documented修饰,则所有使用该Annotation修饰的程序元素的API文档中将会包含该Annotation说明

3.1.4 @Inherited

package java.lang.annotation;@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

@Inheritd用于指定被它修饰的Annotation将具有继承性。如果定义Annotation类时使用了@Inherited修饰,那么当使用该Annotation修饰了某个类,则这个类的子类也将自动被此Annotation修饰。

4. Java中提供的基本Annotation介绍

4.1 @Override

package java.lang;import java.lang.annotation.*;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

@Override用来修饰方法,强制类中的方法必须是重写了父类中的方法。当一个类中的某个方法使用@Override修饰后,则必须保证此类的父类或父接口中定义了一个被该方法重写的方法,否则会编译报错。使用@Override可以避免重写方法时的笔误。

4.2 @Deprecated

package java.lang;import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}

@Deprecated用于表示某个程序元素已过时。当程序元素(如成员变量,方法等)被@Deprecated修饰后的,调用这些程序元素时,编译器将会给出警告。

4.3 @SuppressWarnings

package java.lang;import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {String[] value();
}

@SuppressWarnings修饰某个程序元素后,对于作用在此程序元素上的@SuppressWarning指定的编译器警告会被取消掉。如果某个程序元素被@SuppressWarnings修饰,那么该程序元素范围内的其他程序元素也会被此@SuppressWarnings修饰。例如当一个类被@SuppressWarnings修饰,此类中的某个方法又被另一个@SuppressWarnings修饰,那么对于这两个@SuppressWarnings指定的所有编译器警告,作用在此方法上时都会被取消。

5. 提取注解信息

对于用@Retention(RetentionPolicy.RUNTIME)修饰的运行时注解,可以通过反射提取Annotation中的元数据。由于是运行时才处理注解信息,并且使用了大量的反射技术,所以效率会比较慢。

对于用@Retention(RetentionPolicy.CLASS)@Retention(RetentionPolicy.SOURCE)修饰的注解,需要在编译时处理,此时不能使用反射技术,需通过javax.annotation.processing包下的Processor接口来实现一个注解处理器(Annotation Processor),一般采用继承AbstractProcessor的方式定义一个注解处理器。通过使用注解处理器来处理和提取Annotation信息。

5.1 提取运行时注解信息

上文已经提到过,在Java中,通过Annotation对象表示一个注解。所以只有得到注解的Annotation对象才能提取此注解中的元数据。

那么怎么得到Annotation对象呢?上文也有提到,对于可以接受注解的程序元素,需要实现AnnotatedElement接口,而其中的AnnotationElement接口就提供了获取Annotation对象的方法。所以只需要得到AnnotatedElement的实现类对象,就能获取到Annotation对象,从而提取Annotation中的元数据。

AnnotatedElement的实现类,在上文中也有提到,就是ClassMethodFieldConstructor等这些程序元素在Java中对应的类。于是,我们可以通过反射获取到ClassConstructorMethodField这些类的对象。

通过反射提取Annotation中的元数据的步骤总结如下:

1. 通过反射得到Class、Constructor、Method、Field这些实现了AnnotationElement接口的类的对象。2. 通过AnnotationElement实现类对象调AnnotationElement提供的API,获取注解对应的Annotation对象。3. 通过Annotation对象获取到注解的元数据。
5.1.1 AnnotationElement接口的API
/**
* 判断该程序元素上是否存在指定注解类型的注解,存在返回true,否则返回false。
*/
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass);/**
* 返回此程序元素上存在的、指定注解类型的Annotation对象。如果该注解类型的注解不存在,则返回null。
*/
<T extends Annotation> T getAnnotation(Class<T> annotationClass);/**
* 返回该程序元素上存在的所有注解
*/
Annotation[] getAnnotations();/**
* 返回直接修饰该程序元素的所有注解
*/
Annotation[] getDeclaredAnnotations();
5.1.2 代码示例

通过反射处理运行时注解,实现AndroidButton按键的点击事件绑定

1. 定义注解类型

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ButtonAnnotation {int resId();Class<? extends View.OnClickListener> listenClazz();
}

2. 定义注解处理工具

public class ButtonAnnotationProcessTools {public static void process(Object obj){//获取使用了注解的程序元素所在的类的class对象Class clazz = obj.getClass();//因为@ButtonAnnotation只能修饰成员变量,//所以只需变量此类中的成员变量,找到被@ButtonAnnotation修饰的成员变量Field[] declaredFields = clazz.getDeclaredFields();for (Field field : declaredFields) {//因为后面需要给@ButtonAnnotation修饰的成员变量赋值,//所以对于私有的成员变量,需获取访问权限field.setAccessible(true);//判断成员变量是否被@ButtonAnnotation修饰boolean annotationPresent = field.isAnnotationPresent(ButtonAnnotation.class);if (annotationPresent) {//获取修饰此成员变量的ButtonAnnotation注解对象ButtonAnnotation buttonAnnotation = field.getAnnotation(ButtonAnnotation.class);try {Class fieldType = field.getType();//对Button类型的成员变量,通过注解@ButtonAnnotation中的元数据对其进行赋值和设置点击事件监听if (fieldType.equals(Button.class) && obj instanceof MainActivity){Activity activity = (Activity) obj;//通过@ButtonAnnotation中的资源ID得到view对象View view = activity.findViewById(buttonAnnotation.resId());//通过@ButtonAnnotation中的监听器Class对象,利用反射得到View.OnClickListener对象Class<? extends View.OnClickListener> listenClazz = buttonAnnotation.listenClazz();View.OnClickListener onClickListener =  listenClazz.newInstance();view.setOnClickListener(onClickListener);//给@ButtonAnnotation修饰的成员变量赋值field.set(obj, view);}} catch (Exception e) {e.printStackTrace();}}}}
}

3. 使用注解对Button类型的成员变量进行赋值并绑定点击事件监听器

public class MainActivity extends AppCompatActivity{@ButtonAnnotation(resId = R.id.btn, listenClazz = ButtonOnClickListener.class)private Button mBtn;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//使用注解处理工具对@ButtonAnnotation进行处理ButtonAnnotationProcessTools.process(this);}class ButtonOnClickListener implements View.OnClickListener{@Overridepublic void onClick(View v) {Log.d(TAG, "onclick");}}
}

5.2 提取编译时注解信息

step1. 继承AbstractProcessor类,处理注解

AbstractProcessor实现了Processor接口,继承AbstractProcessor重写process方法处理注解信息

/**
* 在 javax.annotation.processing 包下提供了 AbstractProcessor抽象类
*/
public abstract class AbstractProcessor implements Processor/**
* 重写此方法。返回此注解处理器APT 处理的注解类型
* 返回Set集合包含要处理的注解类型,String表示注解类名(最好使用全路径名)
* Java1.7 以后也可以不重写此方法,通过注解@SupportedAnnotationTypes指定处理的注解类型,
* @SupportedAnnotationTypes的目标程序元素是类,即在自定义的AbstractProcessor子类上使用
*/
public Set<String> getSupportedAnnotationTypes()/**
* 重写此方法,返回处理注解时采用的Java版本
* Java1.7以后也可以不重写此方法,通过注解@SupportedSourceVersion指定Java版本
* @SupportedAnnotationTypes也是作用在自定义的AbstractProcessor子类上
*/
public SourceVersion getSupportedSourceVersion()/**
* 通常不需要重写此方法
*/
public synchronized void init(ProcessingEnvironment processingEnv)/**
* 重写此方法,处理注解
*/
public abstract boolean process(Set<? extends TypeElement> annotation, RoundEnvironment roundEnv);
5.2.1 相关API

javax.annotation.processing.RoundEnvironment

public interface RoundEnvironment/**
* 获取被注解a修饰的Element集合,此处返回的Element表示的程序元素是类
*/
Set<? extends Element> getElementsAnnotatedWith(Class<? extends Annotation> a);

javax.lang.model.element.Element

Element表示程序元素,可以是包、类、构造器、方法、成员变量等

public interface Element extends javax.lang.model.AnnotatedConstruct/**
* 获取此Element表示的程序元素的名字,如当表示类时,返回的是类名
*/
Name getSimpleName();/**
* 获取修饰此程序元素的注解,参数annotationType指定要获取的注解类型
*/
<A extends Annotation> A getAnnotation(Class<A> annotationType);/**
* 返回此Element所表示的程度元素的类型,如包、类、构造器、方法、成员变量等
*/
ElementKind getKind();/**
* 返回此程序元素范围内包含的程序元素,如类中包含有构造器,方法,成员变量
*/
List<? extends Element> getEnclosedElements();

javax.lang.model.element.ElementKind

public enum ElementKind {PACKAGE,/*--------------Declared types------------*/ENUM,  //An enum typeCLASS,  //A class not described by a more specific kind (like ENUM)ANNOTATION_TYPE,  //An annotation typeINTERFACE,  //An interface not described by a more specific kind (like ANNOTATION_TYPE)/*-------------Declared Variables--------------*/ENUM_CONSTANT,  //An enum constantFIELD,  //A field not described by a more specific kind (like ENUM_CONSTANT)PARAMETER,  //A parameter of a method or constructorLOCAL_VARIABLE, //A local variableEXCEPTION_PARAMETER,  //A parameter of an exception handler/*------------Declared Executables--------------*/METHOD,  //A methodCONSTRUCTOR,  //A constructorSTATIC_INIT,  //A static initializerINSTANCE_INIT,  //An instance initializer/** A type parameter. */TYPE_PARAMETER,  //A type parameterOTHER,  //An implementation-reserved element. This is not the element you are looking for.RESOURCE_VARIABLE;  //A resource variable
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/630583.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【分布式技术】分布式存储ceph之RGW接口

目录 1、对象存储概念 2、创建 RGW 接口 //在管理节点创建一个 RGW 守护进程 #创建成功后默认情况下会自动创建一系列用于 RGW 的存储池 #默认情况下 RGW 监听 7480 号端口 //开启 httphttps &#xff0c;更改监听端口 #更改监听端口 ​ //创建 RadosGW 账户 …

【Go学习】macOS+IDEA运行golang项目,报command-line-arguments,undefined

写在前面的话&#xff1a;idea如何配置golang&#xff0c;自行百度 问题1&#xff1a;通过idea的terminal执行go test报错 ✘ xxxxxmacdeMacBook-Pro-3  /Volumes/mac/.../LearnGoWithTests/hello  go test go: go.mod file not found in current directory or any parent …

【LeetCode热题100】【子串】和为 K 的子数组

题目 给你一个整数数组 nums 和一个整数 k &#xff0c;请你统计并返回 该数组中和为 k 的子数组的个数 。 子数组是数组中元素的连续非空序列。 示例 1&#xff1a; 输入&#xff1a;nums [1,1,1], k 2 输出&#xff1a;2示例 2&#xff1a; 输入&#xff1a;nums [1,…

Jet Brains 2023 开发者生态系统现状

一、前言 今天刷到Jet Brains官方发布了2023 开发者生态系统现状&#xff0c;这个相信大家都不陌生&#xff0c;我们的开发工具IDEA就是它旗下的。 分析的蛮不错的&#xff0c;今天整理一下&#xff0c;和大家一起分享。 有想法大家可以一起交流一下哈&#xff01; 有兴趣的…

unity SqLite读取行和列

项目文件 链接&#xff1a;https://pan.baidu.com/s/1BabHvQ-y0kX_w15r7UvIGQ 提取码&#xff1a;emsg –来自百度网盘超级会员V6的分享 using System.Collections; using System.Collections.Generic; using UnityEngine; using Mono.Data.Sqlite; using System; using Syste…

LeetCode刷题--- 买卖股票的最佳时机含冷冻期

个人主页&#xff1a;元清加油_【C】,【C语言】,【数据结构与算法】-CSDN博客 个人专栏 力扣递归算法题 http://t.csdnimg.cn/yUl2I 【C】 ​​​​​​http://t.csdnimg.cn/6AbpV 数据结构与算法 ​​​http://t.csdnimg.cn/hKh2l 前言&#xff1a;这个专栏主要讲述动…

一个月学会Python,零基础入门数据分析

在数据分析领域&#xff0c;python是一个绕不开的知识和工具&#xff0c;如果不会用python就很难说自己会数据分析&#xff0c;但是最近很多想要入门数据分析的小白经常问我&#xff0c;Python怎么入门&#xff1f;Python虽然被称作是“最简洁的语言”&#xff0c;但是它终究还…

Jenkins 敏感信息实战指南

在 Jenkins 中&#xff0c;安全地管理敏感信息对于构建和部署过程至关重要。本实战指南将详细介绍如何添加凭据、使用 HashiCorp Vault 插件&#xff0c;并通过创建 Pipeline 脚本、在 shell 脚本中使用&#xff0c;以及在 Python 脚本中使用来管理敏感信息。 步骤 1: 添加凭据…

一文读懂——如何把网站改成HTTPS访问

HTTPS&#xff08;全称为Hyper Text Transfer Protocol Secure&#xff09;是一种在计算机网络上进行安全通信的协议&#xff0c;它通过SSL/TLS证书对传输数据进行加密&#xff0c;确保了用户与服务器之间信息交换的私密性和完整性。 获取SSL/TLS证书 选择证书类型&#xff1a…

构建高效数据生态:数据库、数据仓库、数据湖、大数据平台与数据中台解析_光点科技

在数字化的浪潮中&#xff0c;一套高效的数据管理系统是企业竞争力的核心。从传统的数据库到现代的数据中台&#xff0c;每一种技术都在数据的旅程中扮演着关键角色。本文将深入探讨数据库、数据仓库、数据湖、大数据平台以及数据中台的功能和价值&#xff0c;帮助您构建一个符…

《C++入门篇》——弥补C不足

文章目录 前言一.命名空间二.缺省参数三.函数重载四.引用4.1引用做参数4.2引用做返回值 五.内联函数六.小语法6.1auto6.2范围for6.3空指针 前言 C是业内一门久负盛名的计算机语言&#xff0c;从C语言发展起来的它&#xff0c;不仅支持C语言的语法&#xff0c;还新添加了面向对…

java基础之设计模式(单例模式,工厂模式)

设计模式 是一种编码套路 单例模式 一个类只能创建一个实例 饿汉式 直接创建唯一实例 package com.by.entity; ​ /*** 单例模式-饿汉式*/ public class ClassA {//static: 1. newClassA可以访问返回 2. 静态属性内存中只会存在一个//private: 防止外界直接访问属性priva…

redis总结--常见问题与解决办法,推荐等级

因为csdn只支持这种文档形式&#xff1a;不支持思维导图&#xff1a; 更好友好的阅读&#xff1a;可以看我的飞书--------思维导图&#xff08;这样食用更加&#xff09;&#xff1a; 缓存穿透问题原因每次从缓存中都查不到数据&#xff0c;而需要查询数据库&#xff0c;同时数…

Kafka-消费者-KafkaConsumer分析-ConsumerCoordinator

在前面介绍了Kafka中Rebalance操作的相关方案和原理。 在KafkaConsumer中通过ConsumerCoordinator组件实现与服务端的GroupCoordinator的交互&#xff0c;ConsumerCoordinator继承了AbstractCoordinator抽象类。 下面我们先来介绍AbstractCoordinator的核心字段&#xff0c;如…

GO——与PHP的并发对比

背景 go比php可支持的并发数更高&#xff0c;为什么 目标 分析点&#xff1a; 系统的并发瓶颈go语言的并发瓶颈php语言的并发瓶颈 系统并发 参考&#xff1a;https://juejin.cn/post/6844904025553534990 提到并发&#xff0c;我们这里指的是web服务web系统的第一层&…

如何在Linux上安装Stable Diffusion WebUI

Stable Diffusion WebUI是一个基于AUTOMATIC1111的stable-diffusion-webui仓库的项目&#xff0c;允许用户通过web界面轻松地生成AI驱动的图像。本文将指导您在Linux系统上完成Stable Diffusion WebUI的安装过程。 准备工作 在安装Stable Diffusion WebUI之前&#xff0c;请确…

Apache JMeter 3.1压力测试监控服务器数据(cpu、内存、磁盘io等)

Apache JMeter 3.1压力测试 Apache JMeter 3.1压力测试监控cpu、内存情况1.下载Apache JMeter 3.11.1 添加线程组1.2 添加http请求1.3 增加http请求头设置1.4 添加csv配置1.5 添加测试结果监控配置 2. 监控插件下载3. 服务端插件下载并启动3.1 下载3.2 解压并启动3.3 增加服务器…

渗透测试之Kali如何利用CVE-2019-0708漏洞渗透Win7

环境: 1.攻击者IP:192.168.1.10 系统: KALI2022(vmware 16.0) 2.靶机IP:192.168.1.8 系统:Windows 7 6.1.7601 Service Pack 1 Build 7601 已开启远程协助RDP服务开启了3389端口 问题描述: KALI 如何利用CVE-2019-0708漏洞渗透Win7 解决方案: 1.打开kali,msf搜索…

最新React面试题:说说对React的理解?有哪些特性?

最新React面试题&#xff1a;说说对React的理解&#xff1f;有哪些特性&#xff1f; 回答思路&#xff1a;是什么&#xff1f;---》特性---》优势是什么&#xff1f;特性声明式编程Component组件的特点 优势 回答思路&#xff1a;是什么&#xff1f;—》特性—》优势 是什么&a…