手写SpringMVC3.0版本

本文介绍了自定义SpringMVC框架3.0版本的改进,更贴近SpringMVC源码,遵循开闭原则和最少知识原则。主要变更集中在Controller层和DispatcherServlet类,详细解析了DispatcherServlet的初始化、请求处理流程,以及HandlerMapping的初始化。通过代码展示了类扫描、依赖注入和URL映射等关键功能。
摘要由CSDN通过智能技术生成

一简介

相较于2.0版本,3.0版本更接近SpringMVC的源码,更符合开闭原则,最少知识原则。

二.废话少说,上代码

改变的地方
一.Controller层

在这里插入图片描述

二.DispatcherServlet类(核心)

public class DDispatcherServlet3 extends HttpServlet {

    /*保存Properties中的内容*/
    private Properties contextConfig=new Properties();
    /*保存扫描到的所有类名*/
    private List<String> classNames=new ArrayList<>();
   /* 准备IOC容器*/
    private Map<String,Object> ioc=new ConcurrentHashMap<>();
    /*url映射容器*/
    private List<HandlerMapping> handlerMapping=new ArrayList<>();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //6.调用,运行阶段
        try {
            doDispatcher(req,resp);
        } catch (Exception e) {
            e.printStackTrace();
            resp.getWriter().write("500"+Arrays.toString(e.getStackTrace()));
        }
    }

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

        //6.调用,运行阶段
        try {
            doDispatcher(req,resp);
        } catch (Exception e) {
            e.printStackTrace();
            resp.getWriter().write("500"+Arrays.toString(e.getStackTrace()));
        }
    }

    private void doDispatcher(HttpServletRequest req, HttpServletResponse resp) throws IOException, InvocationTargetException, IllegalAccessException {

        HandlerMapping hasHandler= getHandler(req);
        if (null==hasHandler){
            resp.getWriter().write("404");
        }

       //获得形参列表
        Class<?>[] paramTypes = hasHandler.getParamTypes();
        Object[] paramValues=new Object[paramTypes.length];

        Map<String, String[]> params = req.getParameterMap();
        for (Map.Entry<String, String[]> param : params.entrySet()) {
            String methodName = convert(param.getKey());
            if (!hasHandler.getParamIndexMap().containsKey(methodName)){
                continue;
            }
            Integer index = hasHandler.getParamIndexMap().get(param.getKey());
            String[] value = param.getValue();
            String newValue = convert(value);
            paramValues[index] =convert(paramTypes[index],newValue);
        }

        if(hasHandler.getParamIndexMap().containsKey(HttpServletRequest.class.getName())) {
            int reqIndex = hasHandler.getParamIndexMap().get(HttpServletRequest.class.getName());
            paramValues[reqIndex] = req;
        }

        if(hasHandler.getParamIndexMap().containsKey(HttpServletResponse.class.getName())) {
            int respIndex = hasHandler.getParamIndexMap().get(HttpServletResponse.class.getName());
            paramValues[respIndex] = resp;
        }
        Method method = hasHandler.getMethod();
        method.setAccessible(true);
        Object returnValue = method.invoke(hasHandler.getController(),paramValues);
        if(returnValue == null || returnValue instanceof Void){ return; }
        resp.getWriter().write(returnValue.toString());

    }

    private HandlerMapping getHandler(HttpServletRequest req) {
        String url = req.getRequestURI();
        //处理成相对路径
        String contextPath = req.getContextPath();
        url=url.replaceAll(contextPath,"").replaceAll("/+","/");

        for (int i = 0; i < handlerMapping.size(); i++) {
            if (url.equals(handlerMapping.get(i).getUrl())){
                return handlerMapping.get(i);
            }
        }
        return null;
    }

    /**
     * url都是字符串
     */
    private Object convert(Class<?> type,String value){
        value = value.replaceAll("\\[|\\]","")
                .replaceAll("\\s",",");

        if (Integer.class==type){
            return Integer.valueOf(value);
        }
        return value;
    }

    private String convert(String... value){
        String newValue = Arrays.toString(value).replaceAll("\\[|\\]", "")
                .replaceAll("\\s", ",");
        return newValue;
    }

    /**
     * 初始化阶段
     * @throws ServletException
     */
    @Override
    public void init(ServletConfig config) throws ServletException {
        //1.加载配置文件
            doLoadConfig(config.getInitParameter("contextConfigLocation"));

        // 2.扫描相关类
            doScanner(contextConfig.getProperty("scanPackage"));
        //3.初始化扫描到的类,并放入ioc中
            DoInstance();
        //4.完成依赖注入
            doAutoWired();
        //5.初始化HandlerMapping
            initHandlerMapping();

        System.out.println("D Spring frame is init");
    }

    /**
     * 初始化url和Method的一对一关系
     */
    private void initHandlerMapping() {
        if (ioc.isEmpty()){
            return;
        }
        Set<Map.Entry<String, Object>> entries = ioc.entrySet();

        for (Map.Entry<String, Object> entry : entries) {
            Class<?> clazz = entry.getValue().getClass();
            if (!clazz.isAnnotationPresent(DController.class)){
                continue;
            }
            //保存写在类上的url
            String baseUrl="/";
            if (clazz.isAnnotationPresent(DController.class)){
                DRequestMapping requestMapping = clazz.getAnnotation(DRequestMapping.class);
                baseUrl += requestMapping.value();
            }
            //获取所有public的方法
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                if (!method.isAnnotationPresent(DRequestMapping.class)){
                    continue;
                }
                DRequestMapping methodMapping = method.getAnnotation(DRequestMapping.class);
                //如果出现多个斜杆,替换成一个
                String url=(baseUrl+"/"+methodMapping.value()).replaceAll("/+","/");
                //放入映射容器
                handlerMapping.add(new HandlerMapping(url,method,entry.getValue()));
                System.out.println("url:"+url+":"+method.toString());
            }
        }

    }

    private void doAutoWired() {
        if (ioc.isEmpty()){
            return;
        }

        Set<Map.Entry<String, Object>> entries = ioc.entrySet();
        for (Map.Entry<String, Object> entry : entries) {
            //获得一个对象的所有方法
            Field[] fields = entry.getValue().getClass().getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(DAutowired.class)){
                    DAutowired autowired = field.getAnnotation(DAutowired.class);
                    String beanName = autowired.value().trim();
                    //如果注解默认值为空,则根据类型注入
                    if ("".equals(beanName)){
                        beanName=field.getType().getName();
                    }
                    //赋值
                    try {
                        field.setAccessible(true);
                        field.set(entry.getValue(),ioc.get(beanName));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }

                }
            }

        }
    }

    private void DoInstance() {
        //初始化,为DI做准备
        if (classNames.isEmpty()){return;}

        for (String className : classNames) {
            try {

                Class<?> clazz = Class.forName(className);

                //加了注解的类需要初始化,这里只举例几个注解
                if (clazz.isAnnotationPresent(DController.class)){
                    Object instance = clazz.getDeclaredConstructor().newInstance();
                    //类名首字母小写
                    String beanName = toLowerFirstCase(clazz.getSimpleName());
                    ioc.put(beanName,instance);
                }else if (clazz.isAnnotationPresent(DService.class)){
                    DService service = clazz.getAnnotation(DService.class);
                    //1.自定义beanName
                    // 获得该注解的默认值
                    String beanName = service.value();

                    //2.默认类名首字母小写
                    if ("".equals(beanName.trim())){
                         beanName = toLowerFirstCase(clazz.getSimpleName());
                    }
                    Object instance = clazz.getDeclaredConstructor().newInstance();
                    //3.根据类型自动注入,扫描该类的所有接口
                    for (Class<?> i:clazz.getInterfaces()){
                        //防止使用相同key名
                        if (ioc.containsKey(i.getName())){
                            throw  new Exception(i.getName()+"该类实例已存在,请更换名字");
                        }
                        ioc.put(i.getName(),instance);
                    }
                }else {
                    continue;
                }

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

    private String toLowerFirstCase(String simpleName) {
        char[] chars = simpleName.toCharArray();
        //大小写字母ASCII码相差32
        if (chars[0]>=65&&chars[0]<=90){
            //说明是一个大写字母
            chars[0] +=32;
        }
        return String.valueOf(chars);
    }

    //扫描相关类
    private void doScanner(String scanPackage) {
        //转化成文件路径:classpath,注意双亲委派机制
        URL url = this.getClass().getClassLoader().getResource(scanPackage.replaceAll("\\.","/"));
        File classpath = new File(url.getFile());
        File[] files = classpath.listFiles();
        for (File file : files) {
            if (file.isDirectory()){
                doScanner(scanPackage+"."+file.getName());
            }else {
                if (!file.getName().endsWith(".class")){continue;}
                //得到完整的类名
                String className=(scanPackage+"."+file.getName().replace(".class",""));
                //保存到容器中
                classNames.add(className);
            }
        }
    }

    /**
     * 加载配置文件
     * @param contextConfigLocation
     */
    private void doLoadConfig(String contextConfigLocation) {
        InputStream fis=null;
        //直接从类路径下找到Spring主配置文件所在路径
        //将properties文件放到Properties对象中
        fis=this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
        try {
            contextConfig.load(fis);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (null!=fis){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

三.HandlerMapping类

public class HandlerMapping {
    private String url;
    private Method method;
    private Object controller;
    private Class<?>[] paramTypes;
    /**
     * 保存参数的名称和索引
     */
    private Map<String,Integer> paramIndexMap;


    public HandlerMapping(String url, Method method, Object controller) {
        this.url = url;
        this.method = method;
        this.controller = controller;

        //初始化参数类型
        this.paramTypes= method.getParameterTypes();

        paramIndexMap=new HashMap<>();
        putParamIndexMap(method);
    }

    public Map<String, Integer> getParamIndexMap() {
        return paramIndexMap;
    }

    private void putParamIndexMap(Method method) {

        //拿到方法上的参数注解,一个字段可以有多个注解
        Annotation[][] annotations = method.getParameterAnnotations();
        for (int i = 0; i < annotations.length; i++) {
            for (Annotation annotation:annotations[i]){
                //获取参数上的注解
                DRequestParam requestParam = (DRequestParam) annotation;
                if (requestParam instanceof DRequestParam){
                    String paramName = requestParam.value();
                    if (!"".equals(paramName.trim())){
                        paramIndexMap.put(paramName,i);
                    }
                }

            }
        }

        //提取方法中的request和response参数
        Class<?> [] paramsTypes = method.getParameterTypes();
        for (int i = 0; i < paramsTypes.length ; i ++) {
            Class<?> type = paramsTypes[i];
            if(type == HttpServletRequest.class ||
                    type == HttpServletResponse.class){
                paramIndexMap.put(type.getName(),i);
            }
        }

    }

    public Class<?>[] getParamTypes() {
        return paramTypes;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public Method getMethod() {
        return method;
    }

    public void setMethod(Method method) {
        this.method = method;
    }

    public Object getController() {
        return controller;
    }

    public void setController(Object controller) {
        this.controller = controller;
    }
}

其他地方没有做改变,参考2.0版本
手写SpringMVC2.0版本

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

愛沢かりん

感谢您对我的支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值