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

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

前言

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

  • 重要:反射 (参考
  • 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的灵魂。 通过反射,我们才拥有了简单并优美的自定义注释,而不需要进行复杂的实例化,更具灵活性。

如转载请在文章尾部添加

原作者来自AdlerED个人技术博客:https://www.stackoverflow.wiki/

死钻技术 | 绝不抄袭