Day46

Day46

手写Spring-MVC

解决Controller层的方案

思路:监听器在项目启动时DispatherServlet会将controller层的信息记录下来,当前端发送请求的时候DispatherServlet就会根据信息分发给controller层。

准备工作

在这里插入图片描述

准备工作的目的是准备好监听器,而监听器的具体实现是获取controlle层的controller类的信息(用类描述类来封装这些信息,并将URI和描述类整合到集合map中)。

步骤1:自定义注解-作用是包含路径信息、URI、标识controller类等。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Configuration {String value();
}@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
}@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {String value();
}

步骤2:DispatcherServlet如何记录controller层的信息?

-在web.xml配置文件中配置好config配置参数,这个参数可以直接通过请求的getInitParameter()方法获得,于是可以在web项目模块中写一个配置文件的类Appconfig,在web.xml配置文件中直接写明这个配置文件类的路径,这样在项目启动时可以直接访问到这个类。

而写配置文件类的目的是为了通过这个配置文件类拿到web项目中controller层的信息(路径),这样在java模块中就可以访问到controller层了。(具体实现是利用反射,拿到类对象,再拿到注解里写好的路径)。

在这一步中需要写好配置类以及配置文件:

package com.qf.shop.web.config;import com.qf.shop.mvc.annotation.Configuration;/*** 当前项目的配置类*/
@Configuration("com.qf.shop.web.controller")
public class AppConfig {
}

配置类中是第一步自定义的configuration注解,此注解的目的就是给出当前web项目中controller层的路径。

<context-param><param-name>config</param-name><param-value>com.qf.shop.web.config.AppConfig</param-value></context-param>

步骤3: 为创建监听器准备条件,即写好类描述类,而来描述类中又需要方法描述类,方法描述类中需要参数描述类集合。

