• JSD-2204-WebServer(项目终章)-Day18


    1.V25(版本)

    利用反射机制重构DispatcherServlet,使得将来添加新的业务时DispatcherServlet
    不必再添加分支判断(不进行修改)
    
    实现:
    1:新建包com.webserver.annotation
    2:在annotation包下添加两个注解
      @Controller:用于标注哪些类是处理业务的Controller类
      @RequestMapping:用于标注处理某个业务请求的业务方法
    3:将com.webserver.controller包下的所有Controller类添加注解@Controller
      并将里面用于处理某个业务的方法标注@RequestMapping并指定该方法处理的请求
    4:DispatcherServlet在处理请求时,先扫描controller包下的所有Controller类
      并找到处理该请求的业务方法,使用反射调用.

    1.1项目目录

    1.2Java文件 

    1.2.1Controller(注解)

    1. package com.webserver.annotations;
    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. @Target(ElementType.TYPE)
    7. @Retention(RetentionPolicy.RUNTIME)
    8. public @interface Controller {
    9. }

    1.2.2RequestMapping(注解)

    1. package com.webserver.annotations;
    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. @Target(ElementType.METHOD)
    7. @Retention(RetentionPolicy.RUNTIME)
    8. public @interface RequestMapping {
    9. String value();
    10. }

    1.2.3ClientHandler

    1. package com.webserver.core;
    2. import com.webserver.http.EmptyRequestException;
    3. import com.webserver.http.HttpServletRequest;
    4. import com.webserver.http.HttpServletResponse;
    5. import java.io.*;
    6. import java.lang.reflect.InvocationTargetException;
    7. import java.net.Socket;
    8. import java.net.URISyntaxException;
    9. import java.nio.charset.StandardCharsets;
    10. import java.util.HashMap;
    11. import java.util.Locale;
    12. import java.util.Map;
    13. /**
    14. * 该线程任务是负责与一个客户端进行一次HTTP交互
    15. * 浏览器与服务端交互组从一问一答的原则。因此服务端处理一次HTTP交互,步骤如下:
    16. * 1:解析请求(接受浏览器发送过来的请求内容)
    17. * 2:处理请求(根据浏览器发送的请求理解其意图并进行对应的处理)
    18. * 3:发送响应(将处理结果发送给浏览器)
    19. *
    20. *
    21. */
    22. public class ClientHandler implements Runnable{
    23. private Socket socket;
    24. public ClientHandler(Socket socket){
    25. this.socket = socket;
    26. }
    27. public void run() {
    28. try {
    29. //1解析请求
    30. HttpServletRequest request = new HttpServletRequest(socket);
    31. HttpServletResponse response = new HttpServletResponse(socket);
    32. //2处理请求
    33. DispatcherServlet servlet = new DispatcherServlet();
    34. servlet.service(request,response);
    35. //3发送响应
    36. response.response();
    37. } catch (IOException e) {
    38. e.printStackTrace();
    39. } catch (EmptyRequestException e) {
    40. } catch (ClassNotFoundException e) {
    41. e.printStackTrace();
    42. } catch (InvocationTargetException e) {
    43. e.printStackTrace();
    44. } catch (InstantiationException e) {
    45. e.printStackTrace();
    46. } catch (IllegalAccessException e) {
    47. e.printStackTrace();
    48. } finally {
    49. try {
    50. //按照HTTP协议要求,一次交互后要断开连接
    51. socket.close();
    52. } catch (IOException e) {
    53. e.printStackTrace();
    54. }
    55. }
    56. }
    57. }

    1.2.4DispatcherServlet

    1. package com.webserver.core;
    2. import com.webserver.annotations.Controller;
    3. import com.webserver.annotations.RequestMapping;
    4. import com.webserver.controller.ArticleController;
    5. import com.webserver.controller.ToolsController;
    6. import com.webserver.controller.UserController;
    7. import com.webserver.http.HttpServletRequest;
    8. import com.webserver.http.HttpServletResponse;
    9. import java.io.File;
    10. import java.io.IOException;
    11. import java.lang.reflect.InvocationTargetException;
    12. import java.lang.reflect.Method;
    13. import java.net.URISyntaxException;
    14. import java.nio.file.Files;
    15. /**
    16. * 负责处理请求环节
    17. */
    18. public class DispatcherServlet {
    19. private static File rootDir;
    20. private static File staticDir;
    21. static {
    22. try {
    23. rootDir = new File(
    24. ClientHandler.class.getClassLoader()
    25. .getResource(".").toURI()
    26. );
    27. staticDir = new File(rootDir, "static");
    28. } catch (URISyntaxException e) {
    29. e.printStackTrace();
    30. }
    31. }
    32. public void service(HttpServletRequest request, HttpServletResponse response) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException {
    33. String path = request.getRequestURI();
    34. //根据请求路径判断是否为处理某个业务
    35. /*
    36. 当我们得到本次请求路径path的值后,我们首先要查看是否为请求业务:
    37. 1:扫描controller包下的所有类
    38. 2:查看哪些被注解@Controller标注的过的类(只有被该注解标注的类才认可为业务处理类)
    39. 3:遍历这些类,并获取他们的所有方法,并查看哪些时业务方法
    40. 只有被注解@RequestMapping标注的方法才是业务方法
    41. 4:遍历业务方法时比对该方法上@RequestMapping中传递的参数值是否与本次请求
    42. 路径path值一致?如果一致则说明本次请求就应当由该方法进行处理
    43. 因此利用反射机制调用该方法进行处理。
    44. 5:如果扫描了所有的Controller中所有的业务方法,均未找到与本次请求匹配的路径
    45. 则说明本次请求并非处理业务,那么执行下面请求静态资源的操作
    46. */
    47. File dir = new File(rootDir,"/com/webserver/controller");
    48. File[] files = dir.listFiles(f->f.getName().endsWith(".class"));
    49. for(File file : files){
    50. String fileName = file.getName();
    51. String className = fileName.substring(0,fileName.indexOf("."));
    52. Class cls = Class.forName("com.webserver.controller."+className);
    53. //判断当前类是否为Controller(是否被@Controller标注)
    54. if(cls.isAnnotationPresent(Controller.class)){
    55. //获取该类中所有方法
    56. Method[] methods = cls.getDeclaredMethods();
    57. for(Method method : methods){
    58. //判断当前方法是否为处理业务的方法(是否被@RequestMapping标注)
    59. if(method.isAnnotationPresent(RequestMapping.class)){
    60. //获取注解@RequestMapping
    61. RequestMapping rm = method.getAnnotation(RequestMapping.class);
    62. String value = rm.value();
    63. //判断该注解上的参数值是否与本次请求路径一致?若一致说明就是该方法处理这个请求
    64. if(value.equals(path)){
    65. //实例化当前Controller
    66. Object controller = cls.newInstance();
    67. //调用当前方法处理该请求
    68. method.invoke(controller,request,response);
    69. return;//当业务处理完毕直接return,防止执行下面请求静态资源的逻辑
    70. }
    71. }
    72. }
    73. }
    74. }
    75. File file = new File(staticDir, path);
    76. System.out.println("资源是否存在:" + file.exists());
    77. if (file.isFile()) {
    78. //将请求的实际文件设置到response的正文上等待响应
    79. response.setContentFile(file);
    80. } else {
    81. file = new File(staticDir, "/root/404.html");
    82. //将404内容设置到response上等待响应
    83. response.setStatusCode(404);
    84. response.setStatusReason("NotFound");
    85. response.setContentFile(file);
    86. }
    87. }
    88. }

    1.3HTML文件

    和之前的版本一样

    2.V26版本

    重构代码
    上一个版本那种DispatcherServlet每次处理请求时都要扫描controller包,这样性能
    底下.因此改为仅扫描一次.
    
    实现:
    1:在com.webserver.core包下新建类HandlerMapping
    2:定义静态属性Map mapping
      key:请求路径
      value:MethodMapping(内部类),记录处理该请求的方法对象和该方法所属对象
    3:在静态块中初始化,完成原DispatcherServlet扫描controller包的工作并初始化map
    4:提供静态方法:getMethod可根据请求路径获取处理该请求的方法
    5:在DispatcherServlet中处理业务时只需要根据请求获取对应的处理方法利用反射
      机制调用即可

    2.1项目目录

     2.2Java文件

    2.2.1ClientHandler

    1. package com.webserver.core;
    2. import com.webserver.http.EmptyRequestException;
    3. import com.webserver.http.HttpServletRequest;
    4. import com.webserver.http.HttpServletResponse;
    5. import java.io.*;
    6. import java.lang.reflect.InvocationTargetException;
    7. import java.net.Socket;
    8. import java.net.URISyntaxException;
    9. import java.nio.charset.StandardCharsets;
    10. import java.util.HashMap;
    11. import java.util.Locale;
    12. import java.util.Map;
    13. /**
    14. * 该线程任务是负责与一个客户端进行一次HTTP交互
    15. * 浏览器与服务端交互组从一问一答的原则。因此服务端处理一次HTTP交互,步骤如下:
    16. * 1:解析请求(接受浏览器发送过来的请求内容)
    17. * 2:处理请求(根据浏览器发送的请求理解其意图并进行对应的处理)
    18. * 3:发送响应(将处理结果发送给浏览器)
    19. *
    20. *
    21. */
    22. public class ClientHandler implements Runnable{
    23. private Socket socket;
    24. public ClientHandler(Socket socket){
    25. this.socket = socket;
    26. }
    27. public void run() {
    28. try {
    29. //1解析请求
    30. HttpServletRequest request = new HttpServletRequest(socket);
    31. HttpServletResponse response = new HttpServletResponse(socket);
    32. //2处理请求
    33. DispatcherServlet servlet = new DispatcherServlet();
    34. servlet.service(request,response);
    35. //3发送响应
    36. response.response();
    37. } catch (IOException e) {
    38. e.printStackTrace();
    39. } catch (EmptyRequestException e) {
    40. } catch (ClassNotFoundException e) {
    41. e.printStackTrace();
    42. } catch (InvocationTargetException e) {
    43. e.printStackTrace();
    44. } catch (InstantiationException e) {
    45. e.printStackTrace();
    46. } catch (IllegalAccessException e) {
    47. e.printStackTrace();
    48. } finally {
    49. try {
    50. //按照HTTP协议要求,一次交互后要断开连接
    51. socket.close();
    52. } catch (IOException e) {
    53. e.printStackTrace();
    54. }
    55. }
    56. }
    57. }

    2.2.2DispatcherServlet

    1. package com.webserver.core;
    2. import com.webserver.annotations.Controller;
    3. import com.webserver.annotations.RequestMapping;
    4. import com.webserver.controller.ArticleController;
    5. import com.webserver.controller.ToolsController;
    6. import com.webserver.controller.UserController;
    7. import com.webserver.http.HttpServletRequest;
    8. import com.webserver.http.HttpServletResponse;
    9. import java.io.File;
    10. import java.io.IOException;
    11. import java.lang.reflect.InvocationTargetException;
    12. import java.lang.reflect.Method;
    13. import java.net.URISyntaxException;
    14. import java.nio.file.Files;
    15. /**
    16. * 负责处理请求环节
    17. */
    18. public class DispatcherServlet {
    19. private static File rootDir;
    20. private static File staticDir;
    21. static {
    22. try {
    23. rootDir = new File(
    24. ClientHandler.class.getClassLoader()
    25. .getResource(".").toURI()
    26. );
    27. staticDir = new File(rootDir, "static");
    28. } catch (URISyntaxException e) {
    29. e.printStackTrace();
    30. }
    31. }
    32. public void service(HttpServletRequest request, HttpServletResponse response) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException {
    33. String path = request.getRequestURI();
    34. //path:/myweb/index.html
    35. //判断当前请求是否为请求业务
    36. HandlerMapping.MethodMapping mm = HandlerMapping.getMethod(path);
    37. if(mm!=null){
    38. Method method = mm.getMethod();
    39. Object controller = mm.getController();
    40. method.invoke(controller,request,response);
    41. return;
    42. }
    43. File file = new File(staticDir, path);
    44. System.out.println("资源是否存在:" + file.exists());
    45. if (file.isFile()) {
    46. //将请求的实际文件设置到response的正文上等待响应
    47. response.setContentFile(file);
    48. } else {
    49. file = new File(staticDir, "/root/404.html");
    50. //将404内容设置到response上等待响应
    51. response.setStatusCode(404);
    52. response.setStatusReason("NotFound");
    53. response.setContentFile(file);
    54. }
    55. }
    56. }

    2.2.3HandlerMapping

    1. package com.webserver.core;
    2. import com.webserver.annotations.Controller;
    3. import com.webserver.annotations.RequestMapping;
    4. import java.io.File;
    5. import java.lang.reflect.Method;
    6. import java.net.URISyntaxException;
    7. import java.util.HashMap;
    8. import java.util.Map;
    9. /**
    10. * 使用当前类维护请求路径与对应的Controller中处理该请求的方法
    11. * 使得DispatcherServlet在处理请求时判断是否为业务可以使用当前类完成。
    12. */
    13. public class HandlerMapping {
    14. public static Map<String,MethodMapping> mapping = new HashMap();
    15. static{
    16. initMapping();
    17. }
    18. private static void initMapping(){
    19. try {
    20. File rootDir = new File(HandlerMapping.class.getClassLoader().
    21. getResource(".").toURI());
    22. File dir = new File(rootDir,"/com/webserver/controller");
    23. File[] files = dir.listFiles(f->f.getName().endsWith(".class"));
    24. for(File file : files){
    25. String fileName = file.getName();
    26. String className = fileName.substring(0,fileName.indexOf("."));
    27. Class cls = Class.forName("com.webserver.controller."+className);
    28. //判断当前类是否为Controller(是否被@Controller标注)
    29. if(cls.isAnnotationPresent(Controller.class)){
    30. //将该Controller实例化
    31. Object controller = cls.newInstance();
    32. //获取该类中所有方法
    33. Method[] methods = cls.getDeclaredMethods();
    34. for(Method method : methods){
    35. //判断当前方法是否为处理业务的方法(是否被@RequestMapping标注)
    36. if(method.isAnnotationPresent(RequestMapping.class)){
    37. //获取注解@RequestMapping
    38. RequestMapping rm = method.getAnnotation(RequestMapping.class);
    39. /*
    40. 比如:
    41. @Controller
    42. public class UserController{
    43. @RequestMapping("/myweb/reg")
    44. public void reg(HttpServletRequest request,HttpServletResponse response){...}
    45. ...
    46. }
    47. Object controller : UserController对象
    48. Method method : reg()方法
    49. String value : "/myweb/reg"
    50. */
    51. String value = rm.value();//该方法处理的请求路径
    52. MethodMapping mm = new MethodMapping(method,controller);
    53. mapping.put(value,mm);
    54. }
    55. }
    56. }
    57. }
    58. } catch (URISyntaxException e) {
    59. e.printStackTrace();
    60. } catch (ClassNotFoundException e) {
    61. e.printStackTrace();
    62. } catch (InstantiationException e) {
    63. e.printStackTrace();
    64. } catch (IllegalAccessException e) {
    65. e.printStackTrace();
    66. }
    67. }
    68. /**
    69. * 根据请求路径获取处理该请求的Controller以及对应的方法
    70. * @param uri
    71. * @return
    72. */
    73. public static MethodMapping getMethod(String uri){
    74. return mapping.get(uri);
    75. }
    76. public static void main(String[] args) {
    77. mapping.forEach(
    78. (k,v)->{
    79. System.out.println("请求路径"+k);
    80. System.out.println("对应的处理方法:"+v.getMethod().getName());
    81. }
    82. );
    83. }
    84. public static class MethodMapping{
    85. //method.invoke(controller,...);
    86. private Method method; //方法对象
    87. private Object controller; //方法所属的Controller对象
    88. public MethodMapping(Method method, Object controller) {
    89. this.method = method;
    90. this.controller = controller;
    91. }
    92. public Method getMethod() {
    93. return method;
    94. }
    95. public Object getController() {
    96. return controller;
    97. }
    98. }
    99. }

    2.3HTML文件

    没有改动

  • 相关阅读:
    Mac版本如何安装docker
    oauth2的知识点
    基于vue和nodejs毕业设计电影购票微信小程序影院
    leetCode 746. 使用最小花费爬楼梯
    SpringCloud搭建微服务之OAuth2实现SSO单点登录
    python 线程 (概念+示例代码)
    Leetcode刷题_贪心相关_c++版
    提高 Web 开发效率的10个VS Code扩展插件,你知道吗?
    【C++】最通俗的多态、虚表、虚指针讲解
    基于 HarmonyOS 的 HTTPS 请求过程开发示例(ArkTS)
  • 原文地址:https://blog.csdn.net/TheNewSystrm/article/details/125433395