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

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

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 应用程序开发提供集成的框架。

    948 引用 • 1460 回帖
  • Java

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

    3197 引用 • 8215 回帖
  • 代码
    467 引用 • 586 回帖 • 9 关注

相关帖子

欢迎来到这里!

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

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

推荐标签 标签

  • SVN

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

    29 引用 • 98 回帖 • 693 关注
  • 正则表达式

    正则表达式(Regular Expression)使用单个字符串来描述、匹配一系列遵循某个句法规则的字符串。

    31 引用 • 94 回帖
  • 尊园地产

    昆明尊园房地产经纪有限公司,即:Kunming Zunyuan Property Agency Company Limited(简称“尊园地产”)于 2007 年 6 月开始筹备,2007 年 8 月 18 日正式成立,注册资本 200 万元,公司性质为股份经纪有限公司,主营业务为:代租、代售、代办产权过户、办理银行按揭、担保、抵押、评估等。

    1 引用 • 22 回帖 • 786 关注
  • Electron

    Electron 基于 Chromium 和 Node.js,让你可以使用 HTML、CSS 和 JavaScript 构建应用。它是一个由 GitHub 及众多贡献者组成的活跃社区共同维护的开源项目,兼容 Mac、Windows 和 Linux,它构建的应用可在这三个操作系统上面运行。

    15 引用 • 136 回帖
  • DNSPod

    DNSPod 建立于 2006 年 3 月份,是一款免费智能 DNS 产品。 DNSPod 可以为同时有电信、网通、教育网服务器的网站提供智能的解析,让电信用户访问电信的服务器,网通的用户访问网通的服务器,教育网的用户访问教育网的服务器,达到互联互通的效果。

    6 引用 • 26 回帖 • 532 关注
  • 书籍

    宋真宗赵恒曾经说过:“书中自有黄金屋,书中自有颜如玉。”

    78 引用 • 396 回帖
  • 生活

    生活是指人类生存过程中的各项活动的总和,范畴较广,一般指为幸福的意义而存在。生活实际上是对人生的一种诠释。生活包括人类在社会中与自己息息相关的日常活动和心理影射。

    230 引用 • 1454 回帖
  • Solidity

    Solidity 是一种智能合约高级语言,运行在 [以太坊] 虚拟机(EVM)之上。它的语法接近于 JavaScript,是一种面向对象的语言。

    3 引用 • 18 回帖 • 437 关注
  • 运维

    互联网运维工作,以服务为中心,以稳定、安全、高效为三个基本点,确保公司的互联网业务能够 7×24 小时为用户提供高质量的服务。

    151 引用 • 257 回帖
  • 外包

    有空闲时间是接外包好呢还是学习好呢?

    26 引用 • 233 回帖 • 1 关注
  • ActiveMQ

    ActiveMQ 是 Apache 旗下的一款开源消息总线系统,它完整实现了 JMS 规范,是一个企业级的消息中间件。

    19 引用 • 13 回帖 • 679 关注
  • Redis

    Redis 是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。从 2010 年 3 月 15 日起,Redis 的开发工作由 VMware 主持。从 2013 年 5 月开始,Redis 的开发由 Pivotal 赞助。

    286 引用 • 248 回帖 • 1 关注
  • Tomcat

    Tomcat 最早是由 Sun Microsystems 开发的一个 Servlet 容器,在 1999 年被捐献给 ASF(Apache Software Foundation),隶属于 Jakarta 项目,现在已经独立为一个顶级项目。Tomcat 主要实现了 JavaEE 中的 Servlet、JSP 规范,同时也提供 HTTP 服务,是市场上非常流行的 Java Web 容器。

    162 引用 • 529 回帖 • 6 关注
  • 电影

    这是一个不能说的秘密。

    122 引用 • 608 回帖
  • GitHub

    GitHub 于 2008 年上线,目前,除了 Git 代码仓库托管及基本的 Web 管理界面以外,还提供了订阅、讨论组、文本渲染、在线文件编辑器、协作图谱(报表)、代码片段分享(Gist)等功能。正因为这些功能所提供的便利,又经过长期的积累,GitHub 的用户活跃度很高,在开源世界里享有深远的声望,并形成了社交化编程文化(Social Coding)。

    210 引用 • 2040 回帖
  • Spark

    Spark 是 UC Berkeley AMP lab 所开源的类 Hadoop MapReduce 的通用并行框架。Spark 拥有 Hadoop MapReduce 所具有的优点;但不同于 MapReduce 的是 Job 中间输出结果可以保存在内存中,从而不再需要读写 HDFS,因此 Spark 能更好地适用于数据挖掘与机器学习等需要迭代的 MapReduce 的算法。

    74 引用 • 46 回帖 • 568 关注
  • 人工智能

    人工智能(Artificial Intelligence)是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门技术科学。

    163 引用 • 309 回帖
  • 脑图

    脑图又叫思维导图,是表达发散性思维的有效图形思维工具 ,它简单却又很有效,是一种实用性的思维工具。

    32 引用 • 99 回帖
  • WebSocket

    WebSocket 是 HTML5 中定义的一种新协议,它实现了浏览器与服务器之间的全双工通信(full-duplex)。

    48 引用 • 206 回帖 • 296 关注
  • 酷鸟浏览器

    安全 · 稳定 · 快速
    为跨境从业人员提供专业的跨境浏览器

    3 引用 • 59 回帖 • 45 关注
  • 深度学习

    深度学习(Deep Learning)是机器学习的分支,是一种试图使用包含复杂结构或由多重非线性变换构成的多个处理层对数据进行高层抽象的算法。

    54 引用 • 41 回帖
  • Follow
    4 引用 • 12 回帖 • 10 关注
  • GAE

    Google App Engine(GAE)是 Google 管理的数据中心中用于 WEB 应用程序的开发和托管的平台。2008 年 4 月 发布第一个测试版本。目前支持 Python、Java 和 Go 开发部署。全球已有数十万的开发者在其上开发了众多的应用。

    14 引用 • 42 回帖 • 811 关注
  • Flutter

    Flutter 是谷歌的移动 UI 框架,可以快速在 iOS 和 Android 上构建高质量的原生用户界面。 Flutter 可以与现有的代码一起工作,它正在被越来越多的开发者和组织使用,并且 Flutter 是完全免费、开源的。

    39 引用 • 92 回帖
  • JRebel

    JRebel 是一款 Java 虚拟机插件,它使得 Java 程序员能在不进行重部署的情况下,即时看到代码的改变对一个应用程序带来的影响。

    26 引用 • 78 回帖 • 679 关注
  • 架构

    我们平时所说的“架构”主要是指软件架构,这是有关软件整体结构与组件的抽象描述,用于指导软件系统各个方面的设计。另外还有“业务架构”、“网络架构”、“硬件架构”等细分领域。

    143 引用 • 442 回帖
  • 印象笔记
    3 引用 • 16 回帖