package com.qf.shop.mvc.model;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;/*** 类描述类*/
@NoArgsConstructor
@AllArgsConstructor
@Data
public class BeanDefinition<T> {private String requestMappingPath;//父级URiprivate Class<?> clazz;//Controller类的class对象private String name;//类名private T t;//Controller类对象private MethodDefinition methodDefinition;//方法描述类对象
}
package com.qf.shop.mvc.model;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.lang.reflect.Method;
import java.util.List;/*** 方法描述类*/
@NoArgsConstructor
@AllArgsConstructor
@Data
public class MethodDefinition {private String requestMappingPath;//子级URiprivate String name;//方法名private Method method;//方法对象private Class<?> returnClazz;//返回值类型private List<ParameterDefinition> parameterDefinitions;//参数描述类对象的集合
}
package com.qf.shop.mvc.model;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@NoArgsConstructor
@AllArgsConstructor
@Data
public class ParameterDefinition {private String name;//参数名private Class<?> clazz;//参数类型private int index;//参数下标}

注意:此处用到了lombok插件,该插件提供了通过注解自动编写有参、无参、getset方法及toString方法的功能。可以在IDEA中的file->settings->plugin中搜索下载。

步骤4:因为可能出现配置信息有误、配置信息残缺、class文件转换异常、uri映射错误、requestMapping配置错误等等情况,所以写一个错误码接口及其实现枚举类;

对于所有可能出现的异常写一个异常类,继承RuntimeException非受检性异常,抛出枚举类中对应的错误码和错误信息。

又因为监听器最后的目的是将信息存储到集合中,这些信息应该是共享的,所以要将map放进一个静态类的静态map属性中。

错误码接口及其实现类:

package com.qf.shop.mvc.constant;/*** 错误码的接口*/
public interface ResponseCodeInterface {int getCode();void setCode(int code);String getMessage();void setMessage(String message);
}
package com.qf.shop.mvc.constant;public enum ResponseCode implements ResponseCodeInterface{CONFIG_EXCEPTION(100,"config的配置信息出错"),CONFIGURATION_EXCEPTION(101,"需要配置Configuration这个注解"),CLASS_FILE_EXCEPTION(102,"class文件转换异常"),REQUEST_MAPPING_PATH_EXCEPTION(103,"RequestMapping地址设置有误"),REQUEST_PATH_EXCEPTION(104,"uri映射错误"),EXCEPTION_CONFIG_EXCEPTION(105,"未配置全局异常的路径"),ADVISER_CONFIG_EXCEPTION(106,"未配置处理器的路径");private int code;private String message;ResponseCode(int code, String message) {this.code = code;this.message = message;}@Overridepublic int getCode() {return code;}@Overridepublic void setCode(int code) {this.code = code;}@Overridepublic String getMessage() {return message;}@Overridepublic void setMessage(String message) {this.message = message;}
}

异常信息类:

package com.qf.shop.mvc.exception;import lombok.AllArgsConstructor;
import lombok.Data;@AllArgsConstructor
@Data
public class FrameWorkException extends RuntimeException{private int code;private String message;
}

容器类:

package com.qf.shop.mvc.container;import com.qf.shop.mvc.model.BeanDefinition;import java.util.HashMap;public class TypeContainer {private static HashMap<String, BeanDefinition> maps = null;static{maps=new HashMap<>();}public static HashMap<String, BeanDefinition> getMaps() {return maps;}public static void setMaps(HashMap<String, BeanDefinition> maps) {TypeContainer.maps = maps;}
}

步骤5:创建监听器。

思路:通过config配置参数获取配置文件类对象,然后获取其注解信息,通过信息找到controller层路径,但是此时不知道controller层下面的类信息。所以要先通过全局域对象获取项目发布的绝对路径,然后和层拼接成层的绝对路径,再封装为File对象,利用listFiles()函数获取文件下面的子文件对象,整合到列表中,再对每个file文件更改路径,利用反射再次转换回Class对象,这样就获得了controller层下面的类对象。

获得类对象后对于每个类对象遍历,依次获得类描述类、方法描述类、参数描述类(多层for循环)的属性,然后进行封装,再调用静态容器整合。

package com.qf.shop.mvc.listerner;import com.qf.shop.mvc.annotation.Configuration;
import com.qf.shop.mvc.annotation.Controller;
import com.qf.shop.mvc.annotation.RequestMapping;
import com.qf.shop.mvc.constant.ResponseCode;
import com.qf.shop.mvc.container.TypeContainer;
import com.qf.shop.mvc.exception.FrameWorkException;
import com.qf.shop.mvc.model.BeanDefinition;
import com.qf.shop.mvc.model.MethodDefinition;
import com.qf.shop.mvc.model.ParameterDefinition;import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;public class ApplicationListener implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent sce) {/** 扫描web项目的Controller层* 封装成类描述类的对象* 添加到容器中 -- Key(父级uri+子级uri) Value(类描述类的对象)*/System.out.println("项目启动");//获取web项目的配置文件全路径ServletContext servletContext = sce.getServletContext();String config = servletContext.getInitParameter("config");if(config==null){throw new FrameWorkException(ResponseCode.EXCEPTION_CONFIG_EXCEPTION.getCode(),ResponseCode.EXCEPTION_CONFIG_EXCEPTION.getMessage());}//获取配置文件的class对象Class<?> configClass = getConfigClass(config);//获取扫描Controller层的位置String controllerPosition = getControllerPosition(configClass);//拼接Web项目中Controller层的绝对路径String filePath = getControllerPath(servletContext, controllerPosition);//获取controller层的所有文件:fileListList<File> fileList= new ArrayList<>();getFileList(filePath,fileList);//将fileList转换为classListList<Class<?>> classList = tramToClassList(servletContext, fileList);//将类对象封装为描述类对象,并添加到容器中handlerClassList(classList);Set<Map.Entry<String, BeanDefinition>> entries = TypeContainer.getMaps().entrySet();for (Map.Entry<String, BeanDefinition> entry : entries) {System.out.println(entry);}}public void handlerClassList(List<Class<?>> classList){for (Class<?> clazz : classList) {try {String clazzName = clazz.getName();//类名Object t = clazz.newInstance();//类对象RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);if(requestMapping==null){throw new FrameWorkException(ResponseCode.REQUEST_MAPPING_PATH_EXCEPTION.getCode(),ResponseCode.REQUEST_MAPPING_PATH_EXCEPTION.getMessage());}String fatherUri = requestMapping.value();//父级URIMethod[] methods = clazz.getDeclaredMethods();for (Method method : methods) {method.setAccessible(true);String methodName = method.getName();//方法名Class<?> returnType = method.getReturnType();//返回值类型RequestMapping methodAnnotation = method.getAnnotation(RequestMapping.class);if(methodAnnotation==null){throw new FrameWorkException(ResponseCode.REQUEST_MAPPING_PATH_EXCEPTION.getCode(),ResponseCode.REQUEST_MAPPING_PATH_EXCEPTION.getMessage());}String sonUri = methodAnnotation.value();//获取子级URIList<ParameterDefinition> parameterList = new ArrayList<>();Parameter[] parameters = method.getParameters();for (int i = 0; i < parameters.length; i++) {String parameterName = parameters[i].getName();//获取参数名Class<?> parameterType = parameters[i].getType();//参数类型int index = i;//下标ParameterDefinition parameterDefinition = new ParameterDefinition(parameterName, parameterType, index);//封装参数描述类对象parameterList.add(parameterDefinition);}MethodDefinition methodDefinition = new MethodDefinition(sonUri, methodName, method, returnType, parameterList);//封装方法描述类对象BeanDefinition<Object> beanDefinition = new BeanDefinition<>(fatherUri, clazz, clazzName, t, methodDefinition);//封装类描述类对象String key = sonUri+fatherUri;//拼接uriHashMap<String, BeanDefinition> map = TypeContainer.getMaps();map.put(key,beanDefinition);//                    Set<Map.Entry<String, BeanDefinition>> entries = map.entrySet();
//                    for (Map.Entry<String, BeanDefinition> entry : entries) {
//                        System.out.println(entry);
//                    }}} catch (InstantiationException | IllegalAccessException e) {throw new RuntimeException(e);}}}public List<Class<?>> tramToClassList(ServletContext servletContext,List<File> fileList){String realPath = servletContext.getRealPath("WEB-INF\\classes");ArrayList<Class<?>> classList = new ArrayList<>();for (File file : fileList) {//F:\apache-tomcat-8.0.49\webapps\ROOT\WEB-INF\classes\com\qf\shop\web\controller\A.classString fileAbsolutePath = file.getAbsolutePath();//\com\qf\shop\web\controller\A.classfileAbsolutePath = fileAbsolutePath.replace(realPath, "");//com\qf\shop\web\controller\AfileAbsolutePath = fileAbsolutePath.substring(1,fileAbsolutePath.lastIndexOf("."));//com.qf.shop.web.controller.AString name = fileAbsolutePath.replace("\\", ".");try {Class<?> clazz = Class.forName(name);Annotation annotation = clazz.getAnnotation(Controller.class);if(annotation!=null){classList.add(clazz);}} catch (ClassNotFoundException e) {throw new RuntimeException(e);}}return classList;}public void getFileList(String filePath,List<File> fileList){File file = new File(filePath);File[] files = file.listFiles();for (File afile : files) {if(afile.isDirectory()){String s = afile.getAbsolutePath();getFileList(s,fileList);}else if(afile.isFile()){fileList.add(afile);}}}public String getControllerPath(ServletContext servletContext,String controllerPosition){//F:\apache-tomcat-8.0.49\webapps\ROOT\WEB-INF\classesString realPath = servletContext.getRealPath("WEB-INF\\classes");//发布路径中的编译后的.class文件路径//com\qf\shop\web\controllercontrollerPosition = controllerPosition.replaceAll("\\.", "\\\\");//F:\apache-tomcat-8.0.49\webapps\ROOT\WEB-INF\classes/com\qf\shop\web\controllerString filePath = realPath + File.separator +controllerPosition;return filePath;}public String getControllerPosition(Class<?> configClass){Configuration configClassAnnotation = configClass.getAnnotation(Configuration.class);if(configClassAnnotation==null){throw new FrameWorkException(ResponseCode.CONFIGURATION_EXCEPTION.getCode(),ResponseCode.CONFIGURATION_EXCEPTION.getMessage());}String controllerPosition = configClassAnnotation.value();return controllerPosition;}public Class<?> getConfigClass(String config){Class<?> appConfigClass = null;try {appConfigClass = Class.forName(config);} catch (ClassNotFoundException e) {throw new RuntimeException(e);}return appConfigClass;}}

注意:由于JVM在编译时不会将方法名编译进class文件中,所以需要maven-compiler-plugin插件,作用:将参数名编译进class文件。

写完监听器后别忘了在web.xml中进行配置

<listener><listener-class>com.qf.shop.mvc.listerner.ApplicationListener</listener-class></listener>

可能遇到的问题

1.发布路径报错

解决方案:将发布路径改为Tomcat的webApps下的ROOT根路径

2.监听器没有工作

解决方案:在web项目的pom.xml中添加java框架项目的依赖

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

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

相关文章

AI-智能体基础设施

个性化记忆需要世界模型来协助构建 业界有一个精简的Agent表达公示&#xff0c;即&#xff1a;Agent大模型&#xff08;LLM&#xff09;记忆&#xff08;Memory&#xff09;主动规划&#xff08;Planning&#xff09;工具使用&#xff08;Tool Use&#xff09;。基于该公式&am…

零信任价值获全面认可 新场景下展现无穷潜力

2023年&#xff0c;零信任在全球范围内持续快速发展&#xff0c;已经从新的安全理念发展成为云时代的主流安全架构&#xff0c;进入了全面普及期。 2023年&#xff0c;中国零信任市场同样涨势迅猛&#xff0c;产业生态越来越成熟&#xff0c;应用范围越来越广&#xff0c;应用…

面试题-Redis简介

1.主流应用框架 概念&#xff1a; 穿透查询&#xff1a;数据库中的名词&#xff0c;与逐层查询不同&#xff0c;通过超链接可直接查询想要的结果&#xff0c;更加方便快捷 熔断机制&#xff1a;指软件系统中&#xff0c;由于某些原因使得服务出现了过载现象&#xff0c;为防止…

「2024中国数据要素产业图谱1.0版」重磅发布,景联文科技凭借高质量数据采集服务入选!

近日&#xff0c;景联文科技入选数据猿和上海大数据联盟发布的《2024中国数据要素产业图谱1.0版》数据采集服务板块。 景联文科技是专业数据服务公司&#xff0c;提供从数据采集、清洗、标注的全流程数据解决方案&#xff0c;协助人工智能企业解决整个AI链条中数据采集和数据标…

Maven高级的多环境配置与应用

多环境配置与应用 这一节中&#xff0c;我们会讲两个内容&#xff0c;分别是多环境开发和跳过测试 5.1 多环境开发 我们平常都是在自己的开发环境进行开发&#xff0c;当开发完成后&#xff0c;需要把开发的功能部署到测试环境供测试人员进行测试使用&#xff0c;等测试人员测…

Redis报错:MISCONF Redis is configured to save RDB snapshots

错误提示内容&#xff1a; 2024-06-25 16:30:49 : Connection: Redis_Server > [runCommand] PING 2024-06-25 16:30:49 : Connection: Redis_Server > Response received : -MISCONF Redis is configured to save RDB snapshots, but it is currently not able to pers…

Qt Quick Effect Maker 工具使用介绍

一、介绍 随着 Qt 版本的不断升级,越来越多的功能被加入 Qt,一些新的工具也随之应运而生,Qt Quick Effect Maker 工具是 Qt 6.5 之后才新添加的工具,之前的名字应该是叫做 Qt shader tool 这个模块。 以下是官方的释义:Qt Quick Effect Maker是一个用于为Qt Quick创建自定…

C语⾔数据类型和变量

C语⾔数据类型和变量 1.数据类型介绍1.1 字符型1.2 整型1.3 浮点型1.4 布尔类型1.5 各种数据类型的长度1.5.1 sizeof操作符1.5.2 数据类型长度1.5.3 sizeof中表达式不计算 2. signed 和 unsigned3. 数据类型的取值范围4. 变量4.1 变量的创建4.2 变量的分类 5. 算术操作符&#…

社区团购小程序开发

在快节奏的现代生活中&#xff0c;人们越来越追求便利与效率。社区团购小程序应运而生&#xff0c;以其独特的优势成为连接社区居民与优质商品的重要桥梁。本文将探讨社区团购小程序的特点、优势以及未来发展趋势&#xff0c;为大家揭示这一新型购物模式的魅力。 社区团购小程序…

MAC 查看公钥私钥

电脑配置过公钥私钥&#xff0c;现在需要查看&#xff1a; 1、 查看本地是否存在SSH密钥 命令&#xff1a;ls -al ~/.ssh 如果在输出的文件列表中发现id_rsa和id_rsa.pub的存在&#xff0c;证明本地已经存在SSH密钥&#xff0c;请执行第3步 2、 生成SSH密钥 命令&#xff1…

一本好的电子画册应这样做,你做对了吗?

​一本好的电子画册&#xff0c;不仅要有吸引人的图文&#xff0c;还可能包括视频、音频等多媒体元素&#xff0c;为读者提供全方位的阅读体验。连贯性是指画册的整体设计风格、内容布局要协调一致&#xff0c;让读者在阅读过程中感受到流畅和自然。创新性则要求创作者在内容呈…

39 - 电影评分(高频 SQL 50 题基础版)

39 - 电影评分 (selectu.name as results fromMovieRating m left join Users u on m.user_idu.user_id GROUP BYm.user_id order by count(*) desc,u.name asc limit 1) union all (selectm1.title as results fromMovieRating m left join Movies m1 on m.movie_idm1.movie…

加速业务布局,30年老将加盟ATFX,掌舵运营新篇章

全球领先的差价合约经纪商ATFX日前宣布了一项重大人事任命&#xff0c;聘请业界资深人士约翰博格(John Bogue)为机构业务运营总监。约翰博格是一名行业老将&#xff0c;曾在差价合约界深耕三十余载。伴随其加入ATFX&#xff0c;相信他的深厚专业知识和从业经验将为ATFX机构业务…

数据分析python基础实战分析

数据分析python基础实战分析 安装python&#xff0c;建议安装Anaconda 【Anaconda下载链接】https://repo.anaconda.com/archive/ 记得勾选上这个框框 安装完后&#xff0c;然后把这两个框框给取消掉再点完成 在电脑搜索框输入"Jupyter"&#xff0c;牛马启动&am…

司美格鲁肽在中国获批!深度解析报告附上

在中国&#xff0c;肥胖问题日益严重&#xff0c;但有效的治疗方法却相对匮乏。然而&#xff0c;这一现状随着国家药品监督管理局&#xff08;NMPA&#xff09;对诺和诺德公司研发的司美格鲁肽注射液&#xff08;商品名&#xff1a;诺和盈&#xff09;的批准而得到改变。6月25日…

LabVIEW中卡尔曼滤波的作用与意义

卡尔曼滤波&#xff08;Kalman Filter&#xff09;是一种在控制系统和信号处理领域广泛应用的递推滤波算法&#xff0c;能够在噪声环境下对动态系统的状态进行最优估计。其广泛应用于导航、目标跟踪、图像处理、经济预测等多个领域。本文将详细介绍卡尔曼滤波在LabVIEW中的作用…

pytorch基础知识Tensor算术运算

1、Tensor的基本概念 标量是零维的张量&#xff0c;向量是一维的张量&#xff0c;矩阵是二维的张量 2、Tensor的创建 import torch"""常见的几个tensor创建""" a torch.Tensor([[1,2],[3,4]]) #2行2列的 print(a, a.type()) print(torch.on…

大数据平台需要存算分离吗?某保险集团:以 ZBS 优化资源利用率,缩短业务用时超一半

金融机构普遍采用“存算一体”架构支撑基于 Hadoop 框架的大数据平台。而随着金融业务的多元化发展&#xff0c;不同业务对计算和存储的需求差异较大&#xff0c;由于“存算一体”架构共享存储与计算资源&#xff0c;经常会出现资源需求不均衡、资源利用率低下、难以灵活调度等…

c++网络通信

TCP/IP协议 OSI参考模型采用分层划分原则&#xff0c;将网络中的数据传输划分为7层&#xff0c;其中&#xff0c;物理层居于最下层&#xff0c;是最基础、核心的网络硬件层&#xff1b;应用层居于最上层&#xff0c;负责应用资源的管理。每一层使用下层的服务&#xff0c;并向…

程序设计语言前言

1.机器语言及特点 2.编译语言及特点 3.高级语言及特点 4.编译和解释 5.IPO编程方式 一、机器语言 机器语言&#xff0c;也被称为二进制代码语言&#xff0c;是计算机硬件能够直接识别的程序语言或指令代码。它是由一系列由0和1组成的二进制指令码构成&#xff0c;每一条指令码…