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

前言

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

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

先说说注解

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

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

这条注释的意思是:

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

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

让我们先大体猜测一下:

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

实例

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

 1import java.lang.annotation.ElementType;
 2import java.lang.annotation.Retention;
 3import java.lang.annotation.RetentionPolicy;
 4import java.lang.annotation.Target;
 5import java.lang.reflect.Method;
 6
 7/**
 8 * 模拟Spring的@RequestMapping注解实现原理
 9 * @implNote 注意:本示例仅用于展示@RequestMapping注释的原理,并非真正实现了@RequestMapping注解!
10 * @author GitHub: AdlerED
11 */
12
13/**
14 * 使用@interface定义一个注解,注解名为RequestMapping
15 * 枚举 RequestMethod 包含两个String对象:GET | POST
16 * value() 指定URL,必填
17 * method() 指定方式,选填,默认值RequestMethod.POST
18 *
19 * @Target 元注解,指定注解用于什么地方,ElementType.METHOD 表示用于描述方法
20 * @Retention 元注解,指定什么时候使用该注解,RetentionPolicy.RUNTIME 表示运行期也保留注解,因此可以使用反射机制读取该注解的信息。
21 */
22@Target(ElementType.METHOD)
23@Retention(RetentionPolicy.RUNTIME)
24@interface RequestMapping {
25    public enum RequestMethod {GET, POST}
26    String value();
27    RequestMethod method() default RequestMethod.POST;
28}
29
30/**
31 * Controller类,用两种方法使用自定义的@RequestMapping注解
32 */
33class Controller {
34    //由于method()方法有默认值,在注解只需要一个必填时,可以不指定方法名
35    @RequestMapping("/index.html")
36    public void mainPage() {
37        boolean status = true;
38    }
39
40    //手动指定value()方法值和method()方法值
41    @RequestMapping(value = "/admin/index.html", method = RequestMapping.RequestMethod.GET)
42    public void adminPage() {
43        boolean status = false;
44    }
45}
46
47public class Main {
48    /**
49     * 主方法,用于将Controller类中使用注解的内容进行反射
50     */
51    public static void main(String[] args) {
52        try {
53            //获取Controller类的反射
54            Class clazz = Controller.class;
55            //反射获取Controller类中的所有方法,并遍历
56            for(Method method : clazz.getMethods()) {
57                //获取方法中的@RequestMapping注解信息
58                RequestMapping methodAnnotation = method.getAnnotation(RequestMapping.class);
59                //如果该方法使用了@RequestMapping注解,则
60                if(methodAnnotation != null) {
61                    //打印方法名
62                    System.out.println(" Method Name : " + method.getName());
63                    //打印注解value()的值
64                    System.out.println(" Value : " + methodAnnotation.value());
65                    //打印注解method()的值
66                    System.out.println(" Method : " + methodAnnotation.method());
67                    System.out.println(" --------------------------- ");
68                }
69            }
70        } catch (Exception E) {
71            E.printStackTrace();
72        }
73    }
74}

反复理解并运行语句。

运行结果:

1 Method Name : mainPage
2 Value : /index.html
3 Method : POST
4 --------------------------- 
5 Method Name : adminPage
6 Value : /admin/index.html
7 Method : GET
8 --------------------------- 

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

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

后语

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

如转载请在文章尾部添加

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

评论

  1. 自定义注解。

取消