背景
读了一些SpringBoot的源码,为了加深理解,准备手写一个简易版SpringBoot框架。一期功能包括了基础的SpringBoot的功能和ASM获取Controller参数名的功能。
配置
首先是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初始化完成了,下一步该处理请求了。当请求来时,
- 从handlerMap中获取请求url对应的方法包装类。
- 从request中获取parameterMap,从方法包装中获取到该Controller方法的参数,若参数名一致,将parameterMap中的参数值赋给该Controller方法。
- 若Controller方法的参数存在request、response、session三个类型,则直接赋值。
- 反射执行该Controller方法,将方法返回值转成json格式并返给前端。
- 若获取不到方法包装类则返回404。
结语
这次只是搭了个架子,能简单的处理ajax请求并返回JSON数据,以后会慢慢丰富这个手写工程,加入MyBatis、AOP、处理循环依赖等。