前言
阅读本篇文章,你需要理解以下的知识:
- 重要:反射 (参考)
- Java enum
- 接口的使用
- 了解注解是什么、该什么时候使用
- 最好接触过Spring(因为用@RequestMapping举例)
先说说注解
如果你熟悉Spring框架,你一定使用过这个注解:
1@RequestMapping(value = "/admin/index.html", method = RequestMethod.GET)
这条注释的意思是:
- 用户访问"/admin/index.html"页面,则执行下方方法
- 限制用户只能使用"GET"方式访问
那么Spring在启动后是如何知道你用了这个注解,并且调用这个注解指定的方法的呢?
让我们先大体猜测一下:
- 我们可以配置Spring扫描的
@Controller
所在目录
- Spring扫描目录,并逐个扫描
@Controller
类中的注解
- 解析所有扫描到的注解,并通过反射,执行指定注释下的方法
实例
打开你的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的灵魂。 通过反射,我们才拥有了简单并优美的自定义注释,而不需要进行复杂的实例化,更具灵活性。