【仿写spring】一、通过反射读取带有@RequestMapping与@Controller注解的类并模拟请求路径调用方法

news2024/11/27 18:29:57

目录

      • 简介
      • 思路
      • 实践
        • 一、自定义注解@RequestMapping,@Controller
        • 二、路径转全限定名方法
        • 三、扫描文件夹
        • 四、通过反射来寻找有@RequestMapping以及@Controller的类
        • 五、获取对象实例
        • 六、通过invoke调用方法
      • 文件结构以及测试结果
        • 1、文件结构
        • 2、TestController
        • 3、测试结果

简介

通过反射读取带有@RequestMapping与@Controller注解的类并模拟请求路径调用方法

思路

首先拆分问题,我们在问题中有几个需求:

  1. 自定义注解@RequestMapping、@Controller
  2. 扫描文件夹
  3. 通过反射读取类信息找到有@Controller和@RequestMapping注解的类
  4. 通过new Instance()获取对象
  5. 通过invoke调用方法

进一步思考:

1、@RequestMapping可以用在哪些地方?是否支持运行时检测?
类、方法
支持运行时检测

2、反射读取类信息有三种方式:

  • 类名.class
  • 对象.getClass()
  • Class.forName()

该选用哪一种呢?

实际上在编写框架的时候,我们并不知道使用者会定义哪些类,所以我们应该通过Class.forName()来获取类信息。
而Class.forName()传递的参数是类的全限定名即:包名+类名(com.ez4sterben.entity.Student)
随之第五个需求产生,编写一个根据路径转化为全限定名的方法。

实践

一、自定义注解@RequestMapping,@Controller

根据前一部分的分析,自定义注解

package com.spring.annotation;

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

/**
 * 控制器
 *
 * @author ez4sterben
 * @date 2023/07/22
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
}
package com.spring.annotation;

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

/**
 * 请求映射
 *
 * @author ez4sterben
 * @date 2023/07/22
 */
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {

    String value() default "";
}

二、路径转全限定名方法

    /**
     * 获取java路径
     *
     * @param file 文件
     * @return {@link String}
     */
    private static String getJavaPath(File file) {
        return file.toString().replace("\\",".").split("src.")[1].replace(".java","");
    }

三、扫描文件夹

我们通过递归实现扫描文件夹,获取其中以.java结尾的文件,调用刚才编写的方法存入List中

	public static String JAVA = ".java";
	public static List<String> javaFiles = new ArrayList<>();
	/**
     * 获取所有文件
     *
     * @param file 文件
     */
    public static void getAllFile(File file){
        if (file.isFile()){
            if (file.toString().endsWith(JAVA)) {
                String javaPath = getJavaPath(file);
                javaFiles.add(javaPath);
            }
        }
        if (file.exists() && file.isDirectory()) {
            File[] files = file.listFiles();
            if (files != null) {
                for (File file1 : files) {
                    getAllFile(file1);
                }
            }
        }
    }

文件扫描相关的工作到这里就完成了,完整代码如下:

package com.spring.utils;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
 * 文件扫描
 *
 * @author ez4sterben
 * @date 2023/07/22
 */
public class FileScannerUtil {

    public static List<String> javaFiles = new ArrayList<>();
    public static String JAVA = ".java";


    /**
     * 得到所有java文件
     *
     * @param path 路径
     * @return {@link List}<{@link String}>
     */
    public static List<String> getAllJavaFile(String path){
        getAllFile(new File(path));
        return javaFiles;
    }

    /**
     * 获取所有文件
     *
     * @param file 文件
     */
    public static void getAllFile(File file){
        if (file.isFile()){
            if (file.toString().endsWith(JAVA)) {
                String javaPath = getJavaPath(file);
                javaFiles.add(javaPath);
            }
        }
        if (file.exists() && file.isDirectory()) {
            File[] files = file.listFiles();
            if (files != null) {
                for (File file1 : files) {
                    getAllFile(file1);
                }
            }
        }
    }

    /**
     * 获取java路径
     *
     * @param file 文件
     * @return {@link String}
     */
    private static String getJavaPath(File file) {
        return file.toString().replace("\\",".").split("src.")[1].replace(".java","");
    }

}

四、通过反射来寻找有@RequestMapping以及@Controller的类

从这里开始我们就需要一个测试类来调用方法了,这里我将测试类的代码全部展示出来。

package com.spring;

import com.spring.utils.BeanUtil;
import com.spring.utils.FileScannerUtil;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;

/**
 * 测试
 *
 * @author ez4sterben
 * @date 2023/07/22
 */
