我们都知道 Spring MVC 下,给类加一个 @Controller,给方法加一个 @RequestMapping,就可以完成客户端调用,访问资源,我们可以模拟一下 MVC 的实现

设计思路

  1. 写一个 Servlet 加载配置文件,也就是读取 Spring 的 xml
  2. 根据配置文件给定的目录扫描包,扫描加了 @Controller 注解的类,
  3. 当扫描到 @Controller 注解的类后,扫描类下面的所有方法,并判断上面是否加了 @RequestMapping 注解
  4. 底层定义一个集合,把符合要求的方法保存起来 Map

POM 文件

  1. <dependencies>
  2. <dependency>
  3. <groupId>javax.servlet</groupId>
  4. <artifactId>javax.servlet-api</artifactId>
  5. <version>3.1.0</version>
  6. <scope>provided</scope>
  7. </dependency>
  8. <dependency>
  9. <groupId>dom4j</groupId>
  10. <artifactId>dom4j</artifactId>
  11. <version>1.6.1</version>
  12. </dependency>
  13. <dependency>
  14. <groupId>com.alibaba</groupId>
  15. <artifactId>fastjson</artifactId>
  16. <version>1.2.70</version>
  17. </dependency>
  18. </dependencies>
  19. <build>
  20. <finalName>web-mvc</finalName>
  21. <plugins>
  22. <plugin>
  23. <groupId>org.apache.tomcat.maven</groupId>
  24. <artifactId>tomcat7-maven-plugin</artifactId>
  25. <version>2.2</version>
  26. <configuration>
  27. <port>8080</port>
  28. <path>/</path>
  29. </configuration>
  30. </plugin>
  31. <plugin>
  32. <groupId>org.apache.maven.plugins</groupId>
  33. <artifactId>maven-compiler-plugin</artifactId>
  34. <version>3.3</version>
  35. <configuration>
  36. <source>1.8</source>
  37. <target>1.8</target>
  38. <compilerArgs>
  39. <arg>-parameters</arg>
  40. </compilerArgs>
  41. </configuration>
  42. </plugin>
  43. </plugins>
  44. </build>

