手写简易版SpringMVC

package com.mvc.myServlet;

import com.mvc.annotation.MyAutowired;
import com.mvc.annotation.MyController;
import com.mvc.annotation.MyRequertMapping;
import com.mvc.annotation.MyService;

import javax.servlet.ServletConfig;
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.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.URL;
import java.util.*;

/**

  • @ Author :gtj

  • @ Date :Created in 12:58 2019/2/2

  • @ Description:自己写一个SpringMVc核心代码
    */
    public class MyDispathcherServlet extends HttpServlet {

    //配置文件属性
    private Properties contextConfig=new Properties();

    //扫描类的路径
    List classNames=new ArrayList();

    //创建一个map作为IOC容器
    private Map<String ,Object> ioc=new HashMap<String ,Object>();

    //声明handlerMapping映射容器
    private Map<String ,Object> handlerMapping=new HashMap<String ,Object>();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    try {
    //7.调用方法
    doDispatch(req,resp);
    } catch (Exception e) {
    e.printStackTrace();
    resp.getWriter().write(“500 Exception”+Arrays.toString(e.getStackTrace()));
    }
    }

    /**

    • 通过反射传参到controller 完成业务调用

    • @param req HttpServletRequest对象

    • @param resp HttpServletResponse对象

    • @throws Exception
      */
      private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {

      if (this.handlerMapping.isEmpty()){return;}
      //把URL改为绝对路径 即去除前面的路径
      String url=req.getRequestURI();
      String contextPath = req.getContextPath();
      url=url.replace(contextPath,"").replaceAll("/+","/");

      //如果不存在对应的路径,直接返回404
      if (!this.handlerMapping.containsKey(url)){
      resp.getWriter().write(“404 Not Found!!!”);
      return;
      }

      //根据url找到对应的方法
      Method method = (Method) this.handlerMapping.get(url);

      //定义values 为数组可以后续添加一些数组类型的参数
      Map<String,String[]> params = req.getParameterMap();

      String beanName=lowerFistrCase(method.getDeclaringClass().getSimpleName());

      //调用方法的参数列表
      Parameter[] parameters = method.getParameters();

      //用户给method反射调用方法的参数数组
      Object[] paramValues=new Object[parameters.length];

      //给paramValues进行赋值
      for (int i = 0; i <parameters.length ; i++) {

       //如果参数类型为HttpServletRequest或者HttpServletResponse进行自动注入  后续可以添加更多的参数如Model(可以提取出来)
       if (parameters[i].getParameterizedType().getTypeName().endsWith("HttpServletRequest")){
           paramValues[i]=req;
           continue;
       }
       if (parameters[i].getParameterizedType().getTypeName().endsWith("HttpServletResponse")){
           paramValues[i]=resp;
           continue;
       }
      
       //给前端传递的参数赋值到paramValues数组中
       for (Map.Entry<String, String[]> entry : params.entrySet()) {
               if(parameters[i].getName().equals(entry.getKey())){
      
                   paramValues[i]=entry.getValue()[0].trim();
      
               }
           }
      

      }

      //反射调用对用的业务方法
      method.invoke(ioc.get(beanName),paramValues);

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    this.doGet(req,resp);
    }

    @Override
    public void init(ServletConfig config) throws ServletException {

     //1.加载配置文件
     doLoadConfig(config.getInitParameter("contextConfigLocation"));
    
     //2.解析配置文件,扫描类
     doScanner(contextConfig.getProperty("scanPackage"));
    
     //3.初始化所有的类,保存到IOC容器中
     doInstance();
    
     //4.完成依赖注入 DI
     doAutoWired();
    
     //5.创建HandlerMapping与url和method建立联系
     initHandlerMapping();
    
     System.out.println("My SpringMVC is init complete");
    

    }

    /**

    • //1.加载配置文件

    • @param contextConfigLocation 配置文件的参数 在web.xml配置
      */
      private void doLoadConfig(String contextConfigLocation) {

      //从类路径下取得文件路径转化为文件流
      InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);

      try {

       contextConfig.load(inputStream);
      

      } catch (IOException e) {

       e.printStackTrace();
      

      }finally {

       //关闭流
       if (null!=inputStream){
           try {
               inputStream.close();
           } catch (IOException e) {
               e.printStackTrace();
           }
       }
      

      }
      }

    /**

    • //2.解析配置文件,扫描类

    • @param scanPackage 配置文件的值(简化版)
      */
      private void doScanner(String scanPackage) {

      //获取扫描包的url路径
      URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\.", “/”));
      File classDir = new File(url.getFile());
      for (File file : classDir.listFiles()) {
      //递归扫描 找到对应的类
      if (file.isDirectory()){

           doScanner(scanPackage+"."+file.getName());
      
       }else {
      
           //如果后缀为.class 则把路径存入classNames中
           if (!file.getName().endsWith(".class")){continue;}
           String className=(scanPackage+"."+file.getName().replace(".class","")).trim();
           classNames.add(className);
      
       }
      

      }
      }

    /**

    • //3.初始化所有相关的类,保存到IOC容器中
      */
      private void doInstance() {

      //如果扫描类为空直接结束
      if (classNames.isEmpty()){return; }

      try {
      for (String className : classNames) {
      Class<?> clazz = Class.forName(className);

           //带注解的类才被实例化
           if (clazz.isAnnotationPresent(MyController.class)){
      
               //获取类名并且把首字母转为小写作为key
               String key=lowerFistrCase( clazz.getSimpleName());
               ioc.put(key,clazz.newInstance());
      
      
           }else if(clazz.isAnnotationPresent(MyService.class)){
      
               //类名首字母小写 自定义命名优先
               MyService service = clazz.getAnnotation(MyService.class);
               //得到自定义命名
               String beanName = service.value();
               //如果没有自定义命名则使用默认命名(类名首字母小写)
               if ("".equals(beanName.trim())) {
      
                   beanName=lowerFistrCase(clazz.getSimpleName());
      
               }
               Object instance = clazz.newInstance();
      
               //存入IOC容器中
               ioc.put(beanName,instance);
      
               //用接口的全称名做为key,这里简化一点,只能有一个实现类  后续可以再完善多个类的
               Class<?>[] interfaces = clazz.getInterfaces();
               for (Class<?> i : interfaces) {
                   if (ioc.containsKey(i.getName())){
                       throw new Exception("The beanName is exists");
                   }
      
                   ioc.put(i.getName(),instance);
               }
           }else {
               continue;
           }
       }
      

      }catch (Exception e){

      }
      }

    /**

    • //4.完成依赖注入 DI
      */
      private void doAutoWired() {

      //如果IOC容器为空 结束
      if (ioc.isEmpty()){return;}

      for (Map.Entry<String, Object> entry : ioc.entrySet()) {

       //获取IOC容器类中的字段
       Field[] fields = entry.getValue().getClass().getDeclaredFields();
       for (Field field : fields) {
      
           //进行注入,这里就只考虑Antowired注入
           if (!field.isAnnotationPresent(MyAutowired.class)){continue;}
      
           //得到注解的实例
           MyAutowired autowired = field.getAnnotation(MyAutowired.class);
      
           //给到Autowired的属性值
           String beanName = autowired.value();
           if ("".equals(beanName)){
               beanName=field.getType().getName();
           }
      
           //强制授权,否则不能注入到私有的属性
           field.setAccessible(true);
      
           try {
               //把IOC容器的实例化对象 放入属性中
               field.set(entry.getValue(),ioc.get(beanName));
           } catch (IllegalAccessException e) {
               e.printStackTrace();
           }
       }
      

      }
      }

    /**

    • //5.创建HandlerMapping与url和method建立联系
      */
      private void initHandlerMapping() {

      //如果IOC为空,结束
      if (ioc.isEmpty()){return; }

      for (Map.Entry<String,Object> entry : ioc.entrySet()) {

       //得到IOC容器的实例化类
       Class<?> clazz = entry.getValue().getClass();
      
       //只有controller的类才能与前端进行映射
       if (!clazz.isAnnotationPresent(MyController.class)){continue;}
      
       //得到MyRequertMapping的地址
       String baseUrl= "";
       if (clazz.isAnnotationPresent(MyRequertMapping.class)){
           MyRequertMapping requertMapping = clazz.getAnnotation(MyRequertMapping.class);
           baseUrl=requertMapping.value();
       }
      
       //获取所有的公共方法(私有方法不考虑)
       Method[] methods = clazz.getMethods();
       for (Method method : methods) {
      
           //如果没有路径,则跳过
           if (!method.isAnnotationPresent(MyRequertMapping.class)){continue;}
      
           //得到对应的MyRequertMapping对象
           MyRequertMapping requertMapping = method.getAnnotation(MyRequertMapping.class);
      
           //组装访问地址的绝对路径
           String url=("/"+baseUrl+"/"+requertMapping.value()).replaceAll("/+","/");
      
           //存入handlerMapping
           handlerMapping.put(url,method);
      
           System.out.println("Mapping: " +url+","+method);
       }
      

      }
      }

    /**
    *把类名的首字母改为小写

    • @param simpleName 类名
    • @return 返回类名首字母小写
      */
      private String lowerFistrCase(String simpleName) {
      char[] chars=simpleName.toCharArray();
      chars[0]+=32;
      return String.valueOf(chars);
      }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值