commit 866800979cd52eab8df079029f55571000409f3f
Author: fuck_zhn <2218834824@qq.com>
Date: Mon Oct 30 09:01:55 2023 +0800
手写springmvc
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5ff6309
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,38 @@
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### IntelliJ IDEA ###
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+*.iws
+*.iml
+*.ipr
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store
\ No newline at end of file
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..35410ca
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..47c8e19
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..8d66637
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..544713a
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml
new file mode 100644
index 0000000..2b63946
--- /dev/null
+++ b/.idea/uiDesigner.xml
@@ -0,0 +1,124 @@
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/REDME.md b/REDME.md
new file mode 100644
index 0000000..dcf7e3f
--- /dev/null
+++ b/REDME.md
@@ -0,0 +1,49 @@
+是的,Spring MVC 项目通常是Web项目,用于构建Web应用程序。它基于MVC(Model-View-Controller)模式,用于处理HTTP请求和响应。
+
+下面是一个典型的Spring MVC项目的整体文件目录结构示例:
+
+```
+- src
+ - main
+ - java
+ - com
+ - example
+ - controller
+ - HomeController.java # 控制器类
+ - model
+ - User.java # 模型类
+ - repository
+ - UserRepository.java # 数据访问接口
+ - service
+ - UserService.java # 服务接口
+ - UserServiceImpl.java # 服务实现类
+ - config
+ - WebConfig.java # Spring MVC配置类
+ - resources
+ - static # 静态资源文件夹(如CSS、JavaScript等)
+ - templates # 视图模板文件夹(如HTML、JSP等)
+ - application.properties # 应用程序配置文件
+ - webapp
+ - WEB-INF
+ - views # 视图文件夹(如JSP、Thymeleaf模板等)
+ - web.xml # Web应用配置文件
+```
+
+以上是一个基本的示例文件目录结构,你可以根据实际的需要进行调整和扩展。在该目录结构中:
+
+- `src/main/java` 目录存放应用程序的Java源代码。
+- `src/main/resources` 目录存放应用程序的配置文件和资源文件。
+- `src/main/java/com/example/controller` 目录存放控制器(Controller)类,处理HTTP请求。
+- `src/main/java/com/example/model` 目录存放模型(Model)类,定义业务实体。
+- `src/main/java/com/example/repository` 目录存放数据访问接口。
+- `src/main/java/com/example/service` 目录存放服务(Service)接口和实现类。
+- `src/main/java/com/example/config` 目录存放Spring MVC的配置类。
+- `src/main/resources/static` 目录存放静态资源文件,如CSS、JavaScript等。
+- `src/main/resources/templates` 目录存放视图模板文件,如HTML、JSP等。
+- `src/main/resources/application.properties` 是应用程序的配置文件,配置各种属性和参数。
+- `webapp/WEB-INF` 目录是Web应用的根目录,存放Web相关的配置和资源。
+- `webapp/WEB-INF/views` 目录存放JSP等视图文件。
+
+这只是一个基本的示例,实际的Spring MVC项目的文件目录结构可能根据具体需求有所不同。你可以根据自己的项目要求和团队的约定进行调整。
+
+希望能解答你的问题!如果还有其他疑问,请随时提问。
diff --git a/out/artifacts/springZHN_jar/springZHN.jar b/out/artifacts/springZHN_jar/springZHN.jar
new file mode 100644
index 0000000..2922a70
Binary files /dev/null and b/out/artifacts/springZHN_jar/springZHN.jar differ
diff --git a/out/artifacts/springZHN_war_exploded/META-INF/MANIFEST.MF b/out/artifacts/springZHN_war_exploded/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..22d66c7
--- /dev/null
+++ b/out/artifacts/springZHN_war_exploded/META-INF/MANIFEST.MF
@@ -0,0 +1,5 @@
+Manifest-Version: 1.0
+Created-By: IntelliJ IDEA
+Built-By: Administrator
+Build-Jdk: Oracle OpenJDK version 17.0.8
+
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..dcd27a3
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,57 @@
+
+ 4.0.0
+
+ com.zhn
+ springZHN
+ 1.0.0
+ war
+
+ springZHN
+ http://maven.apache.org
+
+
+ UTF-8
+
+
+
+
+ junit
+ junit
+ 3.8.1
+ test
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-core
+ 9.0.78
+
+
+ javax.servlet
+ javax.servlet-api
+ 4.0.1
+ provided
+
+
+ org.projectlombok
+ lombok
+ 1.18.28
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ 2.7.14
+
+
+ dom4j
+ dom4j
+ 1.6.1
+
+
+
+ org.apache.commons
+ commons-lang3
+ 3.5
+
+
+
diff --git a/src/main/java/com/zhn/annotation/ZhnAuto.java b/src/main/java/com/zhn/annotation/ZhnAuto.java
new file mode 100644
index 0000000..ab618af
--- /dev/null
+++ b/src/main/java/com/zhn/annotation/ZhnAuto.java
@@ -0,0 +1,15 @@
+package com.zhn.annotation;
+
+import java.lang.annotation.*;
+
+//代表在成员变量上的注解
+@Target ({ElementType.FIELD})
+//代表在运行上可以通过反射机制获取对象
+@Retention (RetentionPolicy.RUNTIME)
+//javadoc
+@Documented
+public @interface ZhnAuto {
+ //加了这就注解后面就可以带值
+ String value () default "";
+
+}
diff --git a/src/main/java/com/zhn/annotation/ZhnContainer.java b/src/main/java/com/zhn/annotation/ZhnContainer.java
new file mode 100644
index 0000000..2515ac1
--- /dev/null
+++ b/src/main/java/com/zhn/annotation/ZhnContainer.java
@@ -0,0 +1,18 @@
+package com.zhn.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 本质上都是在不同的层次去注解扫描,这里对其做了融合
+ */
+//代表作用范围实在类上的
+@Target ({ElementType.TYPE})
+//代表在运行上可以通过反射机制获取对象
+@Retention (RetentionPolicy.RUNTIME)
+//javadoc
+@Documented
+public @interface ZhnContainer {
+ //加了这就注解后面就可以带值
+ String value () default "";
+
+}
diff --git a/src/main/java/com/zhn/annotation/ZhnMapping.java b/src/main/java/com/zhn/annotation/ZhnMapping.java
new file mode 100644
index 0000000..fd195f3
--- /dev/null
+++ b/src/main/java/com/zhn/annotation/ZhnMapping.java
@@ -0,0 +1,14 @@
+package com.zhn.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 ZhnMapping {
+ String value () default "";
+
+}
diff --git a/src/main/java/com/zhn/annotation/ZhnReqParam.java b/src/main/java/com/zhn/annotation/ZhnReqParam.java
new file mode 100644
index 0000000..a8659a3
--- /dev/null
+++ b/src/main/java/com/zhn/annotation/ZhnReqParam.java
@@ -0,0 +1,11 @@
+package com.zhn.annotation;
+
+
+import java.lang.annotation.*;
+
+@Target ({ElementType.PARAMETER})
+@Retention (RetentionPolicy.RUNTIME)
+@Documented
+public @interface ZhnReqParam {
+ String value () default "";
+}
diff --git a/src/main/java/com/zhn/controller/ZhnController.java b/src/main/java/com/zhn/controller/ZhnController.java
new file mode 100644
index 0000000..7e40a05
--- /dev/null
+++ b/src/main/java/com/zhn/controller/ZhnController.java
@@ -0,0 +1,23 @@
+package com.zhn.controller;
+
+import com.zhn.annotation.ZhnAuto;
+import com.zhn.annotation.ZhnContainer;
+import com.zhn.annotation.ZhnMapping;
+import com.zhn.service.ZhnService;
+
+
+@ZhnContainer
+@ZhnMapping ("/ni")
+public class ZhnController {
+ @ZhnAuto
+ ZhnService zhnService;
+
+
+ @ZhnMapping ("/aa")
+ public String aa (String name, String age) {
+ System.out.println ("chia");
+ return zhnService.initReq (name, age);
+ }
+
+
+}
diff --git a/src/main/java/com/zhn/domain/Handler.java b/src/main/java/com/zhn/domain/Handler.java
new file mode 100644
index 0000000..ccc228e
--- /dev/null
+++ b/src/main/java/com/zhn/domain/Handler.java
@@ -0,0 +1,36 @@
+package com.zhn.domain;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.ToString;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+/**
+ * @author : [Administrator]
+ * @version : [v1.0]
+ * @description : [类用于存储处理器相关的信息,包括控制器对象、方法对象、URL 正则表达式以及参数索引映射。]
+ * @createTime : [2023/10/29 15:30]
+ * @updateUser : [Administrator]
+ * @updateTime : [2023/10/29 15:30]
+ * @updateRemark : [说明本次修改内容]
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@ToString
+public class Handler {
+ //属性表示处理器对象,通常是一个控制器类的实例。
+ private Object controller;
+ //属性表示处理器方法,即处理请求的方法.
+ private Method method;
+ //属性表示 URL 请求的正则表达式模式,用于匹配请求的 URL。
+ private Pattern pattern;
+ //属性是一个 Map,用于存储请求参数和参数在方法参数列表中的索引位置的映射关系。键为参数名,值为参数在方法参数列表中的索引
+ private Map paramIndexMapping;
+
+
+}
diff --git a/src/main/java/com/zhn/service/ZhnService.java b/src/main/java/com/zhn/service/ZhnService.java
new file mode 100644
index 0000000..47ce7c0
--- /dev/null
+++ b/src/main/java/com/zhn/service/ZhnService.java
@@ -0,0 +1,16 @@
+package com.zhn.service;
+
+/**
+ * @author : [Administrator]
+ * @version : [v1.0]
+ * @description : [一句话描述该类的功能]
+ * @createTime : [2023/10/29 10:55]
+ * @updateUser : [Administrator]
+ * @updateTime : [2023/10/29 10:55]
+ * @updateRemark : [说明本次修改内容]
+ */
+public interface ZhnService {
+ String initReq (String name, String age);
+
+
+}
diff --git a/src/main/java/com/zhn/service/impl/ZhnServiceImpl.java b/src/main/java/com/zhn/service/impl/ZhnServiceImpl.java
new file mode 100644
index 0000000..38ce092
--- /dev/null
+++ b/src/main/java/com/zhn/service/impl/ZhnServiceImpl.java
@@ -0,0 +1,21 @@
+package com.zhn.service.impl;
+
+import com.zhn.annotation.ZhnContainer;
+import com.zhn.service.ZhnService;
+
+/**
+ * @author : [Administrator]
+ * @version : [v1.0]
+ * @description : [Zhn业务层]
+ * @createTime : [2023/10/29 10:55]
+ * @updateUser : [Administrator]
+ * @updateTime : [2023/10/29 10:55]
+ * @updateRemark : [说明本次修改内容]
+ */
+@ZhnContainer
+public class ZhnServiceImpl implements ZhnService {
+ @Override
+ public String initReq (String name, String age) {
+ return "name===" + name + "; age===" + age;
+ }
+}
diff --git a/src/main/java/com/zhn/servlet/ZhnServlet.java b/src/main/java/com/zhn/servlet/ZhnServlet.java
new file mode 100644
index 0000000..eae50bd
--- /dev/null
+++ b/src/main/java/com/zhn/servlet/ZhnServlet.java
@@ -0,0 +1,243 @@
+package com.zhn.servlet;
+
+
+import com.zhn.annotation.ZhnAuto;
+import com.zhn.annotation.ZhnContainer;
+import com.zhn.annotation.ZhnMapping;
+import com.zhn.annotation.ZhnReqParam;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.File;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+public class ZhnServlet extends HttpServlet {
+
+ List classNames = new ArrayList ();
+
+ Map beans = new HashMap ();
+ Map handlerMap = new HashMap ();
+
+ // private static final long serialVersionUID = 1L;
+ @Override
+ public void init (ServletConfig config) throws ServletException {
+ //把所有的bean扫描 ----扫描所有的class文件
+ scanPackage ("com.zhn");
+
+ doInstance (); //根据全类名创建bean
+
+ doIoc (); //根据bean进行依赖注入
+
+ buildUrlMapping (); //建立映射关系
+ }
+
+ private void scanPackage (String basePackage) {
+ URL url = this.getClass ().getClassLoader ().getResource ("/" + basePackage.replaceAll ("\\.", "/"));
+ String fileStr = url.getFile ();
+
+ File file = new File (fileStr);
+
+ String[] filesStr = file.list ();
+
+ if (filesStr != null) {
+ for (String path : filesStr) {
+ File filePath = new File (fileStr + path);
+
+ if (filePath.isDirectory ()) {
+ scanPackage (basePackage + "." + path);
+ }
+ else {
+ //加入list
+ classNames.add (basePackage + "." + filePath.getName ());
+ }
+ }
+ }
+ }
+
+ //根据扫描的list全类名,进行实例化
+ private void doInstance () {
+ if (classNames.size () <= 0) {
+ System.out.println ("包扫描失败.....");
+ return;
+ }
+
+ //遍历list的所有Class类
+ for (String className : classNames) {
+ String cn = className.replace (".class", "");
+ try {
+ Class> clazz = Class.forName (cn);
+ if (!clazz.isAnnotationPresent (ZhnContainer.class)) {
+ if (clazz.isAnnotationPresent (ZhnContainer.class)) {
+ ZhnContainer service = clazz.getAnnotation (ZhnContainer.class);
+ Object instance = clazz.newInstance ();
+ beans.put (service.value (), instance);
+ }
+ else {
+ continue;
+ }
+ }
+ else {
+ Object instance = clazz.newInstance ();//创建控制类
+ ZhnMapping requestMapping = clazz.getAnnotation (ZhnMapping.class);
+ String rmvalue = requestMapping.value ();
+ beans.put (rmvalue, instance);
+ }
+ }
+ catch (ClassNotFoundException | NullPointerException | IllegalAccessException | InstantiationException e) {
+ e.printStackTrace ();
+ }
+ }
+ }
+
+ //把service注入到controller
+ public void doIoc () {
+ if (beans.entrySet ().size () <= 0) {
+ System.out.println ("没有一个实例化类");
+ }
+ //把map里所有的实例化遍历出来
+ for (Map.Entry entry : beans.entrySet ()) {
+ Object instance = entry.getValue ();
+ Class> clazz = instance.getClass ();
+
+ if (clazz.isAnnotationPresent (ZhnContainer.class)) {
+ Field[] fields = clazz.getDeclaredFields ();
+ for (Field field : fields) {
+ if (field.isAnnotationPresent (ZhnAuto.class)) {
+ ZhnAuto auto = field.getAnnotation (ZhnAuto.class);
+ String key = auto.value ();
+ field.setAccessible (true);
+ try {
+ field.set (instance, beans.get (key));
+ }
+ catch (IllegalAccessException e) {
+ e.printStackTrace ();
+ }
+ }
+ else {
+ continue;
+ }
+ }
+ }
+ else {
+ continue;
+ }
+ }
+ }
+
+ private void buildUrlMapping () {
+ if (beans.entrySet ().size () <= 0) {
+ System.out.println ("没有实例化____");
+ return;
+ }
+ for (Map.Entry entry : beans.entrySet ()) {
+ Object instance = entry.getValue ();
+ Class> clazz = instance.getClass ();
+
+ if (clazz.isAnnotationPresent (ZhnContainer.class)) {
+ ZhnMapping requestMapping = clazz.getAnnotation (ZhnMapping.class);
+ String classPath = requestMapping.value ();
+
+ Method[] methods = clazz.getMethods ();
+ for (Method method : methods) {
+ if (method.isAnnotationPresent (ZhnMapping.class)) {
+ ZhnMapping methodMapping = method.getAnnotation (ZhnMapping.class);
+ String methodPath = methodMapping.value ();
+
+ handlerMap.put (classPath + methodPath, method);
+ }
+ else {
+ continue;
+ }
+ }
+ }
+ else {
+ continue;
+ }
+ }
+ }
+
+ @Override
+ protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ this.doPost (req, resp);
+ }
+
+ @Override
+ protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ // 获取请求路径
+ String uri = req.getRequestURI ();
+ String context = req.getContextPath ();
+ String path = uri.replace (context, "");
+
+ Method method = (Method) handlerMap.get (path);
+ ZhnContainer instance = null;
+ try {
+ instance = (ZhnContainer) beans.get ("/" + path.split ("/")[1]);
+ }
+ catch (ArrayIndexOutOfBoundsException e) {
+ e.printStackTrace ();
+ }
+
+ Object[] args = hand (req, resp, method);
+ try {
+ method.invoke (instance, args);
+ }
+ catch (IllegalAccessException | InvocationTargetException e) {
+ e.printStackTrace ();
+ }
+ }
+
+ private static Object[] hand (HttpServletRequest request, HttpServletResponse response, Method method) {
+ //拿到待执行的方法有哪些参数
+ Class>[] paramClazzs = null;
+ try {
+ paramClazzs = method.getParameterTypes ();
+
+ }
+ catch (NullPointerException e) {
+ e.printStackTrace ();
+ }
+ //根据参数的个数,new 一个参数的数组,将方法的所有参数赋值到args
+ Object[] args = new Object[0];
+ if (paramClazzs != null) {
+ args = new Object[paramClazzs.length];
+ }
+
+ int args_i = 0;
+ int index = 0;
+ if (paramClazzs != null) {
+ for (Class> paramClazz : paramClazzs) {
+ if (ServletRequest.class.isAssignableFrom (paramClazz)) {
+ args[args_i++] = request;
+ }
+ if (ServletResponse.class.isAssignableFrom (paramClazz)) {
+ args[args_i++] = response;
+ }
+ Annotation[] paramAns = method.getParameterAnnotations ()[index];
+ for (Annotation paramAn : paramAns) {
+ if (ZhnReqParam.class.isAssignableFrom (paramAn.getClass ())) {
+ ZhnReqParam rp = (ZhnReqParam) paramAn;
+
+ args[args_i++] = request.getParameter (rp.value ());
+ }
+ }
+ index++;
+ }
+ }
+ return args;
+ }
+}
diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..7288551
--- /dev/null
+++ b/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,23 @@
+
+
+
+ springZHN
+
+
+ ZhnServlet
+ com.zhn.servlet.ZhnServlet
+
+ 0
+
+
+
+ ZhnServlet
+ /*
+
+
+
+
diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html
new file mode 100644
index 0000000..2f7a3ec
--- /dev/null
+++ b/src/main/webapp/index.html
@@ -0,0 +1,16 @@
+
+<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
+
+
+
+
+
+ Document
+
+
+hash????
+
+
+
+
diff --git a/src/test/java/com/zhn/AppTest.java b/src/test/java/com/zhn/AppTest.java
new file mode 100644
index 0000000..0d192d7
--- /dev/null
+++ b/src/test/java/com/zhn/AppTest.java
@@ -0,0 +1,34 @@
+package com.zhn;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Unit test for simple App.
+ */
+public class AppTest
+ extends TestCase {
+ /**
+ * Create the test case
+ *
+ * @param testName name of the test case
+ */
+ public AppTest (String testName) {
+ super (testName);
+ }
+
+ /**
+ * @return the suite of tests being tested
+ */
+ public static Test suite () {
+ return new TestSuite (AppTest.class);
+ }
+
+ /**
+ * Rigourous Test :-)
+ */
+ public void testApp () {
+ assertTrue (true);
+ }
+}