手写简易版SpringMVC框架

秒速五厘米 2022-03-20 15:48 392阅读 0赞

1.反射工具类

  1. package com.weichai.util;
  2. import java.io.File;
  3. import java.io.FileFilter;
  4. import java.io.IOException;
  5. import java.net.JarURLConnection;
  6. import java.net.URL;
  7. import java.net.URLDecoder;
  8. import java.util.ArrayList;
  9. import java.util.Enumeration;
  10. import java.util.List;
  11. import java.util.jar.JarEntry;
  12. import java.util.jar.JarFile;
  13. /**
  14. * 反射工具类
  15. * @author linhaiy
  16. * @date 2019.01.26
  17. */
  18. public class ClassUtil {
  19. /**
  20. * 取得某个接口下所有实现这个接口的类
  21. * @param c
  22. * @return
  23. */
  24. public static List<Class> getAllClassByInterface(Class c) {
  25. List<Class> returnClassList = null;
  26. if (c.isInterface()) {
  27. // 获取当前的包名
  28. String packageName = c.getPackage().getName();
  29. // 获取当前包下以及子包下所以的类
  30. List<Class<?>> allClass = getClasses(packageName);
  31. if (allClass != null) {
  32. returnClassList = new ArrayList<Class>();
  33. for (Class classes : allClass) {
  34. // 判断是否是同一个接口
  35. if (c.isAssignableFrom(classes)) {
  36. // 本身不加入进去
  37. if (!c.equals(classes)) {
  38. returnClassList.add(classes);
  39. }
  40. }
  41. }
  42. }
  43. }
  44. return returnClassList;
  45. }
  46. /**
  47. * 取得某一类所在包的所有类名 不含迭代
  48. * @param classLocation
  49. * @param packageName
  50. * @return
  51. */
  52. public static String[] getPackageAllClassName(String classLocation, String packageName) {
  53. // 将packageName分解
  54. String[] packagePathSplit = packageName.split("[.]");
  55. String realClassLocation = classLocation;
  56. int packageLength = packagePathSplit.length;
  57. for (int i = 0; i < packageLength; i++) {
  58. realClassLocation = realClassLocation + File.separator + packagePathSplit[i];
  59. }
  60. File packeageDir = new File(realClassLocation);
  61. if (packeageDir.isDirectory()) {
  62. String[] allClassName = packeageDir.list();
  63. return allClassName;
  64. }
  65. return null;
  66. }
  67. /**
  68. * 从包package中获取所有的Class
  69. * @param packageName
  70. * @return
  71. */
  72. public static List<Class<?>> getClasses(String packageName) {
  73. // 第一个class类的集合
  74. List<Class<?>> classes = new ArrayList<Class<?>>();
  75. // 是否循环迭代
  76. boolean recursive = true;
  77. // 获取包的名字 并进行替换
  78. String packageDirName = packageName.replace('.', '/');
  79. // 定义一个枚举的集合 并进行循环来处理这个目录下的things
  80. Enumeration<URL> dirs;
  81. try {
  82. dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
  83. // 循环迭代下去
  84. while (dirs.hasMoreElements()) {
  85. // 获取下一个元素
  86. URL url = dirs.nextElement();
  87. // 得到协议的名称
  88. String protocol = url.getProtocol();
  89. // 如果是以文件的形式保存在服务器上
  90. if ("file".equals(protocol)) {
  91. // 获取包的物理路径
  92. String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
  93. // 以文件的方式扫描整个包下的文件 并添加到集合中
  94. findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes);
  95. } else if ("jar".equals(protocol)) {
  96. // 如果是jar包文件
  97. // 定义一个JarFile
  98. JarFile jar;
  99. try {
  100. // 获取jar
  101. jar = ((JarURLConnection) url.openConnection()).getJarFile();
  102. // 从此jar包 得到一个枚举类
  103. Enumeration<JarEntry> entries = jar.entries();
  104. // 同样的进行循环迭代
  105. while (entries.hasMoreElements()) {
  106. // 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
  107. JarEntry entry = entries.nextElement();
  108. String name = entry.getName();
  109. // 如果是以/开头的
  110. if (name.charAt(0) == '/') {
  111. // 获取后面的字符串
  112. name = name.substring(1);
  113. }
  114. // 如果前半部分和定义的包名相同
  115. if (name.startsWith(packageDirName)) {
  116. int idx = name.lastIndexOf('/');
  117. // 如果以"/"结尾 是一个包
  118. if (idx != -1) {
  119. // 获取包名 把"/"替换成"."
  120. packageName = name.substring(0, idx).replace('/', '.');
  121. }
  122. // 如果可以迭代下去 并且是一个包
  123. if ((idx != -1) || recursive) {
  124. // 如果是一个.class文件 而且不是目录
  125. if (name.endsWith(".class") && !entry.isDirectory()) {
  126. // 去掉后面的".class" 获取真正的类名
  127. String className = name.substring(packageName.length() + 1, name.length() - 6);
  128. try {
  129. // 添加到classes
  130. classes.add(Class.forName(packageName + '.' + className));
  131. } catch (ClassNotFoundException e) {
  132. e.printStackTrace();
  133. }
  134. }
  135. }
  136. }
  137. }
  138. } catch (IOException e) {
  139. e.printStackTrace();
  140. }
  141. }
  142. }
  143. } catch (IOException e) {
  144. e.printStackTrace();
  145. }
  146. return classes;
  147. }
  148. /**
  149. * 以文件的形式来获取包下的所有Class
  150. * @param packageName
  151. * @param packagePath
  152. * @param recursive
  153. * @param classes
  154. */
  155. public static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive,
  156. List<Class<?>> classes) {
  157. // 获取此包的目录 建立一个File
  158. File dir = new File(packagePath);
  159. // 如果不存在或者 也不是目录就直接返回
  160. if (!dir.exists() || !dir.isDirectory()) {
  161. return;
  162. }
  163. // 如果存在 就获取包下的所有文件 包括目录
  164. File[] dirfiles = dir.listFiles(new FileFilter() {
  165. // 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
  166. public boolean accept(File file) {
  167. return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
  168. }
  169. });
  170. // 循环所有文件
  171. for (File file : dirfiles) {
  172. // 如果是目录 则继续扫描
  173. if (file.isDirectory()) {
  174. findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive,
  175. classes);
  176. } else {
  177. // 如果是java类文件 去掉后面的.class 只留下类名
  178. String className = file.getName().substring(0, file.getName().length() - 6);
  179. try {
  180. // 添加到集合中去
  181. classes.add(Class.forName(packageName + '.' + className));
  182. } catch (ClassNotFoundException e) {
  183. e.printStackTrace();
  184. }
  185. }
  186. }
  187. }
  188. /**
  189. * 首字母转小写
  190. * @param s
  191. * @return
  192. */
  193. public static String toLowerCaseFirstOne(String s) {
  194. if (Character.isLowerCase(s.charAt(0)))
  195. return s;
  196. else
  197. return (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();
  198. }
  199. /**
  200. * 初始化对象
  201. * @param classInfo
  202. * @return
  203. * @throws ClassNotFoundException
  204. * @throws InstantiationException
  205. * @throws IllegalAccessException
  206. */
  207. public static Object newInstance(Class<?> classInfo)
  208. throws ClassNotFoundException, InstantiationException, IllegalAccessException {
  209. return classInfo.newInstance();
  210. }
  211. }

