手写SpringMVC(代码搬运)

350 阅读1分钟

Step1 中央调度器的类

public class GPDispatcherServlet extends HttpServlet {
   
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doGet(req, resp);
    }

   //以doPost为例
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try{
            doDispatch(req,resp);
        } catch(Exception e){
            e.printStackTrace();
            resp.getWriter().write("500"+Arrays.toString(e.getStackTrace()));
        }
    }
    
    //属性是后面要用到什么就再添加的
    
     //用properties代替xml的springmvc配置文件
    private Properties contextConfig=new Properties();
    //用list把类名缓存起来
    private List<String> classNames=new ArrayList<>();
    //保存urlMapping
    private Map<String,Method> handlerMapping=new HashMap<>();
    //创建ioc容器
    private Map<String,Object> ioc=new HashMap<>();

Step2 重写init()方法

    @Override
    public void init(ServletConfig config) throws ServletException {
        //加载配置文件
        doLoadConfig(config.getInitParameter("contextConfigLocation"));
        //扫描相关的类
        doScanner(contextConfig.getProperty("scanPackage"));
        //实例化相关的类
        doInstance();
        //依赖注入
        doAutowired();
        //初始化handlerMapping
        doInitHandlerMapping();
        
        System.out.println("has been inited");
    }

Step3 doLoadConfig()方法

  private void doLoadConfig(String contextConfigLocation) {
        //io流
        InputStream is= this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
        try {
        //在类体声明Properties的变量contextConfig
            contextConfig.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(is!=null){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

Step4 doScanner()方法

private void doScanner(String scanPackage) {
       //把scanPackage的.路径转成/
        URL url= this.getClass().getClassLoader().getResource("/"+scanPackage.replaceAll("\\.","/"));
        File classPath=new File(url.getFile());
        for (File file : classPath.listFiles()) {
            //文件递归
            if(file.isDirectory()){
                doScanner(scanPackage+"."+file.getName());
            }else{
                if(!file.getName().endsWith(".class")){continue;}
                String className=scanPackage+file.getName().replace(".class","");
                //在类体声明List<String> classNames
                classNames.add(className);
            }
        }
    }

Step5 doInstance()方法

//为了简化,只考虑了Service和Controller两种注解
  private void doInstance() {
          if(classNames.isEmpty()){ return;}
          try{
              for (String className : classNames) {
                  Class clazz=Class.forName(className);
                  String name=clazz.getSimpleName();
                  String beanName=name.substring(0,1).toLowerCase()+name.substring(1);
                  if(clazz.isAnnotationPresent(GPController.class)){
                      ioc.put(beanName,clazz.newInstance());
                  }
                  else if(clazz.isAnnotationPresent(GPService.class)){
                      GPService service=(GPService)clazz.getAnnotation(GPService.class);
                      if(!"".equals(service.value())){
                          beanName=service.value();
                      }
                      Object instance=clazz.newInstance();
                      ioc.put(beanName,instance);
                      //接口赋予的是实现类的对象
                      for (Class i : clazz.getInterfaces()) {
                      //不考虑一个接口多个实现类的情况
                          if(ioc.containsKey(i.getName())){
                              throw new Exception("the beanName has existed");
                          }
                          ioc.put(i.getName(),instance);
                      }
                  }else{ continue;}
              }
          }catch (Exception e){
              e.printStackTrace();
          }
      }

Step6 doAutowired()方法

  private void doAutowired() {
        if(ioc.isEmpty()){return;}
        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            Field[] fields=entry.getValue().getClass().getDeclaredFields();
            for (Field field : fields) {
                if(!field.isAnnotationPresent(GPAutowired.class)){continue;}
                GPAutowired autowired=field.getAnnotation(GPAutowired.class);
                String beanName=autowired.value().trim();
                if("".equals(beanName)){
                    beanName=field.getType().getName();
                }
                //通过反射强制访问private,protected修饰属性
                field.setAccessible(true);

                try {
                    field.set(entry.getValue(),ioc.get(beanName));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

Step7 doInitHandlerMapping()方法

    private void doInitHandlerMapping() {
        if(ioc.isEmpty()){return;}
        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            Class clazz = entry.getValue().getClass();
            if(!clazz.isAnnotationPresent(GPController.class)){continue;}
            for (Method method : clazz.getMethods()) {
                if(!method.isAnnotationPresent(GPRequestMapping.class)){continue;}
                GPRequestMapping requestMapping =method.getAnnotation(GPRequestMapping.class);
                //用正则/去重
                String url=("/"+requestMapping.value()).replaceAll("/+","/");
                handlerMapping.put(url,method);
                System.out.println(url+";"+method);
            }
        }
    }

Step8 doDispatch()方法

   private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws IOException, InvocationTargetException, IllegalAccessException {
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        //绝对路径转相对路径
        url=url.replace(contextPath,"").replaceAll("/+","");
        if(!this.handlerMapping.containsKey(url)){
            resp.getWriter().write("404 Not Found");
            return;
        }
        Map<String,String[]> params=req.getParameterMap();
        Method method = this.handlerMapping.get(url);
        //获得调用方法的对象,还是从ioc容器中取
        String name=method.getDeclaringClass().getSimpleName();
        String beanName=name.substring(0,1).toLowerCase()+name.substring(1);
        method.invoke(ioc.get(beanName),new Object[]{req,resp,params.get("name")[0]});
    }

Reference

www.bilibili.com/video/BV1RJ…