首先自己写一个类继承HttpServlet类并重写它的doGet,doPost方法

复制代码
package com.spring.mvc.config;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**

  • @Author: XiaoZhe
  • @Description:
  • @Date: Created in 17:39 2019/12/16
    */
    public class MyDispatchServlet extends HttpServlet{br/>@Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    System.out.println("这是调用了doGet方法");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    System.out.println("这是调用了doPost方法");
    }
    }
    复制代码

三丶修改web.xml文件

复制代码
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >





httpServletTest

com.spring.mvc.servlet.MyDispatchServlet




httpServletTest

/


复制代码

四丶 启动项目,在地址栏输入项目地址并回车

可以看到控制台打印输出了我们定义的话

这是调用了doGet方法
这是调用了doGet方法

五丶创建几个注解@Controller,@RequestMapping

用过SpringMVC框架的人都知道在类上打了@Controller注解的才能被认作是一个Controller,而打了@RequestMapping才能被请求映射。

@MyController

复制代码
package com.spring.mvc.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**

  • @Author: ZhengZhe
  • @Description: 作用于class上的功能类似于Spring的@Controller注解
  • @Date: Created in 10:34 2019/12/17br/>*/
    @Target(ElementType.TYPE)//标识此注解只能作用在类上面
    @Retention(RetentionPolicy.RUNTIME)//标识此注解一直存活,可被反射获取
    public @interface MyController {
    }
    复制代码

@MyRequestMapping

复制代码
package com.spring.mvc.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**

  • @Author: ZhengZhe
  • @Description: 作用于class或者method上的功能类似于Spring的@RequestMapping注解
  • @Date: Created in 10:38 2019/12/17br/>*/
    @Target({ElementType.TYPE,ElementType.METHOD})//标识此注解只能作用在类或者方法上面
    @Retention(RetentionPolicy.RUNTIME)//标识此注解一直存活,可被反射获取
    public @interface MyRequestMapping {
    String value();//用来存储对应的url , 网络请求路径
    }
    复制代码

六丶DispatchServlet

DispatchServlet在MVC引导着非常强大的作用,网络中的请求传到DispatchServlet中,由DispatchServlet进行截取分析并传到对应的由@Controller和@RequestMapping注解的类或方法中,使得网路请求能正确的请求到对应的资源上.

下面我们自定义一个DispatchServlet实现他所实现的功能

首先看一下源码中MVC做了什么

复制代码
protected void initStrategies(ApplicationContext context) {
this.initMultipartResolver(context);
this.initLocaleResolver(context);
this.initThemeResolver(context);
this.initHandlerMappings(context);
this.initHandlerAdapters(context);
this.initHandlerExceptionResolvers(context);
this.initRequestToViewNameTranslator(context);
this.initViewResolvers(context);
this.initFlashMapManager(context);
}
复制代码

从上面我们可以看到初始化方法的参数是ApplicationContext,这个是IOC的初始化容器,我之前的博客中解析过IOC的源码,不懂的可以去里面解读.

initStrategies方法的目的就是从容器中获取已经解析出来的bean资源,并获取其带有@Controller和@RequestMapping注解的bean资源.

复制代码
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (this.logger.isDebugEnabled()) {
String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
this.logger.debug("DispatcherServlet with name '" + this.getServletName() + "'" + resumed + " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
}

    Map attributesSnapshot = null;
    if (WebUtils.isIncludeRequest(request)) {
        attributesSnapshot = new HashMap();
        Enumeration attrNames = request.getAttributeNames();

        label108:
        while(true) {
            String attrName;
            do {
                if (!attrNames.hasMoreElements()) {
                    break label108;
                }

                attrName = (String)attrNames.nextElement();
            } while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));

            attributesSnapshot.put(attrName, request.getAttribute(attrName));
        }
    }

    request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());
    request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
    request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
    request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource());
    FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
    if (inputFlashMap != null) {
        request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
    }

    request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
    request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);

    try {
        this.doDispatch(request, response);
    } finally {
        if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {
            this.restoreAttributesAfterInclude(request, attributesSnapshot);
        }

    }

}

复制代码

