手写SpringMVC之ApplicationContextListener

news/2024/12/22 20:25:25/

什么是Spring MVC?

Spring Web MVC是基于Servlet API构建的原始Web框架,从一开始就包含在Spring Framework中。正式名称“Spring Web MVC”来自其源模块的名称( spring-webmvc ),但它通常被称为“Spring MVC”。

手写Spring MVC(一)

创建项目

创建父工程 [ 选择9号模板 :site-simple],命名为shop(以商城为例子)

创建子工程 [ 选择10号模板:webapp],命名为shop-web

创建子工程 [ 选择7号模板:quickstart],命名为shop-mvc

依赖准备

servlet-api
<dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.1</version><scope>provided</scope>
</dependency>
lombok
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.24</version>
</dependency>
编译插件
<build><finalName>shop_web</finalName><pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (maybe moved to parent pom) --><plugins><!--这个插件就是java类生成class的编译插件--><plugin><artifactId>maven-compiler-plugin</artifactId><configuration><!--编译参数--><compilerArgument>-parameters</compilerArgument><source>1.8</source><target>1.8</target></configuration></plugin></plugins></pluginManagement>
</build>

创建文件

  1. 在shop-web工程下创建以下目录

    image-20240626170051128

  2. 在mvc工程下创建以下目录

    image-20240626170120536

文件详解