2.框架需要的注解类

  1. package com.weichai.annotation;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6. /**
  7. * 自定义控制器注解
  8. * @author linhaiy
  9. * @date 2019.01.26
  10. */
  11. @Target({ ElementType.TYPE })
  12. @Retention(RetentionPolicy.RUNTIME)
  13. public @interface ExtController {
  14. }
  15. package com.weichai.annotation;
  16. import java.lang.annotation.ElementType;
  17. import java.lang.annotation.Retention;
  18. import java.lang.annotation.RetentionPolicy;
  19. import java.lang.annotation.Target;
  20. /**
  21. * 自定义RequestMapping
  22. * @author linhaiy
  23. * @date 2019.01.26
  24. */
  25. @Target({ ElementType.METHOD, ElementType.TYPE })
  26. @Retention(RetentionPolicy.RUNTIME)
  27. public @interface ExtRequestMapping {
  28. String value() default "";
  29. }

3. 自定义前端控制器(SpringMVC框架核心处理)

  1. package com.weichai.servlet;
  2. import java.io.IOException;
  3. import java.lang.reflect.Method;
  4. import java.util.List;
  5. import java.util.Map;
  6. import java.util.concurrent.ConcurrentHashMap;
  7. import javax.servlet.ServletException;
  8. import javax.servlet.http.HttpServlet;
  9. import javax.servlet.http.HttpServletRequest;
  10. import javax.servlet.http.HttpServletResponse;
  11. import org.apache.commons.lang.StringUtils;
  12. import com.weichai.annotation.ExtController;
  13. import com.weichai.annotation.ExtRequestMapping;
  14. import com.weichai.util.ClassUtil;
  15. /**
  16. * 自定义前端控制器
  17. * 手写springmvc 原理分析<br>
  18. * 1.创建一个前端控制器()ExtDispatcherServlet 拦截所有请求(springmvc 基于servlet实现)<br>
  19. * ####2.初始化操作 重写servlet init 方法<br>
  20. * #######2.1 将扫包范围所有的类,注入到springmvc容器里面,存放在Map集合中 key为默认类名小写,value 对象<br>
  21. * #######2.2 将url映射和方法进行关联 <br>
  22. * ##########2.2.1 判断类上是否有注解,使用java反射机制循环遍历方法 ,判断方法上是否存在注解,进行封装url和方法对应存入集合中<br>
  23. * ####3.处理请求 重写Get或者是Post方法 <br>
  24. * ##########3.1
  25. * 获取请求url,从urlBeans集合获取实例对象,获取成功实例对象后,调用urlMethods集合获取方法名称,使用反射机制执行 2.
  26. * @author linhaiy
  27. * @date 2019.01.26
  28. */
  29. public class ExtDispatcherServlet extends HttpServlet {
  30. // SpringMVC容器对象 key:类名id ,value 对象
  31. private ConcurrentHashMap<String, Object> springmvcBeans = new ConcurrentHashMap<String, Object>();
  32. // SpringMvc 容器对象 keya:请求地址 ,vlue类
  33. private ConcurrentHashMap<String, Object> urlBeans = new ConcurrentHashMap<String, Object>();
  34. // SpringMvc 容器对象 key:请求地址 ,value 方法名称
  35. private ConcurrentHashMap<String, String> urlMethods = new ConcurrentHashMap<String, String>();
  36. public void init() throws ServletException {
  37. // 1.获取当前包下的所有的类
  38. List<Class<?>> classes = ClassUtil.getClasses("com.weichai.controller");
  39. try {
  40. // 2.将扫包范围所有的类,注入到SpringMvc容器里面,存放在Map集合中 key为默认类名小写,value 对象
  41. findClassMVCAnnotation(classes);
  42. } catch (Exception e) {
  43. // TODO Auto-generated catch block
  44. e.printStackTrace();
  45. }
  46. //3.将url映射和方法进行关联
  47. handlerMapping();
  48. }
  49. /**
  50. * get请求方法
  51. */
  52. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
  53. doPost(request,response);
  54. }
  55. /**
  56. * post请求方法
  57. */
  58. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  59. // #################处理请求####################
  60. // 1.获取请求url地址
  61. String requestURL = request.getRequestURI(); //此处获取的是带工程名的路径
  62. String URL = requestURL.substring(requestURL.indexOf("/", 2) + 1); //去除工程名
  63. if(StringUtils.isEmpty(requestURL)) {
  64. return;
  65. }
  66. URL = "/"+URL;
  67. // 2.从Map集合中获取控制对象
  68. Object object = urlBeans.get(URL); // /Main/test
  69. if(object == null) {
  70. response.getWriter().println(" not found 404 url");
  71. return;
  72. }
  73. // 3.使用url地址获取方法
  74. String methodName = urlMethods.get(URL);
  75. if(StringUtils.isEmpty(methodName)) {
  76. response.getWriter().println(" not found method");
  77. }
  78. // 4.使用java的反射机制调用方法
  79. String resultPage = (String) methodInvoke(object, methodName);
  80. // 5.调用视图转换器渲染给页面展示
  81. extResourceViewResolver(resultPage, request, response);
  82. }
  83. /**
  84. * 调用视图转换器渲染给页面展示
  85. * @param resultPage
  86. * @param request
  87. * @param response
  88. * @throws IOException
  89. * @throws ServletException
  90. */
  91. private void extResourceViewResolver(String resultPage, HttpServletRequest request, HttpServletResponse response)
  92. throws ServletException, IOException {
  93. // TODO Auto-generated method stub
  94. String prefix = "/"; // 根路径
  95. String suffix = ".jsp";
  96. request.getRequestDispatcher(prefix + resultPage + suffix).forward(request, response);
  97. }
  98. /**
  99. * 使用java的反射机制调用方法
  100. * @param object
  101. * @param methodName
  102. * @return
  103. */
  104. private Object methodInvoke(Object object, String methodName) {
  105. // TODO Auto-generated method stub
  106. try {
  107. Class<? extends Object> classInfo = object.getClass();
  108. Method method = classInfo.getMethod(methodName);
  109. Object result = method.invoke(object);
  110. return result;
  111. } catch (Exception e) {
  112. // TODO Auto-generated catch block
  113. e.printStackTrace();
  114. }
  115. return null;
  116. }
  117. /**
  118. * 将url映射和方法进行关联
  119. */
  120. private void handlerMapping() {
  121. // TODO Auto-generated method stub
  122. // 1.遍历springmvc bean容器 判断类上属否有url映射注解
  123. for(Map.Entry<String, Object> mvcBean : springmvcBeans.entrySet()) {
  124. // 2.遍历所有的方法上是否有url映射注解
  125. Object object = mvcBean.getValue(); // 获取bean的对象
  126. // 3.判断类上是否有加url映射注解
  127. Class<? extends Object> classInfo = object.getClass();
  128. ExtRequestMapping declaredAnnotation = classInfo.getDeclaredAnnotation(ExtRequestMapping.class);
  129. String baseUrl = "";
  130. if(declaredAnnotation !=null) {
  131. baseUrl = declaredAnnotation.value(); //获取类上的url映射地址
  132. }
  133. //4.判断方法上是否有加url映射地址
  134. Method[] declaredMethods = classInfo.getDeclaredMethods();
  135. for(Method method : declaredMethods) {
  136. // 判断方法上是否有加url映射注解
  137. ExtRequestMapping methodExtRequestMapping = method.getDeclaredAnnotation(ExtRequestMapping.class);
  138. if(methodExtRequestMapping !=null) {
  139. String methodUrl = baseUrl + methodExtRequestMapping.value();
  140. // springmvc 容器对象 keya:请求地址 ,vlue类
  141. urlBeans.put(methodUrl, object);
  142. // springmvc 容器对象 key:请求地址 ,value 方法名称
  143. urlMethods.put(methodUrl, method.getName());
  144. }
  145. }
  146. }
  147. }
  148. /**
  149. * 将扫包范围所有的类,注入到SpringMvc容器里面,存放在Map集合中 key为默认类名小写,value 对象
  150. * @param classes
  151. */
  152. private void findClassMVCAnnotation(List<Class<?>> classes)
  153. throws ClassNotFoundException, InstantiationException, IllegalAccessException {
  154. // TODO Auto-generated method stub
  155. for (Class<?> classInfo : classes) {
  156. ExtController extController = classInfo.getDeclaredAnnotation(ExtController.class); // 判断类上是否有加上注解
  157. if (extController != null) {
  158. String beanId = ClassUtil.toLowerCaseFirstOne(classInfo.getSimpleName()); // 默认类名是小写
  159. Object object = ClassUtil.newInstance(classInfo); // 实例化对象
  160. springmvcBeans.put(beanId, object);
  161. }
  162. }
  163. }
  164. }