核心类 DispathServlet

  1. public class DispatcherServlet extends HttpServlet {
  2. private static Map<String, Method> methodMap = new LinkedHashMap<String, Method>();
  3. private static String prefix = "";
  4. private static String suffix = "";
  5. /** 项目根路径 */
  6. // /D:/Documents/Java/Spring/spring/web-mvc/target/classes/
  7. private static String PROJECT_ROOT_PATH;
  8. static {
  9. try {
  10. PROJECT_ROOT_PATH = URLDecoder.decode(DispatcherServlet.class.getResource("/").getPath(), "UTF-8");
  11. } catch (UnsupportedEncodingException e) {
  12. e.printStackTrace();
  13. }
  14. }
  15. private static final String XML_COMPONENT_SCAN_ELEMENT = "componentScan";
  16. private static final String XML_COMPONENT_SCAN_ELEMENT_PACKAGE_ATTR = "package";
  17. private static final String XML_VIEW_ELEMENT = "view";
  18. private static final String XML_VIEW_ELEMENT_PREFIX_ATTR = "prefix";
  19. private static final String XML_VIEW_ELEMENT_SUFFIX_ATTR = "suffix";
  20. @Override
  21. public void init(ServletConfig config) throws ServletException {
  22. // 解析 web.xml, mvc.xml
  23. String xmlPath = config.getInitParameter("xmlPath");
  24. Document document = parseXML(new File(PROJECT_ROOT_PATH + "//" + xmlPath));
  25. Element beans = document.getRootElement();
  26. Element componentScanEle = beans.element(XML_COMPONENT_SCAN_ELEMENT);
  27. String packageValue = componentScanEle.attribute(XML_COMPONENT_SCAN_ELEMENT_PACKAGE_ATTR).getValue();
  28. Element viewEle = beans.element(XML_VIEW_ELEMENT);
  29. prefix = viewEle.attribute(XML_VIEW_ELEMENT_PREFIX_ATTR).getValue();
  30. suffix = viewEle.attribute(XML_VIEW_ELEMENT_SUFFIX_ATTR).getValue();
  31. // 扫描文件
  32. doScan(new File(PROJECT_ROOT_PATH + packageValue.replace(".", File.separator)));
  33. super.init(config);
  34. }
  35. @Override
  36. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  37. doPost(req, resp);
  38. }
  39. @Override
  40. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  41. String requestURI = req.getRequestURI();
  42. Method method = methodMap.get(requestURI);
  43. if (method != null) {
  44. Parameter[] parameters = method.getParameters();
  45. Object[] objects = new Object[parameters.length];
  46. for (int i = 0; i < parameters.length; i++) {
  47. Parameter parameter = parameters[i];
  48. String name = parameter.getName();
  49. Class<?> type = parameter.getType();
  50. if (type.equals(String.class)) {
  51. objects[i] = req.getParameter(name);
  52. } else if (type.equals(HttpServletRequest.class)) {
  53. objects[i] = req;
  54. } else if (type.equals(HttpServletResponse.class)) {
  55. objects[i] = resp;
  56. } else if (type.getInterfaces()[0].equals(Entity.class)) {
  57. try {
  58. Object entity = type.newInstance();
  59. for (Field field : type.getDeclaredFields()) {
  60. field.setAccessible(true);
  61. String fieldName = field.getName();
  62. field.set(entity, req.getParameter(fieldName));
  63. }
  64. objects[i] = entity;
  65. } catch (Exception e) {
  66. e.printStackTrace();
  67. }
  68. }
  69. }
  70. try {
  71. Object controllerClass = method.getDeclaringClass().newInstance();
  72. Object ret = method.invoke(controllerClass, objects);
  73. Class<?> returnType = method.getReturnType();
  74. if (!returnType.equals(Void.class)) {
  75. ResponseBody responseBody = method.getAnnotation(ResponseBody.class);
  76. if (responseBody != null) {
  77. resp.setContentType("text/json;charset=UTF-8");
  78. resp.setCharacterEncoding("UTF-8");
  79. resp.getWriter().write(JSONObject.toJSONString(ret));
  80. } else {
  81. req.getRequestDispatcher(prefix + "/" + String.valueOf(ret) + suffix).forward(req, resp);
  82. }
  83. }
  84. } catch (Exception e) {
  85. e.printStackTrace();
  86. }
  87. } else {
  88. resp.setStatus(404);
  89. }
  90. }
  91. private void doScan(File file) {
  92. if (file.isDirectory()) {
  93. for (File _file : file.listFiles()) {
  94. doScan(_file);
  95. }
  96. } else {
  97. String filePath = file.getPath();
  98. String suffix = filePath.substring(filePath.lastIndexOf("."));
  99. if (suffix.equals(".class")) {
  100. String classPath = filePath.replace(new File(PROJECT_ROOT_PATH).getPath() + File.separator, "");
  101. classPath = classPath.replace(File.separator, ".");
  102. String className = classPath.substring(0, classPath.lastIndexOf("."));
  103. try {
  104. Class<?> clazz = Class.forName(className);
  105. if (clazz.isAnnotationPresent(Controller.class)) {
  106. RequestMapping classRequestMapping = clazz.getAnnotation(RequestMapping.class);
  107. String classRequestMappingUrl = "";
  108. if (classRequestMapping != null) {
  109. classRequestMappingUrl = classRequestMapping.value();
  110. }
  111. for (Method method : clazz.getDeclaredMethods()) {
  112. RequestMapping methodAnnotation = method.getAnnotation(RequestMapping.class);
  113. if (methodAnnotation != null) {
  114. String methodRequestMappingUrl = "";
  115. methodRequestMappingUrl = methodAnnotation.value();
  116. String url = classRequestMappingUrl + methodRequestMappingUrl;
  117. System.out.println(clazz.getName() + " => " + method.getName() + " => " + url);
  118. methodMap.put(url, method);
  119. }
  120. }
  121. }
  122. } catch (ClassNotFoundException e) {
  123. e.printStackTrace();
  124. }
  125. }
  126. }
  127. }
  128. /**
  129. * 解析 XML 文件对象
  130. *
  131. * @param file XML 文件
  132. */
  133. private Document parseXML(File file) {
  134. SAXReader reader = new SAXReader();
  135. Document document = null;
  136. try {
  137. document = reader.read(file);
  138. } catch (DocumentException e) {
  139. e.printStackTrace();
  140. }
  141. return document;
  142. }
  143. }

注解类

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. public @interface Controller {
  4. }
  5. @Target({ElementType.TYPE, ElementType.METHOD})
  6. @Retention(RetentionPolicy.RUNTIME)
  7. public @interface RequestMapping {
  8. String value() default "";
  9. }
  10. @Target(ElementType.METHOD)
  11. @Retention(RetentionPolicy.RUNTIME)
  12. public @interface ResponseBody {
  13. }

测试类

  1. @Controller
  2. @RequestMapping("/test")
  3. public class TestController {
  4. @ResponseBody
  5. @RequestMapping("/test.do")
  6. public Object test(String name, UserEntity userEntity, HttpServletRequest request, HttpServletResponse resp) {
  7. System.out.println(name);
  8. System.out.println(request);
  9. System.out.println(resp);
  10. System.out.println(userEntity);
  11. return userEntity;
  12. }
  13. @RequestMapping("/index.do")
  14. public Object view(HttpServletRequest request, HttpServletResponse resp) {
  15. return "index";
  16. }
  17. }

返回 JSON:
image.png
返回页面:
image.png