mvc工程
  1. cn.cnmd.shop.mvc.annotation:主要是注解,包括控制器、路由映射、配置

    1. cn.cnmd.shop.mvc.annotation.Controller
    java">package cn.cnmd.shop.mvc.annotation;import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;@Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Controller {
    }
    
    1. cn.cnmd.shop.mvc.annotation.RequestMapping
    java">package cn.cnmd.shop.mvc.annotation;import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;@Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface RequestMapping {String value();
    }
    
    1. cn.cnmd.shop.mvc.annotation.Configuration
    java">package cn.cnmd.shop.mvc.annotation;import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;@Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Configuration {String value();
    }
    
  2. cn.cnmd.shop.mvc.canstant:包含了常见的错误码和错误信息

    1. cn.cnmd.shop.mvc.canstant.ResponseCodeInterface

      java">package cn.cnmd.shop.mvc.constant;public interface ResponseCodeInterface {int getCode();String getMessage();void setCode(int code);void setMessage(String message);
      }
      
    2. cn.cnmd.shop.mvc.canstant.ResponseCode

      java">package cn.cnmd.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 String getMessage() {return message;}@Overridepublic void setCode(int code) {this.code = code;}@Overridepublic void setMessage(String message) {this.message = message;}
      }
  3. cn.cnmd.shop.mvc.container:容器,用于存储项目启动之后创建的BeanDefinition对象

    1. cn.cnmd.shop.mvc.container.BeanContainer

      java">package cn.cnmd.shop.mvc.container;import cn.cnmd.shop.mvc.model.BeanDefinition;
      import lombok.Getter;import java.util.Map;
      import java.util.concurrent.ConcurrentHashMap;public class BeanContainer {@Getterprivate static Map<String, BeanDefinition<?>> maps = null;static {maps = new ConcurrentHashMap<>();}
      }
  4. cn.cnmd.shop.mvc.exception:异常类,用于处理异常

    1. cn.cnmd.shop.mvc.exception.FrameWorkException

      java">package cn.cnmd.shop.mvc.exception;import lombok.AllArgsConstructor;
      import lombok.Data;@AllArgsConstructor
      @Datapublic class FrameWorkException extends RuntimeException {private int code;private String message;
      }
      
  5. cn.cnmd.shop.mvc.listener:监听器,主要作用是在项目启动时扫描controller下的文件生成BeanDefinition对象

    1. cn.cnmd.shop.mvc.listener.ApplicationListener

      java">package cn.cnmd.shop.mvc.listener;import cn.cnmd.shop.mvc.annotation.Configuration;
      import cn.cnmd.shop.mvc.annotation.RequestMapping;
      import cn.cnmd.shop.mvc.constant.ResponseCode;
      import cn.cnmd.shop.mvc.container.BeanContainer;
      import cn.cnmd.shop.mvc.exception.FrameWorkException;
      import cn.cnmd.shop.mvc.model.BeanDefinition;
      import cn.cnmd.shop.mvc.model.MethodDefinition;
      import cn.cnmd.shop.mvc.model.ParameterDefinition;import javax.servlet.ServletContext;
      import javax.servlet.ServletContextEvent;
      import javax.servlet.ServletContextListener;
      import java.io.File;
      import java.lang.reflect.Method;
      import java.lang.reflect.Parameter;
      import java.util.ArrayList;
      import java.util.List;
      import java.util.Map;public class ApplicationListener implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent sce) {/** 扫描web项目的Controller层* 封装成类描述类的对象* 添加到容器中 -- Key(父级uri+子级uri) Value(类描述类的对象)*/ServletContext servletContext = sce.getServletContext();String config = servletContext.getInitParameter("config");//cn.cnmd.config.AppConfigif (config == null) {throw new FrameWorkException(ResponseCode.CONFIG_EXCEPTION.getCode(), ResponseCode.CONFIG_EXCEPTION.getMessage());}//获取配置类的class对象Class<?> configurationClass = getConfiguration(config);//通过配置类的class对象拿到 AppConfig注解 中的cn.cnmd.controllerString controllerPosition = getControllerPosition(configurationClass);//D:\Desktop\code\apache-tomcat-8.0.49\webapps\ROOT\WEB-INF\classes\cn\cnmd\controllerString controllerAbsolutePath = getControllerAbsolutePath(servletContext, controllerPosition);//获取controller文件夹下所有的文件List<File> fileList = new ArrayList<>();findFileByPath(fileList, controllerAbsolutePath);//文件对象集合 --> class对象集合List<Class<?>> classes = transformTo(servletContext, fileList);//封装成类描述类的对象: BeanDefinition -> MethodDefinition -> ParameterDefinitionhandleController(classes);}/*** 通过web.xml的配置* <context-param>* <param-name>config</param-name>* <param-value>cn.cnmd.config.AppConfig</param-value>* </context-param>* 参数 [config] 获取的 [cn.cnmd.config.AppConfig] 获取配置文件类的class对象** @param config [cn.cnmd.config.AppConfig]* @return class对象*/public Class<?> getConfiguration(String config) {try {return Class.forName(config);} catch (ClassNotFoundException e) {throw new RuntimeException(e);}}/*** 通过配置文件类的class对象获取注解中的controller的类的全限定名 [cn.cnmd.controller]** @param configurationClass 配置文件类的class对象* @return controller的类的全限定名*/public String getControllerPosition(Class<?> configurationClass) {Configuration annotation = configurationClass.getAnnotation(Configuration.class);if (annotation == null) {throw new FrameWorkException(ResponseCode.CONFIGURATION_EXCEPTION.getCode(), ResponseCode.CONFIGURATION_EXCEPTION.getMessage());}return annotation.value();}/*** 通过配置文件类 [AppConfig] 的注解信息 [cn.cnmd.controller] 获取controller包的发布路径** @param servletContext     servlet上下文对象* @param controllerPosition 配置文件类 [AppConfig] 的注解信息 [cn.cnmd.controller]* @return controller包的发布路径*/public String getControllerAbsolutePath(ServletContext servletContext, String controllerPosition) {//D:\Desktop\code\apache-tomcat-8.0.49\webapps\ROOT\WEB-INF\classesString absolutePath = servletContext.getRealPath("WEB-INF" + File.separator + "classes");controllerPosition = controllerPosition.replace(".", File.separator);return absolutePath + File.separator + controllerPosition;}/*** 通过controller包路径找到路径下所有文件对象** @param fileList               文件对象集合* @param controllerAbsolutePath controller包的发布路径*/public void findFileByPath(List<File> fileList, String controllerAbsolutePath) {File file = new File(controllerAbsolutePath);File[] files = file.listFiles();if (files != null) {for (File f : files) {if (f.isDirectory()) {findFileByPath(fileList, f.getAbsolutePath());} else if (f.isFile()) {fileList.add(f);}}}}public List<Class<?>> transformTo(ServletContext servletContext, List<File> fileList) {List<Class<?>> classes = new ArrayList<>();for (File file : fileList) {//D:\Desktop\code\apache-tomcat-8.0.49\webapps\ROOT\WEB-INF\classes\cn\cnmd\controller\UserController.classString classPath = file.getAbsolutePath();String absolutePath = servletContext.getRealPath("WEB-INF" + File.separator + "classes");//cn\cnmd\controller\UserController.class --> cn.cnmd.controller.UserControllerclassPath = classPath.substring(absolutePath.length() + 1).split("\\.")[0].replace("\\", ".");System.out.println(classPath);Class<?> clazz = null;try {clazz = Class.forName(classPath);} catch (ClassNotFoundException e) {throw new RuntimeException(e);}classes.add(clazz);}return classes;}public void handleController(List<Class<?>> classes) {Map<String, BeanDefinition<?>> maps = BeanContainer.getMaps();for (Class<?> clazz : classes) {try {//父级url requestMappingPath --> requestMapping.value()RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);String requestMappingPath1 = requestMapping.value();//类名 beanName --> clazz.getName()String beanName = clazz.getName();//controller的类对象 t --> clazz.newInstance()Object t = clazz.newInstance();//方法描述对象Method[] methods = clazz.getMethods();for (Method method : methods) {method.setAccessible(true);if (!method.isAnnotationPresent(RequestMapping.class)) {continue;}RequestMapping requestMapping1 = method.getAnnotation(RequestMapping.class);//子级url requestMappingPath --> requestMapping1.value()String requestMappingPath2 = requestMapping1.value();//方法名 methodName --> method.getName()String methodName = method.getName();//方法对象 method --> method//返回值类型 returnType -->  method.getReturnType()Class<?> returnType = method.getReturnType();//参数描述对象Parameter[] parameters = method.getParameters();List<ParameterDefinition> parameterDefinitions = new ArrayList<>();for (int i = 0; i < parameters.length; i++) {Class<? extends Parameter> paramClass = parameters[i].getClass();//参数名 parameterName --> paramClass.getName()String parameterName = paramClass.getName();//参数类型 type --> paramClass//参数下标 index --> iParameterDefinition parameterDefinition = new ParameterDefinition(parameterName, paramClass, i);parameterDefinitions.add(parameterDefinition);}MethodDefinition methodDefinition = new MethodDefinition(requestMappingPath2, methodName, method, returnType, parameterDefinitions);BeanDefinition<?> beanDefinition = new BeanDefinition<>(requestMappingPath1, beanName, clazz, t, methodDefinition);String route = requestMappingPath1 + File.separator + requestMappingPath2;maps.put(route, beanDefinition);}} catch (InstantiationException | IllegalAccessException e) {throw new RuntimeException(e);}}}
      }
  6. cn.cnmd.shop.mvc.model:类描述类,包括参数描述类、方法描述类、类描述类

    1. cn.cnmd.shop.mvc.model.ParameterDefinition

      java">package cn.cnmd.shop.mvc.model;import com.sun.istack.internal.NotNull;
      import lombok.AllArgsConstructor;
      import lombok.Data;
      import lombok.NoArgsConstructor;/*** 参数描述类*/@NoArgsConstructor
      @AllArgsConstructor
      @Data
      public class ParameterDefinition {private String name;//参数名private Class<?> type;//参数类型private int index;//参数下标
      }
    2. cn.cnmd.shop.mvc.model.MethodDefinition

      java">package cn.cnmd.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;//子级urlprivate String methodName;//方法名private Method method;//方法对象private Class<?> returnType;//返回值类型private List<ParameterDefinition> parameters;//参数描述列表
      }
    3. cn.cnmd.shop.mvc.model.BeanDefinition

      java">package cn.cnmd.shop.mvc.model;import lombok.AllArgsConstructor;
      import lombok.Data;
      import lombok.NoArgsConstructor;@NoArgsConstructor
      @AllArgsConstructor
      @Data
      public class BeanDefinition<T> {private String requestMappingPath;//父级urlprivate String name;//类名private Class<?> beanClass;//controller类的class对象private T t;//controller类对象private MethodDefinition methodDefinition;//方法描述对象
      }
      
