背景

读了一些SpringBoot的源码,为了加深理解,准备手写一个简易版SpringBoot框架。一期功能包括了基础的SpringBoot的功能和ASM获取Controller参数名的功能。

git地址在此

配置

首先是pom.xml,只需引入3个依赖,servlet-api,tomcat-embed,fastjson。

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
</dependency>
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-jasper</artifactId>
    <version>8.5.29</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.46</version>
</dependency>

创建注解类,现在支持如下注解:ComponentScan、Controller、RequestMapping、RequestParam、Service、Autowired、PostConstruct,功能和Spring的一样。

然后创建参数和方法的包装类,用于对Controller的方法和方法中的参数进行包装,便于处理请求。

初始化

初始化包含了启动tomcat和执行DispatchServlet的init方法,流程如下:

1、启动tomcat

使用内置tomcat,读取application.properties配置文件,获取端口,项目名等配置,并配置DispatchServlet,执行DispatchServlet的初始化方法,然后启动tomcat。

2、获取需要扫描包的路径

获取启动类上面的注解ComponentScan,若有,扫描包路径为ComponentScan的value值,若没有扫描包路径为启动类所在的包路径。

3、扫描路径下的类

使用递归扫描路径下所有的class文件,把文件名放入classNameList。

4、bean实例化

循环classNameList,将带有Controller和Service注解的类实例化,并将实例化后的对象,放入instanceMap,键为类名或Service注解的value值。

5、建立依赖关系

遍历instanceMap,获取Bean实例的fields,遍历这些fields,如果带有Autowired注解,则赋值。这里有个关键点,就是需要设置field的访问权限,因为一般情况下,这个field是私有的。

field.setAccessible(true);

6.1、ASM获取参数名

因为通过反射无法获取参数名,我们可以通过给参数增加RequestParam注解,通过该注解获取参数名,然后给其赋值。但SpringBoot中Controller未增加RequestParam也能赋值,研究后发现,SpringBoot是通过ASM读取class文件的字节码来获取参数名的。

这里也通过ASM获取Controller方法中的参数,然后增加一个方法包装类,将方法的信息和参数的信息放在里面。 具体操作可查看源代码。

6.2、建立URL和Method的映射关系

获取Controller类上的RequestMapping和方法中的RequestMapping注解的value,拼装url值,将url和对应的方法放入handlerMap,key为url,value为方法的包装类。

7、执行初始化方法

遍历instanceMap,获取Bean实例的所有方法,遍历这些方法,如果有PostContruct注解,则通过反射执行该方法。

处理请求

SpringBoot初始化完成了,下一步该处理请求了。当请求来时,

  1. 从handlerMap中获取请求url对应的方法包装类。
  2. 从request中获取parameterMap,从方法包装中获取到该Controller方法的参数,若参数名一致,将parameterMap中的参数值赋给该Controller方法。
  3. 若Controller方法的参数存在request、response、session三个类型,则直接赋值。
  4. 反射执行该Controller方法,将方法返回值转成json格式并返给前端。
  5. 若获取不到方法包装类则返回404。

结语

这次只是搭了个架子,能简单的处理ajax请求并返回JSON数据,以后会慢慢丰富这个手写工程,加入MyBatis、AOP、处理循环依赖等。