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); + } +}