手写Spring实现1.0

总览

  1. 引入servlet-api 依赖
  2. 在web.xml中的配置servlet,将对应请求都转发到自写的DispatcherServlet类(并在init-param参数中设置配置文件参数)
  3. DispatcherServlet类继承HttpServlet接口,并实现对应的方法。(get,post,init)
    - init():完成IOC,DI,handlerMapping等的初始化
    - get/post():根据用户的请求uri,从handlerMapping中获取到对应的调用方法和参数,使用反射实现调用

DispatcherServlet

field:

 Properties contextConfig;      //配置信息
 List<String> classNames = new ArrayList<>();            // 扫描根目录下的所有的类文件名
 Map<String,Object> ioc = new HashMap<>();               // ioc容器,存储已经实例化和依赖注入的对象(控制反转、依赖注入,单例)
 Map<String,Method> handlerMapping = new HashMap<>();    // url映射表,每个url对应一个method方法

init(ServletConfig config):

  1. doLoadContextConfig()从servlet参数中获取配置文件路径 - 并将配置读取到contextConfig中
  2. doScanner()从config中获取到扫描根目录,扫描其下的所有.class文件,保存到classNames数组中
  3. doInstance()遍历扫描到的所有类,如果类存在相关的注解,则初始化bean,并注册到ioc容器
  4. doAutowired()扫描ioc容器中的所有实例的属性,如果存在@Autowired注解,则从ioc中找到对应的实例进行注入(私有属性需要开启访问权限)
  5. doInitHandlerMapping()对ioc中所有具有@Controller标签的类,读取其方法,解析出对应的请求uri,以<uri,method>的方式存放到map中

doDipatch(req,resp) <- doGet(),doPost()

  1. 获取用户请求uri
  2. 组装请求实参列表
  3. method.invoke(instance,params)反射调用

代码详解

init(ServletConfig config)

    @Override
    public void init(ServletConfig config){
        // 1. 从servlet参数中获取配置文件路径 - 并将配置读取到contextConfig中
        doLoadContextConfig(config.getInitParameter("contextConfigLocation"));

        // 2. 从config中获取到扫描根目录,扫描其下的所有.class文件,保存到classNames数组中
        doScanner(contextConfig.getProperty("scan-package"));

        // 3. 遍历扫描到的所有类,如果类存在相关的注解,则初始化bean,并注册到ioc容器
        doInstance();

        // 4. 扫描ioc容器中的所有实例的属性,如果存在@Autowired注解,则从ioc中找到对应的实例进行注入
        doAutowired();

        // 5. 对ioc中所有具有@Controller标签的类,读取其方法,解析出对应的请求uri,以<uri,method>的方式存放到map中
        doInitHandlerMapping();

        logger.info("哦豁,初始化完成了!");
    }

配置加载和读取

doLoadContextConfig(String contextConfigLocation)

    private void doLoadContextConfig(String contextConfigLocation) {
        InputStream fis = null;

        contextConfig = new Properties();
        try {
            fis = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
            contextConfig.load(fis);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

IOC

doScanner(String packageName)

    private void doScanner(String packageName) {
        URL url = this.getClass().getClassLoader().getResource("/" + packageName.replace('.','/'));
        File fileDir = new File(url.getFile());
        for(File file : fileDir.listFiles()){
            if(file.isDirectory()){
                doScanner(packageName + "." + file.getName());
                continue;
            }

            if(file.getName().endsWith(".class")){
                classNames.add(packageName + "." + file.getName().replace(".class","").trim());
            }
        }
    }

doInstance()

    private void doInstance() {
        if(classNames.isEmpty()){return;}

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

                if (clazz.isAnnotationPresent(GPController.class)) {
                    String beanName = toLowerFirstCase(clazz.getSimpleName());
                    Object instance = clazz.newInstance();
                    ioc.put(beanName, instance);
                } else if (clazz.isAnnotationPresent(GPService.class)) {
                    Object instance = clazz.newInstance();

                    // 1. 根据对象名或Autowired参数作为beanName注册到ioc容器
                    GPService service = clazz.getAnnotation(GPService.class);
                    String beanName = service.value().trim();
                    if("".equals(service.value())){
                        beanName = toLowerFirstCase(clazz.getSimpleName());
                    }

                    ioc.put(beanName,instance);

                    // 2. 将对象的所有实现接口,以接口的全类名作为beanName注册到ioc容器
                    for(Class<?> interfaceClass : clazz.getInterfaces() ) {
                        beanName = interfaceClass.getName();
                        if(ioc.containsKey(beanName)){
                            logger.info("The bean " + interfaceClass.getName() + " is already exists!");
                            continue;
                        }

                        ioc.put(beanName,instance);
                    }
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

DI

doAutowired()

    /**
     * 反射中Filed对象,Method等对象,均只与来源类有关,与来源类的实例无关联,
     * 当需要使用对应的filed对象或method对象去调用来源类的具体实例时,需要将具体的实例作为参数传递给filed对象或method对象
     */
    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();
                }

                field.setAccessible(true);

                //field 相当于@GPAutowired private IDemoService demoService;
                //entry.getValue() 相当于DemoAction的实例
                //ioc.get(beanName)相当于 ioc.get("com.gupaoedu.demo.service.IDemoService");
                try {
                    field.set(entry.getValue(),ioc.get(beanName));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

MVC

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;}

            // 获取controller上的base路径
            String baseUrl = "";
            if(clazz.isAnnotationPresent(GPRequestMapping.class)){
                GPRequestMapping requestMapping = clazz.getAnnotation(GPRequestMapping.class);
                baseUrl = requestMapping.value().trim();
            }

            // 依次扫描所有的方法,对带有GPRequestMapping注解的方法解析相对路径拼接上base路径,保存到HandlerMapping
            for(Method method : clazz.getMethods()){
                if(!method.isAnnotationPresent(GPRequestMapping.class)){continue;}

                GPRequestMapping requestMapping = method.getAnnotation(GPRequestMapping.class);
                String handlerUrl = ("/" + baseUrl + "/" + requestMapping.value()).replaceAll("/+","/");
                handlerMapping.put(handlerUrl,method);
                logger.info("handler mapping init:" + handlerUrl);
            }
        }
    }

dispatch

doDispatch(req,resp)

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        String uri = req.getRequestURI();
        String contextPath = req.getContextPath();
        uri = uri.replace(contextPath,"/").replaceAll("/+","/");

        Method method = handlerMapping.get(uri);
        if(method == null){
            resp.getWriter().write("404 Not Found!");
            return;
        }

        // 方法的形参列表
        Class<?>[] parameterTypes = method.getParameterTypes();

        // 方法的实参列表
        Object[] parameters = new Object[parameterTypes.length];

        for(int i = 0; i < parameterTypes.length; i++){
            if(parameterTypes[i] == HttpServletRequest.class){
                parameters[i] = req;
            }else if(parameterTypes[i] == HttpServletResponse.class){
                parameters[i] = resp;
            }else{
                Annotation[][] annotations = method.getParameterAnnotations();
                for(Annotation annotation : annotations[i]){
                    if(annotation instanceof GPRequestParam){
                        String parameterName = ((GPRequestParam) annotation).value();
                        parameters[i] = req.getParameter(parameterName);
                    }
                }
            }
        }

        String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
        method.invoke(ioc.get(beanName),parameters);
    }

欢迎疑问、期待评论、感谢指点 -- kiqi,愿同您为友

-- 星河有灿灿,愿与之辉

原文地址:https://www.cnblogs.com/kiqi/p/14346683.html