• Java注解-最通俗易懂的讲解


    注解的重要性就不用我来说了,controller层有@controller注解,service层有@service注解,基本上到处都是注解,任何一个Java框架都是通过注解+反射来实现的!所以注解是Java程序员的必备技能,如果你对注解还不是很了解,那么我强烈建议您把这一篇文章好好读一下!

    本篇文章相对来说比较长,但是很多都是代码示例,有时候光通过理论并不能很好的掌握新技能,当看完理论感觉确实掌握了,但是过一段时间就忘了,所以学习一项技能,一定是理论加实践才会更加牢固!

    一、概念

    1.1. 什么是注解?

    Java注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。

    如果要对于元数据的作用进行分类,还没有明确的定义,不过我们可以根据它所起的作用,大致可分为三类:

    • ① 编写文档:通过代码里标识的元数据生成文档【生成文档doc文档】
    • ② 代码分析:通过代码里标识的元数据对代码进行分析【使用反射】
    • ③ 编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检查【Override】

    注解是以‘@注解名’在代码中存在的,根据注解参数的个数,我们可以将注解分为:标记注解单值注解完整注解三类。它们都不会直接影响到程序的语义,只是作为注解(标识)存在,我们可以通过反射机制编程实现对这些元数据(用来描述数据的数据)的访问

    首先一定要明白注解他本身是没有任何作用的,比如@RequestMapping,在controller层@RequestMapping基本上可以说是必见的,我们都知道他的作用是请求映射,通过他来设置请求的url地址,举例:将@RequestMapping("config")写在方法上,然后我们就可以通过url地址访问到这个方法了,但是记住这并不是@RequestMapping注解的功能,SpringMVC会通过反射将注解当中设置的属性值config拿到,然后将url和你声明注解的方法进行绑定。记住:注解只是用来标记,而这个注解真正的功能都是由框架通过反射来实现的

    1.2. 什么是元数据?

    元数据是一个非常广泛的概念,元数据的定义也非常简单,只要是描述数据的数据都是元数据。很简单,一个数字170,单看数据你肯定不知道这个数据代表什么,这就需要元数据支持,你才能明白数据代表的事实是什么。它可能是一个人的身高,也可能指一个人的体重,这需要数据对应的标题、单位等信息来描述这个数据,这些就是描述这个数据的元数据了

    1.3. 注解的属性

    注解的属性也叫做成员变量。注解只有成员变量,没有方法。注解的成员变量在注解的定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface TestAnnotation {
        int id();
        String msg();
    }
    

    上面代码定义了 TestAnnotation 这个注解中拥有 id 和 msg 两个属性。在使用的时候,我们应该给它们进行赋值。

    赋值的方式是在注解的括号内以 value=”” 形式,多个属性之前用 ,隔开。

    @TestAnnotation(id=3,msg="hello annotation")
    public class Test {
    }
    

    需要注意的是,在注解中定义属性时它的类型必须是 8 种基本数据类型外加 类、接口、注解及它们的数组。

    注解中属性可以有默认值,默认值需要用 default 关键值指定。比如:

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface TestAnnotation {
        public int id() default -1;
        public String msg() default "Hi";
    }
    

    TestAnnotation 中 id 属性默认值为 -1,msg 属性默认值为 Hi。

    二、根据【注解参数】 分类

    根据注解参数的个数,我们可以将注解分为:标记注解单值注解完整注解三类。

    2.1. 标记注解

    标记注解不包含成员/元素。它仅用于标记声明。

    其语法为:@AnnotationName()

    • 由于这些注解不包含元素,因此不需要括号。例如:@Override

    2.2. 单元素注解

    单个元素注解仅包含一个元素。

    其语法为:@AnnotationName(elementName = "elementValue")

    • 如果只有一个元素,则习惯上将该元素命名为value:@AnnotationName(value = "elementValue")
    • 在这种情况下,也可以移除元素名称。元素名称默认为value:@AnnotationName("elementValue")

    2.3. 多元素注解

    这些注解包含多个用逗号分隔的元素。

    其语法为:@AnnotationName(element1 = "value1", element2 = "value2")

    三、根据【注解作用】分类

    3.1. 预定义的注解

    这几个注解都是java.lang包下的,也就是Java提供的基础注解,我们在使用的时候是不需要导包的!

    3.1.1. @Deprecated

    此注解可以用在方法,属性,类上,表示不推荐程序员使用,但是还可以使用,示例如下:

    在这里插入图片描述

    /**
     * 测试Deprecated注解
     * @author Administrator
     */
    public class DeprecatedDemoTest {
        public static void main(String[]args) {
            // 使用DeprecatedClass里声明被过时的方法
            DeprecatedClass.DeprecatedMethod();
        }
    }
    
    class DeprecatedClass {
        @Deprecated
        public static void DeprecatedMethod() {
        }
    }
    
    3.1.2. @Override

    它的作用是对覆盖超类中方法的方法进行标记,如果被标记的方法并没有实际覆盖超类中的方法,则编译器会发出错误警告。

    public interface Test {
        public String getStr();
    }
    
    class TestImpl implements Test{
    
    	// 假如返回参数和方法参数其中一个不一致,就会警告
        @Override
        public String getStr() {
            return null;
        }
    }
    
    3.1.3. @SuppressWarnings

    我们在写代码的时候,不论是导入的包,还是声明的对象,有时候会出现黄线,感觉就很难受!

    @SuppressWarnings注解主要用在取消一些编译器产生的警告,警告对于运行代码实际上并没有影响,但是出于部分程序员具有洁癖的嗜好,通常会采用@SuppressWarnings来消除警告。

    示例一:警告如图所示

    在这里插入图片描述
    这只是一个service接口:

    public interface BannerService {
    }
    

    这时候我们在方法上加上@SuppressWarnings注解就可以消除这些警告的产生,注解的使用方式有三种:

    1. @SuppressWarnings(“unchecked”) [^ 抑制单类型的警告]
    2. @SuppressWarnings(“unchecked”,“rawtypes”) [^ 抑制多类型的警告]
    3. @SuppressWarnings(“all”) [^ 抑制所有类型的警告]

    通过源码分析可知@SuppressWarnings其注解目标为类、字段、函数、函数入参、构造函数和函数的局部变量。建议把注解放在警告发生的位置。

    消除警告:

    在这里插入图片描述

    这个警告是spring framerwork 4.0以后开始出现的,spring 4.0开始就不推荐使用属性注入,改为推荐构造器注入和setter注入。当然他只是推荐,如果我们想要消除警告也可以直接使用@Resource。尽管他推荐了,但是一般实际开发当中很少会使用构造器注入和setter注入。

    1. @Autowired 是Spring提供的,@Resource 是J2EE提供的。

    2. @Autowired与@Resource都可以用来装配bean,都可以写在字段或setter方法上

    3. @Autowired默认按类型装配,默认情况下必须要求依赖对象存在,如果要允许null值,可以设置它的required属性为false。如果想使用名称装配可以结合@Qualifier注解进行使用。

    4. @Resource,默认按照名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行名称查找。如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配

    示例二:警告如图所示

    在这里插入图片描述

    通过添加@SuppressWarnings("unchecked")来消除unchecked的警告,这个警告实际上主要是集合没有加泛型导致的!

    在这里插入图片描述

    在这里插入图片描述

    3.1.4. @SafeVarargs

    在声明具有模糊类型(比如:泛型)的可变参数的构造函数或方法时,Java编译器会报unchecked警告。鉴于这些情况,如果程序员断定声明的构造函数和方法的主体不会对其varargs参数执行潜在的不安全的操作,可使用@SafeVarargs进行标记,这样的话,Java编译器就不会报unchecked警告。

    使用前提:

    1. 方法必须是由static或者final修饰的方法
    2. 只能用于标记构造函数和方法
    3. 只能用在可变长参数的方法上
    @SafeVarargs
    public static <T> T useVarargs(T... args) {  
        return args.length > 0 ? args[0] : null;  
    } 
    
    3.1.5. @FunctionalInterface

    被@FunctionalInterface注解标记的类型表明这是一个函数接口。从概念上讲,函数接口只有一个抽象方法。也就是一旦不满足函数接口,就会报错,比如他有两个抽象方法。

    @FunctionalInterface注解,只能用于接口类。其实,它的应用范围更小,只能应用于接口类型。

    @FunctionalInterface
    public interface Test {
        public String getStr();
    }
    

    3.2. 元注解

    以下的注解都来源于java.lang.annotation,描述数据的数据都是元数据,描述注解的注解都是元注解!也就是这些注解只能用在修饰注解上,不能使用在其他地方,比如方法、类等等!

    @Native注解除外,他的使用范围是FIELD(字段、枚举的常量),但是@Native也是在java.lang.annotation包下!

    在这里插入图片描述

    3.2.1. @Retention

    注解按生命周期来划分可分为3类:

    • RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;也就是编译时有效。
    • RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;加载时被抛弃。
    • RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;一直有效!

    lombok可以通过@Data注解省去get set 方法,实际上@Data的生命周期就是RetentionPolicy.SOURCE,他是通过注解来标记这个方法要生成get set方法,然后在编译的时候直接会生成get set。生成过后,就被抛弃了。

    在这里插入图片描述

    3.2.2. @Documented

    @Documented@Deprecated注解长得有点像,@Deprecated是用来标注某个类或者方法不建议再继续使用,@Documented只能用在注解上,如果一个注解@B,被@Documented标注,那么被@B修饰的类,生成文档时,会显示@B。如果@B没有被@Documented标注,最终生成的文档中就不会显示@B。这里的生成文档指的JavaDoc文档!

    @Deprecated注解基本上所有框架自定义的注解都会添加,所谓javadoc其实就是JDK给我们提供的一个生成文档的工具!

    由于篇幅问题,@Documented详解请看这篇文章:https://blog.csdn.net/weixin_43888891/article/details/126981711

    3.2.3. @Target

    @Target:@Target只能用在注解上,指定修饰的注解的使用范围

    • @Target(ElementType.TYPE) —— 接口、类、枚举、注解
    • @Target(ElementType.FIELD) —— 字段、枚举的常量
    • @Target(ElementType.METHOD) —— 方法
    • @Target(ElementType.PARAMETER) —— 方法参数
    • @Target(ElementType.CONSTRUCTOR) —— 构造函数
    • @Target(ElementType.LOCAL_VARIABLE) —— 局部变量
    • @Target(ElementType.ANNOTATION_TYPE) —— 注解
    • @Target(ElementType.PACKAGE) —— 包

    比如@Target({ElementType.TYPE, ElementType.METHOD}),就代表着@RequestMapping可以用在 接口、类、枚举、注解上、还可以用在方法上!

    在这里插入图片描述

    3.2.4. @Inherited

    如果一个类用上了@Inherited修饰的注解,那么其子类也会继承这个注解。

    当用了@Inherited修饰的注解的@Retention是RetentionPolicy.RUNTIME,则增强了继承性,在反射中可以获取得到

    代码示例:

    1.首先自定义一个注解使用@Inherited修饰,然后这个注解用来修饰到父类上

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Inherited
    public @interface ATable {
        public String name() default "";
    }
    

    2.这个注解带不带@Inherited都可以,我们主要用来修饰子类

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface BTable {
        public String name() default "";
    }
    

    2.定义一个父类跟一个子类,然后在父类上用上我们自定义的@ATable注解

    @ATable
    public class Super {
        public static void main(String[] args) {
            Class<Sub> clazz = Sub.class;
            System.out.println("============================AnnotatedElement===========================");
            // 获取自身和父级带有@Inherited修饰的注解。如果@ATable未加@Inherited修饰,则获取的只是自身的注解而无法获取父级修饰的@ATable注解
            System.out.println(Arrays.toString(clazz.getAnnotations()));
            System.out.println("------------------");
        }
    }
    @BTable
    class Sub extends Super {
    }
    

    3.这个是@ATable带有@Inherited修饰的 运行结果。

    在这里插入图片描述
    4.这个是没有用@Inherited修饰的 运行结果。

    在这里插入图片描述

    3.2.5. @Repeatable

    @Repeatable 注解是用于声明其它类型注解的元注解,来表示这个声明的注解是可重复的。@Repeatable的值是另一个注解,其可以通过这个另一个注解的值来包含这个可重复的注解

    代码示例:

    1.自定义Value注解

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Repeatable(Values.class)
    public @interface Value {
        String value() default "value";
    }
    

    2.自定义Values 注解

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Values {
        Value[] value();
    }
    

    其中,@Value注解上的元注解@Repeatable中的值,使用了@Values注解,@Values注解中包含的值类型是一个@Value注解的数组!这就解释了官方文档中@Repeatable中值的使用。

    3.测试

    import java.lang.annotation.Annotation;
    import java.lang.reflect.Method;
    import java.util.Arrays;
    
    public class AnnotationClass {
    
        @Value("hello")
        @Value("world")
        public void test(String var1, String var2) {
            System.out.println(var1 + " " + var2);
        }
    
        public static void main(String[] args) {
            Method[] methods = AnnotationClass.class.getMethods();
            for (Method method : methods){
                if (method.getName().equals("test")) {
                    Annotation[] annotations = method.getDeclaredAnnotations();
                    System.out.println(annotations.length);
                    System.out.println(method.getName() + " = " + Arrays.toString(annotations));
                }
            }
        }
    }
    

    4.运行结果

    在这里插入图片描述

    5.假如Value注解没有使用@Repeatable修饰,编译器会报错,是不允许出现注解重复的

    在这里插入图片描述
    注意事项:

    1. @Repeatable 所声明的注解,其元注解@Target的使用范围要比@Repeatable的值声明的注解中的@Target的范围要大或相同,否则编译器错误,显示@Repeatable值所声明的注解的元注解@Target不是@Repeatable声明的注解的@Target的子集
    2. @Repeatable注解声明的注解的元注解@Retention的周期要比@Repeatable的值指向的注解的@Retention得周期要小或相同。

    周期长度为 SOURCE(源码) < CLASS (字节码) < RUNTIME(运行)

    3.2.6. @Native

    @Native注解修饰成员变量,则表示这个变量可以被本地代码引用,常常被代码生成工具使用。对于 @Native 注解不常使用,了解即可!

    3.3. 自定义注解

    1. 修饰符: 访问修饰符必须为public,不写默认为pubic;
    2. 关键字: 关键字为@interface;
    3. 注解名称: 注解名称为自定义注解的名称,使用时还会用到;
    4. 注解内容: 注解中内容,对注解的描述。

    我们自定义一个注解,然后这个注解可以在entity当中的set方法上初始化值。只是一个简单案例,供大家参考学习!

    第一步:自定义@Init注解

    @Documented
    @Inherited
    @Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})  //可以在字段、枚举的常量、方法
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Init {
        String value() default "";
    }
    

    第二步:创建User类

    public class User {
        private String name;
        private String age;
    
        public String getName() {
            return name;
        }
    
        @Init("louis")
        public User setName(String name) {
            this.name = name;
            return this;
        }
    
        public String getAge() {
            return age;
        }
    
        @Init("22")
        public User setAge(String age) {
            this.age = age;
            return this;
        }
    }
    

    第三步:创建User的工厂类,所谓工厂类就是专门为了生产User对象

    import java.lang.reflect.Method;
    
    public class UserFactory {
        public static User create() {
            User user = new User();
            // 获取User类中所有的方法(getDeclaredMethods也行)
            Method[] methods = User.class.getMethods();
            try {
                for (Method method : methods) {
                    // 判断方法上是否存在Init注解,存在就返回true,否则返回false
                    if (method.isAnnotationPresent(Init.class)) {
                        // 此方法返回该元素的注解在此元素的指定注释类型(如果存在),否则返回null
                        Init init = method.getAnnotation(Init.class);
                        // 如果Method代表了一个方法 那么调用它的invoke就相当于执行了它代表的这个方法,在这里就是给set方法赋值
                        method.invoke(user, init.value());
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
            return user;
        }
    }
    

    第四步:测试

    public static void main(String[] args) {
        User user = UserFactory.create();
        user.setAge("30");
        System.out.println(user.getName());
        System.out.println(user.getAge());
    }
    

    第五步:运行结果,我们并没有设置User的name属性,只是在User类的 set方法 添加了一个注解。输出结果如下:

    在这里插入图片描述

    四、反射

    JAVA机制反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。Java注解一旦离开了反射就什么都不是!!!

    4.1. 反射常用到的API

    1.只能拿到public方法(包括继承的类或接口的方法)

    public Method[] getMethods()
    

    2.getDeclaredMethods返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。

    public Method[] getDeclaredMethods()
    

    3.在Class对象和Method对象是都存在isAnnotationPresent这个方法的,作用是判断它是否应用了某个注解

    public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {}
    

    4.通过 getAnnotation() 方法来获取 Annotation 对象。

     public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {}
    

    5.或者是 getAnnotations() 方法,获取所有的注解

    如果获取到的 Annotation 如果不为 null,则就可以调用它们的属性方法了。

    public Annotation[] getAnnotations() {}
    

    6.获取到Annotation之后,就可以通过annotationType获取注解的class结构信息,有了class信息就可以获取注解的变量名,等等

    Class<? extends Annotation> annotationType();
    

    7.getParameterAnnotations :返回表示按照声明顺序对此 Method 对象所表示方法的形参进行注释的那个数组的数组

    public Annotation[][] getParameterAnnotations();
    

    4.2. 读取类上的注解

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface TestAnnotation {
        public int id() default -1;
    
        public String msg() default "Hi";
    }
    
    import java.lang.reflect.Method;
    
    @TestAnnotation(id = 222,msg = "awdawd")
    public class Test {
        public static void main(String[] args) {
            Method[] declaredMethods1 = TestAnnotation.class.getDeclaredMethods();
            for (Method meth : declaredMethods1) {
                System.out.println("注解的变量名为:" + meth.getName());
            }
    
            // 首先判断Test类上是否使用了TestAnnotation注解
            boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
            if (hasAnnotation) {
                // 获取注解,这个相当于是真正的拿到注解了,只有获取到这个才能获取到注解当中设置的值
                TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);
                System.out.println("id:" + testAnnotation.id());
                System.out.println("msg:" + testAnnotation.msg());
            }
        }
    }
    

    输出结果:

    在这里插入图片描述

    4.3. 读取方法上的注解

    public class User {
        private String name;
        private String age;
    
        public String getName() {
            return name;
        }
    
        @Init("louis")
        public User setName(String name) {
            this.name = name;
            return this;
        }
    
        public String getAge() {
            return age;
        }
    
        @Init("22")
        public User setAge(String age) {
            this.age = age;
            return this;
        }
    }
    
    //读取注解信息
    public class ReadAnnotationInfoTest {
        public static void main(String[] args) throws Exception {
            // 测试AnnotationTest类,得到此类的类对象
            Class c = Class.forName("com.gzl.cn.springbootnacos.my.User");
            // 获取该类所有声明的方法
            Method[] methods = c.getDeclaredMethods();
            // 声明注解集合
            Annotation[] annotations;
            // 遍历所有的方法得到各方法上面的注解信息
            for (Method method : methods) {
                // 获取每个方法上面所声明的所有注解信息
                annotations = method.getDeclaredAnnotations();
                System.out.println("方法名为:" + method.getName());
    
                if (method.isAnnotationPresent(Init.class)){
                    Init annotation = method.getAnnotation(Init.class);
                    System.out.println("注解设置的value值为:" + annotation.value());
                }
    
                for (Annotation an : annotations) {
                    System.out.println("其上面的注解为:" + an.annotationType().getSimpleName());
                    // 获取注解class对象
                    Class<? extends Annotation> aClass = an.annotationType();
                    // 通过class对象获取他的所有属性方法
                    Method[] meths = aClass.getDeclaredMethods();
                    // 遍历每个注解的所有变量
                    for (Method meth : meths) {
                        System.out.println("注解的变量名为:" + meth.getName());
                    }
                }
            }
        }
    }
    

    输出结果:

    在这里插入图片描述

    4.4. 读取字段上的注解

    public class User {
        @Init("张三")
        private String name;
        @Init("33")
        private String age;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getAge() {
            return age;
        }
    
        public void setAge(String age) {
            this.age = age;
        }
    }
    
    
    public static void main(String[] args) throws IllegalAccessException {
            User user = new User();
            user.setAge("111");
            user.setName("www");
    
            Class<User> userClass = User.class;
            Field[] fields = userClass.getDeclaredFields();
            for (Field field : fields) {
                System.out.println("属性名称是:" + field.getName());
                Init annotation = field.getAnnotation(Init.class);
                System.out.println("注解的value值是:" + annotation.value());
                // 字段是私有的想要获取就需要将Accessible设置为true,否则报错
                field.setAccessible(true);
                Object o = field.get(user);
                System.out.println("user对象的属性值是:" + o);
                System.out.println();
            }
        }
    

    输出的结果:

    在这里插入图片描述

    4.5. 读取方法参数注解

    @RequestMapping(value = "/get", method = RequestMethod.GET)
    @ResponseBody
    public boolean get(@RequestParam("username") String usname) {
        return useLocalCache;
    }
    
    public static void main(String[] args) throws NoSuchMethodException {
        Class<ConfigController> configControllerClass = ConfigController.class;
        // 获取get方法,第一个参数是方法名,第二个是参数类型
        Method get = configControllerClass.getDeclaredMethod("get", String.class);
        // 方法上可能有多个参数,每个参数上可能有多个注解,所以是二维数组
        // annotations[0][0]表示第1个参数的第1个注解
        // annotations[0][1]表示第1个参数的第2个注解
        Annotation[][] annotations = get.getParameterAnnotations();
    
        for (int i = 0; i < annotations.length; i++) {
            for (int j = 0; j < annotations[i].length; j++) {
                if (annotations[i][j] instanceof RequestParam) {
                    RequestParam myParam1 = (RequestParam) annotations[i][j];
                    System.out.println(myParam1.value());
                }
            }
        }
    }
    

    输出结果:

    在这里插入图片描述

    4.6. 注解配合枚举使用

    @RequestMapping当中method属性就是使用的枚举。

    @RequestMapping(value = "/get", method = RequestMethod.GET)
    @ResponseBody
    public boolean get(@RequestParam("username") String usname) {
        return useLocalCache;
    }
    
    public static void main(String[] args) throws NoSuchMethodException {
        Class<ConfigController> configControllerClass = ConfigController.class;
        // 获取get方法,第一个参数是方法名,第二个是参数类型
        Method get = configControllerClass.getDeclaredMethod("get", String.class);
        RequestMapping annotation = get.getAnnotation(RequestMapping.class);
        RequestMethod[] method = annotation.method();
        RequestMethod requestMethod = method[0];
        switch (requestMethod) {
            case GET:
                System.out.println("get请求");
                break;
            case POST:
                System.out.println("post请求");
                break;
        }
    }
    

    输出结果:

    在这里插入图片描述

    写作不易,如果这篇文章确实帮助到您了,麻烦您给小编留个赞,谢谢!

  • 相关阅读:
    人工智能基础第三次作业
    微信小程序 通过响应式数据控制元素class属性
    Java自学第6课:电商项目(2)
    win安装vue并运行 vue-admin-template
    js for循环设置循环变量和循环体内部是两个单独作用域
    CN_组帧@帧定界
    云原生之Docker的容器资源管理
    App自动化测试持续集成效率提高50%
    从零开始写 Docker(十二)---实现 mydocker stop 停止容器
    PostgreSQL 查询修改max_connections(最大连接数)及其它配置
  • 原文地址:https://blog.csdn.net/weixin_43888891/article/details/126963074