4.前端控制器web.xm配置文件

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app xmlns="http://java.sun.com/xml/ns/j2ee"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
  5. version="2.4">
  6. <!-- Spring MVC 核心控制器 DispatcherServlet 配置 -->
  7. <servlet>
  8. <servlet-name>dispatcher</servlet-name>
  9. <servlet-class>com.weichai.servlet.ExtDispatcherServlet
  10. </servlet-class>
  11. <load-on-startup>1</load-on-startup>
  12. </servlet>
  13. <servlet-mapping>
  14. <servlet-name>dispatcher</servlet-name>
  15. <!-- 拦截所有/* 的请求,交给DispatcherServlet处理,性能最好 -->
  16. <url-pattern>/</url-pattern>
  17. </servlet-mapping>
  18. </web-app>

5.控制层测试类

  1. package com.weichai.controller;
  2. import com.weichai.annotation.ExtController;
  3. import com.weichai.annotation.ExtRequestMapping;
  4. @ExtController
  5. @ExtRequestMapping("/Main")
  6. public class ExtIndexController {
  7. @ExtRequestMapping("/test")
  8. public String test() {
  9. System.out.println("手写springmvc框架...");
  10. return "index";
  11. }
  12. }

发表评论

表情:
评论列表 (有 0 条评论,392人围观)

还没有评论,来说两句吧...

相关阅读

    相关 迷你SpringMVC框架

    前言 本文总结自慕课网同名课程。 学习如何使用Spring,SpringMVC是很快的,但是在往后使用的过程中难免会想探究一下框架背后的原理是什么,本文将通过讲解如何手

    相关 SpringMVC框架

    引言 在分析springMVC框架之前,我们根据我们对整个框架的流程分析,先来手写一个简易版的springMVC框架, 这样我们在看源码的时候会更清晰,毕竟框架源码还是非