利用反射机制重构DispatcherServlet,使得将来添加新的业务时DispatcherServlet 不必再添加分支判断(不进行修改) 实现: 1:新建包com.webserver.annotation 2:在annotation包下添加两个注解 @Controller:用于标注哪些类是处理业务的Controller类 @RequestMapping:用于标注处理某个业务请求的业务方法 3:将com.webserver.controller包下的所有Controller类添加注解@Controller 并将里面用于处理某个业务的方法标注@RequestMapping并指定该方法处理的请求 4:DispatcherServlet在处理请求时,先扫描controller包下的所有Controller类 并找到处理该请求的业务方法,使用反射调用.
- package com.webserver.annotations;
-
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
-
- @Target(ElementType.TYPE)
- @Retention(RetentionPolicy.RUNTIME)
- public @interface Controller {
- }
- package com.webserver.annotations;
-
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
-
- @Target(ElementType.METHOD)
- @Retention(RetentionPolicy.RUNTIME)
- public @interface RequestMapping {
- String value();
- }
- package com.webserver.core;
-
- import com.webserver.http.EmptyRequestException;
- import com.webserver.http.HttpServletRequest;
- import com.webserver.http.HttpServletResponse;
-
- import java.io.*;
- import java.lang.reflect.InvocationTargetException;
- import java.net.Socket;
- import java.net.URISyntaxException;
- import java.nio.charset.StandardCharsets;
- import java.util.HashMap;
- import java.util.Locale;
- import java.util.Map;
-
- /**
- * 该线程任务是负责与一个客户端进行一次HTTP交互
- * 浏览器与服务端交互组从一问一答的原则。因此服务端处理一次HTTP交互,步骤如下:
- * 1:解析请求(接受浏览器发送过来的请求内容)
- * 2:处理请求(根据浏览器发送的请求理解其意图并进行对应的处理)
- * 3:发送响应(将处理结果发送给浏览器)
- *
- *
- */
- public class ClientHandler implements Runnable{
- private Socket socket;
- public ClientHandler(Socket socket){
- this.socket = socket;
- }
-
- public void run() {
- try {
- //1解析请求
- HttpServletRequest request = new HttpServletRequest(socket);
- HttpServletResponse response = new HttpServletResponse(socket);
-
- //2处理请求
- DispatcherServlet servlet = new DispatcherServlet();
- servlet.service(request,response);
-
- //3发送响应
- response.response();
-
-
-
- } catch (IOException e) {
- e.printStackTrace();
- } catch (EmptyRequestException e) {
-
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- e.printStackTrace();
- } catch (InstantiationException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- } finally {
- try {
- //按照HTTP协议要求,一次交互后要断开连接
- socket.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- }
-
-
-
-
-
- }
- package com.webserver.core;
-
- import com.webserver.annotations.Controller;
- import com.webserver.annotations.RequestMapping;
- import com.webserver.controller.ArticleController;
- import com.webserver.controller.ToolsController;
- import com.webserver.controller.UserController;
- import com.webserver.http.HttpServletRequest;
- import com.webserver.http.HttpServletResponse;
-
- import java.io.File;
- import java.io.IOException;
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- import java.net.URISyntaxException;
- import java.nio.file.Files;
-
- /**
- * 负责处理请求环节
- */
- public class DispatcherServlet {
- private static File rootDir;
- private static File staticDir;
-
- static {
- try {
- rootDir = new File(
- ClientHandler.class.getClassLoader()
- .getResource(".").toURI()
- );
- staticDir = new File(rootDir, "static");
- } catch (URISyntaxException e) {
- e.printStackTrace();
- }
- }
-
- public void service(HttpServletRequest request, HttpServletResponse response) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException {
- String path = request.getRequestURI();
- //根据请求路径判断是否为处理某个业务
- /*
- 当我们得到本次请求路径path的值后,我们首先要查看是否为请求业务:
- 1:扫描controller包下的所有类
- 2:查看哪些被注解@Controller标注的过的类(只有被该注解标注的类才认可为业务处理类)
- 3:遍历这些类,并获取他们的所有方法,并查看哪些时业务方法
- 只有被注解@RequestMapping标注的方法才是业务方法
- 4:遍历业务方法时比对该方法上@RequestMapping中传递的参数值是否与本次请求
- 路径path值一致?如果一致则说明本次请求就应当由该方法进行处理
- 因此利用反射机制调用该方法进行处理。
- 5:如果扫描了所有的Controller中所有的业务方法,均未找到与本次请求匹配的路径
- 则说明本次请求并非处理业务,那么执行下面请求静态资源的操作
- */
- File dir = new File(rootDir,"/com/webserver/controller");
- File[] files = dir.listFiles(f->f.getName().endsWith(".class"));
- for(File file : files){
- String fileName = file.getName();
- String className = fileName.substring(0,fileName.indexOf("."));
- Class cls = Class.forName("com.webserver.controller."+className);
- //判断当前类是否为Controller(是否被@Controller标注)
- if(cls.isAnnotationPresent(Controller.class)){
- //获取该类中所有方法
- Method[] methods = cls.getDeclaredMethods();
- for(Method method : methods){
- //判断当前方法是否为处理业务的方法(是否被@RequestMapping标注)
- if(method.isAnnotationPresent(RequestMapping.class)){
- //获取注解@RequestMapping
- RequestMapping rm = method.getAnnotation(RequestMapping.class);
- String value = rm.value();
- //判断该注解上的参数值是否与本次请求路径一致?若一致说明就是该方法处理这个请求
- if(value.equals(path)){
- //实例化当前Controller
- Object controller = cls.newInstance();
- //调用当前方法处理该请求
- method.invoke(controller,request,response);
- return;//当业务处理完毕直接return,防止执行下面请求静态资源的逻辑
- }
- }
- }
- }
-
- }
-
-
-
- File file = new File(staticDir, path);
- System.out.println("资源是否存在:" + file.exists());
- if (file.isFile()) {
- //将请求的实际文件设置到response的正文上等待响应
- response.setContentFile(file);
- } else {
- file = new File(staticDir, "/root/404.html");
- //将404内容设置到response上等待响应
- response.setStatusCode(404);
- response.setStatusReason("NotFound");
- response.setContentFile(file);
- }
-
- }
- }
和之前的版本一样
重构代码 上一个版本那种DispatcherServlet每次处理请求时都要扫描controller包,这样性能 底下.因此改为仅扫描一次. 实现: 1:在com.webserver.core包下新建类HandlerMapping 2:定义静态属性Map mapping key:请求路径 value:MethodMapping(内部类),记录处理该请求的方法对象和该方法所属对象 3:在静态块中初始化,完成原DispatcherServlet扫描controller包的工作并初始化map 4:提供静态方法:getMethod可根据请求路径获取处理该请求的方法 5:在DispatcherServlet中处理业务时只需要根据请求获取对应的处理方法利用反射 机制调用即可
- package com.webserver.core;
-
- import com.webserver.http.EmptyRequestException;
- import com.webserver.http.HttpServletRequest;
- import com.webserver.http.HttpServletResponse;
-
- import java.io.*;
- import java.lang.reflect.InvocationTargetException;
- import java.net.Socket;
- import java.net.URISyntaxException;
- import java.nio.charset.StandardCharsets;
- import java.util.HashMap;
- import java.util.Locale;
- import java.util.Map;
-
- /**
- * 该线程任务是负责与一个客户端进行一次HTTP交互
- * 浏览器与服务端交互组从一问一答的原则。因此服务端处理一次HTTP交互,步骤如下:
- * 1:解析请求(接受浏览器发送过来的请求内容)
- * 2:处理请求(根据浏览器发送的请求理解其意图并进行对应的处理)
- * 3:发送响应(将处理结果发送给浏览器)
- *
- *
- */
- public class ClientHandler implements Runnable{
- private Socket socket;
- public ClientHandler(Socket socket){
- this.socket = socket;
- }
-
- public void run() {
- try {
- //1解析请求
- HttpServletRequest request = new HttpServletRequest(socket);
- HttpServletResponse response = new HttpServletResponse(socket);
-
- //2处理请求
- DispatcherServlet servlet = new DispatcherServlet();
- servlet.service(request,response);
-
- //3发送响应
- response.response();
-
-
-
- } catch (IOException e) {
- e.printStackTrace();
- } catch (EmptyRequestException e) {
-
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- e.printStackTrace();
- } catch (InstantiationException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- } finally {
- try {
- //按照HTTP协议要求,一次交互后要断开连接
- socket.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- }
-
-
-
-
-
- }
- package com.webserver.core;
-
- import com.webserver.annotations.Controller;
- import com.webserver.annotations.RequestMapping;
- import com.webserver.controller.ArticleController;
- import com.webserver.controller.ToolsController;
- import com.webserver.controller.UserController;
- import com.webserver.http.HttpServletRequest;
- import com.webserver.http.HttpServletResponse;
-
- import java.io.File;
- import java.io.IOException;
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- import java.net.URISyntaxException;
- import java.nio.file.Files;
-
- /**
- * 负责处理请求环节
- */
- public class DispatcherServlet {
- private static File rootDir;
- private static File staticDir;
-
- static {
- try {
- rootDir = new File(
- ClientHandler.class.getClassLoader()
- .getResource(".").toURI()
- );
- staticDir = new File(rootDir, "static");
- } catch (URISyntaxException e) {
- e.printStackTrace();
- }
- }
-
- public void service(HttpServletRequest request, HttpServletResponse response) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException {
- String path = request.getRequestURI();
- //path:/myweb/index.html
- //判断当前请求是否为请求业务
- HandlerMapping.MethodMapping mm = HandlerMapping.getMethod(path);
- if(mm!=null){
- Method method = mm.getMethod();
- Object controller = mm.getController();
- method.invoke(controller,request,response);
- return;
- }
-
- File file = new File(staticDir, path);
- System.out.println("资源是否存在:" + file.exists());
- if (file.isFile()) {
- //将请求的实际文件设置到response的正文上等待响应
- response.setContentFile(file);
- } else {
- file = new File(staticDir, "/root/404.html");
- //将404内容设置到response上等待响应
- response.setStatusCode(404);
- response.setStatusReason("NotFound");
- response.setContentFile(file);
- }
-
- }
- }
- package com.webserver.core;
-
- import com.webserver.annotations.Controller;
- import com.webserver.annotations.RequestMapping;
-
- import java.io.File;
- import java.lang.reflect.Method;
- import java.net.URISyntaxException;
- import java.util.HashMap;
- import java.util.Map;
-
- /**
- * 使用当前类维护请求路径与对应的Controller中处理该请求的方法
- * 使得DispatcherServlet在处理请求时判断是否为业务可以使用当前类完成。
- */
- public class HandlerMapping {
- public static Map<String,MethodMapping> mapping = new HashMap();
- static{
- initMapping();
- }
- private static void initMapping(){
- try {
- File rootDir = new File(HandlerMapping.class.getClassLoader().
- getResource(".").toURI());
- File dir = new File(rootDir,"/com/webserver/controller");
- File[] files = dir.listFiles(f->f.getName().endsWith(".class"));
- for(File file : files){
- String fileName = file.getName();
- String className = fileName.substring(0,fileName.indexOf("."));
- Class cls = Class.forName("com.webserver.controller."+className);
- //判断当前类是否为Controller(是否被@Controller标注)
- if(cls.isAnnotationPresent(Controller.class)){
- //将该Controller实例化
- Object controller = cls.newInstance();
- //获取该类中所有方法
- Method[] methods = cls.getDeclaredMethods();
- for(Method method : methods){
- //判断当前方法是否为处理业务的方法(是否被@RequestMapping标注)
- if(method.isAnnotationPresent(RequestMapping.class)){
- //获取注解@RequestMapping
- RequestMapping rm = method.getAnnotation(RequestMapping.class);
- /*
- 比如:
- @Controller
- public class UserController{
- @RequestMapping("/myweb/reg")
- public void reg(HttpServletRequest request,HttpServletResponse response){...}
- ...
- }
- Object controller : UserController对象
- Method method : reg()方法
- String value : "/myweb/reg"
- */
- String value = rm.value();//该方法处理的请求路径
- MethodMapping mm = new MethodMapping(method,controller);
- mapping.put(value,mm);
- }
- }
- }
- }
- } catch (URISyntaxException e) {
- e.printStackTrace();
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- } catch (InstantiationException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- }
- }
-
- /**
- * 根据请求路径获取处理该请求的Controller以及对应的方法
- * @param uri
- * @return
- */
- public static MethodMapping getMethod(String uri){
- return mapping.get(uri);
- }
-
- public static void main(String[] args) {
- mapping.forEach(
- (k,v)->{
- System.out.println("请求路径"+k);
- System.out.println("对应的处理方法:"+v.getMethod().getName());
- }
- );
- }
-
-
- public static class MethodMapping{
- //method.invoke(controller,...);
- private Method method; //方法对象
- private Object controller; //方法所属的Controller对象
-
- public MethodMapping(Method method, Object controller) {
- this.method = method;
- this.controller = controller;
- }
-
- public Method getMethod() {
- return method;
- }
-
- public Object getController() {
- return controller;
- }
- }
- }
-
-
-
-
-
-
-
没有改动