web工程
  1. cn.cnmd.config

    1. cn.cnmd.config.AppConfig

      java">package cn.cnmd.config;import cn.cnmd.shop.mvc.annotation.Configuration;@Configuration("cn.cnmd.controller")
      public class AppConfig {}
      
  2. cn.cnmd.controller:存放各种controller

    1. cn.cnmd.controller.UserController

      java">package cn.cnmd.controller;import cn.cnmd.pojo.User;
      import cn.cnmd.shop.mvc.annotation.Controller;
      import cn.cnmd.shop.mvc.annotation.RequestMapping;@Controller
      @RequestMapping("user")
      public class UserController {@RequestMapping("login1")public void login(String username, String password) {}@RequestMapping("login2")public void login(User user) {}public void method(String name){}
      }
      
    2. cn.cnmd.controller.back.AdminController

      java">package cn.cnmd.controller.back;import cn.cnmd.shop.mvc.annotation.Controller;
      import cn.cnmd.shop.mvc.annotation.RequestMapping;@Controller
      @RequestMapping("admin")
      public class AdminController {@RequestMapping("login")public void login(String name, String password){}
      }
  3. cn.cnmd.pojo:用于存放web项目中的JavaBean对象

    1. cn.cnmd.pojo.User

      java">package cn.cnmd.pojo;import lombok.AllArgsConstructor;
      import lombok.Data;
      import lombok.NoArgsConstructor;@NoArgsConstructor
      @AllArgsConstructor
      @Data
      public class User {String name;int age;String sex;
      }
      

