纯手写springmvc,深入了解spring核心思想

本文纯手写springmvc核心功能,主要通过反射,注解的形式帮助大家清晰了解spring的核心思想。

1.idea新建web maven工程

2.配置web.xml,注入DispatcherServlet




  Archetype Created Web Application
  
    gfhMvc
    com.gfh.mvc.framework.DispatcherServlet
    
      contextConfigLocation
      application.properties
    
    1
  
  
    gfhMvc
    /*
  


新增application.properties,写入扫描的包 scanPackage=com.gfh.mvc.framework

3.初始化配置文件

private void initConfig(ServletConfig config) {
    System.out.println("initConfig");
    InputStream is = this.getClass().getClassLoader().getResourceAsStream(config.getInitParameter("contextConfigLocation"));
    try {
        properties.load(is);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (null != is)
                is.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

4.扫描包下的类,并注入ioc容器

/**
 * 扫描包路径下的controller,并完成ioc注入
 *
 * @param contextPath:扫描包的路径
 */
private void scanPackage(String contextPath) {
    URL url = this.getClass().getClassLoader().getResource("/" + contextPath.replaceAll("\\.", "/"));

    File classDir = new File(Objects.requireNonNull(url).getFile());
    for (File file : Objects.requireNonNull(classDir.listFiles())) {
        if (file.isDirectory()) {
            scanPackage(contextPath + "." + file.getName());
            continue;
        }
        //不是文件夹,是class类
        classList.add((contextPath + "." + file.getName()).replaceAll(".class", ""));
    }
    //反射实例化,存放入ioc容器中
    for (String clsName : classList) {
        try {
            Class cls = Class.forName(clsName);
            //查询是否包含指定注解,如果不包含,则不注入
            if (!isAnnotationPresent(cls, classes))
                continue;
            if (cls.isAnnotationPresent(Controller.class)) {
                iocMap.put(lowerFirstCapse(cls.getSimpleName()), cls.newInstance());

            } else if (cls.isAnnotationPresent(Service.class)) {
                iocMap.put(lowerFirstCapse(cls.getSimpleName()), cls.newInstance());

            }
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

5.自动注入service,使用注解@Autowired

/**
 * 自动注入service
 */
private void autowired() {
    for (Map.Entry entry : iocMap.entrySet()) {
        Field[] fields = entry.getValue().getClass().getDeclaredFields();
        for (Field field : fields) {
            if (!field.isAnnotationPresent(Autowired.class)) continue;
            try {
                field.setAccessible(true);
                field.set(entry.getValue(), iocMap.get(lowerFirstCapse(field.getType().getSimpleName())));
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
}

扫描ioc实例化过后的controller,找出带有注解@Autowired的field,并且将改field进行反射注入

6.注册路径映射,拦截路径并解析到容器中

private void initHandlerMapping() {
    for (Map.Entry entry : iocMap.entrySet()) {
        Class cls = entry.getValue().getClass();
        if (!cls.isAnnotationPresent(Controller.class))
            continue;
        String baseUrl = cls.getAnnotation(RequestMapping.class).value();
        Method[] methods = cls.getMethods();
        if (null == methods)
            continue;
        if (methods.length == 0) continue;

        for (Method method : methods) {
            if (!method.isAnnotationPresent(RequestMapping.class)) continue;
            urlMap.put(baseUrl + method.getAnnotation(RequestMapping.class).value(), method);
        }
    }

}

7.doDispatch方法处理来访

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

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    try {
        doDispatch(req, resp);
    } catch (Exception e) {
        e.printStackTrace();
        resp.getWriter().write("500");
    }

}

如果该方法出现异常,则返回500的错误给到前端.如果路径容器中不包含来访路径,则返回404,下面是处理来访的核心代码

/**
 * 处理get,post反射
 *
 * @param req                      :HttpServletRequest
 * @param resp:HttpServletResponse
 */
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    String uri = req.getRequestURI();
    if (!urlMap.containsKey(uri)) {
        resp.getWriter().write("404");
        return;
    }
    resp.setContentType("text/html;charset=UTF-8");
    req.setCharacterEncoding("UTF-8");
    Map parameterMap = req.getParameterMap();
    Method method = urlMap.get(uri);
    String clsName = lowerFirstCapse(method.getDeclaringClass().getSimpleName());
    Object o = iocMap.get(clsName);
    method.setAccessible(true);
    Parameter[] parameters = method.getParameters();

    Object[] paramValues = new Object[parameters.length];

    int i = 0;
    for (Map.Entry entry : parameterMap.entrySet()) {

        if (parameters[i].getName().equals(entry.getKey())) {

            Object val = entry.getValue()[0];
            //此处只做几个简单数据类型展示,后续可以在此基础上拓展
            switch (parameters[i].getType().toString()) {
                case "class java.lang.Integer":
                    paramValues[i] = Integer.parseInt(val.toString());
                    break;
                case "class java.lang.String":
                    paramValues[i] = val;
                    break;
                default:
                    paramValues[i] = JSON.parseObject(val.toString(), parameters[i].getType());
                    break;
            }
        }
        i++;
    }
    try {
        Object result = method.invoke(o, paramValues);
        resp.getWriter().write(JSON.toJSONString(result));
    } catch (IllegalAccessException | InvocationTargetException e) {
        e.printStackTrace();
    }
}

8.测试controller

package com.gfh.mvc.framework.controller;

import com.gfh.mvc.framework.User;
import com.gfh.mvc.framework.annotation.Autowired;
import com.gfh.mvc.framework.annotation.Controller;
import com.gfh.mvc.framework.annotation.RequestMapping;
import com.gfh.mvc.framework.service.iml.TestServiceIml;


@Controller
@RequestMapping("/test")
public class TestController {
    @Autowired
    private TestServiceIml testServiceIml;

    @RequestMapping("/test")
    public User user(String name, Integer age) {
        System.out.println("请求来自test:=" + name + ";age=" + age + ";");
        testServiceIml.say("你在说什么呢");
        User user = new User();
        user.setName("gfh");
        return user;
    }

}

启动项目后,在浏览器输入http://localhost:8080/test/test?name=df

控制台打印service方法结果

到此便实现了spring的核心部分,需要注意的是,笔者使用的是jdk1.8环境,获取类的Parameter需要配置idea,如下图

代码已上传至github开源仓库

 

 

 

你可能感兴趣的:(spring,手写,核心)