mini-spring 第四期:类扫描器,控制器初始化

本贴最后更新于 1604 天前,其中的信息可能已经时移俗易

1.在 core 包中定义 ClassScanner,并定义 scanClass 方法,目的是给定一个包路径,扫描出该包下面的所有类
主要的流程是:
@1.拿到改包路径下的所有资源,遍历
@2.如果是 jar 包,就继续遍历这个 jar 包,把 jar 包的需要的 class 扫描进来,也就是以我们包名的开头的.class 文件
@3.jar 包中的 jarentryname 的格式是 cn/chenforcode/xxx.class,所以必须转换成包名,然后才可以用类加载器进行扫描。

package cn.chenforcode.core;

import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class ClassSacnner {
    public static List<Class<?>> scanClass(String packageName) throws IOException, ClassNotFoundException {
        List<Class<?>> classList = new ArrayList<>();
        //根据包名获取路径
        String path = packageName.replace(".", "/");
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        //获取到这个路径下的所有资源
        Enumeration<URL> resources = classLoader.getResources(path);
        //遍历这些资源
        while (resources.hasMoreElements()) {
            URL resource = resources.nextElement();
            //如果这个文件是一个jar包,需要单独处理
            if (resource.getProtocol().contains("jar")) {
                //拿到jar包连接
                JarURLConnection jarURLConnection = (JarURLConnection) resource.openConnection();
                //拿到jar包路径
                String jarFilePath = jarURLConnection.getJarFile().getName();
                classList.addAll(getClassesFromJar(jarFilePath, path));
            } else {
                //todo 处理不是jar包类型的资源
            }
        }
        return classList;
    }

    public static List<Class<?>> getClassesFromJar(String jarFilePath, String path) throws IOException, ClassNotFoundException {
        //todo 从jar包中获取所需要的类
        List<Class<?>> classes = new ArrayList<>();
        //获取这个jar文件
        JarFile jarFile = new JarFile(jarFilePath);
        //拿到jar文件的entry集合
        Enumeration<JarEntry> jarEntries = jarFile.entries();
        //遍历jar文件
        while (jarEntries.hasMoreElements()) {
            JarEntry jarEntry = jarEntries.nextElement();
            //拿到某个类的entryname,集体格式如cn/chenforcode/xxx.class
            String entryName = jarEntry.getName();
            //判断这个文件是否是我们需要的
            if (entryName.startsWith(path) && entryName.endsWith(".class")) {
                String classFullName = entryName.replace("/", ".")
                        .substring(0, entryName.length() - 6);
                //如果是把它加载进去,并加入class的list返回
                classes.add(Class.forName(classFullName));
            }
        }
        return classes;
    }
}

2.开始编写 handler

package cn.chenforcode.web.handler;

import java.lang.reflect.Method;

public class MappingHandler {
    private String uri;
    private Method method;
    private Class<?> controller;
    private String[] args;

    MappingHandler(String uri, Method method, Class<?> cls, String[] args) {
        this.uri = uri;
        this.method = method;
        this.cls = cls;
        this.args = args;
    }
}

3.创建 handlerManager 来管理 mappingHandler

4.创建 resolveMappingHandler 方法,对给出的类列表进行遍历

/**
     * @Author <a href="http://www.chenforcode.cn">PKUCoder</a>
     * @Date 2019/11/6 4:50 下午
     * @Param [classList]
     * @Return void
     * @Description 对给出的一个类列表遍历,解析其中的方法
     **/
    public static void resolveMappingHandler(List<Class<?>> classList) {
        for (Class<?> cls : classList) {
            if (cls.isAnnotationPresent(Controller.class)) {
                parseHandlerFromController(cls);
            }
        }
    }

5.创建 parseHandlerFromController 方法,对某个类中的方法进行解析

/**
     * @Author <a href="http://www.chenforcode.cn">PKUCoder</a>
     * @Date 2019/11/6 5:17 下午
     * @Param [cls]
     * @Return void
     * @Description 对某个类中的方法进行解析
     **/
    private static void parseHandlerFromController(Class<?> cls) {
        Method[] methods = cls.getDeclaredMethods();
        for (Method method : methods) {
            //如果没有被requestMapping注解修饰就不用解析了
            if (!method.isAnnotationPresent(RequestMapping.class)) {
                continue;
            }
            //得到mapping需要的参数
            //获取uri
            String uri = method.getDeclaredAnnotation(RequestMapping.class).value();
            //获取参数
            List<String> paramNameList = new ArrayList<>();
            //遍历参数,如果带有了requestParam注解的话,就进行解析
            for (Parameter parameter: method.getParameters()) {
                if (parameter.isAnnotationPresent(RequsetParam.class)) {
                    //得到所有的参数名称,加入一个list
                    paramNameList.add(parameter.getDeclaredAnnotation(RequsetParam.class).value());
                }
            }
            //将参数list转化成参数数组
            String[] args = paramNameList.toArray(new String[paramNameList.size()]);
            //构造一个mappingHandler,注意这个handler是方法级别的,每一个方法都对应着一个handler
            MappingHandler mappingHandler = new MappingHandler(uri, method, cls, args);
            //加入到handler的集合中
            HandlerManager.mappingHandlerList.add(mappingHandler);
        }
    }

