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

目录

      • 简介
      • 思路
      • 实践
        • 一、自定义注解@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.mfbz.cn/a/43916.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C#is、as关键字及获取当前活动窗体的实例

这篇日志记录一下C#中is关键字及as关键字的用法。 Is&#xff1a;判断检查对象是否与给定类型兼容 As&#xff1a;将对象转换为指定类型&#xff08;强转&#xff09;&#xff0c;就跟&#xff08;int&#xff09;这样的用法是一样的。 获取当前窗体的活动子窗体。 有一个属…

MATLAB与ROS联合仿真——Simulink生成ROS代码

当我们用simulink完成控制程序的搭建后&#xff0c;我们期望下一次可以直接对ROS进行控制&#xff0c;而不是每次都需要启动matlab和simulink&#xff0c;因此我们可以使用simulink的代码生成器&#xff0c;生成ROS代码 1、生成代码前需要进行如下的设置 &#xff08;1&#xf…

Thanos工作原理及组件简介

Thanos 简介 Thanos 是一个「开源的&#xff0c;高可用的 Prometheus 系统&#xff0c;具有长期存储能力」。很多知名公司都在使用 Thanos&#xff0c;也是 CNCF 孵化项目的一部分。 Thanos 的一个主要特点就是通过使用对象存储&#xff08;比如 S3&#xff09;可以允许 “无…

Training-Time-Friendly Network for Real-Time Object Detection 论文学习

1. 解决了什么问题&#xff1f; 目前的目标检测器很少能做到快速训练、快速推理&#xff0c;并同时保持准确率。直觉上&#xff0c;推理越快的检测器应该训练也很快&#xff0c;但大多数的实时检测器反而需要更长的训练时间。准确率高的检测器大致可分为两类&#xff1a;推理时…

Sentinel nacos spring cloud 持久化配置---分布式/微服务流量控制

文章目录 sentinel控制台安装目标实现代码地址版本说明maven spring-cloud-starter-alibaba-sentinel依赖yml文件Nacos业务规则配置看源码配置规则SentinelProperties 总配置加载DataSourcePropertiesConfiguration 配置标准的nacos配置注册具体sentinel配置 外传 sentinel控制…

MySQL:MHA高可用

目录 1&#xff0e;什么是 MHA 2&#xff0e;MHA 的组成 3&#xff0e;MHA 的特点 4、MHA工作原理 5、搭建 MySQL MHA 5.1 实验思路 5.1.1 MHA架构 5.1.2 故障模拟 5.2 实验环境 5.3 准备工作 5.4 安装MHA所有组件与测试 5.4.1 安装 MHA 软件 5.4.2 manager与node工…

OpenCV:图像直方图计算

图像直方图为图像中像素强度的分布提供了有价值的见解。通过了解直方图&#xff0c;你可以获得有关图像对比度、亮度和整体色调分布的信息。这些知识对于图像增强、图像分割和特征提取等任务非常有用。 本文旨在为学习如何使用 OpenCV 执行图像直方图计算提供清晰且全面的指南。…

JVM系统优化实践(22):GC生产环境案例(五)

您好&#xff0c;这里是「码农镖局」CSDN博客&#xff0c;欢迎您来&#xff0c;欢迎您再来&#xff5e; 除了Tomcat、Jetty&#xff0c;另一个常见的可能出现OOM的地方就是微服务架构下的一次RPC调用过程中。笔者曾经经历过的一次OOM就是基于Thrift框架封装出来的一个RPC框架导…

swagger对json数据的处理

在实习中遇到了一个不寻常的事情&#xff0c;今天和同事讨论一个小问题&#xff0c;同事使用swagger&#xff0c;想要调用一个接口&#xff0c;这个接口要传递一个json对象&#xff0c;对应java的一个实体类&#xff0c;但是有一个属性同事不想看到它&#xff0c;就用JsonIgnor…

【学会动态规划】按摩师(11)