public class Test {

    public static String PATH = "F:\\projects\\JavaSEContainer\\sterben\\src\\com\\spring\\controller";
    public static String REQUEST_PATH = "/test/index";


    public static void main(String[] args) {
        // 模拟启动tomcat
        init();
        // 模拟接收请求
        request(REQUEST_PATH);
    }

    /**
     * 初始化
     */
    public static void init(){
        List<String> files = FileScannerUtil.getAllJavaFile(PATH);
        try {
            for (String file : files) {
                if (BeanUtil.isControllerAndRequestMapping(file)){
                    BeanUtil.createBean(file);
                }
            }
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 请求
     *
     * @param url url
     */
    public static void request(String url){
        String[] urls = url.split("/");
        String controllerName = urls[1];
        String methodName = urls[2];
        Object bean = BeanUtil.getBean("/" + controllerName);
        try {
            Map<String, Method> beanMethods = BeanUtil.getBeanMethods(bean.getClass());
            Method method = beanMethods.get("/" + methodName);
            Object invoke = method.invoke(bean);
            System.out.println(invoke);
        } catch (ClassNotFoundException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

判断是否有Controller以及RequestMapping注解,这里通过测试类中的init方法调用,遍历我们扫描文件夹得到的List判断每个全限定名是否有这两个注解。

    /**
     * 控制器和请求映射
     *
     * @param name 全限定名
     * @return {@link Boolean}
     * @throws ClassNotFoundException 类没有发现异常
     */
    public static Boolean isControllerAndRequestMapping(String name) throws ClassNotFoundException {
        Class<?> clazz = Class.forName(name);
        Controller controller = clazz.getAnnotation(Controller.class);
        RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
        if (controller != null && requestMapping != null){
            return Boolean.TRUE;
        }
        return Boolean.FALSE;
    }

五、获取对象实例

这部分代码在测试类中的init方法里面调用,创建一个bean并且存入Map中。key是annotation中的value即我们传入的请求路径,value是实例。

    public static Map<String,Object> beanMap = new HashMap<>();

    /**
     * 创建bean
     *
     * @param name 名字
     * @throws ClassNotFoundException 类没有发现异常
     * @throws InstantiationException 实例化异常
     * @throws IllegalAccessException 非法访问异常
     */
    public static void createBean(String name) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        Class<?> clazz = Class.forName(name);
        RequestMapping annotation = clazz.getAnnotation(RequestMapping.class);
        Object instance = clazz.newInstance();
        beanMap.put(annotation.value(), instance);
    }

六、通过invoke调用方法

在日常开发中我们都是通过请求来调用方法的,那么理所当然,我们需要列出controller中的所有method并匹配执行(注意:这里博主写的不够好,这个方法得到的结果也应该保存在一个Map中管理,可以自行优化)

	/**
     * get bean方法
     *
     * @param clazz clazz
     * @return {@link Map}<{@link String},{@link Method}>
     * @throws ClassNotFoundException 类没有发现异常
     */
    public static Map<String,Method> getBeanMethods(Class<?> clazz) throws ClassNotFoundException {
        Map<String,Method> methodMap = new HashMap<>();
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            declaredMethod.setAccessible(true);
            RequestMapping annotation = declaredMethod.getAnnotation(RequestMapping.class);
            if (annotation != null){
                methodMap.put(annotation.value(), declaredMethod);
            }
        }
        return methodMap;
    }

调用详见测试类的request方法。

BeanUtil详细代码:

package com.spring.utils;

import com.spring.annotation.Controller;
import com.spring.annotation.RequestMapping;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * javaBean工具类
 *
 * @author ez4sterben
 * @date 2023/07/22
 */
public class BeanUtil {

    public static Map<String,Object> beanMap = new HashMap<>();

    /**
     * 创建bean
     *
     * @param name 名字
     * @throws ClassNotFoundException 类没有发现异常
     * @throws InstantiationException 实例化异常
     * @throws IllegalAccessException 非法访问异常
     */
    public static void createBean(String name) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        Class<?> clazz = Class.forName(name);
        RequestMapping annotation = clazz.getAnnotation(RequestMapping.class);
        Object instance = clazz.newInstance();
        beanMap.put(annotation.value(), instance);
    }


    /**
     * get bean方法
     *
     * @param clazz clazz
     * @return {@link Map}<{@link String},{@link Method}>
     * @throws ClassNotFoundException 类没有发现异常
     */
    public static Map<String,Method> getBeanMethods(Class<?> clazz) throws ClassNotFoundException {
        Map<String,Method> methodMap = new HashMap<>();
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            declaredMethod.setAccessible(true);
            RequestMapping annotation = declaredMethod.getAnnotation(RequestMapping.class);
            if (annotation != null){
                methodMap.put(annotation.value(), declaredMethod);
            }
        }
        return methodMap;
    }

    /**
     * get bean
     *
     * @param name 名字
     * @return {@link Object}
     */
    public static Object getBean(String name){
        return beanMap.get(name);
    }

    /**
     * 控制器和请求映射
     *
     * @param name 名字
     * @return {@link Boolean}
     * @throws ClassNotFoundException 类没有发现异常
     */
    public static Boolean isControllerAndRequestMapping(String name) throws ClassNotFoundException {
        Class<?> clazz = Class.forName(name);
        Controller controller = clazz.getAnnotation(Controller.class);
        RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
        if (controller != null && requestMapping != null){
            return Boolean.TRUE;
        }
        return Boolean.FALSE;
    }
}

文件结构以及测试结果

1、文件结构

在这里插入图片描述

2、TestController

package com.spring.controller;

import com.spring.annotation.Controller;
import com.spring.annotation.RequestMapping;

/**
 * 测试控制器
 *
 * @author ez4sterben
 * @date 2023/07/22
 */
@RequestMapping("/test")
@Controller
public class TestController {