6.接下来是要在 dispatcherservlet 中使用 handler

@Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        for (MappingHandler handler: HandlerManager.mappingHandlerList) {
            try {
                if (handler.handle(req, res)) {
                    return;
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }

7.编写 handler 的 handle 方法,即开始处理请求

public boolean handle(ServletRequest req, ServletResponse res) throws IllegalAccessException, InstantiationException, InvocationTargetException, IOException {
        String requestURI = ((HttpServletRequest)req).getRequestURI();
        //如果uri不相等,说明这个handler是不能处理的
        if (!uri.equals(requestURI)) {
            return false;
        }
        //开始处理请求
        Object[] parameters = new Object[args.length];
        //初始化参数
        for (int i = 0; i < args.length; i++) {
            parameters[i] = args[i];
        }
        Object ctl = controller.newInstance();
        //利用反射调用controller,这个response就相当于controller执行完返回的结果
        Object response = method.invoke(ctl, parameters);
        res.getWriter().println(response.toString());
        return true;
    }

8.这个时候可以捋一捋。。首先 dispatcherServlet 已经封装在了服务器中,并以一个“/”路径进行拦截,也就是说,每一个请求都会经过这个 servlet,并通过他的 service 方法。然后在这个方法里,会进行这次请求的 uri 比对,根据这次请求来的 uri,在所有的 handlermapping 中遍历,如果能够找到一个 hanlermapping 与之相对应,那么就让这个 handlermapping 进行处理,即调用 handle 方法。同时呢,在这个方法里,给 controller 实例化一个对象,然后利用 method 的 invoke 反射机制调用 controller,得到处理结果,放入 response 并返回。

9.这个时候重新打包项目,运行,已经能够响应 controller 的请求了。

10.写到这里我仍让有个疑问,mappinghandler 中保存的参数,我觉得应该仅仅是参数的名称的一个数组吧,如果仅仅把名称传入 invoke,是如何调用的呢???那个真正的参数值是什么时候传递过来的呢。。

  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    938 引用 • 1456 回帖 • 163 关注
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3165 引用 • 8206 回帖 • 1 关注
  • 代码
    459 引用 • 591 回帖 • 8 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • Sphinx

    Sphinx 是一个基于 SQL 的全文检索引擎,可以结合 MySQL、PostgreSQL 做全文搜索,它可以提供比数据库本身更专业的搜索功能,使得应用程序更容易实现专业化的全文检索。

    1 引用 • 171 关注
  • webpack

    webpack 是一个用于前端开发的模块加载器和打包工具,它能把各种资源,例如 JS、CSS(less/sass)、图片等都作为模块来使用和处理。

    41 引用 • 130 回帖 • 293 关注
  • 服务

    提供一个服务绝不仅仅是简单的把硬件和软件累加在一起,它包括了服务的可靠性、服务的标准化、以及对服务的监控、维护、技术支持等。

    41 引用 • 24 回帖
  • SVN

    SVN 是 Subversion 的简称,是一个开放源代码的版本控制系统,相较于 RCS、CVS,它采用了分支管理系统,它的设计目标就是取代 CVS。

    29 引用 • 98 回帖 • 688 关注
  • Gitea

    Gitea 是一个开源社区驱动的轻量级代码托管解决方案,后端采用 Go 编写,采用 MIT 许可证。

    4 引用 • 16 回帖 • 7 关注
  • Ngui

    Ngui 是一个 GUI 的排版显示引擎和跨平台的 GUI 应用程序开发框架,基于
    Node.js / OpenGL。目标是在此基础上开发 GUI 应用程序可拥有开发 WEB 应用般简单与速度同时兼顾 Native 应用程序的性能与体验。

    7 引用 • 9 回帖 • 339 关注
  • 小说

    小说是以刻画人物形象为中心,通过完整的故事情节和环境描写来反映社会生活的文学体裁。

    28 引用 • 108 回帖 • 1 关注
  • 分享

    有什么新发现就分享给大家吧!

    240 引用 • 1729 回帖
  • HTML

    HTML5 是 HTML 下一个的主要修订版本,现在仍处于发展阶段。广义论及 HTML5 时,实际指的是包括 HTML、CSS 和 JavaScript 在内的一套技术组合。

    103 引用 • 294 回帖
  • Bootstrap

    Bootstrap 是 Twitter 推出的一个用于前端开发的开源工具包。它由 Twitter 的设计师 Mark Otto 和 Jacob Thornton 合作开发,是一个 CSS / HTML 框架。

    18 引用 • 33 回帖 • 683 关注
  • Gzip

    gzip (GNU zip)是 GNU 自由软件的文件压缩程序。我们在 Linux 中经常会用到后缀为 .gz 的文件,它们就是 Gzip 格式的。现今已经成为互联网上使用非常普遍的一种数据压缩格式,或者说一种文件格式。

    9 引用 • 12 回帖 • 106 关注
  • Bug

    Bug 本意是指臭虫、缺陷、损坏、犯贫、窃听器、小虫等。现在人们把在程序中一些缺陷或问题统称为 bug(漏洞)。

    76 引用 • 1738 回帖 • 3 关注
  • 微信

    腾讯公司 2011 年 1 月 21 日推出的一款手机通讯软件。用户可以通过摇一摇、搜索号码、扫描二维码等添加好友和关注公众平台,同时可以将自己看到的精彩内容分享到微信朋友圈。

    129 引用 • 791 回帖 • 1 关注
  • CodeMirror
    1 引用 • 2 回帖 • 108 关注
  • ReactiveX

    ReactiveX 是一个专注于异步编程与控制可观察数据(或者事件)流的 API。它组合了观察者模式,迭代器模式和函数式编程的优秀思想。

    1 引用 • 2 回帖 • 125 关注
  • frp

    frp 是一个可用于内网穿透的高性能的反向代理应用,支持 TCP、UDP、 HTTP 和 HTTPS 协议。

    15 引用 • 7 回帖
  • PHP

    PHP(Hypertext Preprocessor)是一种开源脚本语言。语法吸收了 C 语言、 Java 和 Perl 的特点,主要适用于 Web 开发领域,据说是世界上最好的编程语言。

    164 引用 • 406 回帖 • 524 关注
  • 单点登录

    单点登录(Single Sign On)是目前比较流行的企业业务整合的解决方案之一。SSO 的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

    9 引用 • 25 回帖 • 9 关注
  • wolai

    我来 wolai:不仅仅是未来的云端笔记!

    1 引用 • 11 回帖 • 1 关注
  • BND

    BND(Baidu Netdisk Downloader)是一款图形界面的百度网盘不限速下载器,支持 Windows、Linux 和 Mac,详细介绍请看这里

    107 引用 • 1281 回帖 • 22 关注
  • JVM

    JVM(Java Virtual Machine)Java 虚拟机是一个微型操作系统,有自己的硬件构架体系,还有相应的指令系统。能够识别 Java 独特的 .class 文件(字节码),能够将这些文件中的信息读取出来,使得 Java 程序只需要生成 Java 虚拟机上的字节码后就能在不同操作系统平台上进行运行。

    180 引用 • 120 回帖 • 3 关注
  • Lute

    Lute 是一款结构化的 Markdown 引擎,支持 Go 和 JavaScript。

    25 引用 • 191 回帖 • 16 关注
  • Android

    Android 是一种以 Linux 为基础的开放源码操作系统,主要使用于便携设备。2005 年由 Google 收购注资,并拉拢多家制造商组成开放手机联盟开发改良,逐渐扩展到到平板电脑及其他领域上。

    331 引用 • 315 回帖 • 83 关注
  • OAuth

    OAuth 协议为用户资源的授权提供了一个安全的、开放而又简易的标准。与以往的授权方式不同之处是 oAuth 的授权不会使第三方触及到用户的帐号信息(如用户名与密码),即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此 oAuth 是安全的。oAuth 是 Open Authorization 的简写。

    36 引用 • 103 回帖 • 6 关注
  • Ant-Design

    Ant Design 是服务于企业级产品的设计体系,基于确定和自然的设计价值观上的模块化解决方案,让设计者和开发者专注于更好的用户体验。

    17 引用 • 23 回帖
  • 微服务

    微服务架构是一种架构模式,它提倡将单一应用划分成一组小的服务。服务之间互相协调,互相配合,为用户提供最终价值。每个服务运行在独立的进程中。服务于服务之间才用轻量级的通信机制互相沟通。每个服务都围绕着具体业务构建,能够被独立的部署。

    96 引用 • 155 回帖
  • iOS

    iOS 是由苹果公司开发的移动操作系统,最早于 2007 年 1 月 9 日的 Macworld 大会上公布这个系统,最初是设计给 iPhone 使用的,后来陆续套用到 iPod touch、iPad 以及 Apple TV 等产品上。iOS 与苹果的 Mac OS X 操作系统一样,属于类 Unix 的商业操作系统。

    84 引用 • 139 回帖