刨根问底 | 大白话:在使用注解后,框架是怎么知道你哪个方法使用了注解的?用 @RequestMapping 注解举例详解!

本贴最后更新于 2021 天前,其中的信息可能已经时移世易

前言

阅读本篇文章,你需要理解以下的知识:

  • 重要:反射 (参考
  • Java enum
  • 接口的使用
  • 了解注解是什么、该什么时候使用
  • 最好接触过 Spring(因为用 @RequestMapping 举例)

先说说注解

如果你熟悉 Spring 框架,你一定使用过这个注解:

@RequestMapping(value = "/admin/index.html", method = RequestMethod.GET)

这条注释的意思是:

  • 用户访问"/admin/index.html"页面,则执行下方方法
  • 限制用户只能使用"GET"方式访问

那么 Spring 在启动后是如何知道你用了这个注解,并且调用这个注解指定的方法的呢?

让我们先大体猜测一下:

  1. 我们可以配置 Spring 扫描的 @Controller 所在目录
  2. Spring 扫描目录,并逐个扫描 @Controller 类中的注解
  3. 解析所有扫描到的注解,并通过反射,执行指定注释下的方法

实例

打开你的 IDE,新建一个类 Main.java,并复制下方语句:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;

/**
 * 模拟Spring的@RequestMapping注解实现原理
 * @implNote 注意:本示例仅用于展示@RequestMapping注释的原理,并非真正实现了@RequestMapping注解!
 * @author GitHub: AdlerED
 */

/**
 * 使用@interface定义一个注解,注解名为RequestMapping
 * 枚举 RequestMethod 包含两个String对象:GET | POST
 * value() 指定URL,必填
 * method() 指定方式,选填,默认值RequestMethod.POST
 *
 * @Target 元注解,指定注解用于什么地方,ElementType.METHOD 表示用于描述方法
 * @Retention 元注解,指定什么时候使用该注解,RetentionPolicy.RUNTIME 表示运行期也保留注解,因此可以使用反射机制读取该注解的信息。
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface RequestMapping {
    public enum RequestMethod {GET, POST}
    String value();
    RequestMethod method() default RequestMethod.POST;
}

/**
 * Controller类,用两种方法使用自定义的@RequestMapping注解
 */
class Controller {
    //由于method()方法有默认值,在注解只需要一个必填时,可以不指定方法名
    @RequestMapping("/index.html")
    public void mainPage() {
        boolean status = true;
    }

    //手动指定value()方法值和method()方法值
    @RequestMapping(value = "/admin/index.html", method = RequestMapping.RequestMethod.GET)
    public void adminPage() {
        boolean status = false;
    }
}

public class Main {
    /**
     * 主方法,用于将Controller类中使用注解的内容进行反射
     */
    public static void main(String[] args) {
        try {
            //获取Controller类的反射
            Class clazz = Controller.class;
            //反射获取Controller类中的所有方法,并遍历
            for(Method method : clazz.getMethods()) {
                //获取方法中的@RequestMapping注解信息
                RequestMapping methodAnnotation = method.getAnnotation(RequestMapping.class);
                //如果该方法使用了@RequestMapping注解,则
                if(methodAnnotation != null) {
                    //打印方法名
                    System.out.println(" Method Name : " + method.getName());
                    //打印注解value()的值
                    System.out.println(" Value : " + methodAnnotation.value());
                    //打印注解method()的值
                    System.out.println(" Method : " + methodAnnotation.method());
                    System.out.println(" --------------------------- ");
                }
            }
        } catch (Exception E) {
            E.printStackTrace();
        }
    }
}

反复理解并运行语句。

运行结果:

 Method Name : mainPage
 Value : /index.html
 Method : POST
 --------------------------- 
 Method Name : adminPage
 Value : /admin/index.html
 Method : GET
 --------------------------- 

整理实例语句,有以下几点:

  • @interface RequestMapping {} 定义一个自定义注解,此处的 interface 不是接口,因为在 interface 前边还有一个 @
  • Controller {} 类调用了 @RequestMapping() 接口,并演示了数据传递的定义
  • Main {} 类用来模拟 Spring,通过反射的方式扫描 Controller 类,并将使用了 @RequestMapping() 注解的方法也通过反射的方式,使其能够被修改调用。

后语

反射是 Java 的灵魂。 通过反射,我们才拥有了简单并优美的自定义注释,而不需要进行复杂的实例化,更具灵活性。

  • 刨根问底
    1 引用 • 1 回帖
  • 专项练习
    1 引用 • 1 回帖
  • 大白话
    17 引用 • 27 回帖
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3190 引用 • 8214 回帖 • 1 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • someone

    自定义注解。