    @RequestMapping("/index")
    public String index(){
        System.out.println("/test/index");
        return "执行完了";
    }
}

3、测试结果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/778032.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

uniapp 条件编译失败,跑不起来

因为这行代码整个uniapp都跑不起来&#xff0c;谁懂救命。再说uniapp的异常提示也太反人类了<!-- <image :src"require(/ baseListItem.url)" /> -->

Linux进程控制(三)---进程替换+简易shell的实现

目录 execl() execv() execlp() execvp() 如何利用execl执行自己写的C/C可执行程序&#xff1f; 如何利用makefile同时编译两个文件 execle() execvpe() 简单shell的编写 什么是进程替换&#xff1f; 我们之前fork之后&#xff0c;是父子进程各自执行代码的一部分&am…

【人工智能】大模型平台新贵——文心千帆

个人主页&#xff1a;【&#x1f60a;个人主页】 &#x1f31e;热爱编程&#xff0c;热爱生活&#x1f31e; 文章目录 前言大模型平台文心千帆发布会推理能力模型微调 作用 前言 在不久的之前我们曾讨论过在ChatGPT爆火的大环境下&#xff0c;百度推出的“中国版ChatGPT”—文…

深度神经网络基础——深度学习神经网络基础 Tensorflow在深度学习的应用

目录 一、二、Tesnsflow入门 & 环境配置 & 认识Tensorflow三、线程与队列与IO操作神经网络基础知识1.简单神经网络2.卷积神经网络卷积层新的激活函数-Relu池化层(Pooling)计算 案例&#xff1a;Mnist手写数字图片识别卷积网络案例 一、二、Tesnsflow入门 & 环境配置…

IDEA无法链接代理The driver has not received any packets from the server.

问题如下: 1、本地部署Proxifier,且设置全局代理,截图如下 代理工具 2、通过Navicat 工具连接该远程数据库,连接正常,截图如下 3、通过IDEA或者Eclipse连接(通过代理),抛连接失败(数据库地址绝对无误),如果把数据库地址改成本地的mysql地址,可以正常连接(不用通过代理)…

mac使用mvn下载node-sass 会Binary download failed, trying source

m1 上使用nvm 以下node的版本可以直接下载&#xff08;Binary download&#xff0c;而不是 trying source&#xff09;而不用切换mac cpu架构 zhiwenwenzhiwenwendeMBP cockpit % nvm install 14.15.5 Downloading and installing node v14.15.5... Downloading https://node…

二十五章:TransUNet:Transformer为医学图像分割提供强大的编码器

0.摘要 医学图像分割是发展医疗系统的重要先决条件&#xff0c;特别是对于疾病诊断和治疗计划。在各种医学图像分割任务中&#xff0c;U型架构&#xff0c;也称为U-Net&#xff0c;已成为事实上的标准&#xff0c;并取得了巨大的成功。然而&#xff0c;由于卷积操作的固有局部性…

Zabbix监控linux主机(agent端)

目录 一、Linux-clinet操作&#xff08;agent&#xff09; 二、源码安装zabbix 三、Zabbix添加linux主机 为agent.zabbix.com添加模板 等待一会 查看效果如下 一、Linux-clinet操作&#xff08;agent&#xff09; [rootlocalhost ~]# ifconfig ens33[rootlocalhost ~]# vim…

免费的游戏图标素材库分享

游戏图标设计在游戏UI中占有非常重要的地位。例如&#xff0c;当我们看到一个游戏的启动图标时&#xff0c;很容易区分它是哪个游戏。设计游戏图标不仅是一个图形&#xff0c;也是一个标志。 本文将通过各种游戏图标设计素材分享游戏图标的类别和设计游戏图标的思考。 1. 游戏…

程序员基础知识—IP地址

文章目录 一、什么是IP地址二、IP地址的分类三、子网掩码 一、什么是IP地址 IP地址就像我们需要打电话时的电话号码一样&#xff0c;它用来标识网络中的一台主机&#xff0c;每台主机至少有一个IP地址&#xff0c;而且这个IP地址是全网唯一的。IP地址由网路号和主机号两部分组…

vue 3.0 如何加载图片

.logo { background: url(~/assets/images/logo.svg) no-repeat center center/contain; width: 117px; height: 24px; margin: 0 20px; } <a class"logo" href"#"></a> 比较实用的书写方式

小程序制作教程

步骤一&#xff1a;规划和设计 在开始制作微信小程序之前&#xff0c;首先需要规划和设计您的小程序。确定您想要提供的服务或功能&#xff0c;并考虑用户体验和界面设计。绘制草图和构思完整的页面布局&#xff0c;这将使您更好地理解小程序结构和功能。 步骤二&#xff1a;…

Arrays.asList

文章目录 摘要详解我们再去看看 java.util.ArrayList 为什么可变的呢&#xff1f;Arrays.asList()和 Collections.singletonList()额外&#xff1a;Collections.singletonList() 摘要 先总结要点&#xff0c;接下来详细讲解 返回由指定数组支持的长度不可变的列表&#xff0c…

题目3 文件包含(保姆级教程)

url&#xff1a;http://192.168.154.253:83 #打开http://XXX:81/&#xff0c;XXX为靶机的ip地址 审题 1、打开题目看到有一个提示&#xff0c;此题目需要通过利用存在的文件包含漏洞&#xff0c;尝试获取webshell&#xff0c;最后从根目录下key.php文件中获得flag 2、开始答题…

老年公寓人员定位管理系统:提升安全与关怀的智能解决方案

老年公寓作为提供安全居住环境和关怀服务的重要场所&#xff0c;面临着人员管理和安全控制的挑战。为了解决这些问题&#xff0c;老年公寓人员定位管理系统应运而生。基于为提供全面的安全管理和个性化关怀服务&#xff0c;华安联大便通过老年公寓人员定位管理系统的技术原理、…

在react中配置less

第一步&#xff1a;暴露出webpack配置文件 终端命令&#xff1a;npm run eject (此命令一旦运行不可逆) 第二步&#xff1a;安装less以及less-loader npm install less less-loader --save-dev 第三步&#xff1a;修改webpack的配置文件 运行完以上命令后&#xff0c;项目…

文心千帆大模型测评分享,效果超出预期

一、前言 现如今&#xff0c;随着ChatGPT的爆火越来越多的人开始关注人工智能领域了&#xff0c;大家都在尝试使用它来帮助自己在工作上提高效率亦或是解决一些问题。但ChatGPT是有一定的使用门槛的&#xff1a;首先需要我们“科学上网”才能访问&#xff0c;其次GPT4的价格相…

02-线性结构3 Reversing Linked List

第一次提交 第二次 今日积累&#xff1a;while(n--){} n结束的值是-1而不是0 (꒪⌓꒪) code # include <iostream>struct Node {int data;int p_nxt; } L[100000];int main(void) {int p_start; // first node addressint K; int N; //不保真&#xff0c;待会要顺着链表…

刘铁猛C#教程笔记——方法

方法的由来 C#语言和Java语言都是由C语言发展而来&#xff0c;而C语言是由C语言发展而来&#xff0c;C语言全面兼容C语言&#xff0c;在C语言的基础上引入了类的概念&#xff0c;即面相对象程序设计思想的核心内容&#xff0c;C语言不是完全的面相对象程序设计语言&#xff0c…

十一、正则表达式详解:掌握强大的文本处理工具(三)

文章目录 &#x1f340;贪婪模式&#x1f340;应用的场景&#x1f340;总结 &#x1f340;非贪婪模式&#x1f340;应用的场景&#x1f340;总结 &#x1f340;贪婪模式与非贪婪模式在爬虫的应用&#x1f340;转义字符&#x1f340;正则表达式常见函数 &#x1f340;贪婪模式 在…