doService方法其实目的就是解析用户的请求路径,根据请求路径找到对应类和方法,使用反射调用.

七丶MyDispatchServlet(自定义前端控制器)

我们自己写代码来实现对应的init方法和Service方法的功能.

复制代码
package com.spring.mvc.servlet;

import com.spring.mvc.annotation.MyController;
import com.spring.mvc.annotation.MyRequestMapping;

import javax.servlet.ServletException;
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.reflect.Method;
import java.net.URL;
import java.util.Enumeration;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**

  • @Author: ZhengZhe
  • @Description:
  • @Date: Created in 10:56 2019/12/17
    */
    public class MyDispatchServlet extends HttpServlet{
    //我们定义两个集合去存储扫描到的带有@MyController 和 @MyRequestMapping注解的类或者方法
    //存放 被@MyRequestMapping注解修饰的类或者方法
    private ConcurrentHashMap MyMethodsCollection = new ConcurrentHashMap();
    //存放 被@MyController注解修饰的类
    private ConcurrentHashMap MyControllerCollection = new ConcurrentHashMap();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    MyInit(req,resp);
    
    MyService(req,resp);

    }

    /**

    • 作用是 : 根据配置的包名,扫描所有包含@MyController注解修饰的类和@MyRequestMapping注解修饰的类或者方法
      *并将对应的 beanName放入上面定义的 集合中
    • @param req
    • @param resp
      */
      private void MyInit(HttpServletRequest req, HttpServletResponse resp) {
      //在源码中,有IOC实现了容器的扫描和初始化,MVC中只是直接拿出来,但是这个方法中我们就不通过容器去获取了
      //直接扫描并且存入上面的集合中就可以了
      String basePackage ="com.spring.mvc";br/>//根据传入的包路径,扫描包,此时只是将该包下所有的文件资源存入集合中,但是并没有筛选加了@MyController和
      //@RequestMapping注解的类,即扫描所有.class字节码对象并保存起来
      ConcurrentHashMap> scannerClass = scannerClass(basePackage);

      //下面直接进行筛选@MyController和@RequestMapping注解的类
      Set>> entrySet = scannerClass.entrySet();
      for (Map.Entry> entry : entrySet) {
      //获取key : 类名称
      String className = entry.getKey();
      //获取value : 对应的.class字节码对象
      Class clazz = entry.getValue();
      //定义MyRequestMapping的url
      String classUrl = "";
      try {
      //该类是否标记了MyController注解
      if (clazz.isAnnotationPresent(MyController.class)){
      //该类是否标记了MyRequestMapping注解
      if (clazz.isAnnotationPresent(MyRequestMapping.class)){
      //如果该类被MyRequestMapping注解所标识,获取其属性url值
      MyRequestMapping requestMapping = clazz.getAnnotation(MyRequestMapping.class);
      classUrl = requestMapping.value();//判断该类中的方法是否标识了@MyRequestMapping注解,如果标识了存入MyMethodsCollection集合中
      br/>}
      //将被标识了MyController注解的类存入集合中
      MyControllerCollection.put("className",clazz.newInstance());
      //判断该类中的方法是否标识了@MyRequestMapping注解,如果标识了存入MyMethodsCollection集合中
      //获取该类下的方法
      Method[] methods = clazz.getMethods();//获取该类中的方法数组
      //遍历该数组
      for (Method method : methods) {br/>//判断是否被@MyRequestMapping注解标识
      if (method.isAnnotationPresent(MyRequestMapping.class)){br/>//已被@MyRequestMapping注解标识
      //获取其url
      MyRequestMapping myRequestMapping = method.getAnnotation(MyRequestMapping.class);
      //获取method上的url
      String methodUrl = myRequestMapping.value();
      //拼接两端MyController和method的url
      MyMethodsCollection.put(classUrl+methodUrl,method);
      }
      }

          }
      } catch (Exception e) {
          e.printStackTrace();
      }

      }
      }
      //根据传入的包路径,进行bean资源的获取,一般可以在xml中设置包路径.但是我们直接给出即可(简单)
      private static ConcurrentHashMap> scannerClass(String basePackage) {
      ConcurrentHashMap> result = new ConcurrentHashMap<>();
      //把com.spring.mvc 换成com/spring/mvc再类加载器读取文件
      String basePath = basePackage.replaceAll("\.", "/");
      try {
      //得到com/spring/mvc的绝对地址 /D:xxxxx/com/spring/mvc
      String rootPath = MyDispatchServlet.class.getClassLoader().getResource(basePath).getPath();
      //只留com/ming/mvc 目的为了后续拼接成一个全限定名
      if (rootPath != null) {
      rootPath = rootPath.substring(rootPath.indexOf(basePath));
      }
      Enumeration enumeration = MyDispatchServlet.class.getClassLoader().getResources(basePath);
      while (enumeration.hasMoreElements()) {
      URL url = enumeration.nextElement();
      if (url.getProtocol().equals("file")) {//如果是个文件
      File file = new File(url.getPath().substring(1));
      scannerFile(file, rootPath, result);
      }
      }

      } catch (IOException e) {
      e.printStackTrace();
      }
      return result;

    }

    //递归扫描文件
    private static void scannerFile(File folder, String rootPath, ConcurrentHashMap> classes) {
    try {
    File[] files = folder.listFiles();
    for (int i = 0; files != null && i < files.length; i++) {
    File file = files[i];
    if (file.isDirectory()) {
    scannerFile(file, rootPath + file.getName() + "/", classes);
    } else {
    if (file.getName().endsWith(".class")) {
    String className = (rootPath + file.getName()).replaceAll("/", ".");
    className = className.substring(0, className.indexOf(".class"));//去掉扩展名得到全限定名
    //Map容器存储全限定名和Class
    classes.put(className, Class.forName(className));
    }
    }

        }
    } catch (Exception e) {
        e.printStackTrace();
    }

    }

    /**

    • 作用是 : 解析用户的请求路径,,根据请求路径找到对应的类和方法,并使用反射调用其方法
    • @param req
    • @param resp
      /
      private void MyService(HttpServletRequest req, HttpServletResponse resp) {br/>//在MyInit中我们已将被@MyController和@MyRequestMapping注解标识的类或者方法存入对应的集合中了;
      //下面我们需要将网络请求中的url和我们容器中初始化好的url进行匹配,如果匹配成功,那么直接执行此方法
      //返回除去host(域名或者ip)部分的路径(包含)
      String requestURI = req.getRequestURI();//类似test/test
      //返回工程名部分,如果工程映射为/,此处返回则为空 (工程名即项目名)
      String contextPath = req.getContextPath();//类似test
      //获取实际除 ip,端口,项目名外的请求路径
      //如web.xml中的servlet拦截路径设置的为/
      采用下面的方法,如果采用的是/.do或者/.action类似的后缀,需要把后面的也去掉
      String requestMappingPath = requestURI.substring(contextPath.length());
      //通过截取到的实际的请求url为key获取对应的方法
      Method method = MyMethodsCollection.get(requestMappingPath);
      try {
      if (method == null){
      //此时就是大名鼎鼎的404 了~
      //直接返回404
      resp.sendError(404);
      return;
      }
      //存在,那么直接执行
      //获取方法所对应的的class字节码文件
      Class declaringClass = method.getDeclaringClass();
      //下面按照源码来说还需要去判断是否是单例等操作,我们直接省去
      method.invoke(declaringClass.newInstance());
      } catch (Exception e) {
      e.printStackTrace();
      }
      }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    System.out.println("这是调用了doPost方法");
    }
    }
    复制代码

八丶创建ControllerTest测试类

复制代码
package com.spring.mvc.controller;

import com.spring.mvc.annotation.MyController;
import com.spring.mvc.annotation.MyRequestMapping;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

/**

  • @Author: ZhengZhe
  • @Description:
  • @Date: Created in 15:07 2019/12/17br/>*/
    @MyController
    @MyRequestMapping("/hello")
    public class ControllerTest {

    @MyRequestMapping("/world")
    public void helloworld(){
    System.out.println("自定义MVC测试成功~ ,现在时间是"+System.currentTimeMillis());
    }
    }
    深圳网站建设www.sz886.com