ApplicationContextListener扫描过程(个人理解)

applicationContextListener扫描

roller
@RequestMapping(“admin”)
public class AdminController {

	    @RequestMapping("login")public void login(String name, String password){}}```
  1. cn.cnmd.pojo:用于存放web项目中的JavaBean对象

    1. cn.cnmd.pojo.User

      java">package cn.cnmd.pojo;import lombok.AllArgsConstructor;
      import lombok.Data;
      import lombok.NoArgsConstructor;@NoArgsConstructor
      @AllArgsConstructor
      @Data
      public class User {String name;int age;String sex;
      }
      

ApplicationContextListener扫描过程(个人理解)

[外链图片转存中…(img-wN91i1Ja-1719492618962)]

image-20240626191314663

http://www.ppmy.cn/news/1471958.html

相关文章

HarmonyOS Next开发学习手册——代码混淆

代码混淆简介 针对工程源码的混淆可以降低工程被破解攻击的风险&#xff0c;缩短代码的类与成员的名称&#xff0c;减小应用的大小。 DevEco Studio提供代码混淆的能力并默认开启&#xff0c;API 10及以上版本的Stage模型、 编译模式为release 时自动进行代码混淆。 使用约束…

4.1 四个子空间的正交性

一、四个子空间的正交性 如果两个向量的点积为零&#xff0c;则两个向量正交&#xff1a; v ⋅ w v T w 0 \boldsymbol v\cdot\boldsymbol w\boldsymbol v^T\boldsymbol w0 v⋅wvTw0。本章着眼于正交子空间、正交基和正交矩阵。两个子空间的中的向量&#xff0c;一组基中的向…

win11记事本错误打开一次非常多的文件再次使用时造成卡住

错误地不小心一次性打开数十个数百个文件造成再次使用时&#xff0c;文件卡住。 亲测有效。

【Docker】Docker简介_运行原理

1、简介 1.1基本概念 容器&#xff1a;容器是Docker的基本部署单元。它是一个轻量级的、独立的运行时环境&#xff0c;包含应用程序及其相关依赖。容器利用Linux内核的命名空间和控制组技术&#xff0c;实现了隔离性和资源管理&#xff0c;使得应用程序在不同的容器中运行不会…

HTML静态网页成品作业(HTML+CSS)——企业摄影网介绍网页(3个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有3个页面。 二、作品演示 三、代…

Ipython使用技巧整理

IPython是一个增强的Python交互式shell&#xff0c;提供了许多有用的特性和快捷键&#xff0c;以便更有效地进行编程和数据科学工作。以下是一些IPython的使用技巧&#xff0c;按照不同的功能点进行分类和整理&#xff1a; 1. 内省和查询 打印IPython简介&#xff1a;在IPyth…

【Unity设计模式】观察者模式,发布订阅模式,事件总线

前言 最近在学习Unity游戏设计模式&#xff0c;看到两本比较适合入门的书&#xff0c;一本是unity官方的 《Level up your programming with game programming patterns》 ,另一本是 《游戏编程模式》 这两本书介绍了大部分会使用到的设计模式&#xff0c;因此很值得学习 本…

开源“卖货主播”AI大模型——拳打李佳琦、脚踢小杨哥、人人都能当销冠?

开源“卖货主播”AI大模型——拳打李佳琦、脚踢小杨哥、人人都能当销冠&#xff1f; 刚刚在知名同性交友平台发现了一个或许能让你致富的开源项目——“Streamer-Sales 销冠”。 正如其名字所言&#xff0c;这是一个卖货主播LLM大模型&#xff0c;旨在让你成为销冠。 https:/…