目录 动态规划怎么学&#xff1f; 1. 题目解析 2. 算法原理 1. 状态表示 2. 状态转移方程 3. 初始化 4. 填表顺序 5. 返回值 3. 代码编写 写在最后&#xff1a; 动态规划怎么学&#xff1f; 学习一个算法没有捷径&#xff0c;更何况是学习动态规划&#xff0c; 跟我…

【Linux】信号保存信号处理

前言&#xff1a;对信号产生的思考 上一篇博客所说的信号产生&#xff0c;最终都要有OS来进行执行&#xff0c;为什么&#xff1f;OS是进程的管理者&#xff01;信号的处理是否是立即处理的&#xff1f;在合适的时候 -》那什么是合适的时候&#xff1f;信号如图不是被立即处理…

企业知识管理系统安全是重中之重

企业开展知识管理工作的益处是全方位的&#xff0c;效果能从业务的各方面得到体现&#xff0c;最终效果就是企业竞争力的提升与企业经营业绩的提升。 知识管理系统的意义在于&#xff0c;构建系统的知识库&#xff0c;对纷杂的知识内容&#xff08;方案、策划、制度等&#xf…

IDE/mingw下动态库(.dll和.a文件)的生成和部署使用(对比MSVC下.dll和.lib)

文章目录 概述问题的产生基于mingw的DLL动态库基于mingw的EXE可执行程序Makefile文件中使用Qt库的\*.a文件mingw下的*.a 文件 和 *.dll 到底谁起作用小插曲 mingw 生成的 \*.a文件到底是什么为啥mingw的dll可用以编译链接过程转换为lib引导文件 概述 本文介绍了 QtCreator mi…

MySQL的基本概念(数据库类、数据模型、服务启动与连接)

目录 数据库基础 DB和DBMS 数据库的类型 RDBMS的结构 MySQL的服务启动与连接&#xff08;Windows系统下&#xff09; 服务启动 客户端连接 数据库基础 DB和DBMS 什么是DB 将大量的数据保存起来&#xff0c;通过计算机加工而成的可以进行高效访问的数据集合就成为数据…

cc2500 怎么计算理论输出频率

一、需要已知的数据 外部晶振频率&#xff08;26MHz -28MHz&#xff09;FREQ2 - 频率控制词汇&#xff0c;高字节FREQ1 - 频率控制词汇&#xff0c;中字节FREQ0 - 频率控制词汇&#xff0c;低字节 二、计算方法 1. 公式 Fxosc 指的是外部晶振的频率。FREQ [ 23 : 0 ] : 指的…

Navicat远程连接服务器失败 2002 - Can‘t connect to server on ...(10060)

报错如下&#xff1a; 2002 - Can’t connect to server on ‘192.168.33.59’(10060) 解决方案&#xff1a; 下面列举可能出现的几种情况&#xff1a; 1.防火墙原因&#xff0c;需要关闭防火墙 systemctl stop firewalld systemctl disable firewalld2.数据库未开启&#x…

【OpenCV】常见问题及解决办法

文章目录 0 前言1 中文乱码问题2 非法路径问题 0 前言 本篇博客主要是总结OpenCV使用过程中遇到的一些问题&#xff0c;以及对应的解决办法&#xff0c;这里重点是关注OpenCV&#xff0c;既有基于C的&#xff0c;也有基于Python的&#xff0c;比较全面&#xff0c;而且也会随着…

格式工厂5.10.0版本安装

目前格式工厂有很多&#xff0c;大多都可以进行视频转换 之前遇到一个用ffmpeg拉流保存的MP4在vlc和迅雷都无法正常播放的问题&#xff0c;发现视频长度不对&#xff0c;声音也不对&#xff0c;最后换到了格式工厂的格式播放器是可以正常播放的 格式工厂下载之家的地址 http…

【docker】docker

目录 一、docker概念二、docker安装(centos7)三、docker架构3.1 镜像image3.2 容器container 四、配置docker镜像加速器五、docker命令5.1 docker服务命令5.2 docker镜像命令5.3 docker容器命令 六、docker容器的数据卷6.1 容器卷概念及作用6.2 配置数据卷6.3 挂载示例6.4 数据…